@@ -655,20 +655,28 module ApplicationHelper | |||
|
655 | 655 | # identifier:version:1.0.0 |
|
656 | 656 | # identifier:source:some/file |
|
657 | 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 |
|
|
659 |
leading, esc, project_prefix, project_identifier, prefix, sep, identifier = $1, $2, $3, $4, $5, $ |
|
|
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, repo_prefix, repo_identifier, sep, identifier = $1, $2, $3, $4, $5, $10, $11, $8 || $12 || $14, $13 || $15 | |
|
660 | 660 | link = nil |
|
661 | 661 | if project_identifier |
|
662 | 662 | project = Project.visible.find_by_identifier(project_identifier) |
|
663 | 663 | end |
|
664 | 664 | if esc.nil? |
|
665 | 665 | if prefix.nil? && sep == 'r' |
|
666 | if project | |
|
667 | repository = nil | |
|
668 | if repo_identifier | |
|
669 | repository = project.repositories.detect {|repo| repo.identifier == repo_identifier} | |
|
670 | else | |
|
671 | repository = project.repository | |
|
672 | end | |
|
666 | 673 | # project.changesets.visible raises an SQL error because of a double join on repositories |
|
667 |
if |
|
|
668 | link = link_to(h("#{project_prefix}r#{identifier}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision}, | |
|
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}, | |
|
669 | 676 | :class => 'changeset', |
|
670 | 677 | :title => truncate_single_line(changeset.comments, :length => 100)) |
|
671 | 678 | end |
|
679 | end | |
|
672 | 680 | elsif sep == '#' |
|
673 | 681 | oid = identifier.to_i |
|
674 | 682 | case prefix |
@@ -731,23 +739,35 module ApplicationHelper | |||
|
731 | 739 | link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news}, |
|
732 | 740 | :class => 'news' |
|
733 | 741 | end |
|
734 | when 'commit' | |
|
735 | if project && project.repository && (changeset = Changeset.visible.find(:first, :conditions => ["repository_id = ? AND scmid LIKE ?", project.repository.id, "#{name}%"])) | |
|
736 | link = link_to h("#{project_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.identifier}, | |
|
742 | when 'commit', 'source', 'export' | |
|
743 | if project | |
|
744 | repository = nil | |
|
745 | if name =~ %r{^(([a-z0-9\-]+)\|)(.+)$} | |
|
746 | repo_prefix, repo_identifier, name = $1, $2, $3 | |
|
747 | repository = project.repositories.detect {|repo| repo.identifier == repo_identifier} | |
|
748 | else | |
|
749 | repository = project.repository | |
|
750 | end | |
|
751 | if prefix == 'commit' | |
|
752 | if repository && (changeset = Changeset.visible.find(:first, :conditions => ["repository_id = ? AND scmid LIKE ?", repository.id, "#{name}%"])) | |
|
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}, | |
|
737 | 754 | :class => 'changeset', |
|
738 | 755 | :title => truncate_single_line(h(changeset.comments), :length => 100) |
|
739 | 756 | end |
|
740 | when 'source', 'export' | |
|
741 |
if |
|
|
757 | else | |
|
758 | if repository && User.current.allowed_to?(:browse_repository, project) | |
|
742 | 759 | name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$} |
|
743 | 760 | path, rev, anchor = $1, $3, $5 |
|
744 | link = link_to h("#{project_prefix}#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project, | |
|
761 | link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:controller => 'repositories', :action => 'entry', :id => project, :repository_id => repository.identifier_param, | |
|
745 | 762 | :path => to_path_param(path), |
|
746 | 763 | :rev => rev, |
|
747 | 764 | :anchor => anchor, |
|
748 | 765 | :format => (prefix == 'export' ? 'raw' : nil)}, |
|
749 | 766 | :class => (prefix == 'export' ? 'source download' : 'source') |
|
750 | 767 | end |
|
768 | end | |
|
769 | repo_prefix = nil | |
|
770 | end | |
|
751 | 771 | when 'attachment' |
|
752 | 772 | attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil) |
|
753 | 773 | if attachments && attachment = attachments.detect {|a| a.filename == name } |
@@ -761,7 +781,7 module ApplicationHelper | |||
|
761 | 781 | end |
|
762 | 782 | end |
|
763 | 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 | 785 | end |
|
766 | 786 | end |
|
767 | 787 |
@@ -44,13 +44,9 | |||
|
44 | 44 | |
|
45 | 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 | 48 | <ul> |
|
49 | 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 | 50 | </ul> |
|
55 | 51 | |
|
56 | 52 | <p>Wiki links:</p> |
@@ -101,6 +97,18 | |||
|
101 | 97 | </ul> |
|
102 | 98 | |
|
103 | 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 | 112 | <li>Repository files: |
|
105 | 113 | <ul> |
|
106 | 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 | 117 | <li><strong>source:some/file@52#L120</strong> (link to line 120 of the file's revision 52)</li> |
|
110 | 118 | <li><strong>source:"some file@52#L120"</strong> (use double quotes when the URL contains spaces</li> |
|
111 | 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 | 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 | 122 | <li><strong>sandbox:export:some/file</strong> (force the download of the file)</li> |
|
114 | 123 | </ul></li> |
@@ -267,6 +267,7 RAW | |||
|
267 | 267 | 'version:1.0' => version_link, |
|
268 | 268 | 'version:"1.0"' => version_link, |
|
269 | 269 | # source |
|
270 | 'source:some/file' => link_to('source:some/file', source_url, :class => 'source'), | |
|
270 | 271 | 'source:/some/file' => link_to('source:/some/file', source_url, :class => 'source'), |
|
271 | 272 | 'source:/some/file.' => link_to('source:/some/file', source_url, :class => 'source') + ".", |
|
272 | 273 | 'source:/some/file.ext.' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".", |
@@ -341,6 +342,72 RAW | |||
|
341 | 342 | to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" } |
|
342 | 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 | 411 | def test_redmine_links_git_commit |
|
345 | 412 | changeset_link = link_to('abcd', |
|
346 | 413 | { |
General Comments 0
You need to be logged in to leave comments.
Login now