@@ -0,0 +1,98 | |||
|
1 | # Redmine - project management software | |
|
2 | # Copyright (C) 2006-2008 Jean-Philippe Lang | |
|
3 | # | |
|
4 | # This program is free software; you can redistribute it and/or | |
|
5 | # modify it under the terms of the GNU General Public License | |
|
6 | # as published by the Free Software Foundation; either version 2 | |
|
7 | # of the License, or (at your option) any later version. | |
|
8 | # | |
|
9 | # This program is distributed in the hope that it will be useful, | |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
|
12 | # GNU General Public License for more details. | |
|
13 | # | |
|
14 | # You should have received a copy of the GNU General Public License | |
|
15 | # along with this program; if not, write to the Free Software | |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
|
17 | ||
|
18 | require File.dirname(__FILE__) + '/../../../../test_helper' | |
|
19 | ||
|
20 | class Redmine::WikiFormatting::MacrosTest < HelperTestCase | |
|
21 | include ApplicationHelper | |
|
22 | include ActionView::Helpers::TextHelper | |
|
23 | fixtures :projects, :roles, :enabled_modules, :users, | |
|
24 | :repositories, :changesets, | |
|
25 | :trackers, :issue_statuses, :issues, | |
|
26 | :versions, :documents, | |
|
27 | :wikis, :wiki_pages, :wiki_contents, | |
|
28 | :boards, :messages, | |
|
29 | :attachments | |
|
30 | ||
|
31 | def setup | |
|
32 | super | |
|
33 | @project = nil | |
|
34 | end | |
|
35 | ||
|
36 | def teardown | |
|
37 | end | |
|
38 | ||
|
39 | def test_macro_hello_world | |
|
40 | text = "{{hello_world}}" | |
|
41 | assert textilizable(text).match(/Hello world!/) | |
|
42 | # escaping | |
|
43 | text = "!{{hello_world}}" | |
|
44 | assert_equal '<p>{{hello_world}}</p>', textilizable(text) | |
|
45 | end | |
|
46 | ||
|
47 | def test_macro_include | |
|
48 | @project = Project.find(1) | |
|
49 | # include a page of the current project wiki | |
|
50 | text = "{{include(Another page)}}" | |
|
51 | assert textilizable(text).match(/This is a link to a ticket/) | |
|
52 | ||
|
53 | @project = nil | |
|
54 | # include a page of a specific project wiki | |
|
55 | text = "{{include(ecookbook:Another page)}}" | |
|
56 | assert textilizable(text).match(/This is a link to a ticket/) | |
|
57 | ||
|
58 | text = "{{include(ecookbook:)}}" | |
|
59 | assert textilizable(text).match(/CookBook documentation/) | |
|
60 | ||
|
61 | text = "{{include(unknowidentifier:somepage)}}" | |
|
62 | assert textilizable(text).match(/Page not found/) | |
|
63 | end | |
|
64 | ||
|
65 | def test_macro_child_pages | |
|
66 | expected = "<p><ul class=\"pages-hierarchy\">\n" + | |
|
67 | "<li><a href=\"/wiki/ecookbook/Child_1\">Child 1</a></li>\n" + | |
|
68 | "<li><a href=\"/wiki/ecookbook/Child_2\">Child 2</a></li>\n" + | |
|
69 | "</ul>\n</p>" | |
|
70 | ||
|
71 | @project = Project.find(1) | |
|
72 | # child pages of the current wiki page | |
|
73 | assert_equal expected, textilizable("{{child_pages}}", :object => WikiPage.find(2).content) | |
|
74 | # child pages of another page | |
|
75 | assert_equal expected, textilizable("{{child_pages(Another_page)}}", :object => WikiPage.find(1).content) | |
|
76 | ||
|
77 | @project = Project.find(2) | |
|
78 | assert_equal expected, textilizable("{{child_pages(ecookbook:Another_page)}}", :object => WikiPage.find(1).content) | |
|
79 | end | |
|
80 | ||
|
81 | def test_macro_child_pages_with_option | |
|
82 | expected = "<p><ul class=\"pages-hierarchy\">\n" + | |
|
83 | "<li><a href=\"/wiki/ecookbook/Another_page\">Another page</a>\n" + | |
|
84 | "<ul class=\"pages-hierarchy\">\n" + | |
|
85 | "<li><a href=\"/wiki/ecookbook/Child_1\">Child 1</a></li>\n" + | |
|
86 | "<li><a href=\"/wiki/ecookbook/Child_2\">Child 2</a></li>\n" + | |
|
87 | "</ul>\n</li>\n</ul>\n</p>" | |
|
88 | ||
|
89 | @project = Project.find(1) | |
|
90 | # child pages of the current wiki page | |
|
91 | assert_equal expected, textilizable("{{child_pages(parent=1)}}", :object => WikiPage.find(2).content) | |
|
92 | # child pages of another page | |
|
93 | assert_equal expected, textilizable("{{child_pages(Another_page, parent=1)}}", :object => WikiPage.find(1).content) | |
|
94 | ||
|
95 | @project = Project.find(2) | |
|
96 | assert_equal expected, textilizable("{{child_pages(ecookbook:Another_page, parent=1)}}", :object => WikiPage.find(1).content) | |
|
97 | end | |
|
98 | end |
@@ -119,6 +119,22 module ApplicationHelper | |||
|
119 | 119 | end |
|
120 | 120 | end |
|
121 | 121 | |
|
122 | def render_page_hierarchy(pages, node=nil) | |
|
123 | content = '' | |
|
124 | if pages[node] | |
|
125 | content << "<ul class=\"pages-hierarchy\">\n" | |
|
126 | pages[node].each do |page| | |
|
127 | content << "<li>" | |
|
128 | content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'index', :id => page.project, :page => page.title}, | |
|
129 | :title => (page.respond_to?(:updated_on) ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil)) | |
|
130 | content << "\n" + render_page_hierarchy(pages, page.id) if pages[page.id] | |
|
131 | content << "</li>\n" | |
|
132 | end | |
|
133 | content << "</ul>\n" | |
|
134 | end | |
|
135 | content | |
|
136 | end | |
|
137 | ||
|
122 | 138 | # Truncates and returns the string as a single line |
|
123 | 139 | def truncate_single_line(string, *args) |
|
124 | 140 | truncate(string, *args).gsub(%r{[\r\n]+}m, ' ') |
@@ -16,22 +16,6 | |||
|
16 | 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
17 | 17 | |
|
18 | 18 | module WikiHelper |
|
19 | ||
|
20 | def render_page_hierarchy(pages, node=nil) | |
|
21 | content = '' | |
|
22 | if pages[node] | |
|
23 | content << "<ul class=\"pages-hierarchy\">\n" | |
|
24 | pages[node].each do |page| | |
|
25 | content << "<li>" | |
|
26 | content << link_to(h(page.pretty_title), {:action => 'index', :page => page.title}, | |
|
27 | :title => (page.respond_to?(:updated_on) ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil)) | |
|
28 | content << "\n" + render_page_hierarchy(pages, page.id) if pages[page.id] | |
|
29 | content << "</li>\n" | |
|
30 | end | |
|
31 | content << "</ul>\n" | |
|
32 | end | |
|
33 | content | |
|
34 | end | |
|
35 | 19 | |
|
36 | 20 | def html_diff(wdiff) |
|
37 | 21 | words = wdiff.words.collect{|word| h(word)} |
@@ -43,6 +43,25 class Wiki < ActiveRecord::Base | |||
|
43 | 43 | page |
|
44 | 44 | end |
|
45 | 45 | |
|
46 | # Finds a page by title | |
|
47 | # The given string can be of one of the forms: "title" or "project:title" | |
|
48 | # Examples: | |
|
49 | # Wiki.find_page("bar", project => foo) | |
|
50 | # Wiki.find_page("foo:bar") | |
|
51 | def self.find_page(title, options = {}) | |
|
52 | project = options[:project] | |
|
53 | if title.to_s =~ %r{^([^\:]+)\:(.*)$} | |
|
54 | project_identifier, title = $1, $2 | |
|
55 | project = Project.find_by_identifier(project_identifier) || Project.find_by_name(project_identifier) | |
|
56 | end | |
|
57 | if project && project.wiki | |
|
58 | page = project.wiki.find_page(title) | |
|
59 | if page && page.content | |
|
60 | page | |
|
61 | end | |
|
62 | end | |
|
63 | end | |
|
64 | ||
|
46 | 65 | # turn a string into a valid page title |
|
47 | 66 | def self.titleize(title) |
|
48 | 67 | # replace spaces with _ and remove unwanted caracters |
@@ -23,6 +23,15 module Redmine | |||
|
23 | 23 | method_name = "macro_#{name}" |
|
24 | 24 | send(method_name, obj, args) if respond_to?(method_name) |
|
25 | 25 | end |
|
26 | ||
|
27 | def extract_macro_options(args, *keys) | |
|
28 | options = {} | |
|
29 | while args.last.to_s.strip =~ %r{^(.+)\=(.+)$} && keys.include?($1.downcase.to_sym) | |
|
30 | options[$1.downcase.to_sym] = $2 | |
|
31 | args.pop | |
|
32 | end | |
|
33 | return [args, options] | |
|
34 | end | |
|
26 | 35 | end |
|
27 | 36 | |
|
28 | 37 | @@available_macros = {} |
@@ -77,24 +86,29 module Redmine | |||
|
77 | 86 | content_tag('dl', out) |
|
78 | 87 | end |
|
79 | 88 | |
|
80 | desc "Displays a list of child pages." | |
|
89 | desc "Displays a list of child pages. With no argument, it displays the child pages of the current wiki page. Examples:\n\n" + | |
|
90 | " !{{child_pages}} -- can be used from a wiki page only\n" + | |
|
91 | " !{{child_pages(Foo)}} -- lists all children of page Foo\n" + | |
|
92 | " !{{child_pages(Foo, parent=1)}} -- same as above with a link to page Foo" | |
|
81 | 93 | macro :child_pages do |obj, args| |
|
82 | raise 'This macro applies to wiki pages only.' unless obj.is_a?(WikiContent) | |
|
83 | render_page_hierarchy(obj.page.descendants.group_by(&:parent_id), obj.page.id) | |
|
94 | args, options = extract_macro_options(args, :parent) | |
|
95 | page = nil | |
|
96 | if args.size > 0 | |
|
97 | page = Wiki.find_page(args.first.to_s, :project => @project) | |
|
98 | elsif obj.is_a?(WikiContent) | |
|
99 | page = obj.page | |
|
100 | else | |
|
101 | raise 'With no argument, this macro can be called from wiki pages only.' | |
|
102 | end | |
|
103 | raise 'Page not found' if page.nil? || !User.current.allowed_to?(:view_wiki_pages, page.wiki.project) | |
|
104 | pages = ([page] + page.descendants).group_by(&:parent_id) | |
|
105 | render_page_hierarchy(pages, options[:parent] ? page.parent_id : page.id) | |
|
84 | 106 | end |
|
85 | 107 | |
|
86 | 108 | 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)}}" |
|
87 | 109 | macro :include do |obj, args| |
|
88 | project = @project | |
|
89 | title = args.first.to_s | |
|
90 | if title =~ %r{^([^\:]+)\:(.*)$} | |
|
91 | project_identifier, title = $1, $2 | |
|
92 | project = Project.find_by_identifier(project_identifier) || Project.find_by_name(project_identifier) | |
|
93 | end | |
|
94 | raise 'Unknow project' unless project && User.current.allowed_to?(:view_wiki_pages, project) | |
|
95 | raise 'No wiki for this project' unless !project.wiki.nil? | |
|
96 | page = project.wiki.find_page(title) | |
|
97 | raise "Page #{args.first} doesn't exist" unless page && page.content | |
|
110 | page = Wiki.find_page(args.first.to_s, :project => @project) | |
|
111 | raise 'Page not found' if page.nil? || !User.current.allowed_to?(:view_wiki_pages, page.wiki.project) | |
|
98 | 112 | @included_wiki_pages ||= [] |
|
99 | 113 | raise 'Circular inclusion detected' if @included_wiki_pages.include?(page.title) |
|
100 | 114 | @included_wiki_pages << page.title |
@@ -47,4 +47,26 wiki_contents_004: | |||
|
47 | 47 | page_id: 4 |
|
48 | 48 | id: 4 |
|
49 | 49 | version: 1 |
|
50 | author_id: 1 | |
|
51 | comments: | |
|
52 | wiki_contents_005: | |
|
53 | text: |- | |
|
54 | h1. Child page 1 | |
|
55 | ||
|
56 | This is a child page | |
|
57 | updated_on: 2007-03-08 00:18:07 +01:00 | |
|
58 | page_id: 5 | |
|
59 | id: 5 | |
|
60 | version: 1 | |
|
61 | author_id: 1 | |
|
62 | comments: | |
|
63 | wiki_contents_006: | |
|
64 | text: |- | |
|
65 | h1. Child page 2 | |
|
66 | ||
|
67 | This is a child page | |
|
68 | updated_on: 2007-03-08 00:18:07 +01:00 | |
|
69 | page_id: 6 | |
|
70 | id: 6 | |
|
71 | version: 1 | |
|
50 | 72 | author_id: 1 No newline at end of file |
@@ -27,4 +27,18 wiki_pages_004: | |||
|
27 | 27 | wiki_id: 1 |
|
28 | 28 | protected: false |
|
29 | 29 | parent_id: 1 |
|
30 | wiki_pages_005: | |
|
31 | created_on: 2007-03-08 00:18:07 +01:00 | |
|
32 | title: Child_1 | |
|
33 | id: 5 | |
|
34 | wiki_id: 1 | |
|
35 | protected: false | |
|
36 | parent_id: 2 | |
|
37 | wiki_pages_006: | |
|
38 | created_on: 2007-03-08 00:18:07 +01:00 | |
|
39 | title: Child_2 | |
|
40 | id: 6 | |
|
41 | wiki_id: 1 | |
|
42 | protected: false | |
|
43 | parent_id: 2 | |
|
30 | 44 | No newline at end of file |
@@ -359,32 +359,6 EXPECTED | |||
|
359 | 359 | assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '') |
|
360 | 360 | end |
|
361 | 361 | |
|
362 | def test_macro_hello_world | |
|
363 | text = "{{hello_world}}" | |
|
364 | assert textilizable(text).match(/Hello world!/) | |
|
365 | # escaping | |
|
366 | text = "!{{hello_world}}" | |
|
367 | assert_equal '<p>{{hello_world}}</p>', textilizable(text) | |
|
368 | end | |
|
369 | ||
|
370 | def test_macro_include | |
|
371 | @project = Project.find(1) | |
|
372 | # include a page of the current project wiki | |
|
373 | text = "{{include(Another page)}}" | |
|
374 | assert textilizable(text).match(/This is a link to a ticket/) | |
|
375 | ||
|
376 | @project = nil | |
|
377 | # include a page of a specific project wiki | |
|
378 | text = "{{include(ecookbook:Another page)}}" | |
|
379 | assert textilizable(text).match(/This is a link to a ticket/) | |
|
380 | ||
|
381 | text = "{{include(ecookbook:)}}" | |
|
382 | assert textilizable(text).match(/CookBook documentation/) | |
|
383 | ||
|
384 | text = "{{include(unknowidentifier:somepage)}}" | |
|
385 | assert textilizable(text).match(/Unknow project/) | |
|
386 | end | |
|
387 | ||
|
388 | 362 | def test_default_formatter |
|
389 | 363 | Setting.text_formatting = 'unknown' |
|
390 | 364 | text = 'a *link*: http://www.example.net/' |
General Comments 0
You need to be logged in to leave comments.
Login now