Extends child_pages macro to display child pages based on page parameter (#1975).

It can also be called from anywhere now (not only from wiki pages).

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2053 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
Jean-Philippe Lang 2008-11-22 11:44:07 +00:00
parent af7e586b06
commit 06266c8fec
8 changed files with 196 additions and 55 deletions

View File

@ -119,6 +119,22 @@ module ApplicationHelper
end end
end end
def render_page_hierarchy(pages, node=nil)
content = ''
if pages[node]
content << "<ul class=\"pages-hierarchy\">\n"
pages[node].each do |page|
content << "<li>"
content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'index', :id => page.project, :page => page.title},
:title => (page.respond_to?(:updated_on) ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
content << "\n" + render_page_hierarchy(pages, page.id) if pages[page.id]
content << "</li>\n"
end
content << "</ul>\n"
end
content
end
# Truncates and returns the string as a single line # Truncates and returns the string as a single line
def truncate_single_line(string, *args) def truncate_single_line(string, *args)
truncate(string, *args).gsub(%r{[\r\n]+}m, ' ') truncate(string, *args).gsub(%r{[\r\n]+}m, ' ')

View File

@ -16,22 +16,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module WikiHelper module WikiHelper
def render_page_hierarchy(pages, node=nil)
content = ''
if pages[node]
content << "<ul class=\"pages-hierarchy\">\n"
pages[node].each do |page|
content << "<li>"
content << link_to(h(page.pretty_title), {:action => 'index', :page => page.title},
:title => (page.respond_to?(:updated_on) ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
content << "\n" + render_page_hierarchy(pages, page.id) if pages[page.id]
content << "</li>\n"
end
content << "</ul>\n"
end
content
end
def html_diff(wdiff) def html_diff(wdiff)
words = wdiff.words.collect{|word| h(word)} words = wdiff.words.collect{|word| h(word)}

View File

@ -43,6 +43,25 @@ class Wiki < ActiveRecord::Base
page page
end end
# Finds a page by title
# The given string can be of one of the forms: "title" or "project:title"
# Examples:
# Wiki.find_page("bar", project => foo)
# Wiki.find_page("foo:bar")
def self.find_page(title, options = {})
project = options[:project]
if title.to_s =~ %r{^([^\:]+)\:(.*)$}
project_identifier, title = $1, $2
project = Project.find_by_identifier(project_identifier) || Project.find_by_name(project_identifier)
end
if project && project.wiki
page = project.wiki.find_page(title)
if page && page.content
page
end
end
end
# turn a string into a valid page title # turn a string into a valid page title
def self.titleize(title) def self.titleize(title)
# replace spaces with _ and remove unwanted caracters # replace spaces with _ and remove unwanted caracters

View File

@ -23,6 +23,15 @@ module Redmine
method_name = "macro_#{name}" method_name = "macro_#{name}"
send(method_name, obj, args) if respond_to?(method_name) send(method_name, obj, args) if respond_to?(method_name)
end end
def extract_macro_options(args, *keys)
options = {}
while args.last.to_s.strip =~ %r{^(.+)\=(.+)$} && keys.include?($1.downcase.to_sym)
options[$1.downcase.to_sym] = $2
args.pop
end
return [args, options]
end
end end
@@available_macros = {} @@available_macros = {}
@ -77,24 +86,29 @@ module Redmine
content_tag('dl', out) content_tag('dl', out)
end end
desc "Displays a list of child pages." desc "Displays a list of child pages. With no argument, it displays the child pages of the current wiki page. Examples:\n\n" +
" !{{child_pages}} -- can be used from a wiki page only\n" +
" !{{child_pages(Foo)}} -- lists all children of page Foo\n" +
" !{{child_pages(Foo, parent=1)}} -- same as above with a link to page Foo"
macro :child_pages do |obj, args| macro :child_pages do |obj, args|
raise 'This macro applies to wiki pages only.' unless obj.is_a?(WikiContent) args, options = extract_macro_options(args, :parent)
render_page_hierarchy(obj.page.descendants.group_by(&:parent_id), obj.page.id) page = nil
if args.size > 0
page = Wiki.find_page(args.first.to_s, :project => @project)
elsif obj.is_a?(WikiContent)
page = obj.page
else
raise 'With no argument, this macro can be called from wiki pages only.'
end
raise 'Page not found' if page.nil? || !User.current.allowed_to?(:view_wiki_pages, page.wiki.project)
pages = ([page] + page.descendants).group_by(&:parent_id)
render_page_hierarchy(pages, options[:parent] ? page.parent_id : page.id)
end end
desc "Include a wiki page. Example:\n\n !{{include(Foo)}}\n\nor to include a page of a specific project wiki:\n\n !{{include(projectname:Foo)}}" desc "Include a wiki page. Example:\n\n !{{include(Foo)}}\n\nor to include a page of a specific project wiki:\n\n !{{include(projectname:Foo)}}"
macro :include do |obj, args| macro :include do |obj, args|
project = @project page = Wiki.find_page(args.first.to_s, :project => @project)
title = args.first.to_s raise 'Page not found' if page.nil? || !User.current.allowed_to?(:view_wiki_pages, page.wiki.project)
if title =~ %r{^([^\:]+)\:(.*)$}
project_identifier, title = $1, $2
project = Project.find_by_identifier(project_identifier) || Project.find_by_name(project_identifier)
end
raise 'Unknow project' unless project && User.current.allowed_to?(:view_wiki_pages, project)
raise 'No wiki for this project' unless !project.wiki.nil?
page = project.wiki.find_page(title)
raise "Page #{args.first} doesn't exist" unless page && page.content
@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

View File

@ -47,4 +47,26 @@ wiki_contents_004:
version: 1 version: 1
author_id: 1 author_id: 1
comments: comments:
wiki_contents_005:
text: |-
h1. Child page 1
This is a child page
updated_on: 2007-03-08 00:18:07 +01:00
page_id: 5
id: 5
version: 1
author_id: 1
comments:
wiki_contents_006:
text: |-
h1. Child page 2
This is a child page
updated_on: 2007-03-08 00:18:07 +01:00
page_id: 6
id: 6
version: 1
author_id: 1
comments:

View File

@ -27,4 +27,18 @@ wiki_pages_004:
wiki_id: 1 wiki_id: 1
protected: false protected: false
parent_id: 1 parent_id: 1
wiki_pages_005:
created_on: 2007-03-08 00:18:07 +01:00
title: Child_1
id: 5
wiki_id: 1
protected: false
parent_id: 2
wiki_pages_006:
created_on: 2007-03-08 00:18:07 +01:00
title: Child_2
id: 6
wiki_id: 1
protected: false
parent_id: 2

View File

@ -359,32 +359,6 @@ EXPECTED
assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '') assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '')
end end
def test_macro_hello_world
text = "{{hello_world}}"
assert textilizable(text).match(/Hello world!/)
# escaping
text = "!{{hello_world}}"
assert_equal '<p>{{hello_world}}</p>', textilizable(text)
end
def test_macro_include
@project = Project.find(1)
# include a page of the current project wiki
text = "{{include(Another page)}}"
assert textilizable(text).match(/This is a link to a ticket/)
@project = nil
# include a page of a specific project wiki
text = "{{include(ecookbook:Another page)}}"
assert textilizable(text).match(/This is a link to a ticket/)
text = "{{include(ecookbook:)}}"
assert textilizable(text).match(/CookBook documentation/)
text = "{{include(unknowidentifier:somepage)}}"
assert textilizable(text).match(/Unknow project/)
end
def test_default_formatter def test_default_formatter
Setting.text_formatting = 'unknown' Setting.text_formatting = 'unknown'
text = 'a *link*: http://www.example.net/' text = 'a *link*: http://www.example.net/'

View File

@ -0,0 +1,98 @@
# 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 File.dirname(__FILE__) + '/../../../../test_helper'
class Redmine::WikiFormatting::MacrosTest < HelperTestCase
include ApplicationHelper
include ActionView::Helpers::TextHelper
fixtures :projects, :roles, :enabled_modules, :users,
:repositories, :changesets,
:trackers, :issue_statuses, :issues,
:versions, :documents,
:wikis, :wiki_pages, :wiki_contents,
:boards, :messages,
:attachments
def setup
super
@project = nil
end
def teardown
end
def test_macro_hello_world
text = "{{hello_world}}"
assert textilizable(text).match(/Hello world!/)
# escaping
text = "!{{hello_world}}"
assert_equal '<p>{{hello_world}}</p>', textilizable(text)
end
def test_macro_include
@project = Project.find(1)
# include a page of the current project wiki
text = "{{include(Another page)}}"
assert textilizable(text).match(/This is a link to a ticket/)
@project = nil
# include a page of a specific project wiki
text = "{{include(ecookbook:Another page)}}"
assert textilizable(text).match(/This is a link to a ticket/)
text = "{{include(ecookbook:)}}"
assert textilizable(text).match(/CookBook documentation/)
text = "{{include(unknowidentifier:somepage)}}"
assert textilizable(text).match(/Page not found/)
end
def test_macro_child_pages
expected = "<p><ul class=\"pages-hierarchy\">\n" +
"<li><a href=\"/wiki/ecookbook/Child_1\">Child 1</a></li>\n" +
"<li><a href=\"/wiki/ecookbook/Child_2\">Child 2</a></li>\n" +
"</ul>\n</p>"
@project = Project.find(1)
# child pages of the current wiki page
assert_equal expected, textilizable("{{child_pages}}", :object => WikiPage.find(2).content)
# child pages of another page
assert_equal expected, textilizable("{{child_pages(Another_page)}}", :object => WikiPage.find(1).content)
@project = Project.find(2)
assert_equal expected, textilizable("{{child_pages(ecookbook:Another_page)}}", :object => WikiPage.find(1).content)
end
def test_macro_child_pages_with_option
expected = "<p><ul class=\"pages-hierarchy\">\n" +
"<li><a href=\"/wiki/ecookbook/Another_page\">Another page</a>\n" +
"<ul class=\"pages-hierarchy\">\n" +
"<li><a href=\"/wiki/ecookbook/Child_1\">Child 1</a></li>\n" +
"<li><a href=\"/wiki/ecookbook/Child_2\">Child 2</a></li>\n" +
"</ul>\n</li>\n</ul>\n</p>"
@project = Project.find(1)
# child pages of the current wiki page
assert_equal expected, textilizable("{{child_pages(parent=1)}}", :object => WikiPage.find(2).content)
# child pages of another page
assert_equal expected, textilizable("{{child_pages(Another_page, parent=1)}}", :object => WikiPage.find(1).content)
@project = Project.find(2)
assert_equal expected, textilizable("{{child_pages(ecookbook:Another_page, parent=1)}}", :object => WikiPage.find(1).content)
end
end