##// END OF EJS Templates
REST API for retrieving wiki pages (#7082)....
Jean-Philippe Lang -
r10504:9e3130872057
parent child
Show More
@@ -0,0 +1,13
1 api.array :wiki_pages do
2 @pages.each do |page|
3 api.wiki_page do
4 api.title page.title
5 if page.parent
6 api.parent :title => page.parent.title
7 end
8 api.version page.version
9 api.created_on page.created_on
10 api.updated_on page.updated_on
11 end
12 end
13 end
@@ -0,0 +1,11
1 api.wiki_page do
2 api.title @page.title
3 if @page.parent
4 api.parent :title => @page.parent.title
5 end
6 api.text @content.text
7 api.version @content.version
8 api.author(:id => @content.author_id, :name => @content.author.name)
9 api.created_on @page.created_on
10 api.updated_on @content.updated_on
11 end
@@ -0,0 +1,85
1 # Redmine - project management software
2 # Copyright (C) 2006-2012 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.expand_path('../../../test_helper', __FILE__)
19
20 class ApiTest::WikiPagesTest < ActionController::IntegrationTest
21 fixtures :projects, :users, :roles, :members, :member_roles,
22 :enabled_modules, :wikis, :wiki_pages, :wiki_contents,
23 :wiki_content_versions, :attachments
24
25 def setup
26 Setting.rest_api_enabled = '1'
27 end
28
29 test "GET /projects/:project_id/wiki/index.xml should return wiki pages" do
30 get '/projects/ecookbook/wiki/index.xml'
31 assert_response :success
32 assert_equal 'application/xml', response.content_type
33 assert_select 'wiki_pages[type=array]' do
34 assert_select 'wiki_page', :count => Wiki.find(1).pages.count
35 assert_select 'wiki_page' do
36 assert_select 'title', :text => 'CookBook_documentation'
37 assert_select 'version', :text => '3'
38 assert_select 'created_on'
39 assert_select 'updated_on'
40 end
41 end
42 end
43
44 test "GET /projects/:project_id/wiki/:title.xml should return wiki page" do
45 get '/projects/ecookbook/wiki/CookBook_documentation.xml'
46 assert_response :success
47 assert_equal 'application/xml', response.content_type
48 assert_select 'wiki_page' do
49 assert_select 'title', :text => 'CookBook_documentation'
50 assert_select 'version', :text => '3'
51 assert_select 'text'
52 assert_select 'author'
53 assert_select 'created_on'
54 assert_select 'updated_on'
55 end
56 end
57
58 test "GET /projects/:project_id/wiki/:title.xml with unknown title and edit permission should respond with 404" do
59 get '/projects/ecookbook/wiki/Invalid_Page.xml', {}, credentials('jsmith')
60 assert_response 404
61 assert_equal 'application/xml', response.content_type
62 end
63
64 test "GET /projects/:project_id/wiki/:title/:version.xml should return wiki page version" do
65 get '/projects/ecookbook/wiki/CookBook_documentation/2.xml'
66 assert_response :success
67 assert_equal 'application/xml', response.content_type
68 assert_select 'wiki_page' do
69 assert_select 'title', :text => 'CookBook_documentation'
70 assert_select 'version', :text => '2'
71 assert_select 'text'
72 assert_select 'author'
73 assert_select 'created_on'
74 assert_select 'updated_on'
75 end
76 end
77
78 test "GET /projects/:project_id/wiki/:title/:version.xml without permission should be denied" do
79 Role.anonymous.remove_permission! :view_wiki_edits
80
81 get '/projects/ecookbook/wiki/CookBook_documentation/2.xml'
82 assert_response 401
83 assert_equal 'application/xml', response.content_type
84 end
85 end
@@ -1,326 +1,335
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'diff'
19 19
20 20 # The WikiController follows the Rails REST controller pattern but with
21 21 # a few differences
22 22 #
23 23 # * index - shows a list of WikiPages grouped by page or date
24 24 # * new - not used
25 25 # * create - not used
26 26 # * show - will also show the form for creating a new wiki page
27 27 # * edit - used to edit an existing or new page
28 28 # * update - used to save a wiki page update to the database, including new pages
29 29 # * destroy - normal
30 30 #
31 31 # Other member and collection methods are also used
32 32 #
33 33 # TODO: still being worked on
34 34 class WikiController < ApplicationController
35 35 default_search_scope :wiki_pages
36 36 before_filter :find_wiki, :authorize
37 37 before_filter :find_existing_or_new_page, :only => [:show, :edit, :update]
38 38 before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy, :destroy_version]
39 accept_api_auth :index, :show
39 40
40 41 helper :attachments
41 42 include AttachmentsHelper
42 43 helper :watchers
43 44 include Redmine::Export::PDF
44 45
45 46 # List of pages, sorted alphabetically and by parent (hierarchy)
46 47 def index
47 48 load_pages_for_index
48 @pages_by_parent_id = @pages.group_by(&:parent_id)
49
50 respond_to do |format|
51 format.html {
52 @pages_by_parent_id = @pages.group_by(&:parent_id)
53 }
54 format.api
55 end
49 56 end
50 57
51 58 # List of page, by last update
52 59 def date_index
53 60 load_pages_for_index
54 61 @pages_by_date = @pages.group_by {|p| p.updated_on.to_date}
55 62 end
56 63
57 64 # display a page (in editing mode if it doesn't exist)
58 65 def show
59 66 if @page.new_record?
60 if User.current.allowed_to?(:edit_wiki_pages, @project) && editable?
67 if User.current.allowed_to?(:edit_wiki_pages, @project) && editable? && !api_request?
61 68 edit
62 69 render :action => 'edit'
63 70 else
64 71 render_404
65 72 end
66 73 return
67 74 end
68 75 if params[:version] && !User.current.allowed_to?(:view_wiki_edits, @project)
69 # Redirects user to the current version if he's not allowed to view previous versions
70 redirect_to :version => nil
76 deny_access
71 77 return
72 78 end
73 79 @content = @page.content_for_version(params[:version])
74 80 if User.current.allowed_to?(:export_wiki_pages, @project)
75 81 if params[:format] == 'pdf'
76 82 send_data(wiki_page_to_pdf(@page, @project), :type => 'application/pdf', :filename => "#{@page.title}.pdf")
77 83 return
78 84 elsif params[:format] == 'html'
79 85 export = render_to_string :action => 'export', :layout => false
80 86 send_data(export, :type => 'text/html', :filename => "#{@page.title}.html")
81 87 return
82 88 elsif params[:format] == 'txt'
83 89 send_data(@content.text, :type => 'text/plain', :filename => "#{@page.title}.txt")
84 90 return
85 91 end
86 92 end
87 93 @editable = editable?
88 94 @sections_editable = @editable && User.current.allowed_to?(:edit_wiki_pages, @page.project) &&
89 95 @content.current_version? &&
90 96 Redmine::WikiFormatting.supports_section_edit?
91 97
92 render :action => 'show'
98 respond_to do |format|
99 format.html
100 format.api
101 end
93 102 end
94 103
95 104 # edit an existing page or a new one
96 105 def edit
97 106 return render_403 unless editable?
98 107 if @page.new_record?
99 108 @page.content = WikiContent.new(:page => @page)
100 109 if params[:parent].present?
101 110 @page.parent = @page.wiki.find_page(params[:parent].to_s)
102 111 end
103 112 end
104 113
105 114 @content = @page.content_for_version(params[:version])
106 115 @content.text = initial_page_content(@page) if @content.text.blank?
107 116 # don't keep previous comment
108 117 @content.comments = nil
109 118
110 119 # To prevent StaleObjectError exception when reverting to a previous version
111 120 @content.version = @page.content.version
112 121
113 122 @text = @content.text
114 123 if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
115 124 @section = params[:section].to_i
116 125 @text, @section_hash = Redmine::WikiFormatting.formatter.new(@text).get_section(@section)
117 126 render_404 if @text.blank?
118 127 end
119 128 end
120 129
121 130 # Creates a new page or updates an existing one
122 131 def update
123 132 return render_403 unless editable?
124 133 @page.content = WikiContent.new(:page => @page) if @page.new_record?
125 134 @page.safe_attributes = params[:wiki_page]
126 135
127 136 @content = @page.content_for_version(params[:version])
128 137 @content.text = initial_page_content(@page) if @content.text.blank?
129 138 # don't keep previous comment
130 139 @content.comments = nil
131 140
132 141 if !@page.new_record? && params[:content].present? && @content.text == params[:content][:text]
133 142 attachments = Attachment.attach_files(@page, params[:attachments])
134 143 render_attachment_warning_if_needed(@page)
135 144 # don't save content if text wasn't changed
136 145 @page.save
137 146 redirect_to :action => 'show', :project_id => @project, :id => @page.title
138 147 return
139 148 end
140 149
141 150 @content.comments = params[:content][:comments]
142 151 @text = params[:content][:text]
143 152 if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
144 153 @section = params[:section].to_i
145 154 @section_hash = params[:section_hash]
146 155 @content.text = Redmine::WikiFormatting.formatter.new(@content.text).update_section(params[:section].to_i, @text, @section_hash)
147 156 else
148 157 @content.version = params[:content][:version]
149 158 @content.text = @text
150 159 end
151 160 @content.author = User.current
152 161 @page.content = @content
153 162 if @page.save
154 163 attachments = Attachment.attach_files(@page, params[:attachments])
155 164 render_attachment_warning_if_needed(@page)
156 165 call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
157 166 redirect_to :action => 'show', :project_id => @project, :id => @page.title
158 167 else
159 168 render :action => 'edit'
160 169 end
161 170
162 171 rescue ActiveRecord::StaleObjectError, Redmine::WikiFormatting::StaleSectionError
163 172 # Optimistic locking exception
164 173 flash.now[:error] = l(:notice_locking_conflict)
165 174 render :action => 'edit'
166 175 rescue ActiveRecord::RecordNotSaved
167 176 render :action => 'edit'
168 177 end
169 178
170 179 # rename a page
171 180 def rename
172 181 return render_403 unless editable?
173 182 @page.redirect_existing_links = true
174 183 # used to display the *original* title if some AR validation errors occur
175 184 @original_title = @page.pretty_title
176 185 if request.post? && @page.update_attributes(params[:wiki_page])
177 186 flash[:notice] = l(:notice_successful_update)
178 187 redirect_to :action => 'show', :project_id => @project, :id => @page.title
179 188 end
180 189 end
181 190
182 191 def protect
183 192 @page.update_attribute :protected, params[:protected]
184 193 redirect_to :action => 'show', :project_id => @project, :id => @page.title
185 194 end
186 195
187 196 # show page history
188 197 def history
189 198 @version_count = @page.content.versions.count
190 199 @version_pages = Paginator.new self, @version_count, per_page_option, params['p']
191 200 # don't load text
192 201 @versions = @page.content.versions.find :all,
193 202 :select => "id, author_id, comments, updated_on, version",
194 203 :order => 'version DESC',
195 204 :limit => @version_pages.items_per_page + 1,
196 205 :offset => @version_pages.current.offset
197 206
198 207 render :layout => false if request.xhr?
199 208 end
200 209
201 210 def diff
202 211 @diff = @page.diff(params[:version], params[:version_from])
203 212 render_404 unless @diff
204 213 end
205 214
206 215 def annotate
207 216 @annotate = @page.annotate(params[:version])
208 217 render_404 unless @annotate
209 218 end
210 219
211 220 # Removes a wiki page and its history
212 221 # Children can be either set as root pages, removed or reassigned to another parent page
213 222 def destroy
214 223 return render_403 unless editable?
215 224
216 225 @descendants_count = @page.descendants.size
217 226 if @descendants_count > 0
218 227 case params[:todo]
219 228 when 'nullify'
220 229 # Nothing to do
221 230 when 'destroy'
222 231 # Removes all its descendants
223 232 @page.descendants.each(&:destroy)
224 233 when 'reassign'
225 234 # Reassign children to another parent page
226 235 reassign_to = @wiki.pages.find_by_id(params[:reassign_to_id].to_i)
227 236 return unless reassign_to
228 237 @page.children.each do |child|
229 238 child.update_attribute(:parent, reassign_to)
230 239 end
231 240 else
232 241 @reassignable_to = @wiki.pages - @page.self_and_descendants
233 242 return
234 243 end
235 244 end
236 245 @page.destroy
237 246 redirect_to :action => 'index', :project_id => @project
238 247 end
239 248
240 249 def destroy_version
241 250 return render_403 unless editable?
242 251
243 252 @content = @page.content_for_version(params[:version])
244 253 @content.destroy
245 254 redirect_to_referer_or :action => 'history', :id => @page.title, :project_id => @project
246 255 end
247 256
248 257 # Export wiki to a single pdf or html file
249 258 def export
250 259 @pages = @wiki.pages.all(:order => 'title', :include => [:content, :attachments], :limit => 75)
251 260 respond_to do |format|
252 261 format.html {
253 262 export = render_to_string :action => 'export_multiple', :layout => false
254 263 send_data(export, :type => 'text/html', :filename => "wiki.html")
255 264 }
256 265 format.pdf {
257 266 send_data(wiki_pages_to_pdf(@pages, @project), :type => 'application/pdf', :filename => "#{@project.identifier}.pdf")
258 267 }
259 268 end
260 269 end
261 270
262 271 def preview
263 272 page = @wiki.find_page(params[:id])
264 273 # page is nil when previewing a new page
265 274 return render_403 unless page.nil? || editable?(page)
266 275 if page
267 276 @attachements = page.attachments
268 277 @previewed = page.content
269 278 end
270 279 @text = params[:content][:text]
271 280 render :partial => 'common/preview'
272 281 end
273 282
274 283 def add_attachment
275 284 return render_403 unless editable?
276 285 attachments = Attachment.attach_files(@page, params[:attachments])
277 286 render_attachment_warning_if_needed(@page)
278 287 redirect_to :action => 'show', :id => @page.title, :project_id => @project
279 288 end
280 289
281 290 private
282 291
283 292 def find_wiki
284 293 @project = Project.find(params[:project_id])
285 294 @wiki = @project.wiki
286 295 render_404 unless @wiki
287 296 rescue ActiveRecord::RecordNotFound
288 297 render_404
289 298 end
290 299
291 300 # Finds the requested page or a new page if it doesn't exist
292 301 def find_existing_or_new_page
293 302 @page = @wiki.find_or_new_page(params[:id])
294 303 if @wiki.page_found_with_redirect?
295 304 redirect_to params.update(:id => @page.title)
296 305 end
297 306 end
298 307
299 308 # Finds the requested page and returns a 404 error if it doesn't exist
300 309 def find_existing_page
301 310 @page = @wiki.find_page(params[:id])
302 311 if @page.nil?
303 312 render_404
304 313 return
305 314 end
306 315 if @wiki.page_found_with_redirect?
307 316 redirect_to params.update(:id => @page.title)
308 317 end
309 318 end
310 319
311 320 # Returns true if the current user is allowed to edit the page, otherwise false
312 321 def editable?(page = @page)
313 322 page.editable_by?(User.current)
314 323 end
315 324
316 325 # Returns the default content of a new wiki page
317 326 def initial_page_content(page)
318 327 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
319 328 extend helper unless self.instance_of?(helper)
320 329 helper.instance_method(:initial_page_content).bind(self).call(page)
321 330 end
322 331
323 332 def load_pages_for_index
324 @pages = @wiki.pages.with_updated_on.all(:order => 'title', :include => {:wiki => :project})
333 @pages = @wiki.pages.with_updated_on.order("#{WikiPage.table_name}.title").includes(:wiki => :project).includes(:parent).all
325 334 end
326 335 end
@@ -1,235 +1,235
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'diff'
19 19 require 'enumerator'
20 20
21 21 class WikiPage < ActiveRecord::Base
22 22 include Redmine::SafeAttributes
23 23
24 24 belongs_to :wiki
25 25 has_one :content, :class_name => 'WikiContent', :foreign_key => 'page_id', :dependent => :destroy
26 26 acts_as_attachable :delete_permission => :delete_wiki_pages_attachments
27 27 acts_as_tree :dependent => :nullify, :order => 'title'
28 28
29 29 acts_as_watchable
30 30 acts_as_event :title => Proc.new {|o| "#{l(:label_wiki)}: #{o.title}"},
31 31 :description => :text,
32 32 :datetime => :created_on,
33 33 :url => Proc.new {|o| {:controller => 'wiki', :action => 'show', :project_id => o.wiki.project, :id => o.title}}
34 34
35 35 acts_as_searchable :columns => ['title', "#{WikiContent.table_name}.text"],
36 36 :include => [{:wiki => :project}, :content],
37 37 :permission => :view_wiki_pages,
38 38 :project_key => "#{Wiki.table_name}.project_id"
39 39
40 40 attr_accessor :redirect_existing_links
41 41
42 42 validates_presence_of :title
43 43 validates_format_of :title, :with => /^[^,\.\/\?\;\|\s]*$/
44 44 validates_uniqueness_of :title, :scope => :wiki_id, :case_sensitive => false
45 45 validates_associated :content
46 46
47 47 validate :validate_parent_title
48 48 before_destroy :remove_redirects
49 49 before_save :handle_redirects
50 50
51 51 # eager load information about last updates, without loading text
52 52 scope :with_updated_on, {
53 :select => "#{WikiPage.table_name}.*, #{WikiContent.table_name}.updated_on",
53 :select => "#{WikiPage.table_name}.*, #{WikiContent.table_name}.updated_on, #{WikiContent.table_name}.version",
54 54 :joins => "LEFT JOIN #{WikiContent.table_name} ON #{WikiContent.table_name}.page_id = #{WikiPage.table_name}.id"
55 55 }
56 56
57 57 # Wiki pages that are protected by default
58 58 DEFAULT_PROTECTED_PAGES = %w(sidebar)
59 59
60 60 safe_attributes 'parent_id',
61 61 :if => lambda {|page, user| page.new_record? || user.allowed_to?(:rename_wiki_pages, page.project)}
62 62
63 63 def initialize(attributes=nil, *args)
64 64 super
65 65 if new_record? && DEFAULT_PROTECTED_PAGES.include?(title.to_s.downcase)
66 66 self.protected = true
67 67 end
68 68 end
69 69
70 70 def visible?(user=User.current)
71 71 !user.nil? && user.allowed_to?(:view_wiki_pages, project)
72 72 end
73 73
74 74 def title=(value)
75 75 value = Wiki.titleize(value)
76 76 @previous_title = read_attribute(:title) if @previous_title.blank?
77 77 write_attribute(:title, value)
78 78 end
79 79
80 80 def handle_redirects
81 81 self.title = Wiki.titleize(title)
82 82 # Manage redirects if the title has changed
83 83 if !@previous_title.blank? && (@previous_title != title) && !new_record?
84 84 # Update redirects that point to the old title
85 85 wiki.redirects.find_all_by_redirects_to(@previous_title).each do |r|
86 86 r.redirects_to = title
87 87 r.title == r.redirects_to ? r.destroy : r.save
88 88 end
89 89 # Remove redirects for the new title
90 90 wiki.redirects.find_all_by_title(title).each(&:destroy)
91 91 # Create a redirect to the new title
92 92 wiki.redirects << WikiRedirect.new(:title => @previous_title, :redirects_to => title) unless redirect_existing_links == "0"
93 93 @previous_title = nil
94 94 end
95 95 end
96 96
97 97 def remove_redirects
98 98 # Remove redirects to this page
99 99 wiki.redirects.find_all_by_redirects_to(title).each(&:destroy)
100 100 end
101 101
102 102 def pretty_title
103 103 WikiPage.pretty_title(title)
104 104 end
105 105
106 106 def content_for_version(version=nil)
107 107 result = content.versions.find_by_version(version.to_i) if version
108 108 result ||= content
109 109 result
110 110 end
111 111
112 112 def diff(version_to=nil, version_from=nil)
113 113 version_to = version_to ? version_to.to_i : self.content.version
114 114 content_to = content.versions.find_by_version(version_to)
115 115 content_from = version_from ? content.versions.find_by_version(version_from.to_i) : content_to.previous
116 116
117 117 if content_from.version > content_to.version
118 118 content_to, content_from = content_from, content_to
119 119 end
120 120
121 121 (content_to && content_from) ? WikiDiff.new(content_to, content_from) : nil
122 122 end
123 123
124 124 def annotate(version=nil)
125 125 version = version ? version.to_i : self.content.version
126 126 c = content.versions.find_by_version(version)
127 127 c ? WikiAnnotate.new(c) : nil
128 128 end
129 129
130 130 def self.pretty_title(str)
131 131 (str && str.is_a?(String)) ? str.tr('_', ' ') : str
132 132 end
133 133
134 134 def project
135 135 wiki.project
136 136 end
137 137
138 138 def text
139 139 content.text if content
140 140 end
141 141
142 142 def updated_on
143 143 unless @updated_on
144 144 if time = read_attribute(:updated_on)
145 145 # content updated_on was eager loaded with the page
146 146 begin
147 147 @updated_on = (self.class.default_timezone == :utc ? Time.parse(time.to_s).utc : Time.parse(time.to_s).localtime)
148 148 rescue
149 149 end
150 150 else
151 151 @updated_on = content && content.updated_on
152 152 end
153 153 end
154 154 @updated_on
155 155 end
156 156
157 157 # Returns true if usr is allowed to edit the page, otherwise false
158 158 def editable_by?(usr)
159 159 !protected? || usr.allowed_to?(:protect_wiki_pages, wiki.project)
160 160 end
161 161
162 162 def attachments_deletable?(usr=User.current)
163 163 editable_by?(usr) && super(usr)
164 164 end
165 165
166 166 def parent_title
167 167 @parent_title || (self.parent && self.parent.pretty_title)
168 168 end
169 169
170 170 def parent_title=(t)
171 171 @parent_title = t
172 172 parent_page = t.blank? ? nil : self.wiki.find_page(t)
173 173 self.parent = parent_page
174 174 end
175 175
176 176 protected
177 177
178 178 def validate_parent_title
179 179 errors.add(:parent_title, :invalid) if !@parent_title.blank? && parent.nil?
180 180 errors.add(:parent_title, :circular_dependency) if parent && (parent == self || parent.ancestors.include?(self))
181 181 errors.add(:parent_title, :not_same_project) if parent && (parent.wiki_id != wiki_id)
182 182 end
183 183 end
184 184
185 185 class WikiDiff < Redmine::Helpers::Diff
186 186 attr_reader :content_to, :content_from
187 187
188 188 def initialize(content_to, content_from)
189 189 @content_to = content_to
190 190 @content_from = content_from
191 191 super(content_to.text, content_from.text)
192 192 end
193 193 end
194 194
195 195 class WikiAnnotate
196 196 attr_reader :lines, :content
197 197
198 198 def initialize(content)
199 199 @content = content
200 200 current = content
201 201 current_lines = current.text.split(/\r?\n/)
202 202 @lines = current_lines.collect {|t| [nil, nil, t]}
203 203 positions = []
204 204 current_lines.size.times {|i| positions << i}
205 205 while (current.previous)
206 206 d = current.previous.text.split(/\r?\n/).diff(current.text.split(/\r?\n/)).diffs.flatten
207 207 d.each_slice(3) do |s|
208 208 sign, line = s[0], s[1]
209 209 if sign == '+' && positions[line] && positions[line] != -1
210 210 if @lines[positions[line]][0].nil?
211 211 @lines[positions[line]][0] = current.version
212 212 @lines[positions[line]][1] = current.author
213 213 end
214 214 end
215 215 end
216 216 d.each_slice(3) do |s|
217 217 sign, line = s[0], s[1]
218 218 if sign == '-'
219 219 positions.insert(line, -1)
220 220 else
221 221 positions[line] = nil
222 222 end
223 223 end
224 224 positions.compact!
225 225 # Stop if every line is annotated
226 226 break unless @lines.detect { |line| line[0].nil? }
227 227 current = current.previous
228 228 end
229 229 @lines.each { |line|
230 230 line[0] ||= current.version
231 231 # if the last known version is > 1 (eg. history was cleared), we don't know the author
232 232 line[1] ||= current.author if current.version == 1
233 233 }
234 234 end
235 235 end
@@ -1,905 +1,912
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19 require 'wiki_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class WikiController; def rescue_action(e) raise e end; end
23 23
24 24 class WikiControllerTest < ActionController::TestCase
25 25 fixtures :projects, :users, :roles, :members, :member_roles,
26 26 :enabled_modules, :wikis, :wiki_pages, :wiki_contents,
27 27 :wiki_content_versions, :attachments
28 28
29 29 def setup
30 30 @controller = WikiController.new
31 31 @request = ActionController::TestRequest.new
32 32 @response = ActionController::TestResponse.new
33 33 User.current = nil
34 34 end
35 35
36 36 def test_show_start_page
37 37 get :show, :project_id => 'ecookbook'
38 38 assert_response :success
39 39 assert_template 'show'
40 40 assert_tag :tag => 'h1', :content => /CookBook documentation/
41 41
42 42 # child_pages macro
43 43 assert_tag :ul, :attributes => { :class => 'pages-hierarchy' },
44 44 :child => { :tag => 'li',
45 45 :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/Page_with_an_inline_image' },
46 46 :content => 'Page with an inline image' } }
47 47 end
48 48
49 49 def test_export_link
50 50 Role.anonymous.add_permission! :export_wiki_pages
51 51 get :show, :project_id => 'ecookbook'
52 52 assert_response :success
53 53 assert_tag 'a', :attributes => {:href => '/projects/ecookbook/wiki/CookBook_documentation.txt'}
54 54 end
55 55
56 56 def test_show_page_with_name
57 57 get :show, :project_id => 1, :id => 'Another_page'
58 58 assert_response :success
59 59 assert_template 'show'
60 60 assert_tag :tag => 'h1', :content => /Another page/
61 61 # Included page with an inline image
62 62 assert_tag :tag => 'p', :content => /This is an inline image/
63 63 assert_tag :tag => 'img', :attributes => { :src => '/attachments/download/3',
64 64 :alt => 'This is a logo' }
65 65 end
66 66
67 67 def test_show_old_version
68 68 get :show, :project_id => 'ecookbook', :id => 'CookBook_documentation', :version => '2'
69 69 assert_response :success
70 70 assert_template 'show'
71 71
72 72 assert_select 'a[href=?]', '/projects/ecookbook/wiki/CookBook_documentation/1', :text => /Previous/
73 73 assert_select 'a[href=?]', '/projects/ecookbook/wiki/CookBook_documentation/2/diff', :text => /diff/
74 74 assert_select 'a[href=?]', '/projects/ecookbook/wiki/CookBook_documentation/3', :text => /Next/
75 75 assert_select 'a[href=?]', '/projects/ecookbook/wiki/CookBook_documentation', :text => /Current version/
76 76 end
77 77
78 def test_show_old_version_without_permission_should_be_denied
79 Role.anonymous.remove_permission! :view_wiki_edits
80
81 get :show, :project_id => 'ecookbook', :id => 'CookBook_documentation', :version => '2'
82 assert_redirected_to '/login?back_url=http%3A%2F%2Ftest.host%2Fprojects%2Fecookbook%2Fwiki%2FCookBook_documentation%2F2'
83 end
84
78 85 def test_show_first_version
79 86 get :show, :project_id => 'ecookbook', :id => 'CookBook_documentation', :version => '1'
80 87 assert_response :success
81 88 assert_template 'show'
82 89
83 90 assert_select 'a', :text => /Previous/, :count => 0
84 91 assert_select 'a', :text => /diff/, :count => 0
85 92 assert_select 'a[href=?]', '/projects/ecookbook/wiki/CookBook_documentation/2', :text => /Next/
86 93 assert_select 'a[href=?]', '/projects/ecookbook/wiki/CookBook_documentation', :text => /Current version/
87 94 end
88 95
89 96 def test_show_redirected_page
90 97 WikiRedirect.create!(:wiki_id => 1, :title => 'Old_title', :redirects_to => 'Another_page')
91 98
92 99 get :show, :project_id => 'ecookbook', :id => 'Old_title'
93 100 assert_redirected_to '/projects/ecookbook/wiki/Another_page'
94 101 end
95 102
96 103 def test_show_with_sidebar
97 104 page = Project.find(1).wiki.pages.new(:title => 'Sidebar')
98 105 page.content = WikiContent.new(:text => 'Side bar content for test_show_with_sidebar')
99 106 page.save!
100 107
101 108 get :show, :project_id => 1, :id => 'Another_page'
102 109 assert_response :success
103 110 assert_tag :tag => 'div', :attributes => {:id => 'sidebar'},
104 111 :content => /Side bar content for test_show_with_sidebar/
105 112 end
106 113
107 114 def test_show_should_display_section_edit_links
108 115 @request.session[:user_id] = 2
109 116 get :show, :project_id => 1, :id => 'Page with sections'
110 117 assert_no_tag 'a', :attributes => {
111 118 :href => '/projects/ecookbook/wiki/Page_with_sections/edit?section=1'
112 119 }
113 120 assert_tag 'a', :attributes => {
114 121 :href => '/projects/ecookbook/wiki/Page_with_sections/edit?section=2'
115 122 }
116 123 assert_tag 'a', :attributes => {
117 124 :href => '/projects/ecookbook/wiki/Page_with_sections/edit?section=3'
118 125 }
119 126 end
120 127
121 128 def test_show_current_version_should_display_section_edit_links
122 129 @request.session[:user_id] = 2
123 130 get :show, :project_id => 1, :id => 'Page with sections', :version => 3
124 131
125 132 assert_tag 'a', :attributes => {
126 133 :href => '/projects/ecookbook/wiki/Page_with_sections/edit?section=2'
127 134 }
128 135 end
129 136
130 137 def test_show_old_version_should_not_display_section_edit_links
131 138 @request.session[:user_id] = 2
132 139 get :show, :project_id => 1, :id => 'Page with sections', :version => 2
133 140
134 141 assert_no_tag 'a', :attributes => {
135 142 :href => '/projects/ecookbook/wiki/Page_with_sections/edit?section=2'
136 143 }
137 144 end
138 145
139 146 def test_show_unexistent_page_without_edit_right
140 147 get :show, :project_id => 1, :id => 'Unexistent page'
141 148 assert_response 404
142 149 end
143 150
144 151 def test_show_unexistent_page_with_edit_right
145 152 @request.session[:user_id] = 2
146 153 get :show, :project_id => 1, :id => 'Unexistent page'
147 154 assert_response :success
148 155 assert_template 'edit'
149 156 end
150 157
151 158 def test_show_unexistent_page_with_parent_should_preselect_parent
152 159 @request.session[:user_id] = 2
153 160 get :show, :project_id => 1, :id => 'Unexistent page', :parent => 'Another_page'
154 161 assert_response :success
155 162 assert_template 'edit'
156 163 assert_tag 'select', :attributes => {:name => 'wiki_page[parent_id]'},
157 164 :child => {:tag => 'option', :attributes => {:value => '2', :selected => 'selected'}}
158 165 end
159 166
160 167 def test_show_should_not_show_history_without_permission
161 168 Role.anonymous.remove_permission! :view_wiki_edits
162 169 get :show, :project_id => 1, :id => 'Page with sections', :version => 2
163 170
164 171 assert_response 302
165 172 end
166 173
167 174 def test_create_page
168 175 @request.session[:user_id] = 2
169 176 assert_difference 'WikiPage.count' do
170 177 assert_difference 'WikiContent.count' do
171 178 put :update, :project_id => 1,
172 179 :id => 'New page',
173 180 :content => {:comments => 'Created the page',
174 181 :text => "h1. New page\n\nThis is a new page",
175 182 :version => 0}
176 183 end
177 184 end
178 185 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'New_page'
179 186 page = Project.find(1).wiki.find_page('New page')
180 187 assert !page.new_record?
181 188 assert_not_nil page.content
182 189 assert_nil page.parent
183 190 assert_equal 'Created the page', page.content.comments
184 191 end
185 192
186 193 def test_create_page_with_attachments
187 194 @request.session[:user_id] = 2
188 195 assert_difference 'WikiPage.count' do
189 196 assert_difference 'Attachment.count' do
190 197 put :update, :project_id => 1,
191 198 :id => 'New page',
192 199 :content => {:comments => 'Created the page',
193 200 :text => "h1. New page\n\nThis is a new page",
194 201 :version => 0},
195 202 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
196 203 end
197 204 end
198 205 page = Project.find(1).wiki.find_page('New page')
199 206 assert_equal 1, page.attachments.count
200 207 assert_equal 'testfile.txt', page.attachments.first.filename
201 208 end
202 209
203 210 def test_create_page_with_parent
204 211 @request.session[:user_id] = 2
205 212 assert_difference 'WikiPage.count' do
206 213 put :update, :project_id => 1, :id => 'New page',
207 214 :content => {:text => "h1. New page\n\nThis is a new page", :version => 0},
208 215 :wiki_page => {:parent_id => 2}
209 216 end
210 217 page = Project.find(1).wiki.find_page('New page')
211 218 assert_equal WikiPage.find(2), page.parent
212 219 end
213 220
214 221 def test_edit_page
215 222 @request.session[:user_id] = 2
216 223 get :edit, :project_id => 'ecookbook', :id => 'Another_page'
217 224
218 225 assert_response :success
219 226 assert_template 'edit'
220 227
221 228 assert_tag 'textarea',
222 229 :attributes => { :name => 'content[text]' },
223 230 :content => "\n"+WikiPage.find_by_title('Another_page').content.text
224 231 end
225 232
226 233 def test_edit_section
227 234 @request.session[:user_id] = 2
228 235 get :edit, :project_id => 'ecookbook', :id => 'Page_with_sections', :section => 2
229 236
230 237 assert_response :success
231 238 assert_template 'edit'
232 239
233 240 page = WikiPage.find_by_title('Page_with_sections')
234 241 section, hash = Redmine::WikiFormatting::Textile::Formatter.new(page.content.text).get_section(2)
235 242
236 243 assert_tag 'textarea',
237 244 :attributes => { :name => 'content[text]' },
238 245 :content => "\n"+section
239 246 assert_tag 'input',
240 247 :attributes => { :name => 'section', :type => 'hidden', :value => '2' }
241 248 assert_tag 'input',
242 249 :attributes => { :name => 'section_hash', :type => 'hidden', :value => hash }
243 250 end
244 251
245 252 def test_edit_invalid_section_should_respond_with_404
246 253 @request.session[:user_id] = 2
247 254 get :edit, :project_id => 'ecookbook', :id => 'Page_with_sections', :section => 10
248 255
249 256 assert_response 404
250 257 end
251 258
252 259 def test_update_page
253 260 @request.session[:user_id] = 2
254 261 assert_no_difference 'WikiPage.count' do
255 262 assert_no_difference 'WikiContent.count' do
256 263 assert_difference 'WikiContent::Version.count' do
257 264 put :update, :project_id => 1,
258 265 :id => 'Another_page',
259 266 :content => {
260 267 :comments => "my comments",
261 268 :text => "edited",
262 269 :version => 1
263 270 }
264 271 end
265 272 end
266 273 end
267 274 assert_redirected_to '/projects/ecookbook/wiki/Another_page'
268 275
269 276 page = Wiki.find(1).pages.find_by_title('Another_page')
270 277 assert_equal "edited", page.content.text
271 278 assert_equal 2, page.content.version
272 279 assert_equal "my comments", page.content.comments
273 280 end
274 281
275 282 def test_update_page_with_parent
276 283 @request.session[:user_id] = 2
277 284 assert_no_difference 'WikiPage.count' do
278 285 assert_no_difference 'WikiContent.count' do
279 286 assert_difference 'WikiContent::Version.count' do
280 287 put :update, :project_id => 1,
281 288 :id => 'Another_page',
282 289 :content => {
283 290 :comments => "my comments",
284 291 :text => "edited",
285 292 :version => 1
286 293 },
287 294 :wiki_page => {:parent_id => '1'}
288 295 end
289 296 end
290 297 end
291 298 assert_redirected_to '/projects/ecookbook/wiki/Another_page'
292 299
293 300 page = Wiki.find(1).pages.find_by_title('Another_page')
294 301 assert_equal "edited", page.content.text
295 302 assert_equal 2, page.content.version
296 303 assert_equal "my comments", page.content.comments
297 304 assert_equal WikiPage.find(1), page.parent
298 305 end
299 306
300 307 def test_update_page_with_failure
301 308 @request.session[:user_id] = 2
302 309 assert_no_difference 'WikiPage.count' do
303 310 assert_no_difference 'WikiContent.count' do
304 311 assert_no_difference 'WikiContent::Version.count' do
305 312 put :update, :project_id => 1,
306 313 :id => 'Another_page',
307 314 :content => {
308 315 :comments => 'a' * 300, # failure here, comment is too long
309 316 :text => 'edited',
310 317 :version => 1
311 318 }
312 319 end
313 320 end
314 321 end
315 322 assert_response :success
316 323 assert_template 'edit'
317 324
318 325 assert_error_tag :descendant => {:content => /Comment is too long/}
319 326 assert_tag :tag => 'textarea', :attributes => {:id => 'content_text'}, :content => "\nedited"
320 327 assert_tag :tag => 'input', :attributes => {:id => 'content_version', :value => '1'}
321 328 end
322 329
323 330 def test_update_page_with_parent_change_only_should_not_create_content_version
324 331 @request.session[:user_id] = 2
325 332 assert_no_difference 'WikiPage.count' do
326 333 assert_no_difference 'WikiContent.count' do
327 334 assert_no_difference 'WikiContent::Version.count' do
328 335 put :update, :project_id => 1,
329 336 :id => 'Another_page',
330 337 :content => {
331 338 :comments => '',
332 339 :text => Wiki.find(1).find_page('Another_page').content.text,
333 340 :version => 1
334 341 },
335 342 :wiki_page => {:parent_id => '1'}
336 343 end
337 344 end
338 345 end
339 346 page = Wiki.find(1).pages.find_by_title('Another_page')
340 347 assert_equal 1, page.content.version
341 348 assert_equal WikiPage.find(1), page.parent
342 349 end
343 350
344 351 def test_update_page_with_attachments_only_should_not_create_content_version
345 352 @request.session[:user_id] = 2
346 353 assert_no_difference 'WikiPage.count' do
347 354 assert_no_difference 'WikiContent.count' do
348 355 assert_no_difference 'WikiContent::Version.count' do
349 356 assert_difference 'Attachment.count' do
350 357 put :update, :project_id => 1,
351 358 :id => 'Another_page',
352 359 :content => {
353 360 :comments => '',
354 361 :text => Wiki.find(1).find_page('Another_page').content.text,
355 362 :version => 1
356 363 },
357 364 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
358 365 end
359 366 end
360 367 end
361 368 end
362 369 page = Wiki.find(1).pages.find_by_title('Another_page')
363 370 assert_equal 1, page.content.version
364 371 end
365 372
366 373 def test_update_stale_page_should_not_raise_an_error
367 374 @request.session[:user_id] = 2
368 375 c = Wiki.find(1).find_page('Another_page').content
369 376 c.text = 'Previous text'
370 377 c.save!
371 378 assert_equal 2, c.version
372 379
373 380 assert_no_difference 'WikiPage.count' do
374 381 assert_no_difference 'WikiContent.count' do
375 382 assert_no_difference 'WikiContent::Version.count' do
376 383 put :update, :project_id => 1,
377 384 :id => 'Another_page',
378 385 :content => {
379 386 :comments => 'My comments',
380 387 :text => 'Text should not be lost',
381 388 :version => 1
382 389 }
383 390 end
384 391 end
385 392 end
386 393 assert_response :success
387 394 assert_template 'edit'
388 395 assert_tag :div,
389 396 :attributes => { :class => /error/ },
390 397 :content => /Data has been updated by another user/
391 398 assert_tag 'textarea',
392 399 :attributes => { :name => 'content[text]' },
393 400 :content => /Text should not be lost/
394 401 assert_tag 'input',
395 402 :attributes => { :name => 'content[comments]', :value => 'My comments' }
396 403
397 404 c.reload
398 405 assert_equal 'Previous text', c.text
399 406 assert_equal 2, c.version
400 407 end
401 408
402 409 def test_update_section
403 410 @request.session[:user_id] = 2
404 411 page = WikiPage.find_by_title('Page_with_sections')
405 412 section, hash = Redmine::WikiFormatting::Textile::Formatter.new(page.content.text).get_section(2)
406 413 text = page.content.text
407 414
408 415 assert_no_difference 'WikiPage.count' do
409 416 assert_no_difference 'WikiContent.count' do
410 417 assert_difference 'WikiContent::Version.count' do
411 418 put :update, :project_id => 1, :id => 'Page_with_sections',
412 419 :content => {
413 420 :text => "New section content",
414 421 :version => 3
415 422 },
416 423 :section => 2,
417 424 :section_hash => hash
418 425 end
419 426 end
420 427 end
421 428 assert_redirected_to '/projects/ecookbook/wiki/Page_with_sections'
422 429 assert_equal Redmine::WikiFormatting::Textile::Formatter.new(text).update_section(2, "New section content"), page.reload.content.text
423 430 end
424 431
425 432 def test_update_section_should_allow_stale_page_update
426 433 @request.session[:user_id] = 2
427 434 page = WikiPage.find_by_title('Page_with_sections')
428 435 section, hash = Redmine::WikiFormatting::Textile::Formatter.new(page.content.text).get_section(2)
429 436 text = page.content.text
430 437
431 438 assert_no_difference 'WikiPage.count' do
432 439 assert_no_difference 'WikiContent.count' do
433 440 assert_difference 'WikiContent::Version.count' do
434 441 put :update, :project_id => 1, :id => 'Page_with_sections',
435 442 :content => {
436 443 :text => "New section content",
437 444 :version => 2 # Current version is 3
438 445 },
439 446 :section => 2,
440 447 :section_hash => hash
441 448 end
442 449 end
443 450 end
444 451 assert_redirected_to '/projects/ecookbook/wiki/Page_with_sections'
445 452 page.reload
446 453 assert_equal Redmine::WikiFormatting::Textile::Formatter.new(text).update_section(2, "New section content"), page.content.text
447 454 assert_equal 4, page.content.version
448 455 end
449 456
450 457 def test_update_section_should_not_allow_stale_section_update
451 458 @request.session[:user_id] = 2
452 459
453 460 assert_no_difference 'WikiPage.count' do
454 461 assert_no_difference 'WikiContent.count' do
455 462 assert_no_difference 'WikiContent::Version.count' do
456 463 put :update, :project_id => 1, :id => 'Page_with_sections',
457 464 :content => {
458 465 :comments => 'My comments',
459 466 :text => "Text should not be lost",
460 467 :version => 3
461 468 },
462 469 :section => 2,
463 470 :section_hash => Digest::MD5.hexdigest("wrong hash")
464 471 end
465 472 end
466 473 end
467 474 assert_response :success
468 475 assert_template 'edit'
469 476 assert_tag :div,
470 477 :attributes => { :class => /error/ },
471 478 :content => /Data has been updated by another user/
472 479 assert_tag 'textarea',
473 480 :attributes => { :name => 'content[text]' },
474 481 :content => /Text should not be lost/
475 482 assert_tag 'input',
476 483 :attributes => { :name => 'content[comments]', :value => 'My comments' }
477 484 end
478 485
479 486 def test_preview
480 487 @request.session[:user_id] = 2
481 488 xhr :post, :preview, :project_id => 1, :id => 'CookBook_documentation',
482 489 :content => { :comments => '',
483 490 :text => 'this is a *previewed text*',
484 491 :version => 3 }
485 492 assert_response :success
486 493 assert_template 'common/_preview'
487 494 assert_tag :tag => 'strong', :content => /previewed text/
488 495 end
489 496
490 497 def test_preview_new_page
491 498 @request.session[:user_id] = 2
492 499 xhr :post, :preview, :project_id => 1, :id => 'New page',
493 500 :content => { :text => 'h1. New page',
494 501 :comments => '',
495 502 :version => 0 }
496 503 assert_response :success
497 504 assert_template 'common/_preview'
498 505 assert_tag :tag => 'h1', :content => /New page/
499 506 end
500 507
501 508 def test_history
502 509 @request.session[:user_id] = 2
503 510 get :history, :project_id => 'ecookbook', :id => 'CookBook_documentation'
504 511 assert_response :success
505 512 assert_template 'history'
506 513 assert_not_nil assigns(:versions)
507 514 assert_equal 3, assigns(:versions).size
508 515
509 516 assert_select "input[type=submit][name=commit]"
510 517 assert_select 'td' do
511 518 assert_select 'a[href=?]', '/projects/ecookbook/wiki/CookBook_documentation/2', :text => '2'
512 519 assert_select 'a[href=?]', '/projects/ecookbook/wiki/CookBook_documentation/2/annotate', :text => 'Annotate'
513 520 assert_select 'a[href=?]', '/projects/ecookbook/wiki/CookBook_documentation/2', :text => 'Delete'
514 521 end
515 522 end
516 523
517 524 def test_history_with_one_version
518 525 @request.session[:user_id] = 2
519 526 get :history, :project_id => 'ecookbook', :id => 'Another_page'
520 527 assert_response :success
521 528 assert_template 'history'
522 529 assert_not_nil assigns(:versions)
523 530 assert_equal 1, assigns(:versions).size
524 531 assert_select "input[type=submit][name=commit]", false
525 532 assert_select 'td' do
526 533 assert_select 'a[href=?]', '/projects/ecookbook/wiki/Another_page/1', :text => '1'
527 534 assert_select 'a[href=?]', '/projects/ecookbook/wiki/Another_page/1/annotate', :text => 'Annotate'
528 535 assert_select 'a[href=?]', '/projects/ecookbook/wiki/Another_page/1', :text => 'Delete', :count => 0
529 536 end
530 537 end
531 538
532 539 def test_diff
533 540 content = WikiPage.find(1).content
534 541 assert_difference 'WikiContent::Version.count', 2 do
535 542 content.text = "Line removed\nThis is a sample text for testing diffs"
536 543 content.save!
537 544 content.text = "This is a sample text for testing diffs\nLine added"
538 545 content.save!
539 546 end
540 547
541 548 get :diff, :project_id => 1, :id => 'CookBook_documentation', :version => content.version, :version_from => (content.version - 1)
542 549 assert_response :success
543 550 assert_template 'diff'
544 551 assert_select 'span.diff_out', :text => 'Line removed'
545 552 assert_select 'span.diff_in', :text => 'Line added'
546 553 end
547 554
548 555 def test_annotate
549 556 get :annotate, :project_id => 1, :id => 'CookBook_documentation', :version => 2
550 557 assert_response :success
551 558 assert_template 'annotate'
552 559
553 560 # Line 1
554 561 assert_tag :tag => 'tr', :child => {
555 562 :tag => 'th', :attributes => {:class => 'line-num'}, :content => '1', :sibling => {
556 563 :tag => 'td', :attributes => {:class => 'author'}, :content => /John Smith/, :sibling => {
557 564 :tag => 'td', :content => /h1\. CookBook documentation/
558 565 }
559 566 }
560 567 }
561 568
562 569 # Line 5
563 570 assert_tag :tag => 'tr', :child => {
564 571 :tag => 'th', :attributes => {:class => 'line-num'}, :content => '5', :sibling => {
565 572 :tag => 'td', :attributes => {:class => 'author'}, :content => /redMine Admin/, :sibling => {
566 573 :tag => 'td', :content => /Some updated \[\[documentation\]\] here/
567 574 }
568 575 }
569 576 }
570 577 end
571 578
572 579 def test_get_rename
573 580 @request.session[:user_id] = 2
574 581 get :rename, :project_id => 1, :id => 'Another_page'
575 582 assert_response :success
576 583 assert_template 'rename'
577 584 assert_tag 'option',
578 585 :attributes => {:value => ''},
579 586 :content => '',
580 587 :parent => {:tag => 'select', :attributes => {:name => 'wiki_page[parent_id]'}}
581 588 assert_no_tag 'option',
582 589 :attributes => {:selected => 'selected'},
583 590 :parent => {:tag => 'select', :attributes => {:name => 'wiki_page[parent_id]'}}
584 591 end
585 592
586 593 def test_get_rename_child_page
587 594 @request.session[:user_id] = 2
588 595 get :rename, :project_id => 1, :id => 'Child_1'
589 596 assert_response :success
590 597 assert_template 'rename'
591 598 assert_tag 'option',
592 599 :attributes => {:value => ''},
593 600 :content => '',
594 601 :parent => {:tag => 'select', :attributes => {:name => 'wiki_page[parent_id]'}}
595 602 assert_tag 'option',
596 603 :attributes => {:value => '2', :selected => 'selected'},
597 604 :content => /Another page/,
598 605 :parent => {
599 606 :tag => 'select',
600 607 :attributes => {:name => 'wiki_page[parent_id]'}
601 608 }
602 609 end
603 610
604 611 def test_rename_with_redirect
605 612 @request.session[:user_id] = 2
606 613 post :rename, :project_id => 1, :id => 'Another_page',
607 614 :wiki_page => { :title => 'Another renamed page',
608 615 :redirect_existing_links => 1 }
609 616 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'Another_renamed_page'
610 617 wiki = Project.find(1).wiki
611 618 # Check redirects
612 619 assert_not_nil wiki.find_page('Another page')
613 620 assert_nil wiki.find_page('Another page', :with_redirect => false)
614 621 end
615 622
616 623 def test_rename_without_redirect
617 624 @request.session[:user_id] = 2
618 625 post :rename, :project_id => 1, :id => 'Another_page',
619 626 :wiki_page => { :title => 'Another renamed page',
620 627 :redirect_existing_links => "0" }
621 628 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'Another_renamed_page'
622 629 wiki = Project.find(1).wiki
623 630 # Check that there's no redirects
624 631 assert_nil wiki.find_page('Another page')
625 632 end
626 633
627 634 def test_rename_with_parent_assignment
628 635 @request.session[:user_id] = 2
629 636 post :rename, :project_id => 1, :id => 'Another_page',
630 637 :wiki_page => { :title => 'Another page', :redirect_existing_links => "0", :parent_id => '4' }
631 638 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'Another_page'
632 639 assert_equal WikiPage.find(4), WikiPage.find_by_title('Another_page').parent
633 640 end
634 641
635 642 def test_rename_with_parent_unassignment
636 643 @request.session[:user_id] = 2
637 644 post :rename, :project_id => 1, :id => 'Child_1',
638 645 :wiki_page => { :title => 'Child 1', :redirect_existing_links => "0", :parent_id => '' }
639 646 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'Child_1'
640 647 assert_nil WikiPage.find_by_title('Child_1').parent
641 648 end
642 649
643 650 def test_destroy_a_page_without_children_should_not_ask_confirmation
644 651 @request.session[:user_id] = 2
645 652 delete :destroy, :project_id => 1, :id => 'Child_2'
646 653 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
647 654 end
648 655
649 656 def test_destroy_parent_should_ask_confirmation
650 657 @request.session[:user_id] = 2
651 658 assert_no_difference('WikiPage.count') do
652 659 delete :destroy, :project_id => 1, :id => 'Another_page'
653 660 end
654 661 assert_response :success
655 662 assert_template 'destroy'
656 663 assert_select 'form' do
657 664 assert_select 'input[name=todo][value=nullify]'
658 665 assert_select 'input[name=todo][value=destroy]'
659 666 assert_select 'input[name=todo][value=reassign]'
660 667 end
661 668 end
662 669
663 670 def test_destroy_parent_with_nullify_should_delete_parent_only
664 671 @request.session[:user_id] = 2
665 672 assert_difference('WikiPage.count', -1) do
666 673 delete :destroy, :project_id => 1, :id => 'Another_page', :todo => 'nullify'
667 674 end
668 675 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
669 676 assert_nil WikiPage.find_by_id(2)
670 677 end
671 678
672 679 def test_destroy_parent_with_cascade_should_delete_descendants
673 680 @request.session[:user_id] = 2
674 681 assert_difference('WikiPage.count', -4) do
675 682 delete :destroy, :project_id => 1, :id => 'Another_page', :todo => 'destroy'
676 683 end
677 684 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
678 685 assert_nil WikiPage.find_by_id(2)
679 686 assert_nil WikiPage.find_by_id(5)
680 687 end
681 688
682 689 def test_destroy_parent_with_reassign
683 690 @request.session[:user_id] = 2
684 691 assert_difference('WikiPage.count', -1) do
685 692 delete :destroy, :project_id => 1, :id => 'Another_page', :todo => 'reassign', :reassign_to_id => 1
686 693 end
687 694 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
688 695 assert_nil WikiPage.find_by_id(2)
689 696 assert_equal WikiPage.find(1), WikiPage.find_by_id(5).parent
690 697 end
691 698
692 699 def test_destroy_version
693 700 @request.session[:user_id] = 2
694 701 assert_difference 'WikiContent::Version.count', -1 do
695 702 assert_no_difference 'WikiContent.count' do
696 703 assert_no_difference 'WikiPage.count' do
697 704 delete :destroy_version, :project_id => 'ecookbook', :id => 'CookBook_documentation', :version => 2
698 705 assert_redirected_to '/projects/ecookbook/wiki/CookBook_documentation/history'
699 706 end
700 707 end
701 708 end
702 709 end
703 710
704 711 def test_index
705 712 get :index, :project_id => 'ecookbook'
706 713 assert_response :success
707 714 assert_template 'index'
708 715 pages = assigns(:pages)
709 716 assert_not_nil pages
710 717 assert_equal Project.find(1).wiki.pages.size, pages.size
711 718 assert_equal pages.first.content.updated_on, pages.first.updated_on
712 719
713 720 assert_tag :ul, :attributes => { :class => 'pages-hierarchy' },
714 721 :child => { :tag => 'li', :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/CookBook_documentation' },
715 722 :content => 'CookBook documentation' },
716 723 :child => { :tag => 'ul',
717 724 :child => { :tag => 'li',
718 725 :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/Page_with_an_inline_image' },
719 726 :content => 'Page with an inline image' } } } },
720 727 :child => { :tag => 'li', :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/Another_page' },
721 728 :content => 'Another page' } }
722 729 end
723 730
724 731 def test_index_should_include_atom_link
725 732 get :index, :project_id => 'ecookbook'
726 733 assert_tag 'a', :attributes => { :href => '/projects/ecookbook/activity.atom?show_wiki_edits=1'}
727 734 end
728 735
729 736 def test_export_to_html
730 737 @request.session[:user_id] = 2
731 738 get :export, :project_id => 'ecookbook'
732 739
733 740 assert_response :success
734 741 assert_not_nil assigns(:pages)
735 742 assert assigns(:pages).any?
736 743 assert_equal "text/html", @response.content_type
737 744
738 745 assert_select "a[name=?]", "CookBook_documentation"
739 746 assert_select "a[name=?]", "Another_page"
740 747 assert_select "a[name=?]", "Page_with_an_inline_image"
741 748 end
742 749
743 750 def test_export_to_pdf
744 751 @request.session[:user_id] = 2
745 752 get :export, :project_id => 'ecookbook', :format => 'pdf'
746 753
747 754 assert_response :success
748 755 assert_not_nil assigns(:pages)
749 756 assert assigns(:pages).any?
750 757 assert_equal 'application/pdf', @response.content_type
751 758 assert_equal 'attachment; filename="ecookbook.pdf"', @response.headers['Content-Disposition']
752 759 assert @response.body.starts_with?('%PDF')
753 760 end
754 761
755 762 def test_export_without_permission_should_be_denied
756 763 @request.session[:user_id] = 2
757 764 Role.find_by_name('Manager').remove_permission! :export_wiki_pages
758 765 get :export, :project_id => 'ecookbook'
759 766
760 767 assert_response 403
761 768 end
762 769
763 770 def test_date_index
764 771 get :date_index, :project_id => 'ecookbook'
765 772
766 773 assert_response :success
767 774 assert_template 'date_index'
768 775 assert_not_nil assigns(:pages)
769 776 assert_not_nil assigns(:pages_by_date)
770 777
771 778 assert_tag 'a', :attributes => { :href => '/projects/ecookbook/activity.atom?show_wiki_edits=1'}
772 779 end
773 780
774 781 def test_not_found
775 782 get :show, :project_id => 999
776 783 assert_response 404
777 784 end
778 785
779 786 def test_protect_page
780 787 page = WikiPage.find_by_wiki_id_and_title(1, 'Another_page')
781 788 assert !page.protected?
782 789 @request.session[:user_id] = 2
783 790 post :protect, :project_id => 1, :id => page.title, :protected => '1'
784 791 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'Another_page'
785 792 assert page.reload.protected?
786 793 end
787 794
788 795 def test_unprotect_page
789 796 page = WikiPage.find_by_wiki_id_and_title(1, 'CookBook_documentation')
790 797 assert page.protected?
791 798 @request.session[:user_id] = 2
792 799 post :protect, :project_id => 1, :id => page.title, :protected => '0'
793 800 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'CookBook_documentation'
794 801 assert !page.reload.protected?
795 802 end
796 803
797 804 def test_show_page_with_edit_link
798 805 @request.session[:user_id] = 2
799 806 get :show, :project_id => 1
800 807 assert_response :success
801 808 assert_template 'show'
802 809 assert_tag :tag => 'a', :attributes => { :href => '/projects/1/wiki/CookBook_documentation/edit' }
803 810 end
804 811
805 812 def test_show_page_without_edit_link
806 813 @request.session[:user_id] = 4
807 814 get :show, :project_id => 1
808 815 assert_response :success
809 816 assert_template 'show'
810 817 assert_no_tag :tag => 'a', :attributes => { :href => '/projects/1/wiki/CookBook_documentation/edit' }
811 818 end
812 819
813 820 def test_show_pdf
814 821 @request.session[:user_id] = 2
815 822 get :show, :project_id => 1, :format => 'pdf'
816 823 assert_response :success
817 824 assert_not_nil assigns(:page)
818 825 assert_equal 'application/pdf', @response.content_type
819 826 assert_equal 'attachment; filename="CookBook_documentation.pdf"',
820 827 @response.headers['Content-Disposition']
821 828 end
822 829
823 830 def test_show_html
824 831 @request.session[:user_id] = 2
825 832 get :show, :project_id => 1, :format => 'html'
826 833 assert_response :success
827 834 assert_not_nil assigns(:page)
828 835 assert_equal 'text/html', @response.content_type
829 836 assert_equal 'attachment; filename="CookBook_documentation.html"',
830 837 @response.headers['Content-Disposition']
831 838 assert_tag 'h1', :content => 'CookBook documentation'
832 839 end
833 840
834 841 def test_show_versioned_html
835 842 @request.session[:user_id] = 2
836 843 get :show, :project_id => 1, :format => 'html', :version => 2
837 844 assert_response :success
838 845 assert_not_nil assigns(:content)
839 846 assert_equal 2, assigns(:content).version
840 847 assert_equal 'text/html', @response.content_type
841 848 assert_equal 'attachment; filename="CookBook_documentation.html"',
842 849 @response.headers['Content-Disposition']
843 850 assert_tag 'h1', :content => 'CookBook documentation'
844 851 end
845 852
846 853 def test_show_txt
847 854 @request.session[:user_id] = 2
848 855 get :show, :project_id => 1, :format => 'txt'
849 856 assert_response :success
850 857 assert_not_nil assigns(:page)
851 858 assert_equal 'text/plain', @response.content_type
852 859 assert_equal 'attachment; filename="CookBook_documentation.txt"',
853 860 @response.headers['Content-Disposition']
854 861 assert_include 'h1. CookBook documentation', @response.body
855 862 end
856 863
857 864 def test_show_versioned_txt
858 865 @request.session[:user_id] = 2
859 866 get :show, :project_id => 1, :format => 'txt', :version => 2
860 867 assert_response :success
861 868 assert_not_nil assigns(:content)
862 869 assert_equal 2, assigns(:content).version
863 870 assert_equal 'text/plain', @response.content_type
864 871 assert_equal 'attachment; filename="CookBook_documentation.txt"',
865 872 @response.headers['Content-Disposition']
866 873 assert_include 'h1. CookBook documentation', @response.body
867 874 end
868 875
869 876 def test_edit_unprotected_page
870 877 # Non members can edit unprotected wiki pages
871 878 @request.session[:user_id] = 4
872 879 get :edit, :project_id => 1, :id => 'Another_page'
873 880 assert_response :success
874 881 assert_template 'edit'
875 882 end
876 883
877 884 def test_edit_protected_page_by_nonmember
878 885 # Non members can't edit protected wiki pages
879 886 @request.session[:user_id] = 4
880 887 get :edit, :project_id => 1, :id => 'CookBook_documentation'
881 888 assert_response 403
882 889 end
883 890
884 891 def test_edit_protected_page_by_member
885 892 @request.session[:user_id] = 2
886 893 get :edit, :project_id => 1, :id => 'CookBook_documentation'
887 894 assert_response :success
888 895 assert_template 'edit'
889 896 end
890 897
891 898 def test_history_of_non_existing_page_should_return_404
892 899 get :history, :project_id => 1, :id => 'Unknown_page'
893 900 assert_response 404
894 901 end
895 902
896 903 def test_add_attachment
897 904 @request.session[:user_id] = 2
898 905 assert_difference 'Attachment.count' do
899 906 post :add_attachment, :project_id => 1, :id => 'CookBook_documentation',
900 907 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
901 908 end
902 909 attachment = Attachment.first(:order => 'id DESC')
903 910 assert_equal Wiki.find(1).find_page('CookBook_documentation'), attachment.container
904 911 end
905 912 end
@@ -1,131 +1,159
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 class RoutingWikiTest < ActionController::IntegrationTest
21 21 def test_wiki_matching
22 22 assert_routing(
23 23 { :method => 'get', :path => "/projects/567/wiki" },
24 24 { :controller => 'wiki', :action => 'show', :project_id => '567' }
25 25 )
26 26 assert_routing(
27 27 { :method => 'get', :path => "/projects/567/wiki/lalala" },
28 28 { :controller => 'wiki', :action => 'show', :project_id => '567',
29 29 :id => 'lalala' }
30 30 )
31 31 assert_routing(
32 32 { :method => 'get', :path => "/projects/567/wiki/lalala.pdf" },
33 33 { :controller => 'wiki', :action => 'show', :project_id => '567',
34 34 :id => 'lalala', :format => 'pdf' }
35 35 )
36 36 assert_routing(
37 { :method => 'get', :path => "/projects/567/wiki/lalala.xml" },
38 { :controller => 'wiki', :action => 'show', :project_id => '567',
39 :id => 'lalala', :format => 'xml' }
40 )
41 assert_routing(
42 { :method => 'get', :path => "/projects/567/wiki/lalala.json" },
43 { :controller => 'wiki', :action => 'show', :project_id => '567',
44 :id => 'lalala', :format => 'json' }
45 )
46 assert_routing(
37 47 { :method => 'get', :path => "/projects/1/wiki/CookBook_documentation/diff" },
38 48 { :controller => 'wiki', :action => 'diff', :project_id => '1',
39 49 :id => 'CookBook_documentation' }
40 50 )
41 51 assert_routing(
42 52 { :method => 'get', :path => "/projects/1/wiki/CookBook_documentation/2" },
43 53 { :controller => 'wiki', :action => 'show', :project_id => '1',
44 54 :id => 'CookBook_documentation', :version => '2' }
45 55 )
46 56 assert_routing(
57 { :method => 'get', :path => "/projects/1/wiki/CookBook_documentation/2.xml" },
58 { :controller => 'wiki', :action => 'show', :project_id => '1',
59 :id => 'CookBook_documentation', :version => '2', :format => 'xml' }
60 )
61 assert_routing(
62 { :method => 'get', :path => "/projects/1/wiki/CookBook_documentation/2.json" },
63 { :controller => 'wiki', :action => 'show', :project_id => '1',
64 :id => 'CookBook_documentation', :version => '2', :format => 'json' }
65 )
66 assert_routing(
47 67 { :method => 'get', :path => "/projects/1/wiki/CookBook_documentation/2/diff" },
48 68 { :controller => 'wiki', :action => 'diff', :project_id => '1',
49 69 :id => 'CookBook_documentation', :version => '2' }
50 70 )
51 71 assert_routing(
52 72 { :method => 'get', :path => "/projects/1/wiki/CookBook_documentation/2/annotate" },
53 73 { :controller => 'wiki', :action => 'annotate', :project_id => '1',
54 74 :id => 'CookBook_documentation', :version => '2' }
55 75 )
56 76 end
57 77
58 78 def test_wiki_misc
59 79 assert_routing(
60 80 { :method => 'get', :path => "/projects/567/wiki/date_index" },
61 81 { :controller => 'wiki', :action => 'date_index', :project_id => '567' }
62 82 )
63 83 assert_routing(
64 84 { :method => 'get', :path => "/projects/567/wiki/export" },
65 85 { :controller => 'wiki', :action => 'export', :project_id => '567' }
66 86 )
67 87 assert_routing(
68 88 { :method => 'get', :path => "/projects/567/wiki/export.pdf" },
69 89 { :controller => 'wiki', :action => 'export', :project_id => '567', :format => 'pdf' }
70 90 )
71 91 assert_routing(
72 92 { :method => 'get', :path => "/projects/567/wiki/index" },
73 93 { :controller => 'wiki', :action => 'index', :project_id => '567' }
74 94 )
95 assert_routing(
96 { :method => 'get', :path => "/projects/567/wiki/index.xml" },
97 { :controller => 'wiki', :action => 'index', :project_id => '567', :format => 'xml' }
98 )
99 assert_routing(
100 { :method => 'get', :path => "/projects/567/wiki/index.json" },
101 { :controller => 'wiki', :action => 'index', :project_id => '567', :format => 'json' }
102 )
75 103 end
76 104
77 105 def test_wiki_resources
78 106 assert_routing(
79 107 { :method => 'get', :path => "/projects/567/wiki/my_page/edit" },
80 108 { :controller => 'wiki', :action => 'edit', :project_id => '567',
81 109 :id => 'my_page' }
82 110 )
83 111 assert_routing(
84 112 { :method => 'get', :path => "/projects/1/wiki/CookBook_documentation/history" },
85 113 { :controller => 'wiki', :action => 'history', :project_id => '1',
86 114 :id => 'CookBook_documentation' }
87 115 )
88 116 assert_routing(
89 117 { :method => 'get', :path => "/projects/22/wiki/ladida/rename" },
90 118 { :controller => 'wiki', :action => 'rename', :project_id => '22',
91 119 :id => 'ladida' }
92 120 )
93 121 ["post", "put"].each do |method|
94 122 assert_routing(
95 123 { :method => method, :path => "/projects/567/wiki/CookBook_documentation/preview" },
96 124 { :controller => 'wiki', :action => 'preview', :project_id => '567',
97 125 :id => 'CookBook_documentation' }
98 126 )
99 127 end
100 128 assert_routing(
101 129 { :method => 'post', :path => "/projects/22/wiki/ladida/rename" },
102 130 { :controller => 'wiki', :action => 'rename', :project_id => '22',
103 131 :id => 'ladida' }
104 132 )
105 133 assert_routing(
106 134 { :method => 'post', :path => "/projects/22/wiki/ladida/protect" },
107 135 { :controller => 'wiki', :action => 'protect', :project_id => '22',
108 136 :id => 'ladida' }
109 137 )
110 138 assert_routing(
111 139 { :method => 'post', :path => "/projects/22/wiki/ladida/add_attachment" },
112 140 { :controller => 'wiki', :action => 'add_attachment', :project_id => '22',
113 141 :id => 'ladida' }
114 142 )
115 143 assert_routing(
116 144 { :method => 'put', :path => "/projects/567/wiki/my_page" },
117 145 { :controller => 'wiki', :action => 'update', :project_id => '567',
118 146 :id => 'my_page' }
119 147 )
120 148 assert_routing(
121 149 { :method => 'delete', :path => "/projects/22/wiki/ladida" },
122 150 { :controller => 'wiki', :action => 'destroy', :project_id => '22',
123 151 :id => 'ladida' }
124 152 )
125 153 assert_routing(
126 154 { :method => 'delete', :path => "/projects/22/wiki/ladida/3" },
127 155 { :controller => 'wiki', :action => 'destroy_version', :project_id => '22',
128 156 :id => 'ladida', :version => '3' }
129 157 )
130 158 end
131 159 end
@@ -1,162 +1,163
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class WikiPageTest < ActiveSupport::TestCase
21 21 fixtures :projects, :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions
22 22
23 23 def setup
24 24 @wiki = Wiki.find(1)
25 25 @page = @wiki.pages.first
26 26 end
27 27
28 28 def test_create
29 29 page = WikiPage.new(:wiki => @wiki)
30 30 assert !page.save
31 31 assert_equal 1, page.errors.count
32 32
33 33 page.title = "Page"
34 34 assert page.save
35 35 page.reload
36 36 assert !page.protected?
37 37
38 38 @wiki.reload
39 39 assert @wiki.pages.include?(page)
40 40 end
41 41
42 42 def test_sidebar_should_be_protected_by_default
43 43 page = @wiki.find_or_new_page('sidebar')
44 44 assert page.new_record?
45 45 assert page.protected?
46 46 end
47 47
48 48 def test_find_or_new_page
49 49 page = @wiki.find_or_new_page("CookBook documentation")
50 50 assert_kind_of WikiPage, page
51 51 assert !page.new_record?
52 52
53 53 page = @wiki.find_or_new_page("Non existing page")
54 54 assert_kind_of WikiPage, page
55 55 assert page.new_record?
56 56 end
57 57
58 58 def test_parent_title
59 59 page = WikiPage.find_by_title('Another_page')
60 60 assert_nil page.parent_title
61 61
62 62 page = WikiPage.find_by_title('Page_with_an_inline_image')
63 63 assert_equal 'CookBook documentation', page.parent_title
64 64 end
65 65
66 66 def test_assign_parent
67 67 page = WikiPage.find_by_title('Another_page')
68 68 page.parent_title = 'CookBook documentation'
69 69 assert page.save
70 70 page.reload
71 71 assert_equal WikiPage.find_by_title('CookBook_documentation'), page.parent
72 72 end
73 73
74 74 def test_unassign_parent
75 75 page = WikiPage.find_by_title('Page_with_an_inline_image')
76 76 page.parent_title = ''
77 77 assert page.save
78 78 page.reload
79 79 assert_nil page.parent
80 80 end
81 81
82 82 def test_parent_validation
83 83 page = WikiPage.find_by_title('CookBook_documentation')
84 84
85 85 # A page that doesn't exist
86 86 page.parent_title = 'Unknown title'
87 87 assert !page.save
88 88 assert_include I18n.translate('activerecord.errors.messages.invalid'),
89 89 page.errors[:parent_title]
90 90 # A child page
91 91 page.parent_title = 'Page_with_an_inline_image'
92 92 assert !page.save
93 93 assert_include I18n.translate('activerecord.errors.messages.circular_dependency'),
94 94 page.errors[:parent_title]
95 95 # The page itself
96 96 page.parent_title = 'CookBook_documentation'
97 97 assert !page.save
98 98 assert_include I18n.translate('activerecord.errors.messages.circular_dependency'),
99 99 page.errors[:parent_title]
100 100 page.parent_title = 'Another_page'
101 101 assert page.save
102 102 end
103 103
104 104 def test_destroy
105 105 page = WikiPage.find(1)
106 106 page.destroy
107 107 assert_nil WikiPage.find_by_id(1)
108 108 # make sure that page content and its history are deleted
109 109 assert WikiContent.find_all_by_page_id(1).empty?
110 110 assert WikiContent.versioned_class.find_all_by_page_id(1).empty?
111 111 end
112 112
113 113 def test_destroy_should_not_nullify_children
114 114 page = WikiPage.find(2)
115 115 child_ids = page.child_ids
116 116 assert child_ids.any?
117 117 page.destroy
118 118 assert_nil WikiPage.find_by_id(2)
119 119
120 120 children = WikiPage.find_all_by_id(child_ids)
121 121 assert_equal child_ids.size, children.size
122 122 children.each do |child|
123 123 assert_nil child.parent_id
124 124 end
125 125 end
126 126
127 127 def test_updated_on_eager_load
128 128 page = WikiPage.with_updated_on.first(:order => 'id')
129 129 assert page.is_a?(WikiPage)
130 130 assert_not_nil page.read_attribute(:updated_on)
131 131 assert_equal Time.gm(2007, 3, 6, 23, 10, 51), page.content.updated_on
132 132 assert_equal page.content.updated_on, page.updated_on
133 assert_not_nil page.read_attribute(:version)
133 134 end
134 135
135 136 def test_descendants
136 137 page = WikiPage.create!(:wiki => @wiki, :title => 'Parent')
137 138 child1 = WikiPage.create!(:wiki => @wiki, :title => 'Child1', :parent => page)
138 139 child11 = WikiPage.create!(:wiki => @wiki, :title => 'Child11', :parent => child1)
139 140 child111 = WikiPage.create!(:wiki => @wiki, :title => 'Child111', :parent => child11)
140 141 child2 = WikiPage.create!(:wiki => @wiki, :title => 'Child2', :parent => page)
141 142
142 143 assert_equal %w(Child1 Child11 Child111 Child2), page.descendants.map(&:title).sort
143 144 assert_equal %w(Child1 Child11 Child111 Child2), page.descendants(nil).map(&:title).sort
144 145 assert_equal %w(Child1 Child11 Child2), page.descendants(2).map(&:title).sort
145 146 assert_equal %w(Child1 Child2), page.descendants(1).map(&:title).sort
146 147
147 148 assert_equal %w(Child1 Child11 Child111 Child2 Parent), page.self_and_descendants.map(&:title).sort
148 149 assert_equal %w(Child1 Child11 Child111 Child2 Parent), page.self_and_descendants(nil).map(&:title).sort
149 150 assert_equal %w(Child1 Child11 Child2 Parent), page.self_and_descendants(2).map(&:title).sort
150 151 assert_equal %w(Child1 Child2 Parent), page.self_and_descendants(1).map(&:title).sort
151 152 end
152 153
153 154 def test_diff_for_page_with_deleted_version_should_pick_the_previous_available_version
154 155 WikiContent::Version.find_by_page_id_and_version(1, 2).destroy
155 156
156 157 page = WikiPage.find(1)
157 158 diff = page.diff(3)
158 159 assert_not_nil diff
159 160 assert_equal 3, diff.content_to.version
160 161 assert_equal 1, diff.content_from.version
161 162 end
162 163 end
General Comments 0
You need to be logged in to leave comments. Login now