RedCloth librairy is now present in Redmine lib directory. git-svn-id: http://redmine.rubyforge.org/svn/trunk@699 e93f8b46-1217-0410-a6f0-8f06a7374b81tags/0.6.0
@@ -15,14 +15,6 @@ | |||
# along with this program; if not, write to the Free Software | |||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
class RedCloth | |||
# Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet. | |||
# <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a> | |||
def hard_break( text ) | |||
text.gsub!( /(.)\n(?!\n|\Z| *([#*=]+(\s|$)|[{|]))/, "\\1<br />" ) if hard_breaks | |||
end | |||
end | |||
module ApplicationHelper | |||
def current_role | |||
@@ -112,7 +104,27 @@ module ApplicationHelper | |||
# textilize text according to system settings and RedCloth availability | |||
def textilizable(text, options = {}) | |||
return "" if text.blank? | |||
# when using an image link, try to use an attachment, if possible | |||
attachments = options[:attachments] | |||
if attachments | |||
text = text.gsub(/!([<>=]*)(\S+\.(gif|jpg|jpeg|png))!/) do |m| | |||
align = $1 | |||
filename = $2 | |||
rf = Regexp.new(filename, Regexp::IGNORECASE) | |||
# search for the picture in attachments | |||
if found = attachments.detect { |att| att.filename =~ rf } | |||
image_url = url_for :controller => 'attachments', :action => 'download', :id => found.id | |||
"!#{align}#{image_url}!" | |||
else | |||
"!#{align}#{filename}!" | |||
end | |||
end | |||
end | |||
text = (Setting.text_formatting == 'textile') ? | |||
Redmine::WikiFormatting.to_html(text) : simple_format(auto_link(h(text))) | |||
# different methods for formatting wiki links | |||
case options[:wiki_links] | |||
when :local | |||
@@ -148,36 +160,22 @@ module ApplicationHelper | |||
link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page)), :class => 'wiki-page') | |||
end | |||
# turn issue ids into links | |||
# turn issue and revision ids into links | |||
# example: | |||
# #52 -> <a href="/issues/show/52">#52</a> | |||
text = text.gsub(/#(\d+)(?=\b)/) {|m| link_to "##{$1}", {:controller => 'issues', :action => 'show', :id => $1}, :class => 'issue' } | |||
# turn revision ids into links (@project needed) | |||
# example: | |||
# r52 -> <a href="/repositories/revision/6?rev=52">r52</a> (@project.id is 6) | |||
text = text.gsub(/(?=\b)r(\d+)(?=\b)/) {|m| link_to "r#{$1}", {:controller => 'repositories', :action => 'revision', :id => project.id, :rev => $1}, :class => 'changeset' } if project | |||
# when using an image link, try to use an attachment, if possible | |||
attachments = options[:attachments] | |||
if attachments | |||
text = text.gsub(/!([<>=]*)(\S+\.(gif|jpg|jpeg|png))!/) do |m| | |||
align = $1 | |||
filename = $2 | |||
rf = Regexp.new(filename, Regexp::IGNORECASE) | |||
# search for the picture in attachments | |||
if found = attachments.detect { |att| att.filename =~ rf } | |||
image_url = url_for :controller => 'attachments', :action => 'download', :id => found.id | |||
"!#{align}#{image_url}!" | |||
else | |||
"!#{align}#{filename}!" | |||
end | |||
# 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 | |||
link = nil | |||
if otype == 'r' | |||
link = link_to("r#{oid}", {:controller => 'repositories', :action => 'revision', :id => project.id, :rev => oid}, :class => 'changeset') if project | |||
else | |||
link = link_to("##{oid}", {:controller => 'issues', :action => 'show', :id => oid}, :class => 'issue') | |||
end | |||
leading + (link || "#{otype}#{oid}") | |||
end | |||
# finally textilize text | |||
@do_textilize ||= (Setting.text_formatting == 'textile') && (ActionView::Helpers::TextHelper.method_defined? "textilize") | |||
text = @do_textilize ? auto_link(RedCloth.new(text, [:hard_breaks]).to_html) : simple_format(auto_link(h(text))) | |||
text | |||
end | |||
# Same as Rails' simple_format helper without using paragraphs |
@@ -0,0 +1,79 @@ | |||
require 'redcloth' | |||
module Redmine | |||
module WikiFormatting | |||
private | |||
class TextileFormatter < RedCloth | |||
RULES = [:inline_auto_link, :inline_auto_mailto, :textile ] | |||
def initialize(*args) | |||
super | |||
self.hard_breaks=true | |||
end | |||
def to_html | |||
super(*RULES).to_s | |||
end | |||
private | |||
# Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet. | |||
# <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a> | |||
def hard_break( text ) | |||
text.gsub!( /(.)\n(?!\n|\Z| *([#*=]+(\s|$)|[{|]))/, "\\1<br />" ) if hard_breaks | |||
end | |||
AUTO_LINK_RE = %r{ | |||
( # leading text | |||
<\w+.*?>| # leading HTML tag, or | |||
[^=<>!:'"/]| # leading punctuation, or | |||
^ # beginning of line | |||
) | |||
( | |||
(?:https?://)| # protocol spec, or | |||
(?:www\.) # www.* | |||
) | |||
( | |||
[-\w]+ # subdomain or domain | |||
(?:\.[-\w]+)* # remaining subdomains or domain | |||
(?::\d+)? # port | |||
(?:/(?:(?:[~\w\+%-]|(?:[,.;:][^\s$]))+)?)* # path | |||
(?:\?[\w\+%&=.;-]+)? # query string | |||
(?:\#[\w\-]*)? # trailing anchor | |||
) | |||
([[:punct:]]|\s|<|$) # trailing text | |||
}x unless const_defined?(:AUTO_LINK_RE) | |||
# Turns all urls into clickable links (code from Rails). | |||
def inline_auto_link(text) | |||
text.gsub!(AUTO_LINK_RE) do | |||
all, a, b, c, d = $&, $1, $2, $3, $4 | |||
if a =~ /<a\s/i || a =~ /![<>=]?/ | |||
# don't replace URL's that are already linked | |||
# and URL's prefixed with ! !> !< != (textile images) | |||
all | |||
else | |||
text = b + c | |||
%(#{a}<a href="#{b=="www."?"http://www.":b}#{c}">#{text}</a>#{d}) | |||
end | |||
end | |||
end | |||
# Turns all email addresses into clickable links (code from Rails). | |||
def inline_auto_mailto(text) | |||
text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do | |||
text = $1 | |||
%{<a href="mailto:#{$1}" class="email">#{text}</a>} | |||
end | |||
end | |||
end | |||
public | |||
def self.to_html(text, options = {}) | |||
TextileFormatter.new(text).to_html | |||
end | |||
end | |||
end |
@@ -615,11 +615,11 @@ div.wiki table, div.wiki td, div.wiki th { | |||
div.wiki a { | |||
background-position: 0% 60%; | |||
background-repeat: no-repeat; | |||
padding-left: 12px; | |||
padding-left: 14px; | |||
background-image: url(../images/external.png); | |||
} | |||
div.wiki a.wiki-page, div.wiki a.issue, div.wiki a.changeset { | |||
div.wiki a.wiki-page, div.wiki a.issue, div.wiki a.changeset, div.wiki a.email { | |||
padding-left: 0; | |||
background-image: none; | |||
} |
@@ -0,0 +1,35 @@ | |||
# Re-raise errors caught by the controller. | |||
class StubController < ApplicationController | |||
def rescue_action(e) raise e end; | |||
attr_accessor :request, :url | |||
end | |||
class HelperTestCase < Test::Unit::TestCase | |||
# Add other helpers here if you need them | |||
include ActionView::Helpers::ActiveRecordHelper | |||
include ActionView::Helpers::TagHelper | |||
include ActionView::Helpers::FormTagHelper | |||
include ActionView::Helpers::FormOptionsHelper | |||
include ActionView::Helpers::FormHelper | |||
include ActionView::Helpers::UrlHelper | |||
include ActionView::Helpers::AssetTagHelper | |||
include ActionView::Helpers::PrototypeHelper | |||
def setup | |||
super | |||
@request = ActionController::TestRequest.new | |||
@controller = StubController.new | |||
@controller.request = @request | |||
# Fake url rewriter so we can test url_for | |||
@controller.url = ActionController::UrlRewriter.new @request, {} | |||
ActionView::Helpers::AssetTagHelper::reset_javascript_include_default | |||
end | |||
def test_dummy | |||
# do nothing - required by test/unit | |||
end | |||
end |
@@ -18,6 +18,7 @@ | |||
ENV["RAILS_ENV"] ||= "test" | |||
require File.expand_path(File.dirname(__FILE__) + "/../config/environment") | |||
require 'test_help' | |||
require File.expand_path(File.dirname(__FILE__) + '/helper_testcase') | |||
class Test::Unit::TestCase | |||
# Transactional fixtures accelerate your tests by wrapping each test method |
@@ -0,0 +1,66 @@ | |||
# redMine - project management software | |||
# Copyright (C) 2006-2007 Jean-Philippe Lang | |||
# | |||
# This program is free software; you can redistribute it and/or | |||
# modify it under the terms of the GNU General Public License | |||
# as published by the Free Software Foundation; either version 2 | |||
# of the License, or (at your option) any later version. | |||
# | |||
# This program is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | |||
# | |||
# You should have received a copy of the GNU General Public License | |||
# along with this program; if not, write to the Free Software | |||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
require File.dirname(__FILE__) + '/../../test_helper' | |||
class ApplicationHelperTest < HelperTestCase | |||
include ApplicationHelper | |||
fixtures :projects | |||
def setup | |||
super | |||
end | |||
def test_auto_links | |||
to_test = { | |||
'http://foo.bar' => '<a href="http://foo.bar">http://foo.bar</a>', | |||
'www.foo.bar' => '<a href="http://www.foo.bar">www.foo.bar</a>', | |||
'http://foo.bar/page?p=1&t=z&s=' => '<a href="http://foo.bar/page?p=1&t=z&s=">http://foo.bar/page?p=1&t=z&s=</a>', | |||
'http://foo.bar/page#125' => '<a href="http://foo.bar/page#125">http://foo.bar/page#125</a>' | |||
} | |||
to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) } | |||
end | |||
def test_auto_mailto | |||
assert_equal '<p><a href="mailto:test@foo.bar" class="email">test@foo.bar</a></p>', | |||
textilizable('test@foo.bar') | |||
end | |||
def test_textile_tags | |||
to_test = { | |||
# inline images | |||
'!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />', | |||
'floating !>http://foo.bar/image.jpg!' => 'floating <div style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></div>', | |||
# textile links | |||
'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar">link</a>', | |||
'"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title">link</a>' | |||
} | |||
to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) } | |||
end | |||
def test_redmine_links | |||
issue_link = link_to('#52', {:controller => 'issues', :action => 'show', :id => 52}, :class => 'issue') | |||
changeset_link = link_to('r19', {:controller => 'repositories', :action => 'revision', :id => 1, :rev => 19}, :class => 'changeset') | |||
to_test = { | |||
'#52, #52 and #52.' => "#{issue_link}, #{issue_link} and #{issue_link}.", | |||
'r19' => changeset_link | |||
} | |||
@project = Project.find(1) | |||
to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) } | |||
end | |||
end |
@@ -20,7 +20,7 @@ require File.dirname(__FILE__) + '/../test_helper' | |||
class SettingTest < Test::Unit::TestCase | |||
def test_read_default | |||
assert_equal "redMine", Setting.app_title | |||
assert_equal "Redmine", Setting.app_title | |||
assert Setting.self_registration? | |||
assert !Setting.login_required? | |||
end |