diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index e3ac2498fd77991a90d3bafadc2615fdfced7845..b34b5b502242b3aec3937da898706defe5cc8b77 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -220,8 +220,9 @@ module ApplicationHelper project = options[:project] || @project - # turn wiki links into html links - # example: + # Wiki links + # + # Examples: # [[mypage]] # [[mypage|mytext]] # wiki links can refer other project wikis, using project name or identifier: @@ -229,47 +230,94 @@ module ApplicationHelper # [[project:|mytext]] # [[project:mypage]] # [[project:mypage|mytext]] - text = text.gsub(/\[\[([^\]\|]+)(\|([^\]\|]+))?\]\]/) do |m| + text = text.gsub(/(!)?(\[\[([^\]\|]+)(\|([^\]\|]+))?\]\])/) do |m| link_project = project - page = $1 - title = $3 - if page =~ /^([^\:]+)\:(.*)$/ - link_project = Project.find_by_name($1) || Project.find_by_identifier($1) - page = title || $2 - title = $1 if page.blank? - end - - if link_project && link_project.wiki - # check if page exists - wiki_page = link_project.wiki.find_page(page) - link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page)), - :class => ('wiki-page' + (wiki_page ? '' : ' new'))) + esc, all, page, title = $1, $2, $3, $5 + if esc.nil? + if page =~ /^([^\:]+)\:(.*)$/ + link_project = Project.find_by_name($1) || Project.find_by_identifier($1) + page = $2 + title ||= $1 if page.blank? + end + + if link_project && link_project.wiki + # check if page exists + wiki_page = link_project.wiki.find_page(page) + link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page)), + :class => ('wiki-page' + (wiki_page ? '' : ' new'))) + else + # project or wiki doesn't exist + title || page + end else - # project or wiki doesn't exist - title || page + all end end - # turn issue and revision ids into links - # example: - # #52 -> <a href="/issues/show/52">#52</a> - # r52 -> <a href="/repositories/revision/6?rev=52">r52</a> (project.id is 6) - text = text.gsub(%r{([\s\(,-^])(#|r)(\d+)(?=[[:punct:]]|\s|<|$)}) do |m| - leading, otype, oid = $1, $2, $3 + # Redmine links + # + # Examples: + # Issues: + # #52 -> Link to issue #52 + # Changesets: + # r52 -> Link to revision 52 + # Documents: + # document#17 -> Link to document with id 17 + # document:Greetings -> Link to the document with title "Greetings" + # document:"Some document" -> Link to the document with title "Some document" + # Versions: + # version#3 -> Link to version with id 3 + # version:1.0.0 -> Link to version named "1.0.0" + # version:"1.0 beta 2" -> Link to version named "1.0 beta 2" + # Attachments: + # attachment:file.zip -> Link to the attachment of the current object named file.zip + text = text.gsub(%r{([\s\(,-^])(!)?(attachment|document|version)?((#|r)(\d+)|(:)([^"][^\s<>]+|"[^"]+"))(?=[[:punct:]]|\s|<|$)}) do |m| + leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8 link = nil - if otype == 'r' - if project && (changeset = project.changesets.find_by_revision(oid)) - link = link_to("r#{oid}", {:controller => 'repositories', :action => 'revision', :id => project.id, :rev => oid}, :class => 'changeset', - :title => truncate(changeset.comments, 100)) - end - else - if issue = Issue.find_by_id(oid.to_i, :include => [:project, :status], :conditions => Project.visible_by(User.current)) - link = link_to("##{oid}", {:controller => 'issues', :action => 'show', :id => oid}, :class => 'issue', - :title => "#{truncate(issue.subject, 100)} (#{issue.status.name})") - link = content_tag('del', link) if issue.closed? + if esc.nil? + if prefix.nil? && sep == 'r' + if project && (changeset = project.changesets.find_by_revision(oid)) + link = link_to("r#{oid}", {:controller => 'repositories', :action => 'revision', :id => project.id, :rev => oid}, :class => 'changeset', + :title => truncate(changeset.comments, 100)) + end + elsif sep == '#' + oid = oid.to_i + case prefix + when nil + if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current)) + link = link_to("##{oid}", {:controller => 'issues', :action => 'show', :id => oid}, :class => 'issue', + :title => "#{truncate(issue.subject, 100)} (#{issue.status.name})") + link = content_tag('del', link) if issue.closed? + end + when 'document' + if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current)) + link = link_to h(document.title), {:controller => 'documents', :action => 'show', :id => document}, :class => 'document' + end + when 'version' + if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current)) + link = link_to h(version.name), {:controller => 'versions', :action => 'show', :id => version}, :class => 'version' + end + end + elsif sep == ':' + # removes the double quotes if any + name = oid.gsub(%r{^"(.*)"$}, "\\1") + case prefix + when 'document' + if project && document = project.documents.find_by_title(name) + link = link_to h(document.title), {:controller => 'documents', :action => 'show', :id => document}, :class => 'document' + end + when 'version' + if project && version = project.versions.find_by_name(name) + link = link_to h(version.name), {:controller => 'versions', :action => 'show', :id => version}, :class => 'version' + end + when 'attachment' + if attachments && attachment = attachments.detect {|a| a.filename == name } + link = link_to h(attachment.filename), {:controller => 'attachments', :action => 'download', :id => attachment}, :class => 'attachment' + end + end end end - leading + (link || "#{otype}#{oid}") + leading + (link || "#{prefix}#{sep}#{oid}") end text diff --git a/lib/redcloth.rb b/lib/redcloth.rb index ae70db747868e1b915fa7fb3becf8babd17a9d2b..904701c9e31c7980faf9466db0dec65930b50477 100644 --- a/lib/redcloth.rb +++ b/lib/redcloth.rb @@ -395,15 +395,15 @@ class RedCloth < String # Elements to handle GLYPHS = [ # [ /([^\s\[{(>])?\'([dmst]\b|ll\b|ve\b|\s|:|$)/, '\1’\2' ], # single closing - [ /([^\s\[{(>#{PUNCT_Q}][#{PUNCT_Q}]*)\'/, '\1’' ], # single closing - [ /\'(?=[#{PUNCT_Q}]*(s\b|[\s#{PUNCT_NOQ}]))/, '’' ], # single closing - [ /\'/, '‘' ], # single opening + # [ /([^\s\[{(>#{PUNCT_Q}][#{PUNCT_Q}]*)\'/, '\1’' ], # single closing + # [ /\'(?=[#{PUNCT_Q}]*(s\b|[\s#{PUNCT_NOQ}]))/, '’' ], # single closing + # [ /\'/, '‘' ], # single opening [ /</, '<' ], # less-than [ />/, '>' ], # greater-than # [ /([^\s\[{(])?"(\s|:|$)/, '\1”\2' ], # double closing - [ /([^\s\[{(>#{PUNCT_Q}][#{PUNCT_Q}]*)"/, '\1”' ], # double closing - [ /"(?=[#{PUNCT_Q}]*[\s#{PUNCT_NOQ}])/, '”' ], # double closing - [ /"/, '“' ], # double opening + # [ /([^\s\[{(>#{PUNCT_Q}][#{PUNCT_Q}]*)"/, '\1”' ], # double closing + # [ /"(?=[#{PUNCT_Q}]*[\s#{PUNCT_NOQ}])/, '”' ], # double closing + # [ /"/, '“' ], # double opening [ /\b( )?\.{3}/, '\1…' ], # ellipsis [ /\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])/, '<acronym title="\2">\1</acronym>' ], # 3+ uppercase acronym [ /(^|[^"][>\s])([A-Z][A-Z0-9 ]+[A-Z0-9])([^<A-Za-z0-9]|$)/, '\1<span class="caps">\2</span>\3', :no_span_caps ], # 3+ uppercase caps diff --git a/test/fixtures/wiki_contents.yml b/test/fixtures/wiki_contents.yml index a230b9c081adba7cb8bb106ce2cc6563cfacc8f5..6937dbd14814630166424de19ea2bc9e0636e381 100644 --- a/test/fixtures/wiki_contents.yml +++ b/test/fixtures/wiki_contents.yml @@ -21,4 +21,14 @@ wiki_contents_002: version: 1 author_id: 1 comments: - \ No newline at end of file +wiki_contents_003: + text: |- + h1. Start page + + E-commerce web site start page + updated_on: 2007-03-08 00:18:07 +01:00 + page_id: 3 + id: 3 + version: 1 + author_id: 1 + comments: diff --git a/test/fixtures/wiki_pages.yml b/test/fixtures/wiki_pages.yml index ca9d6f5dc403c387f2cc5d9baee0e36eb13a36eb..ee260291d8f42c5f7537ddc3f456894be3386e92 100644 --- a/test/fixtures/wiki_pages.yml +++ b/test/fixtures/wiki_pages.yml @@ -9,4 +9,9 @@ wiki_pages_002: title: Another_page id: 2 wiki_id: 1 +wiki_pages_003: + created_on: 2007-03-08 00:18:07 +01:00 + title: Start_page + id: 3 + wiki_id: 2 \ No newline at end of file diff --git a/test/fixtures/wikis.yml b/test/fixtures/wikis.yml index ff7b4a1ae99e29bf25c3bbcb2dce5ffeb8e29b6d..dd1c55ceaba5c54ddbd7982f49f20e11765144a7 100644 --- a/test/fixtures/wikis.yml +++ b/test/fixtures/wikis.yml @@ -4,3 +4,9 @@ wikis_001: start_page: CookBook documentation project_id: 1 id: 1 +wikis_002: + status: 1 + start_page: Start page + project_id: 2 + id: 2 + \ No newline at end of file diff --git a/test/unit/helpers/application_helper_test.rb b/test/unit/helpers/application_helper_test.rb index 2af6c55994ff86b5529e7d743b73e0246bc22d5c..33509cfc02e5233ed09e8d83f98b728998ec3f50 100644 --- a/test/unit/helpers/application_helper_test.rb +++ b/test/unit/helpers/application_helper_test.rb @@ -20,7 +20,7 @@ require File.dirname(__FILE__) + '/../../test_helper' class ApplicationHelperTest < HelperTestCase include ApplicationHelper include ActionView::Helpers::TextHelper - fixtures :projects, :repositories, :changesets, :trackers, :issue_statuses, :issues + fixtures :projects, :repositories, :changesets, :trackers, :issue_statuses, :issues, :documents, :versions, :wikis, :wiki_pages, :wiki_contents def setup super @@ -66,12 +66,52 @@ class ApplicationHelperTest < HelperTestCase def test_redmine_links issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3}, :class => 'issue', :title => 'Error 281 when updating a recipe (New)') + changeset_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 1, :rev => 1}, :class => 'changeset', :title => 'My very first commit') + document_link = link_to('Test document', {:controller => 'documents', :action => 'show', :id => 1}, + :class => 'document') + + version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2}, + :class => 'version') + to_test = { '#3, #3 and #3.' => "#{issue_link}, #{issue_link} and #{issue_link}.", - 'r1' => changeset_link + 'r1' => changeset_link, + 'document#1' => document_link, + 'document:"Test document"' => document_link, + 'version#2' => version_link, + 'version:1.0' => version_link, + 'version:"1.0"' => version_link, + # escaping + '!#3.' => '#3.', + '!r1' => 'r1', + '!document#1' => 'document#1', + '!document:"Test document"' => 'document:"Test document"', + '!version#2' => 'version#2', + '!version:1.0' => 'version:1.0', + '!version:"1.0"' => 'version:"1.0"', + } + @project = Project.find(1) + to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) } + end + + def test_wiki_links + to_test = { + '[[CookBook documentation]]' => '<a href="/wiki/ecookbook/CookBook_documentation" class="wiki-page">CookBook documentation</a>', + '[[Another page|Page]]' => '<a href="/wiki/ecookbook/Another_page" class="wiki-page">Page</a>', + # page that doesn't exist + '[[Unknown page]]' => '<a href="/wiki/ecookbook/Unknown_page" class="wiki-page new">Unknown page</a>', + '[[Unknown page|404]]' => '<a href="/wiki/ecookbook/Unknown_page" class="wiki-page new">404</a>', + # link to another project wiki + '[[onlinestore:]]' => '<a href="/wiki/onlinestore/" class="wiki-page">onlinestore</a>', + '[[onlinestore:|Wiki]]' => '<a href="/wiki/onlinestore/" class="wiki-page">Wiki</a>', + '[[onlinestore:Start page]]' => '<a href="/wiki/onlinestore/Start_page" class="wiki-page">Start page</a>', + '[[onlinestore:Start page|Text]]' => '<a href="/wiki/onlinestore/Start_page" class="wiki-page">Text</a>', + '[[onlinestore:Unknown page]]' => '<a href="/wiki/onlinestore/Unknown_page" class="wiki-page new">Unknown page</a>', + # escaping + '![[Another page|Page]]' => '[[Another page|Page]]', } @project = Project.find(1) to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }