git-svn-id: http://redmine.rubyforge.org/svn/trunk@897 e93f8b46-1217-0410-a6f0-8f06a7374b81tags/0.6.1
@@ -16,6 +16,7 @@ | |||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
module ApplicationHelper | |||
include Redmine::WikiFormatting::Macros::Definitions | |||
def current_role | |||
@current_role ||= User.current.role_for_project(@project) | |||
@@ -130,15 +131,28 @@ module ApplicationHelper | |||
:preview => 'r', | |||
:quick_search => 'f', | |||
:search => '4', | |||
}.freeze | |||
}.freeze unless const_defined?(:ACCESSKEYS) | |||
def accesskey(s) | |||
ACCESSKEYS[s] | |||
end | |||
# format text according to system settings | |||
def textilizable(text, options = {}) | |||
return "" if text.blank? | |||
# Formats text according to system settings. | |||
# 2 ways to call this method: | |||
# * with a String: textilizable(text, options) | |||
# * with an object and one of its attribute: textilizable(issue, :description, options) | |||
def textilizable(*args) | |||
options = args.last.is_a?(Hash) ? args.pop : {} | |||
case args.size | |||
when 1 | |||
obj = nil | |||
text = args.shift || '' | |||
when 2 | |||
obj = args.shift | |||
text = obj.send(args.shift) | |||
else | |||
raise ArgumentError, 'invalid arguments to textilizable' | |||
end | |||
# when using an image link, try to use an attachment, if possible | |||
attachments = options[:attachments] | |||
@@ -158,7 +172,8 @@ module ApplicationHelper | |||
end | |||
text = (Setting.text_formatting == 'textile') ? | |||
Redmine::WikiFormatting.to_html(text) : simple_format(auto_link(h(text))) | |||
Redmine::WikiFormatting.to_html(text) { |macro, args| exec_macro(macro, obj, args) } : | |||
simple_format(auto_link(h(text))) | |||
# different methods for formatting wiki links | |||
case options[:wiki_links] |
@@ -64,7 +64,7 @@ end %> | |||
<% end %> | |||
<p><strong><%=l(:field_description)%></strong></p> | |||
<%= textilizable @issue.description, :attachments => @issue.attachments %> | |||
<%= textilizable @issue, :description, :attachments => @issue.attachments %> | |||
<% if @issue.attachments.any? %> | |||
<%= link_to_attachments @issue.attachments, :delete_url => (authorize_for('issues', 'destroy_attachment') ? {:controller => 'issues', :action => 'destroy_attachment', :id => @issue} : nil) %> |
@@ -1,3 +1,3 @@ | |||
<div class="wiki"> | |||
<%= textilizable content.text, :attachments => content.page.attachments %> | |||
<%= textilizable content, :text, :attachments => content.page.attachments %> | |||
</div> |
@@ -9,6 +9,6 @@ h1, h2, h3, h4 { font-family: Trebuchet MS,Georgia,"Times New Roman",serif; } | |||
</style> | |||
</head> | |||
<body> | |||
<%= textilizable @content.text, :wiki_links => :local %> | |||
<%= textilizable @content, :text, :wiki_links => :local %> | |||
</body> | |||
</html> |
@@ -20,7 +20,7 @@ h1, h2, h3, h4 { font-family: Trebuchet MS,Georgia,"Times New Roman",serif; } | |||
<% @pages.each do |page| %> | |||
<hr /> | |||
<a name="<%= page.title %>" /> | |||
<%= textilizable page.content.text, :wiki_links => :anchor %> | |||
<%= textilizable page.content ,:text, :wiki_links => :anchor %> | |||
<% end %> | |||
</body> |
@@ -1,13 +1,31 @@ | |||
# 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 'redcloth' | |||
require 'coderay' | |||
require 'pp' | |||
module Redmine | |||
module WikiFormatting | |||
private | |||
class TextileFormatter < RedCloth | |||
RULES = [:inline_auto_link, :inline_auto_mailto, :textile, :inline_toc] | |||
class TextileFormatter < RedCloth | |||
RULES = [:inline_auto_link, :inline_auto_mailto, :textile, :inline_toc, :inline_macros] | |||
def initialize(*args) | |||
super | |||
@@ -15,8 +33,9 @@ module Redmine | |||
self.no_span_caps=true | |||
end | |||
def to_html | |||
def to_html(*rules, &block) | |||
@toc = [] | |||
@macros_runner = block | |||
super(*RULES).to_s | |||
end | |||
@@ -72,6 +91,25 @@ module Redmine | |||
end | |||
end | |||
MACROS_RE = / | |||
\{\{ # opening tag | |||
([\w]+) # macro name | |||
(\(([^\}]*)\))? # optional arguments | |||
\}\} # closing tag | |||
/x unless const_defined?(:MACROS_RE) | |||
def inline_macros(text) | |||
text.gsub!(MACROS_RE) do | |||
all, macro = $&, $1.downcase | |||
args = ($3 || '').split(',').each(&:strip) | |||
begin | |||
@macros_runner.call(macro, args) | |||
rescue => e | |||
"<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>" | |||
end || all | |||
end | |||
end | |||
AUTO_LINK_RE = %r{ | |||
( # leading text | |||
<\w+.*?>| # leading HTML tag, or | |||
@@ -115,8 +153,8 @@ module Redmine | |||
public | |||
def self.to_html(text, options = {}) | |||
TextileFormatter.new(text).to_html | |||
def self.to_html(text, options = {}, &block) | |||
TextileFormatter.new(text).to_html(&block) | |||
end | |||
end | |||
end |
@@ -0,0 +1,81 @@ | |||
# 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. | |||
module Redmine | |||
module WikiFormatting | |||
module Macros | |||
module Definitions | |||
def exec_macro(name, obj, args) | |||
method_name = "macro_#{name}" | |||
send(method_name, obj, args) if respond_to?(method_name) | |||
end | |||
end | |||
@@available_macros = {} | |||
class << self | |||
# Called with a block to define additional macros. | |||
# Macro blocks accept 2 arguments: | |||
# * obj: the object that is rendered | |||
# * args: macro arguments | |||
# | |||
# Plugins can use this method to define new macros: | |||
# | |||
# Redmine::WikiFormatting::Macros.register do | |||
# desc "This is my macro" | |||
# macro :my_macro do |obj, args| | |||
# "My macro output" | |||
# end | |||
# end | |||
def register(&block) | |||
class_eval(&block) if block_given? | |||
end | |||
private | |||
# Defines a new macro with the given name and block. | |||
def macro(name, &block) | |||
name = name.to_sym if name.is_a?(String) | |||
@@available_macros[name] = @@desc || '' | |||
@@desc = nil | |||
raise "Can not create a macro without a block!" unless block_given? | |||
Definitions.send :define_method, "macro_#{name}".downcase, &block | |||
end | |||
# Sets description for the next macro to be defined | |||
def desc(txt) | |||
@@desc = txt | |||
end | |||
end | |||
# Builtin macros | |||
desc "Example macro." | |||
macro :hello_world do |obj, args| | |||
"Hello world! Object: #{obj.class.name}, " + (args.empty? ? "Called with no argument." : "Arguments: #{args.join(', ')}") | |||
end | |||
desc "Displays a list of all available macros, including description if available." | |||
macro :macro_list do | |||
out = '' | |||
@@available_macros.keys.collect(&:to_s).sort.each do |macro| | |||
out << content_tag('dt', content_tag('code', macro)) | |||
out << content_tag('dd', simple_format(@@available_macros[macro.to_sym])) | |||
end | |||
content_tag('dl', out) | |||
end | |||
end | |||
end | |||
end |
@@ -70,4 +70,9 @@ class ApplicationHelperTest < HelperTestCase | |||
@project = Project.find(1) | |||
to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) } | |||
end | |||
def test_macro_hello_world | |||
text = "{{hello_world}}" | |||
assert textilizable(text).match(/Hello world!/) | |||
end | |||
end |