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