From 702b521b453ac5abfe9ae6336ff129cbd83f1eca 2008-01-15 18:12:12 From: Jean-Philippe Lang Date: 2008-01-15 18:12:12 Subject: [PATCH] Redmine links can be used to link to documents, versions and attachments. For now, attachments of the current object can be referenced only (if you're on an issue, it's possible reference attachments of this issue only). git-svn-id: http://redmine.rubyforge.org/svn/trunk@1064 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index e3ac249..b34b5b5 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 -> #52 - # r52 -> r52 (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 ae70db7..904701c 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 [ //, '>' ], # 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(?:[(]([^)]*)[)])/, '\1' ], # 3+ uppercase acronym [ /(^|[^"][>\s])([A-Z][A-Z0-9 ]+[A-Z0-9])([^\2\3', :no_span_caps ], # 3+ uppercase caps diff --git a/test/fixtures/wiki_contents.yml b/test/fixtures/wiki_contents.yml index a230b9c..6937dbd 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 ca9d6f5..ee26029 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 ff7b4a1..dd1c55c 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 2af6c55..33509cf 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 "

#{result}

", textilizable(text) } + end + + def test_wiki_links + to_test = { + '[[CookBook documentation]]' => 'CookBook documentation', + '[[Another page|Page]]' => 'Page', + # page that doesn't exist + '[[Unknown page]]' => 'Unknown page', + '[[Unknown page|404]]' => '404', + # link to another project wiki + '[[onlinestore:]]' => 'onlinestore', + '[[onlinestore:|Wiki]]' => 'Wiki', + '[[onlinestore:Start page]]' => 'Start page', + '[[onlinestore:Start page|Text]]' => 'Text', + '[[onlinestore:Unknown page]]' => 'Unknown page', + # escaping + '![[Another page|Page]]' => '[[Another page|Page]]', } @project = Project.find(1) to_test.each { |text, result| assert_equal "

#{result}

", textilizable(text) }