@@ -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 | end |
|
119 | end | |
120 | end |
|
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 | # Truncates and returns the string as a single line |
|
138 | # Truncates and returns the string as a single line | |
123 | def truncate_single_line(string, *args) |
|
139 | def truncate_single_line(string, *args) | |
124 | truncate(string, *args).gsub(%r{[\r\n]+}m, ' ') |
|
140 | truncate(string, *args).gsub(%r{[\r\n]+}m, ' ') |
@@ -16,22 +16,6 | |||||
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | module WikiHelper |
|
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 | def html_diff(wdiff) |
|
20 | def html_diff(wdiff) | |
37 | words = wdiff.words.collect{|word| h(word)} |
|
21 | words = wdiff.words.collect{|word| h(word)} |
@@ -43,6 +43,25 class Wiki < ActiveRecord::Base | |||||
43 | page |
|
43 | page | |
44 | end |
|
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 | # turn a string into a valid page title |
|
65 | # turn a string into a valid page title | |
47 | def self.titleize(title) |
|
66 | def self.titleize(title) | |
48 | # replace spaces with _ and remove unwanted caracters |
|
67 | # replace spaces with _ and remove unwanted caracters |
@@ -23,6 +23,15 module Redmine | |||||
23 | method_name = "macro_#{name}" |
|
23 | method_name = "macro_#{name}" | |
24 | send(method_name, obj, args) if respond_to?(method_name) |
|
24 | send(method_name, obj, args) if respond_to?(method_name) | |
25 | end |
|
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 | end |
|
35 | end | |
27 |
|
36 | |||
28 | @@available_macros = {} |
|
37 | @@available_macros = {} | |
@@ -77,24 +86,29 module Redmine | |||||
77 | content_tag('dl', out) |
|
86 | content_tag('dl', out) | |
78 | end |
|
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 | macro :child_pages do |obj, args| |
|
93 | macro :child_pages do |obj, args| | |
82 | raise 'This macro applies to wiki pages only.' unless obj.is_a?(WikiContent) |
|
94 | args, options = extract_macro_options(args, :parent) | |
83 | render_page_hierarchy(obj.page.descendants.group_by(&:parent_id), obj.page.id) |
|
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 | end |
|
106 | end | |
85 |
|
107 | |||
86 | 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)}}" |
|
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 | macro :include do |obj, args| |
|
109 | macro :include do |obj, args| | |
88 | project = @project |
|
110 | page = Wiki.find_page(args.first.to_s, :project => @project) | |
89 | title = args.first.to_s |
|
111 | raise 'Page not found' if page.nil? || !User.current.allowed_to?(:view_wiki_pages, page.wiki.project) | |
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 |
|
|||
98 | @included_wiki_pages ||= [] |
|
112 | @included_wiki_pages ||= [] | |
99 | raise 'Circular inclusion detected' if @included_wiki_pages.include?(page.title) |
|
113 | raise 'Circular inclusion detected' if @included_wiki_pages.include?(page.title) | |
100 | @included_wiki_pages << page.title |
|
114 | @included_wiki_pages << page.title |
@@ -47,4 +47,26 wiki_contents_004: | |||||
47 | page_id: 4 |
|
47 | page_id: 4 | |
48 | id: 4 |
|
48 | id: 4 | |
49 | version: 1 |
|
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 | author_id: 1 No newline at end of file |
|
72 | author_id: 1 |
@@ -27,4 +27,18 wiki_pages_004: | |||||
27 | wiki_id: 1 |
|
27 | wiki_id: 1 | |
28 | protected: false |
|
28 | protected: false | |
29 | parent_id: 1 |
|
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 | No newline at end of file |
|
44 |
@@ -359,32 +359,6 EXPECTED | |||||
359 | assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '') |
|
359 | assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '') | |
360 | end |
|
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 | def test_default_formatter |
|
362 | def test_default_formatter | |
389 | Setting.text_formatting = 'unknown' |
|
363 | Setting.text_formatting = 'unknown' | |
390 | text = 'a *link*: http://www.example.net/' |
|
364 | text = 'a *link*: http://www.example.net/' |
General Comments 0
You need to be logged in to leave comments.
Login now