summaryrefslogtreecommitdiffstats
path: root/lib/redmine
diff options
context:
space:
mode:
authorJean-Philippe Lang <jp_lang@yahoo.fr>2008-10-27 11:08:29 +0000
committerJean-Philippe Lang <jp_lang@yahoo.fr>2008-10-27 11:08:29 +0000
commita3b9a5aa5fe344cf33a47ccbf1d26a95fbe4e255 (patch)
tree6b19387c81e2d865a57cf3357340deffdbc5e858 /lib/redmine
parent9b624fd1d6f66bbe60d738522d9fc5fd547ef3af (diff)
downloadredmine-a3b9a5aa5fe344cf33a47ccbf1d26a95fbe4e255.tar.gz
redmine-a3b9a5aa5fe344cf33a47ccbf1d26a95fbe4e255.zip
Makes wiki text formatter pluggable.
Original patch #2025 by Yuki Sonoda slightly edited. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@1955 e93f8b46-1217-0410-a6f0-8f06a7374b81
Diffstat (limited to 'lib/redmine')
-rw-r--r--lib/redmine/plugin.rb10
-rw-r--r--lib/redmine/wiki_formatting.rb193
-rw-r--r--lib/redmine/wiki_formatting/textile/formatter.rb183
-rw-r--r--lib/redmine/wiki_formatting/textile/helper.rb43
4 files changed, 277 insertions, 152 deletions
diff --git a/lib/redmine/plugin.rb b/lib/redmine/plugin.rb
index 8123003cc..87a6ce4d3 100644
--- a/lib/redmine/plugin.rb
+++ b/lib/redmine/plugin.rb
@@ -148,6 +148,16 @@ module Redmine #:nodoc:
def activity_provider(*args)
Redmine::Activity.register(*args)
end
+
+ # Registers a wiki formatter.
+ #
+ # Parameters:
+ # * +name+ - human-readable name
+ # * +formatter+ - formatter class, which should have an instance method +to_html+
+ # * +helper+ - helper module, which will be included by wiki pages
+ def wiki_format_provider(name, formatter, helper)
+ Redmine::WikiFormatting.register(name, formatter, helper)
+ end
# Returns +true+ if the plugin can be configured.
def configurable?
diff --git a/lib/redmine/wiki_formatting.rb b/lib/redmine/wiki_formatting.rb
index 7dffff492..1525dbc19 100644
--- a/lib/redmine/wiki_formatting.rb
+++ b/lib/redmine/wiki_formatting.rb
@@ -1,5 +1,5 @@
-# redMine - project management software
-# Copyright (C) 2006-2007 Jean-Philippe Lang
+# Redmine - project management software
+# Copyright (C) 2006-2008 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
@@ -15,176 +15,65 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-require 'redcloth3'
-require 'coderay'
-
module Redmine
module WikiFormatting
-
- private
-
- class TextileFormatter < RedCloth3
-
- # auto_link rule after textile rules so that it doesn't break !image_url! tags
- RULES = [:textile, :block_markdown_rule, :inline_auto_link, :inline_auto_mailto, :inline_toc, :inline_macros]
+ @@formatters = {}
+
+ class << self
+ def map
+ yield self
+ end
- def initialize(*args)
- super
- self.hard_breaks=true
- self.no_span_caps=true
+ def register(name, formatter, helper)
+ raise ArgumentError, "format name '#{name}' is already taken" if @@formatters[name]
+ @@formatters[name.to_sym] = {:formatter => formatter, :helper => helper}
end
- def to_html(*rules, &block)
- @toc = []
- @macros_runner = block
- super(*RULES).to_s
+ def formatter_for(name)
+ entry = @@formatters[name.to_sym]
+ (entry && entry[:formatter]) || Redmine::WikiFormatting::NullFormatter::Formatter
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 />\n" ) if hard_breaks
+
+ def helper_for(name)
+ entry = @@formatters[name.to_sym]
+ (entry && entry[:helper]) || Redmine::WikiFormatting::NullFormatter::Helper
end
- # Patch to add code highlighting support to RedCloth
- def smooth_offtags( text )
- unless @pre_list.empty?
- ## replace <pre> content
- text.gsub!(/<redpre#(\d+)>/) do
- content = @pre_list[$1.to_i]
- if content.match(/<code\s+class="(\w+)">\s?(.+)/m)
- content = "<code class=\"#{$1} CodeRay\">" +
- CodeRay.scan($2, $1.downcase).html(:escape => false, :line_numbers => :inline)
- end
- content
- end
- end
+ def format_names
+ @@formatters.keys.map
end
- # Patch to add 'table of content' support to RedCloth
- def textile_p_withtoc(tag, atts, cite, content)
- # removes wiki links from the item
- toc_item = content.gsub(/(\[\[|\]\])/, '')
- # removes styles
- # eg. %{color:red}Triggers% => Triggers
- toc_item.gsub! %r[%\{[^\}]*\}([^%]+)%], '\\1'
+ def to_html(format, text, options = {}, &block)
+ formatter_for(format).new(text).to_html(&block)
+ end
+ end
+
+ # Default formatter module
+ module NullFormatter
+ class Formatter
+ include ActionView::Helpers::TagHelper
+ include ActionView::Helpers::TextHelper
- # replaces non word caracters by dashes
- anchor = toc_item.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
-
- unless anchor.blank?
- if tag =~ /^h(\d)$/
- @toc << [$1.to_i, anchor, toc_item]
- end
- atts << " id=\"#{anchor}\""
- content = content + "<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a>"
+ def initialize(text)
+ @text = text
end
- textile_p(tag, atts, cite, content)
- end
-
- alias :textile_h1 :textile_p_withtoc
- alias :textile_h2 :textile_p_withtoc
- alias :textile_h3 :textile_p_withtoc
-
- def inline_toc(text)
- text.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i) do
- div_class = 'toc'
- div_class << ' right' if $1 == '>'
- div_class << ' left' if $1 == '<'
- out = "<ul class=\"#{div_class}\">"
- @toc.each do |heading|
- level, anchor, toc_item = heading
- out << "<li class=\"heading#{level}\"><a href=\"##{anchor}\">#{toc_item}</a></li>\n"
- end
- out << '</ul>'
- out
+
+ def to_html(*args)
+ simple_format(auto_link(CGI::escapeHTML(@text)))
end
end
- MACROS_RE = /
- (!)? # escaping
- (
- \{\{ # opening tag
- ([\w]+) # macro name
- (\(([^\}]*)\))? # optional arguments
- \}\} # closing tag
- )
- /x unless const_defined?(:MACROS_RE)
-
- def inline_macros(text)
- text.gsub!(MACROS_RE) do
- esc, all, macro = $1, $2, $3.downcase
- args = ($5 || '').split(',').each(&:strip)
- if esc.nil?
- begin
- @macros_runner.call(macro, args)
- rescue => e
- "<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
- end || all
- else
- all
- end
+ module Helper
+ def wikitoolbar_for(field_id)
end
- end
- AUTO_LINK_RE = %r{
- ( # leading text
- <\w+.*?>| # leading HTML tag, or
- [^=<>!:'"/]| # leading punctuation, or
- ^ # beginning of line
- )
- (
- (?:https?://)| # protocol spec, or
- (?:ftp://)|
- (?:www\.) # www.*
- )
- (
- (\S+?) # url
- (\/)? # slash
- )
- ([^\w\=\/;\(\)]*?) # post
- (?=<|\s|$)
- }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, leading, proto, url, post = $&, $1, $2, $3, $6
- if leading =~ /<a\s/i || leading =~ /![<>=]?/
- # don't replace URL's that are already linked
- # and URL's prefixed with ! !> !< != (textile images)
- all
- else
- # Idea below : an URL with unbalanced parethesis and
- # ending by ')' is put into external parenthesis
- if ( url[-1]==?) and ((url.count("(") - url.count(")")) < 0 ) )
- url=url[0..-2] # discard closing parenth from url
- post = ")"+post # add closing parenth to post
- end
- %(#{leading}<a class="external" href="#{proto=="www."?"http://www.":proto}#{url}">#{proto + url}</a>#{post})
- end
+ def heads_for_wiki_formatter
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
- mail = $1
- if text.match(/<a\b[^>]*>(.*)(#{Regexp.escape(mail)})(.*)<\/a>/)
- mail
- else
- %{<a href="mailto:#{mail}" class="email">#{mail}</a>}
- end
+
+ def initial_page_content(page)
+ page.pretty_title.to_s
end
end
end
-
- public
-
- def self.to_html(text, options = {}, &block)
- TextileFormatter.new(text).to_html(&block)
- end
end
end
diff --git a/lib/redmine/wiki_formatting/textile/formatter.rb b/lib/redmine/wiki_formatting/textile/formatter.rb
new file mode 100644
index 000000000..c936803d5
--- /dev/null
+++ b/lib/redmine/wiki_formatting/textile/formatter.rb
@@ -0,0 +1,183 @@
+# Redmine - project management software
+# Copyright (C) 2006-2008 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 'redcloth3'
+require 'coderay'
+
+module Redmine
+ module WikiFormatting
+ module Textile
+ class Formatter < RedCloth3
+
+ # auto_link rule after textile rules so that it doesn't break !image_url! tags
+ RULES = [:textile, :block_markdown_rule, :inline_auto_link, :inline_auto_mailto, :inline_toc, :inline_macros]
+
+ def initialize(*args)
+ super
+ self.hard_breaks=true
+ self.no_span_caps=true
+ end
+
+ def to_html(*rules, &block)
+ @toc = []
+ @macros_runner = block
+ 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 />\n" ) if hard_breaks
+ end
+
+ # Patch to add code highlighting support to RedCloth
+ def smooth_offtags( text )
+ unless @pre_list.empty?
+ ## replace <pre> content
+ text.gsub!(/<redpre#(\d+)>/) do
+ content = @pre_list[$1.to_i]
+ if content.match(/<code\s+class="(\w+)">\s?(.+)/m)
+ content = "<code class=\"#{$1} CodeRay\">" +
+ CodeRay.scan($2, $1.downcase).html(:escape => false, :line_numbers => :inline)
+ end
+ content
+ end
+ end
+ end
+
+ # Patch to add 'table of content' support to RedCloth
+ def textile_p_withtoc(tag, atts, cite, content)
+ # removes wiki links from the item
+ toc_item = content.gsub(/(\[\[|\]\])/, '')
+ # removes styles
+ # eg. %{color:red}Triggers% => Triggers
+ toc_item.gsub! %r[%\{[^\}]*\}([^%]+)%], '\\1'
+
+ # replaces non word caracters by dashes
+ anchor = toc_item.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
+
+ unless anchor.blank?
+ if tag =~ /^h(\d)$/
+ @toc << [$1.to_i, anchor, toc_item]
+ end
+ atts << " id=\"#{anchor}\""
+ content = content + "<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a>"
+ end
+ textile_p(tag, atts, cite, content)
+ end
+
+ alias :textile_h1 :textile_p_withtoc
+ alias :textile_h2 :textile_p_withtoc
+ alias :textile_h3 :textile_p_withtoc
+
+ def inline_toc(text)
+ text.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i) do
+ div_class = 'toc'
+ div_class << ' right' if $1 == '>'
+ div_class << ' left' if $1 == '<'
+ out = "<ul class=\"#{div_class}\">"
+ @toc.each do |heading|
+ level, anchor, toc_item = heading
+ out << "<li class=\"heading#{level}\"><a href=\"##{anchor}\">#{toc_item}</a></li>\n"
+ end
+ out << '</ul>'
+ out
+ end
+ end
+
+ MACROS_RE = /
+ (!)? # escaping
+ (
+ \{\{ # opening tag
+ ([\w]+) # macro name
+ (\(([^\}]*)\))? # optional arguments
+ \}\} # closing tag
+ )
+ /x unless const_defined?(:MACROS_RE)
+
+ def inline_macros(text)
+ text.gsub!(MACROS_RE) do
+ esc, all, macro = $1, $2, $3.downcase
+ args = ($5 || '').split(',').each(&:strip)
+ if esc.nil?
+ begin
+ @macros_runner.call(macro, args)
+ rescue => e
+ "<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
+ end || all
+ else
+ all
+ end
+ end
+ end
+
+ AUTO_LINK_RE = %r{
+ ( # leading text
+ <\w+.*?>| # leading HTML tag, or
+ [^=<>!:'"/]| # leading punctuation, or
+ ^ # beginning of line
+ )
+ (
+ (?:https?://)| # protocol spec, or
+ (?:ftp://)|
+ (?:www\.) # www.*
+ )
+ (
+ (\S+?) # url
+ (\/)? # slash
+ )
+ ([^\w\=\/;\(\)]*?) # post
+ (?=<|\s|$)
+ }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, leading, proto, url, post = $&, $1, $2, $3, $6
+ if leading =~ /<a\s/i || leading =~ /![<>=]?/
+ # don't replace URL's that are already linked
+ # and URL's prefixed with ! !> !< != (textile images)
+ all
+ else
+ # Idea below : an URL with unbalanced parethesis and
+ # ending by ')' is put into external parenthesis
+ if ( url[-1]==?) and ((url.count("(") - url.count(")")) < 0 ) )
+ url=url[0..-2] # discard closing parenth from url
+ post = ")"+post # add closing parenth to post
+ end
+ %(#{leading}<a class="external" href="#{proto=="www."?"http://www.":proto}#{url}">#{proto + url}</a>#{post})
+ 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
+ mail = $1
+ if text.match(/<a\b[^>]*>(.*)(#{Regexp.escape(mail)})(.*)<\/a>/)
+ mail
+ else
+ %{<a href="mailto:#{mail}" class="email">#{mail}</a>}
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/redmine/wiki_formatting/textile/helper.rb b/lib/redmine/wiki_formatting/textile/helper.rb
new file mode 100644
index 000000000..af021f990
--- /dev/null
+++ b/lib/redmine/wiki_formatting/textile/helper.rb
@@ -0,0 +1,43 @@
+# Redmine - project management software
+# Copyright (C) 2006-2008 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 Textile
+ module Helper
+ def wikitoolbar_for(field_id)
+ help_link = l(:setting_text_formatting) + ': ' +
+ link_to(l(:label_help), compute_public_path('wiki_syntax', 'help', 'html'),
+ :onclick => "window.open(\"#{ compute_public_path('wiki_syntax', 'help', 'html') }\", \"\", \"resizable=yes, location=no, width=300, height=640, menubar=no, status=no, scrollbars=yes\"); return false;")
+
+ javascript_include_tag('jstoolbar/jstoolbar') +
+ javascript_include_tag('jstoolbar/textile') +
+ javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language}") +
+ javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.setHelpLink('#{help_link}'); toolbar.draw();")
+ end
+
+ def initial_page_content(page)
+ "h1. #{@page.pretty_title}"
+ end
+
+ def heads_for_wiki_formatter
+ stylesheet_link_tag 'jstoolbar'
+ end
+ end
+ end
+ end
+end