@@ -655,19 +655,27 module ApplicationHelper | |||||
655 | # identifier:version:1.0.0 |
|
655 | # identifier:version:1.0.0 | |
656 | # identifier:source:some/file |
|
656 | # identifier:source:some/file | |
657 | def parse_redmine_links(text, project, obj, attr, only_path, options) |
|
657 | def parse_redmine_links(text, project, obj, attr, only_path, options) | |
658 |
text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-]+):)?(attachment|document|version|forum|news|commit|source|export |
|
658 | 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| | |
659 |
leading, esc, project_prefix, project_identifier, prefix, sep, identifier = $1, $2, $3, $4, $5, $ |
|
659 | 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 | |
660 | link = nil |
|
660 | link = nil | |
661 | if project_identifier |
|
661 | if project_identifier | |
662 | project = Project.visible.find_by_identifier(project_identifier) |
|
662 | project = Project.visible.find_by_identifier(project_identifier) | |
663 | end |
|
663 | end | |
664 | if esc.nil? |
|
664 | if esc.nil? | |
665 | if prefix.nil? && sep == 'r' |
|
665 | if prefix.nil? && sep == 'r' | |
666 | # project.changesets.visible raises an SQL error because of a double join on repositories |
|
666 | if project | |
667 | if project && project.repository && (changeset = Changeset.visible.find_by_repository_id_and_revision(project.repository.id, identifier)) |
|
667 | repository = nil | |
668 | link = link_to(h("#{project_prefix}r#{identifier}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision}, |
|
668 | if repo_identifier | |
669 | :class => 'changeset', |
|
669 | repository = project.repositories.detect {|repo| repo.identifier == repo_identifier} | |
670 | :title => truncate_single_line(changeset.comments, :length => 100)) |
|
670 | else | |
|
671 | repository = project.repository | |||
|
672 | end | |||
|
673 | # project.changesets.visible raises an SQL error because of a double join on repositories | |||
|
674 | if repository && (changeset = Changeset.visible.find_by_repository_id_and_revision(repository.id, identifier)) | |||
|
675 | 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}, | |||
|
676 | :class => 'changeset', | |||
|
677 | :title => truncate_single_line(changeset.comments, :length => 100)) | |||
|
678 | end | |||
671 | end |
|
679 | end | |
672 | elsif sep == '#' |
|
680 | elsif sep == '#' | |
673 | oid = identifier.to_i |
|
681 | oid = identifier.to_i | |
@@ -731,22 +739,34 module ApplicationHelper | |||||
731 | link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news}, |
|
739 | link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news}, | |
732 | :class => 'news' |
|
740 | :class => 'news' | |
733 | end |
|
741 | end | |
734 | when 'commit' |
|
742 | when 'commit', 'source', 'export' | |
735 | if project && project.repository && (changeset = Changeset.visible.find(:first, :conditions => ["repository_id = ? AND scmid LIKE ?", project.repository.id, "#{name}%"])) |
|
743 | if project | |
736 | link = link_to h("#{project_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.identifier}, |
|
744 | repository = nil | |
737 | :class => 'changeset', |
|
745 | if name =~ %r{^(([a-z0-9\-]+)\|)(.+)$} | |
738 | :title => truncate_single_line(h(changeset.comments), :length => 100) |
|
746 | repo_prefix, repo_identifier, name = $1, $2, $3 | |
739 | end |
|
747 | repository = project.repositories.detect {|repo| repo.identifier == repo_identifier} | |
740 | when 'source', 'export' |
|
748 | else | |
741 | if project && project.repository && User.current.allowed_to?(:browse_repository, project) |
|
749 | repository = project.repository | |
742 | name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$} |
|
750 | end | |
743 | path, rev, anchor = $1, $3, $5 |
|
751 | if prefix == 'commit' | |
744 | link = link_to h("#{project_prefix}#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project, |
|
752 | if repository && (changeset = Changeset.visible.find(:first, :conditions => ["repository_id = ? AND scmid LIKE ?", repository.id, "#{name}%"])) | |
745 | :path => to_path_param(path), |
|
753 | 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}, | |
746 |
|
|
754 | :class => 'changeset', | |
747 | :anchor => anchor, |
|
755 | :title => truncate_single_line(h(changeset.comments), :length => 100) | |
748 | :format => (prefix == 'export' ? 'raw' : nil)}, |
|
756 | end | |
749 | :class => (prefix == 'export' ? 'source download' : 'source') |
|
757 | else | |
|
758 | if repository && User.current.allowed_to?(:browse_repository, project) | |||
|
759 | name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$} | |||
|
760 | path, rev, anchor = $1, $3, $5 | |||
|
761 | link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:controller => 'repositories', :action => 'entry', :id => project, :repository_id => repository.identifier_param, | |||
|
762 | :path => to_path_param(path), | |||
|
763 | :rev => rev, | |||
|
764 | :anchor => anchor, | |||
|
765 | :format => (prefix == 'export' ? 'raw' : nil)}, | |||
|
766 | :class => (prefix == 'export' ? 'source download' : 'source') | |||
|
767 | end | |||
|
768 | end | |||
|
769 | repo_prefix = nil | |||
750 | end |
|
770 | end | |
751 | when 'attachment' |
|
771 | when 'attachment' | |
752 | attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil) |
|
772 | attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil) | |
@@ -761,7 +781,7 module ApplicationHelper | |||||
761 | end |
|
781 | end | |
762 | end |
|
782 | end | |
763 | end |
|
783 | end | |
764 | (leading + (link || "#{project_prefix}#{prefix}#{sep}#{identifier}")).html_safe |
|
784 | (leading + (link || "#{project_prefix}#{prefix}#{repo_prefix}#{sep}#{identifier}")).html_safe | |
765 | end |
|
785 | end | |
766 | end |
|
786 | end | |
767 |
|
787 |
@@ -44,13 +44,9 | |||||
44 |
|
44 | |||
45 | <h3><a name="3" class="wiki-page"></a>Redmine links</h3> |
|
45 | <h3><a name="3" class="wiki-page"></a>Redmine links</h3> | |
46 |
|
46 | |||
47 |
<p>Redmine allows hyperlinking between issues, changesets |
|
47 | <p>Redmine allows hyperlinking between resources (issues, changesets, wiki pages...) from anywhere wiki formatting is used.</p> | |
48 | <ul> |
|
48 | <ul> | |
49 | <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> |
|
49 | <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> | |
50 | <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> |
|
|||
51 | <li>Link to a changeset with a non-numeric hash: <strong>commit:c6f4d0fd</strong> (displays <a href="#" class="changeset">c6f4d0fd</a>).</li> |
|
|||
52 | <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> |
|
|||
53 | <li>Link to a changeset with a non-numeric hash: <strong>sandbox:c6f4d0fd</strong> (displays <a href="#" class="changeset">sandbox:c6f4d0fd</a>).</li> |
|
|||
54 | </ul> |
|
50 | </ul> | |
55 |
|
51 | |||
56 | <p>Wiki links:</p> |
|
52 | <p>Wiki links:</p> | |
@@ -101,6 +97,18 | |||||
101 | </ul> |
|
97 | </ul> | |
102 |
|
98 | |||
103 | <ul> |
|
99 | <ul> | |
|
100 | <li>Changesets: | |||
|
101 | <ul> | |||
|
102 | <li><strong>r758</strong> (link to a changeset)</li> | |||
|
103 | <li><strong>commit:c6f4d0fd</strong> (link to a changeset with a non-numeric hash)</li> | |||
|
104 | <li><strong>svn1|r758</strong> (link to a changeset of a specific repository, for projects with multiple repositories)</li> | |||
|
105 | <li><strong>commit:hg|c6f4d0fd</strong> (link to a changeset with a non-numeric hash of a specific repository)</li> | |||
|
106 | <li><strong>sandbox:r758</strong> (link to a changeset of another project)</li> | |||
|
107 | <li><strong>sandbox:commit:c6f4d0fd</strong> (link to a changeset with a non-numeric hash of another project)</li> | |||
|
108 | </ul></li> | |||
|
109 | </ul> | |||
|
110 | ||||
|
111 | <ul> | |||
104 | <li>Repository files: |
|
112 | <li>Repository files: | |
105 | <ul> |
|
113 | <ul> | |
106 | <li><strong>source:some/file</strong> (link to the file located at /some/file in the project's repository)</li> |
|
114 | <li><strong>source:some/file</strong> (link to the file located at /some/file in the project's repository)</li> | |
@@ -109,6 +117,7 | |||||
109 | <li><strong>source:some/file@52#L120</strong> (link to line 120 of the file's revision 52)</li> |
|
117 | <li><strong>source:some/file@52#L120</strong> (link to line 120 of the file's revision 52)</li> | |
110 | <li><strong>source:"some file@52#L120"</strong> (use double quotes when the URL contains spaces</li> |
|
118 | <li><strong>source:"some file@52#L120"</strong> (use double quotes when the URL contains spaces</li> | |
111 | <li><strong>export:some/file</strong> (force the download of the file)</li> |
|
119 | <li><strong>export:some/file</strong> (force the download of the file)</li> | |
|
120 | <li><strong>source:svn1|some/file</strong> (link to a file of a specific repository, for projects with multiple repositories)</li> | |||
112 | <li><strong>sandbox:source:some/file</strong> (link to the file located at /some/file in the repository of the project "sandbox")</li> |
|
121 | <li><strong>sandbox:source:some/file</strong> (link to the file located at /some/file in the repository of the project "sandbox")</li> | |
113 | <li><strong>sandbox:export:some/file</strong> (force the download of the file)</li> |
|
122 | <li><strong>sandbox:export:some/file</strong> (force the download of the file)</li> | |
114 | </ul></li> |
|
123 | </ul></li> |
@@ -267,6 +267,7 RAW | |||||
267 | 'version:1.0' => version_link, |
|
267 | 'version:1.0' => version_link, | |
268 | 'version:"1.0"' => version_link, |
|
268 | 'version:"1.0"' => version_link, | |
269 | # source |
|
269 | # source | |
|
270 | 'source:some/file' => link_to('source:some/file', source_url, :class => 'source'), | |||
270 | 'source:/some/file' => link_to('source:/some/file', source_url, :class => 'source'), |
|
271 | 'source:/some/file' => link_to('source:/some/file', source_url, :class => 'source'), | |
271 | 'source:/some/file.' => link_to('source:/some/file', source_url, :class => 'source') + ".", |
|
272 | 'source:/some/file.' => link_to('source:/some/file', source_url, :class => 'source') + ".", | |
272 | 'source:/some/file.ext.' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".", |
|
273 | 'source:/some/file.ext.' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".", | |
@@ -341,6 +342,72 RAW | |||||
341 | to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" } |
|
342 | to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" } | |
342 | end |
|
343 | end | |
343 |
|
344 | |||
|
345 | def test_multiple_repositories_redmine_links | |||
|
346 | svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn1', :url => 'file:///foo/hg') | |||
|
347 | Changeset.create!(:repository => svn, :committed_on => Time.now, :revision => '123') | |||
|
348 | hg = Repository::Mercurial.create!(:project_id => 1, :identifier => 'hg1', :url => '/foo/hg') | |||
|
349 | Changeset.create!(:repository => hg, :committed_on => Time.now, :revision => '123', :scmid => 'abcd') | |||
|
350 | ||||
|
351 | changeset_link = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2}, | |||
|
352 | :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3') | |||
|
353 | svn_changeset_link = link_to('svn1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn1', :rev => 123}, | |||
|
354 | :class => 'changeset', :title => '') | |||
|
355 | hg_changeset_link = link_to('hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'}, | |||
|
356 | :class => 'changeset', :title => '') | |||
|
357 | ||||
|
358 | source_link = link_to('source:some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}, :class => 'source') | |||
|
359 | hg_source_link = link_to('source:hg1|some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 'hg1', :path => ['some', 'file']}, :class => 'source') | |||
|
360 | ||||
|
361 | to_test = { | |||
|
362 | 'r2' => changeset_link, | |||
|
363 | 'svn1|r123' => svn_changeset_link, | |||
|
364 | 'invalid|r123' => 'invalid|r123', | |||
|
365 | 'commit:hg1|abcd' => hg_changeset_link, | |||
|
366 | 'commit:invalid|abcd' => 'commit:invalid|abcd', | |||
|
367 | # source | |||
|
368 | 'source:some/file' => source_link, | |||
|
369 | 'source:hg1|some/file' => hg_source_link, | |||
|
370 | 'source:invalid|some/file' => 'source:invalid|some/file', | |||
|
371 | } | |||
|
372 | ||||
|
373 | @project = Project.find(1) | |||
|
374 | to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" } | |||
|
375 | end | |||
|
376 | ||||
|
377 | def test_cross_project_multiple_repositories_redmine_links | |||
|
378 | svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn1', :url => 'file:///foo/hg') | |||
|
379 | Changeset.create!(:repository => svn, :committed_on => Time.now, :revision => '123') | |||
|
380 | hg = Repository::Mercurial.create!(:project_id => 1, :identifier => 'hg1', :url => '/foo/hg') | |||
|
381 | Changeset.create!(:repository => hg, :committed_on => Time.now, :revision => '123', :scmid => 'abcd') | |||
|
382 | ||||
|
383 | changeset_link = link_to('ecookbook:r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2}, | |||
|
384 | :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3') | |||
|
385 | svn_changeset_link = link_to('ecookbook:svn1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn1', :rev => 123}, | |||
|
386 | :class => 'changeset', :title => '') | |||
|
387 | hg_changeset_link = link_to('ecookbook:hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'}, | |||
|
388 | :class => 'changeset', :title => '') | |||
|
389 | ||||
|
390 | source_link = link_to('ecookbook:source:some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}, :class => 'source') | |||
|
391 | hg_source_link = link_to('ecookbook:source:hg1|some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 'hg1', :path => ['some', 'file']}, :class => 'source') | |||
|
392 | ||||
|
393 | to_test = { | |||
|
394 | 'ecookbook:r2' => changeset_link, | |||
|
395 | 'ecookbook:svn1|r123' => svn_changeset_link, | |||
|
396 | 'ecookbook:invalid|r123' => 'ecookbook:invalid|r123', | |||
|
397 | 'ecookbook:commit:hg1|abcd' => hg_changeset_link, | |||
|
398 | 'ecookbook:commit:invalid|abcd' => 'ecookbook:commit:invalid|abcd', | |||
|
399 | 'invalid:commit:invalid|abcd' => 'invalid:commit:invalid|abcd', | |||
|
400 | # source | |||
|
401 | 'ecookbook:source:some/file' => source_link, | |||
|
402 | 'ecookbook:source:hg1|some/file' => hg_source_link, | |||
|
403 | 'ecookbook:source:invalid|some/file' => 'ecookbook:source:invalid|some/file', | |||
|
404 | 'invalid:source:invalid|some/file' => 'invalid:source:invalid|some/file', | |||
|
405 | } | |||
|
406 | ||||
|
407 | @project = Project.find(3) | |||
|
408 | to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" } | |||
|
409 | end | |||
|
410 | ||||
344 | def test_redmine_links_git_commit |
|
411 | def test_redmine_links_git_commit | |
345 | changeset_link = link_to('abcd', |
|
412 | changeset_link = link_to('abcd', | |
346 | { |
|
413 | { |
General Comments 0
You need to be logged in to leave comments.
Login now