Extract headings and TOC parsing from the textile formatter.
Fixes #2038 and #3707 and will allow to support TOC with other text formatters. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4376 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
parent
666c54e86c
commit
024ff96ee2
|
@ -1,5 +1,5 @@
|
||||||
# redMine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2007 Jean-Philippe Lang
|
# Copyright (C) 2006-2010 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
@ -451,7 +451,7 @@ module ApplicationHelper
|
||||||
text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) { |macro, args| exec_macro(macro, obj, args) }
|
text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) { |macro, args| exec_macro(macro, obj, args) }
|
||||||
|
|
||||||
parse_non_pre_blocks(text) do |text|
|
parse_non_pre_blocks(text) do |text|
|
||||||
[:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links].each do |method_name|
|
[:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_headings].each do |method_name|
|
||||||
send method_name, text, project, obj, attr, only_path, options
|
send method_name, text, project, obj, attr, only_path, options
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -673,6 +673,38 @@ module ApplicationHelper
|
||||||
leading + (link || "#{prefix}#{sep}#{identifier}")
|
leading + (link || "#{prefix}#{sep}#{identifier}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
|
||||||
|
HEADING_RE = /<h(1|2|3)( [^>]+)?>(.+?)<\/h(1|2|3)>/i unless const_defined?(:HEADING_RE)
|
||||||
|
|
||||||
|
# Headings and TOC
|
||||||
|
# Adds ids and links to headings and renders the TOC if needed unless options[:headings] is set to false
|
||||||
|
def parse_headings(text, project, obj, attr, only_path, options)
|
||||||
|
headings = []
|
||||||
|
text.gsub!(HEADING_RE) do
|
||||||
|
level, attrs, content = $1, $2, $3
|
||||||
|
item = strip_tags(content).strip
|
||||||
|
anchor = item.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
|
||||||
|
headings << [level, anchor, item]
|
||||||
|
"<h#{level} #{attrs} id=\"#{anchor}\">#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">¶</a></h#{level}>"
|
||||||
|
end unless options[:headings] == false
|
||||||
|
|
||||||
|
text.gsub!(TOC_RE) do
|
||||||
|
if headings.empty?
|
||||||
|
''
|
||||||
|
else
|
||||||
|
div_class = 'toc'
|
||||||
|
div_class << ' right' if $1 == '>'
|
||||||
|
div_class << ' left' if $1 == '<'
|
||||||
|
out = "<ul class=\"#{div_class}\">"
|
||||||
|
headings.each do |level, anchor, item|
|
||||||
|
out << "<li class=\"heading#{level}\"><a href=\"##{anchor}\">#{item}</a></li>\n"
|
||||||
|
end
|
||||||
|
out << '</ul>'
|
||||||
|
out
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Same as Rails' simple_format helper without using paragraphs
|
# Same as Rails' simple_format helper without using paragraphs
|
||||||
def simple_format_without_paragraph(text)
|
def simple_format_without_paragraph(text)
|
||||||
|
|
|
@ -112,7 +112,7 @@ module Redmine
|
||||||
@included_wiki_pages ||= []
|
@included_wiki_pages ||= []
|
||||||
raise 'Circular inclusion detected' if @included_wiki_pages.include?(page.title)
|
raise 'Circular inclusion detected' if @included_wiki_pages.include?(page.title)
|
||||||
@included_wiki_pages << page.title
|
@included_wiki_pages << page.title
|
||||||
out = textilizable(page.content, :text, :attachments => page.attachments)
|
out = textilizable(page.content, :text, :attachments => page.attachments, :headings => false)
|
||||||
@included_wiki_pages.pop
|
@included_wiki_pages.pop
|
||||||
out
|
out
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2008 Jean-Philippe Lang
|
# Copyright (C) 2006-2010 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
@ -24,7 +24,7 @@ module Redmine
|
||||||
include ActionView::Helpers::TagHelper
|
include ActionView::Helpers::TagHelper
|
||||||
|
|
||||||
# auto_link rule after textile rules so that it doesn't break !image_url! tags
|
# 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]
|
RULES = [:textile, :block_markdown_rule, :inline_auto_link, :inline_auto_mailto]
|
||||||
|
|
||||||
def initialize(*args)
|
def initialize(*args)
|
||||||
super
|
super
|
||||||
|
@ -61,51 +61,6 @@ module Redmine
|
||||||
end
|
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(/(\[\[([^\]\|]*)(\|([^\]]*))?\]\])/) { $4 || $2 }
|
|
||||||
# sanitizes titles from links
|
|
||||||
# see redcloth3.rb, same as "#{pre}#{text}#{post}"
|
|
||||||
toc_item.gsub!(LINK_RE) { [$2, $4, $9].join }
|
|
||||||
# sanitizes image links from titles
|
|
||||||
toc_item.gsub!(IMAGE_RE) { [$5].join }
|
|
||||||
# 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\">¶</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
|
|
||||||
|
|
||||||
AUTO_LINK_RE = %r{
|
AUTO_LINK_RE = %r{
|
||||||
( # leading text
|
( # leading text
|
||||||
<\w+.*?>| # leading HTML tag, or
|
<\w+.*?>| # leading HTML tag, or
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2009 Jean-Philippe Lang
|
# Copyright (C) 2006-2010 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
@ -422,6 +422,8 @@ Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
|
||||||
h2. Subtitle with [[Wiki|another Wiki]] link
|
h2. Subtitle with [[Wiki|another Wiki]] link
|
||||||
|
|
||||||
h2. Subtitle with %{color:red}red text%
|
h2. Subtitle with %{color:red}red text%
|
||||||
|
|
||||||
|
h2. Subtitle with *some* _modifiers_
|
||||||
|
|
||||||
h1. Another title
|
h1. Another title
|
||||||
|
|
||||||
|
@ -436,11 +438,31 @@ RAW
|
||||||
'<li class="heading2"><a href="#Subtitle-with-a-Wiki-link">Subtitle with a Wiki link</a></li>' +
|
'<li class="heading2"><a href="#Subtitle-with-a-Wiki-link">Subtitle with a Wiki link</a></li>' +
|
||||||
'<li class="heading2"><a href="#Subtitle-with-another-Wiki-link">Subtitle with another Wiki link</a></li>' +
|
'<li class="heading2"><a href="#Subtitle-with-another-Wiki-link">Subtitle with another Wiki link</a></li>' +
|
||||||
'<li class="heading2"><a href="#Subtitle-with-red-text">Subtitle with red text</a></li>' +
|
'<li class="heading2"><a href="#Subtitle-with-red-text">Subtitle with red text</a></li>' +
|
||||||
|
'<li class="heading2"><a href="#Subtitle-with-some-modifiers">Subtitle with some modifiers</a></li>' +
|
||||||
'<li class="heading1"><a href="#Another-title">Another title</a></li>' +
|
'<li class="heading1"><a href="#Another-title">Another title</a></li>' +
|
||||||
'<li class="heading2"><a href="#An-Internet-link-inside-subtitle">An Internet link inside subtitle</a></li>' +
|
'<li class="heading2"><a href="#An-Internet-link-inside-subtitle">An Internet link inside subtitle</a></li>' +
|
||||||
'<li class="heading2"><a href="#Project-Name">Project Name</a></li>' +
|
'<li class="heading2"><a href="#Project-Name">Project Name</a></li>' +
|
||||||
'</ul>'
|
'</ul>'
|
||||||
|
|
||||||
|
@project = Project.find(1)
|
||||||
|
assert textilizable(raw).gsub("\n", "").include?(expected)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_table_of_content_should_contain_included_page_headings
|
||||||
|
raw = <<-RAW
|
||||||
|
{{toc}}
|
||||||
|
|
||||||
|
h1. Included
|
||||||
|
|
||||||
|
{{include(Child_1)}}
|
||||||
|
RAW
|
||||||
|
|
||||||
|
expected = '<ul class="toc">' +
|
||||||
|
'<li class="heading1"><a href="#Included">Included</a></li>' +
|
||||||
|
'<li class="heading1"><a href="#Child-page-1">Child page 1</a></li>' +
|
||||||
|
'</ul>'
|
||||||
|
|
||||||
|
@project = Project.find(1)
|
||||||
assert textilizable(raw).gsub("\n", "").include?(expected)
|
assert textilizable(raw).gsub("\n", "").include?(expected)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,9 @@ require File.dirname(__FILE__) + '/../../../../test_helper'
|
||||||
class Redmine::WikiFormatting::MacrosTest < HelperTestCase
|
class Redmine::WikiFormatting::MacrosTest < HelperTestCase
|
||||||
include ApplicationHelper
|
include ApplicationHelper
|
||||||
include ActionView::Helpers::TextHelper
|
include ActionView::Helpers::TextHelper
|
||||||
|
include ActionView::Helpers::SanitizeHelper
|
||||||
|
extend ActionView::Helpers::SanitizeHelper::ClassMethods
|
||||||
|
|
||||||
fixtures :projects, :roles, :enabled_modules, :users,
|
fixtures :projects, :roles, :enabled_modules, :users,
|
||||||
:repositories, :changesets,
|
:repositories, :changesets,
|
||||||
:trackers, :issue_statuses, :issues,
|
:trackers, :issue_statuses, :issues,
|
||||||
|
|
Loading…
Reference in New Issue