Feature #4179 » 4179_link_to_user_in_wiki_syntax.patch
| app/helpers/application_helper.rb | ||
|---|---|---|
| 224 | 224 |
image_tag( |
| 225 | 225 |
thumbnail_path(attachment), |
| 226 | 226 |
:srcset => "#{thumbnail_path(attachment, :size => Setting.thumbnails_size.to_i * 2)} 2x",
|
| 227 |
:width => Setting.thumbnails_size
|
|
| 227 |
:width => Setting.thumbnails_size |
|
| 228 | 228 |
), |
| 229 | 229 |
named_attachment_path( |
| 230 | 230 |
attachment, |
| ... | ... | |
| 807 | 807 |
# Projects: |
| 808 | 808 |
# project:someproject -> Link to project named "someproject" |
| 809 | 809 |
# project#3 -> Link to project with id 3 |
| 810 |
# Users: |
|
| 811 |
# user:jsmith -> Link to user with login jsmith |
|
| 812 |
# @jsmith -> Link to user with login jsmith |
|
| 810 | 813 |
# |
| 811 | 814 |
# Links can refer other objects from other projects, using project identifier: |
| 812 | 815 |
# identifier:r52 |
| ... | ... | |
| 823 | 826 |
prefix = $~[:prefix] |
| 824 | 827 |
repo_prefix = $~[:repo_prefix] |
| 825 | 828 |
repo_identifier = $~[:repo_identifier] |
| 826 |
sep = $~[:sep1] || $~[:sep2] || $~[:sep3] |
|
| 827 |
identifier = $~[:identifier1] || $~[:identifier2] |
|
| 829 |
sep = $~[:sep1] || $~[:sep2] || $~[:sep3] || $~[:sep4]
|
|
| 830 |
identifier = $~[:identifier1] || $~[:identifier2] || $~[:identifier3]
|
|
| 828 | 831 |
comment_suffix = $~[:comment_suffix] |
| 829 | 832 |
comment_id = $~[:comment_id] |
| 830 | 833 | |
| ... | ... | |
| 896 | 899 |
end |
| 897 | 900 |
end |
| 898 | 901 |
elsif sep == ':' |
| 899 |
# removes the double quotes if any |
|
| 900 |
name = identifier.gsub(%r{^"(.*)"$}, "\\1")
|
|
| 901 |
name = CGI.unescapeHTML(name) |
|
| 902 |
name = remove_double_quotes(identifier) |
|
| 902 | 903 |
case prefix |
| 903 | 904 |
when 'document' |
| 904 | 905 |
if project && document = project.documents.visible.find_by_title(name) |
| ... | ... | |
| 954 | 955 |
if p = Project.visible.where("identifier = :s OR LOWER(name) = :s", :s => name.downcase).first
|
| 955 | 956 |
link = link_to_project(p, {:only_path => only_path}, :class => 'project')
|
| 956 | 957 |
end |
| 958 |
when 'user' |
|
| 959 |
u = User.visible.where(:login => name, :type => 'User').first |
|
| 960 |
link = link_to_user(u) if u |
|
| 957 | 961 |
end |
| 962 |
elsif "@" |
|
| 963 |
name = remove_double_quotes(identifier) |
|
| 964 |
u = User.visible.where(:login => name, :type => 'User').first |
|
| 965 |
link = link_to_user(u) if u |
|
| 958 | 966 |
end |
| 959 | 967 |
end |
| 960 | 968 |
(leading + (link || "#{project_prefix}#{prefix}#{repo_prefix}#{sep}#{identifier}#{comment_suffix}"))
|
| ... | ... | |
| 968 | 976 |
(?<leading>[\s\(,\-\[\>]|^) |
| 969 | 977 |
(?<esc>!)? |
| 970 | 978 |
(?<project_prefix>(?<project_identifier>[a-z0-9\-_]+):)? |
| 971 |
(?<prefix>attachment|document|version|forum|news|message|project|commit|source|export)? |
|
| 979 |
(?<prefix>attachment|document|version|forum|news|message|project|commit|source|export|user)?
|
|
| 972 | 980 |
( |
| 973 | 981 |
( |
| 974 | 982 |
(?<sep1>\#)| |
| ... | ... | |
| 984 | 992 |
-(?<comment_id>\d+) |
| 985 | 993 |
)? |
| 986 | 994 |
)| |
| 995 |
( |
|
| 987 | 996 |
(?<sep3>:) |
| 988 | 997 |
(?<identifier2>[^"\s<>][^\s<>]*?|"[^"]+?") |
| 998 |
)| |
|
| 999 |
( |
|
| 1000 |
(?<sep4>@) |
|
| 1001 |
(?<identifier3>[a-z0-9_\-@\.]*) |
|
| 1002 |
) |
|
| 989 | 1003 |
) |
| 990 | 1004 |
(?= |
| 991 | 1005 |
(?=[[:punct:]][^A-Za-z0-9_/])| |
| ... | ... | |
| 1462 | 1476 |
extend helper |
| 1463 | 1477 |
return self |
| 1464 | 1478 |
end |
| 1479 | ||
| 1480 |
# remove double quotes if any |
|
| 1481 |
def remove_double_quotes(identifier) |
|
| 1482 |
name = identifier.gsub(%r{^"(.*)"$}, "\\1")
|
|
| 1483 |
return CGI.unescapeHTML(name) |
|
| 1484 |
end |
|
| 1465 | 1485 |
end |
| lib/redmine/wiki_formatting.rb | ||
|---|---|---|
| 37 | 37 |
args : |
| 38 | 38 |
%w(Formatter Helper HtmlParser).map {|m| "Redmine::WikiFormatting::#{name.classify}::#{m}".constantize rescue nil}
|
| 39 | 39 | |
| 40 |
raise "A formatter class is required" if formatter.nil?
|
|
| 40 |
raise "A formatter class is required" if formatter.nil? |
|
| 41 | 41 | |
| 42 | 42 |
@@formatters[name] = {
|
| 43 | 43 |
:formatter => formatter, |
| ... | ... | |
| 153 | 153 | |
| 154 | 154 |
# Destructively replaces email addresses into clickable links |
| 155 | 155 |
def auto_mailto!(text) |
| 156 |
text.gsub!(/([\w\.!#\$%\-+.\/]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do |
|
| 156 |
text.gsub!(/((?<!@)\b[\w\.!#\$%\-+.\/]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
|
|
| 157 | 157 |
mail = $1 |
| 158 | 158 |
if text.match(/<a\b[^>]*>(.*)(#{Regexp.escape(mail)})(.*)<\/a>/)
|
| 159 | 159 |
|
| ... | ... | |
| 161 | 161 |
%(<a class="email" href="mailto:#{ERB::Util.html_escape mail}">#{ERB::Util.html_escape mail}</a>).html_safe
|
| 162 | 162 |
end |
| 163 | 163 |
end |
| 164 |
end
|
|
| 164 |
end |
|
| 165 | 165 |
end |
| 166 | 166 | |
| 167 | 167 |
# Default formatter module |
| lib/redmine/wiki_formatting/markdown/formatter.rb | ||
|---|---|---|
| 66 | 66 |
html.gsub!(/(\w):"(.+?)"/) do |
| 67 | 67 |
"#{$1}:\"#{$2}\""
|
| 68 | 68 |
end |
| 69 |
# restore user links with @ in login name eg. [@jsmith@somenet.foo] |
|
| 70 |
html.gsub!(%r{[@\A]<a href="mailto:(.*?)">(.*?)</a>}) do
|
|
| 71 |
"@#{$2}"
|
|
| 72 |
end |
|
| 69 | 73 |
html |
| 70 | 74 |
end |
| 71 | 75 | |
| test/unit/helpers/application_helper_test.rb | ||
|---|---|---|
| 383 | 383 |
# invalid expressions |
| 384 | 384 |
'source:' => 'source:', |
| 385 | 385 |
# url hash |
| 386 |
"http://foo.bar/FAQ#3" => '<a class="external" href="http://foo.bar/FAQ#3">http://foo.bar/FAQ#3</a>', |
|
| 386 |
"http://foo.bar/FAQ#3" => '<a class="external" href="http://foo.bar/FAQ#3">http://foo.bar/FAQ#3</a>', |
|
| 387 |
# user |
|
| 388 |
'user:jsmith' => link_to_user(User.find_by_id(2)), |
|
| 389 |
'@jsmith' => link_to_user(User.find_by_id(2)), |
|
| 390 |
# invalid user |
|
| 391 |
'user:foobar' => 'user:foobar', |
|
| 387 | 392 |
} |
| 388 | 393 |
@project = Project.find(1) |
| 389 | 394 |
to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
|
| 390 | 395 |
end |
| 391 | 396 | |
| 397 |
def test_user_links_with_email_as_login_name_should_not_be_parsed |
|
| 398 |
u = User.generate!(:login => 'jsmith@somenet.foo') |
|
| 399 |
raw = "@jsmith@somenet.foo should not be parsed in jsmith@somenet.foo" |
|
| 400 | ||
| 401 |
assert_match %r{<p><a class="user active".*>#{u.name}</a> should not be parsed in <a class="email" href="mailto:jsmith@somenet.foo">jsmith@somenet.foo</a></p>},
|
|
| 402 |
textilizable(raw, :project => Project.find(1)) |
|
| 403 |
end |
|
| 404 | ||
| 392 | 405 |
def test_should_not_parse_redmine_links_inside_link |
| 393 | 406 |
raw = "r1 should not be parsed in http://example.com/url-r1/" |
| 394 | 407 |
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>},
|