##// END OF EJS Templates
Makes the wiki sidebar editable (#5208)....
Jean-Philippe Lang -
r3518:58792737394c
parent child
Show More
@@ -1,248 +1,248
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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
19
20 class WikiController < ApplicationController
20 class WikiController < ApplicationController
21 default_search_scope :wiki_pages
21 default_search_scope :wiki_pages
22 before_filter :find_wiki, :authorize
22 before_filter :find_wiki, :authorize
23 before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy]
23 before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy]
24
24
25 verify :method => :post, :only => [:destroy, :protect], :redirect_to => { :action => :index }
25 verify :method => :post, :only => [:destroy, :protect], :redirect_to => { :action => :index }
26
26
27 helper :attachments
27 helper :attachments
28 include AttachmentsHelper
28 include AttachmentsHelper
29 helper :watchers
29 helper :watchers
30
30
31 # display a page (in editing mode if it doesn't exist)
31 # display a page (in editing mode if it doesn't exist)
32 def index
32 def index
33 page_title = params[:page]
33 page_title = params[:page]
34 @page = @wiki.find_or_new_page(page_title)
34 @page = @wiki.find_or_new_page(page_title)
35 if @page.new_record?
35 if @page.new_record?
36 if User.current.allowed_to?(:edit_wiki_pages, @project)
36 if User.current.allowed_to?(:edit_wiki_pages, @project) && editable?
37 edit
37 edit
38 render :action => 'edit'
38 render :action => 'edit'
39 else
39 else
40 render_404
40 render_404
41 end
41 end
42 return
42 return
43 end
43 end
44 if params[:version] && !User.current.allowed_to?(:view_wiki_edits, @project)
44 if params[:version] && !User.current.allowed_to?(:view_wiki_edits, @project)
45 # Redirects user to the current version if he's not allowed to view previous versions
45 # Redirects user to the current version if he's not allowed to view previous versions
46 redirect_to :version => nil
46 redirect_to :version => nil
47 return
47 return
48 end
48 end
49 @content = @page.content_for_version(params[:version])
49 @content = @page.content_for_version(params[:version])
50 if User.current.allowed_to?(:export_wiki_pages, @project)
50 if User.current.allowed_to?(:export_wiki_pages, @project)
51 if params[:format] == 'html'
51 if params[:format] == 'html'
52 export = render_to_string :action => 'export', :layout => false
52 export = render_to_string :action => 'export', :layout => false
53 send_data(export, :type => 'text/html', :filename => "#{@page.title}.html")
53 send_data(export, :type => 'text/html', :filename => "#{@page.title}.html")
54 return
54 return
55 elsif params[:format] == 'txt'
55 elsif params[:format] == 'txt'
56 send_data(@content.text, :type => 'text/plain', :filename => "#{@page.title}.txt")
56 send_data(@content.text, :type => 'text/plain', :filename => "#{@page.title}.txt")
57 return
57 return
58 end
58 end
59 end
59 end
60 @editable = editable?
60 @editable = editable?
61 render :action => 'show'
61 render :action => 'show'
62 end
62 end
63
63
64 # edit an existing page or a new one
64 # edit an existing page or a new one
65 def edit
65 def edit
66 @page = @wiki.find_or_new_page(params[:page])
66 @page = @wiki.find_or_new_page(params[:page])
67 return render_403 unless editable?
67 return render_403 unless editable?
68 @page.content = WikiContent.new(:page => @page) if @page.new_record?
68 @page.content = WikiContent.new(:page => @page) if @page.new_record?
69
69
70 @content = @page.content_for_version(params[:version])
70 @content = @page.content_for_version(params[:version])
71 @content.text = initial_page_content(@page) if @content.text.blank?
71 @content.text = initial_page_content(@page) if @content.text.blank?
72 # don't keep previous comment
72 # don't keep previous comment
73 @content.comments = nil
73 @content.comments = nil
74 if request.get?
74 if request.get?
75 # To prevent StaleObjectError exception when reverting to a previous version
75 # To prevent StaleObjectError exception when reverting to a previous version
76 @content.version = @page.content.version
76 @content.version = @page.content.version
77 else
77 else
78 if !@page.new_record? && @content.text == params[:content][:text]
78 if !@page.new_record? && @content.text == params[:content][:text]
79 attachments = Attachment.attach_files(@page, params[:attachments])
79 attachments = Attachment.attach_files(@page, params[:attachments])
80 render_attachment_warning_if_needed(@page)
80 render_attachment_warning_if_needed(@page)
81 # don't save if text wasn't changed
81 # don't save if text wasn't changed
82 redirect_to :action => 'index', :id => @project, :page => @page.title
82 redirect_to :action => 'index', :id => @project, :page => @page.title
83 return
83 return
84 end
84 end
85 #@content.text = params[:content][:text]
85 #@content.text = params[:content][:text]
86 #@content.comments = params[:content][:comments]
86 #@content.comments = params[:content][:comments]
87 @content.attributes = params[:content]
87 @content.attributes = params[:content]
88 @content.author = User.current
88 @content.author = User.current
89 # if page is new @page.save will also save content, but not if page isn't a new record
89 # if page is new @page.save will also save content, but not if page isn't a new record
90 if (@page.new_record? ? @page.save : @content.save)
90 if (@page.new_record? ? @page.save : @content.save)
91 attachments = Attachment.attach_files(@page, params[:attachments])
91 attachments = Attachment.attach_files(@page, params[:attachments])
92 render_attachment_warning_if_needed(@page)
92 render_attachment_warning_if_needed(@page)
93 call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
93 call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
94 redirect_to :action => 'index', :id => @project, :page => @page.title
94 redirect_to :action => 'index', :id => @project, :page => @page.title
95 end
95 end
96 end
96 end
97 rescue ActiveRecord::StaleObjectError
97 rescue ActiveRecord::StaleObjectError
98 # Optimistic locking exception
98 # Optimistic locking exception
99 flash[:error] = l(:notice_locking_conflict)
99 flash[:error] = l(:notice_locking_conflict)
100 end
100 end
101
101
102 # rename a page
102 # rename a page
103 def rename
103 def rename
104 return render_403 unless editable?
104 return render_403 unless editable?
105 @page.redirect_existing_links = true
105 @page.redirect_existing_links = true
106 # used to display the *original* title if some AR validation errors occur
106 # used to display the *original* title if some AR validation errors occur
107 @original_title = @page.pretty_title
107 @original_title = @page.pretty_title
108 if request.post? && @page.update_attributes(params[:wiki_page])
108 if request.post? && @page.update_attributes(params[:wiki_page])
109 flash[:notice] = l(:notice_successful_update)
109 flash[:notice] = l(:notice_successful_update)
110 redirect_to :action => 'index', :id => @project, :page => @page.title
110 redirect_to :action => 'index', :id => @project, :page => @page.title
111 end
111 end
112 end
112 end
113
113
114 def protect
114 def protect
115 @page.update_attribute :protected, params[:protected]
115 @page.update_attribute :protected, params[:protected]
116 redirect_to :action => 'index', :id => @project, :page => @page.title
116 redirect_to :action => 'index', :id => @project, :page => @page.title
117 end
117 end
118
118
119 # show page history
119 # show page history
120 def history
120 def history
121 @version_count = @page.content.versions.count
121 @version_count = @page.content.versions.count
122 @version_pages = Paginator.new self, @version_count, per_page_option, params['p']
122 @version_pages = Paginator.new self, @version_count, per_page_option, params['p']
123 # don't load text
123 # don't load text
124 @versions = @page.content.versions.find :all,
124 @versions = @page.content.versions.find :all,
125 :select => "id, author_id, comments, updated_on, version",
125 :select => "id, author_id, comments, updated_on, version",
126 :order => 'version DESC',
126 :order => 'version DESC',
127 :limit => @version_pages.items_per_page + 1,
127 :limit => @version_pages.items_per_page + 1,
128 :offset => @version_pages.current.offset
128 :offset => @version_pages.current.offset
129
129
130 render :layout => false if request.xhr?
130 render :layout => false if request.xhr?
131 end
131 end
132
132
133 def diff
133 def diff
134 @diff = @page.diff(params[:version], params[:version_from])
134 @diff = @page.diff(params[:version], params[:version_from])
135 render_404 unless @diff
135 render_404 unless @diff
136 end
136 end
137
137
138 def annotate
138 def annotate
139 @annotate = @page.annotate(params[:version])
139 @annotate = @page.annotate(params[:version])
140 render_404 unless @annotate
140 render_404 unless @annotate
141 end
141 end
142
142
143 # Removes a wiki page and its history
143 # Removes a wiki page and its history
144 # Children can be either set as root pages, removed or reassigned to another parent page
144 # Children can be either set as root pages, removed or reassigned to another parent page
145 def destroy
145 def destroy
146 return render_403 unless editable?
146 return render_403 unless editable?
147
147
148 @descendants_count = @page.descendants.size
148 @descendants_count = @page.descendants.size
149 if @descendants_count > 0
149 if @descendants_count > 0
150 case params[:todo]
150 case params[:todo]
151 when 'nullify'
151 when 'nullify'
152 # Nothing to do
152 # Nothing to do
153 when 'destroy'
153 when 'destroy'
154 # Removes all its descendants
154 # Removes all its descendants
155 @page.descendants.each(&:destroy)
155 @page.descendants.each(&:destroy)
156 when 'reassign'
156 when 'reassign'
157 # Reassign children to another parent page
157 # Reassign children to another parent page
158 reassign_to = @wiki.pages.find_by_id(params[:reassign_to_id].to_i)
158 reassign_to = @wiki.pages.find_by_id(params[:reassign_to_id].to_i)
159 return unless reassign_to
159 return unless reassign_to
160 @page.children.each do |child|
160 @page.children.each do |child|
161 child.update_attribute(:parent, reassign_to)
161 child.update_attribute(:parent, reassign_to)
162 end
162 end
163 else
163 else
164 @reassignable_to = @wiki.pages - @page.self_and_descendants
164 @reassignable_to = @wiki.pages - @page.self_and_descendants
165 return
165 return
166 end
166 end
167 end
167 end
168 @page.destroy
168 @page.destroy
169 redirect_to :action => 'special', :id => @project, :page => 'Page_index'
169 redirect_to :action => 'special', :id => @project, :page => 'Page_index'
170 end
170 end
171
171
172 # display special pages
172 # display special pages
173 def special
173 def special
174 page_title = params[:page].downcase
174 page_title = params[:page].downcase
175 case page_title
175 case page_title
176 # show pages index, sorted by title
176 # show pages index, sorted by title
177 when 'page_index', 'date_index'
177 when 'page_index', 'date_index'
178 # eager load information about last updates, without loading text
178 # eager load information about last updates, without loading text
179 @pages = @wiki.pages.find :all, :select => "#{WikiPage.table_name}.*, #{WikiContent.table_name}.updated_on",
179 @pages = @wiki.pages.find :all, :select => "#{WikiPage.table_name}.*, #{WikiContent.table_name}.updated_on",
180 :joins => "LEFT JOIN #{WikiContent.table_name} ON #{WikiContent.table_name}.page_id = #{WikiPage.table_name}.id",
180 :joins => "LEFT JOIN #{WikiContent.table_name} ON #{WikiContent.table_name}.page_id = #{WikiPage.table_name}.id",
181 :order => 'title'
181 :order => 'title'
182 @pages_by_date = @pages.group_by {|p| p.updated_on.to_date}
182 @pages_by_date = @pages.group_by {|p| p.updated_on.to_date}
183 @pages_by_parent_id = @pages.group_by(&:parent_id)
183 @pages_by_parent_id = @pages.group_by(&:parent_id)
184 # export wiki to a single html file
184 # export wiki to a single html file
185 when 'export'
185 when 'export'
186 if User.current.allowed_to?(:export_wiki_pages, @project)
186 if User.current.allowed_to?(:export_wiki_pages, @project)
187 @pages = @wiki.pages.find :all, :order => 'title'
187 @pages = @wiki.pages.find :all, :order => 'title'
188 export = render_to_string :action => 'export_multiple', :layout => false
188 export = render_to_string :action => 'export_multiple', :layout => false
189 send_data(export, :type => 'text/html', :filename => "wiki.html")
189 send_data(export, :type => 'text/html', :filename => "wiki.html")
190 else
190 else
191 redirect_to :action => 'index', :id => @project, :page => nil
191 redirect_to :action => 'index', :id => @project, :page => nil
192 end
192 end
193 return
193 return
194 else
194 else
195 # requested special page doesn't exist, redirect to default page
195 # requested special page doesn't exist, redirect to default page
196 redirect_to :action => 'index', :id => @project, :page => nil
196 redirect_to :action => 'index', :id => @project, :page => nil
197 return
197 return
198 end
198 end
199 render :action => "special_#{page_title}"
199 render :action => "special_#{page_title}"
200 end
200 end
201
201
202 def preview
202 def preview
203 page = @wiki.find_page(params[:page])
203 page = @wiki.find_page(params[:page])
204 # page is nil when previewing a new page
204 # page is nil when previewing a new page
205 return render_403 unless page.nil? || editable?(page)
205 return render_403 unless page.nil? || editable?(page)
206 if page
206 if page
207 @attachements = page.attachments
207 @attachements = page.attachments
208 @previewed = page.content
208 @previewed = page.content
209 end
209 end
210 @text = params[:content][:text]
210 @text = params[:content][:text]
211 render :partial => 'common/preview'
211 render :partial => 'common/preview'
212 end
212 end
213
213
214 def add_attachment
214 def add_attachment
215 return render_403 unless editable?
215 return render_403 unless editable?
216 attachments = Attachment.attach_files(@page, params[:attachments])
216 attachments = Attachment.attach_files(@page, params[:attachments])
217 render_attachment_warning_if_needed(@page)
217 render_attachment_warning_if_needed(@page)
218 redirect_to :action => 'index', :page => @page.title
218 redirect_to :action => 'index', :page => @page.title
219 end
219 end
220
220
221 private
221 private
222
222
223 def find_wiki
223 def find_wiki
224 @project = Project.find(params[:id])
224 @project = Project.find(params[:id])
225 @wiki = @project.wiki
225 @wiki = @project.wiki
226 render_404 unless @wiki
226 render_404 unless @wiki
227 rescue ActiveRecord::RecordNotFound
227 rescue ActiveRecord::RecordNotFound
228 render_404
228 render_404
229 end
229 end
230
230
231 # Finds the requested page and returns a 404 error if it doesn't exist
231 # Finds the requested page and returns a 404 error if it doesn't exist
232 def find_existing_page
232 def find_existing_page
233 @page = @wiki.find_page(params[:page])
233 @page = @wiki.find_page(params[:page])
234 render_404 if @page.nil?
234 render_404 if @page.nil?
235 end
235 end
236
236
237 # Returns true if the current user is allowed to edit the page, otherwise false
237 # Returns true if the current user is allowed to edit the page, otherwise false
238 def editable?(page = @page)
238 def editable?(page = @page)
239 page.editable_by?(User.current)
239 page.editable_by?(User.current)
240 end
240 end
241
241
242 # Returns the default content of a new wiki page
242 # Returns the default content of a new wiki page
243 def initial_page_content(page)
243 def initial_page_content(page)
244 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
244 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
245 extend helper unless self.instance_of?(helper)
245 extend helper unless self.instance_of?(helper)
246 helper.instance_method(:initial_page_content).bind(self).call(page)
246 helper.instance_method(:initial_page_content).bind(self).call(page)
247 end
247 end
248 end
248 end
@@ -1,79 +1,85
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
2 # Copyright (C) 2006-2009 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 class Wiki < ActiveRecord::Base
18 class Wiki < ActiveRecord::Base
19 belongs_to :project
19 belongs_to :project
20 has_many :pages, :class_name => 'WikiPage', :dependent => :destroy, :order => 'title'
20 has_many :pages, :class_name => 'WikiPage', :dependent => :destroy, :order => 'title'
21 has_many :redirects, :class_name => 'WikiRedirect', :dependent => :delete_all
21 has_many :redirects, :class_name => 'WikiRedirect', :dependent => :delete_all
22
22
23 acts_as_watchable
23 acts_as_watchable
24
24
25 validates_presence_of :start_page
25 validates_presence_of :start_page
26 validates_format_of :start_page, :with => /^[^,\.\/\?\;\|\:]*$/
26 validates_format_of :start_page, :with => /^[^,\.\/\?\;\|\:]*$/
27
27
28 def visible?(user=User.current)
28 def visible?(user=User.current)
29 !user.nil? && user.allowed_to?(:view_wiki_pages, project)
29 !user.nil? && user.allowed_to?(:view_wiki_pages, project)
30 end
30 end
31
31
32 # Returns the wiki page that acts as the sidebar content
33 # or nil if no such page exists
34 def sidebar
35 @sidebar ||= find_page('Sidebar', :with_redirect => false)
36 end
37
32 # find the page with the given title
38 # find the page with the given title
33 # if page doesn't exist, return a new page
39 # if page doesn't exist, return a new page
34 def find_or_new_page(title)
40 def find_or_new_page(title)
35 title = start_page if title.blank?
41 title = start_page if title.blank?
36 find_page(title) || WikiPage.new(:wiki => self, :title => Wiki.titleize(title))
42 find_page(title) || WikiPage.new(:wiki => self, :title => Wiki.titleize(title))
37 end
43 end
38
44
39 # find the page with the given title
45 # find the page with the given title
40 def find_page(title, options = {})
46 def find_page(title, options = {})
41 title = start_page if title.blank?
47 title = start_page if title.blank?
42 title = Wiki.titleize(title)
48 title = Wiki.titleize(title)
43 page = pages.find_by_title(title)
49 page = pages.find_by_title(title)
44 if !page && !(options[:with_redirect] == false)
50 if !page && !(options[:with_redirect] == false)
45 # search for a redirect
51 # search for a redirect
46 redirect = redirects.find_by_title(title)
52 redirect = redirects.find_by_title(title)
47 page = find_page(redirect.redirects_to, :with_redirect => false) if redirect
53 page = find_page(redirect.redirects_to, :with_redirect => false) if redirect
48 end
54 end
49 page
55 page
50 end
56 end
51
57
52 # Finds a page by title
58 # Finds a page by title
53 # The given string can be of one of the forms: "title" or "project:title"
59 # The given string can be of one of the forms: "title" or "project:title"
54 # Examples:
60 # Examples:
55 # Wiki.find_page("bar", project => foo)
61 # Wiki.find_page("bar", project => foo)
56 # Wiki.find_page("foo:bar")
62 # Wiki.find_page("foo:bar")
57 def self.find_page(title, options = {})
63 def self.find_page(title, options = {})
58 project = options[:project]
64 project = options[:project]
59 if title.to_s =~ %r{^([^\:]+)\:(.*)$}
65 if title.to_s =~ %r{^([^\:]+)\:(.*)$}
60 project_identifier, title = $1, $2
66 project_identifier, title = $1, $2
61 project = Project.find_by_identifier(project_identifier) || Project.find_by_name(project_identifier)
67 project = Project.find_by_identifier(project_identifier) || Project.find_by_name(project_identifier)
62 end
68 end
63 if project && project.wiki
69 if project && project.wiki
64 page = project.wiki.find_page(title)
70 page = project.wiki.find_page(title)
65 if page && page.content
71 if page && page.content
66 page
72 page
67 end
73 end
68 end
74 end
69 end
75 end
70
76
71 # turn a string into a valid page title
77 # turn a string into a valid page title
72 def self.titleize(title)
78 def self.titleize(title)
73 # replace spaces with _ and remove unwanted caracters
79 # replace spaces with _ and remove unwanted caracters
74 title = title.gsub(/\s+/, '_').delete(',./?;|:') if title
80 title = title.gsub(/\s+/, '_').delete(',./?;|:') if title
75 # upcase the first letter
81 # upcase the first letter
76 title = (title.slice(0..0).upcase + (title.slice(1..-1) || '')) if title
82 title = (title.slice(0..0).upcase + (title.slice(1..-1) || '')) if title
77 title
83 title
78 end
84 end
79 end
85 end
@@ -1,106 +1,110
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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 'zlib'
18 require 'zlib'
19
19
20 class WikiContent < ActiveRecord::Base
20 class WikiContent < ActiveRecord::Base
21 set_locking_column :version
21 set_locking_column :version
22 belongs_to :page, :class_name => 'WikiPage', :foreign_key => 'page_id'
22 belongs_to :page, :class_name => 'WikiPage', :foreign_key => 'page_id'
23 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
23 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
24 validates_presence_of :text
24 validates_presence_of :text
25 validates_length_of :comments, :maximum => 255, :allow_nil => true
25 validates_length_of :comments, :maximum => 255, :allow_nil => true
26
26
27 acts_as_versioned
27 acts_as_versioned
28
28
29 def visible?(user=User.current)
29 def visible?(user=User.current)
30 page.visible?(user)
30 page.visible?(user)
31 end
31 end
32
32
33 def project
33 def project
34 page.project
34 page.project
35 end
35 end
36
36
37 def attachments
38 page.nil? ? [] : page.attachments
39 end
40
37 # Returns the mail adresses of users that should be notified
41 # Returns the mail adresses of users that should be notified
38 def recipients
42 def recipients
39 notified = project.notified_users
43 notified = project.notified_users
40 notified.reject! {|user| !visible?(user)}
44 notified.reject! {|user| !visible?(user)}
41 notified.collect(&:mail)
45 notified.collect(&:mail)
42 end
46 end
43
47
44 class Version
48 class Version
45 belongs_to :page, :class_name => '::WikiPage', :foreign_key => 'page_id'
49 belongs_to :page, :class_name => '::WikiPage', :foreign_key => 'page_id'
46 belongs_to :author, :class_name => '::User', :foreign_key => 'author_id'
50 belongs_to :author, :class_name => '::User', :foreign_key => 'author_id'
47 attr_protected :data
51 attr_protected :data
48
52
49 acts_as_event :title => Proc.new {|o| "#{l(:label_wiki_edit)}: #{o.page.title} (##{o.version})"},
53 acts_as_event :title => Proc.new {|o| "#{l(:label_wiki_edit)}: #{o.page.title} (##{o.version})"},
50 :description => :comments,
54 :description => :comments,
51 :datetime => :updated_on,
55 :datetime => :updated_on,
52 :type => 'wiki-page',
56 :type => 'wiki-page',
53 :url => Proc.new {|o| {:controller => 'wiki', :id => o.page.wiki.project_id, :page => o.page.title, :version => o.version}}
57 :url => Proc.new {|o| {:controller => 'wiki', :id => o.page.wiki.project_id, :page => o.page.title, :version => o.version}}
54
58
55 acts_as_activity_provider :type => 'wiki_edits',
59 acts_as_activity_provider :type => 'wiki_edits',
56 :timestamp => "#{WikiContent.versioned_table_name}.updated_on",
60 :timestamp => "#{WikiContent.versioned_table_name}.updated_on",
57 :author_key => "#{WikiContent.versioned_table_name}.author_id",
61 :author_key => "#{WikiContent.versioned_table_name}.author_id",
58 :permission => :view_wiki_edits,
62 :permission => :view_wiki_edits,
59 :find_options => {:select => "#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comments, " +
63 :find_options => {:select => "#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comments, " +
60 "#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title, " +
64 "#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title, " +
61 "#{WikiContent.versioned_table_name}.page_id, #{WikiContent.versioned_table_name}.author_id, " +
65 "#{WikiContent.versioned_table_name}.page_id, #{WikiContent.versioned_table_name}.author_id, " +
62 "#{WikiContent.versioned_table_name}.id",
66 "#{WikiContent.versioned_table_name}.id",
63 :joins => "LEFT JOIN #{WikiPage.table_name} ON #{WikiPage.table_name}.id = #{WikiContent.versioned_table_name}.page_id " +
67 :joins => "LEFT JOIN #{WikiPage.table_name} ON #{WikiPage.table_name}.id = #{WikiContent.versioned_table_name}.page_id " +
64 "LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id " +
68 "LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id " +
65 "LEFT JOIN #{Project.table_name} ON #{Project.table_name}.id = #{Wiki.table_name}.project_id"}
69 "LEFT JOIN #{Project.table_name} ON #{Project.table_name}.id = #{Wiki.table_name}.project_id"}
66
70
67 def text=(plain)
71 def text=(plain)
68 case Setting.wiki_compression
72 case Setting.wiki_compression
69 when 'gzip'
73 when 'gzip'
70 begin
74 begin
71 self.data = Zlib::Deflate.deflate(plain, Zlib::BEST_COMPRESSION)
75 self.data = Zlib::Deflate.deflate(plain, Zlib::BEST_COMPRESSION)
72 self.compression = 'gzip'
76 self.compression = 'gzip'
73 rescue
77 rescue
74 self.data = plain
78 self.data = plain
75 self.compression = ''
79 self.compression = ''
76 end
80 end
77 else
81 else
78 self.data = plain
82 self.data = plain
79 self.compression = ''
83 self.compression = ''
80 end
84 end
81 plain
85 plain
82 end
86 end
83
87
84 def text
88 def text
85 @text ||= case compression
89 @text ||= case compression
86 when 'gzip'
90 when 'gzip'
87 Zlib::Inflate.inflate(data)
91 Zlib::Inflate.inflate(data)
88 else
92 else
89 # uncompressed data
93 # uncompressed data
90 data
94 data
91 end
95 end
92 end
96 end
93
97
94 def project
98 def project
95 page.project
99 page.project
96 end
100 end
97
101
98 # Returns the previous version or nil
102 # Returns the previous version or nil
99 def previous
103 def previous
100 @previous ||= WikiContent::Version.find(:first,
104 @previous ||= WikiContent::Version.find(:first,
101 :order => 'version DESC',
105 :order => 'version DESC',
102 :include => :author,
106 :include => :author,
103 :conditions => ["wiki_content_id = ? AND version < ?", wiki_content_id, version])
107 :conditions => ["wiki_content_id = ? AND version < ?", wiki_content_id, version])
104 end
108 end
105 end
109 end
106 end
110 end
@@ -1,193 +1,202
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
2 # Copyright (C) 2006-2009 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 belongs_to :wiki
22 belongs_to :wiki
23 has_one :content, :class_name => 'WikiContent', :foreign_key => 'page_id', :dependent => :destroy
23 has_one :content, :class_name => 'WikiContent', :foreign_key => 'page_id', :dependent => :destroy
24 acts_as_attachable :delete_permission => :delete_wiki_pages_attachments
24 acts_as_attachable :delete_permission => :delete_wiki_pages_attachments
25 acts_as_tree :dependent => :nullify, :order => 'title'
25 acts_as_tree :dependent => :nullify, :order => 'title'
26
26
27 acts_as_watchable
27 acts_as_watchable
28 acts_as_event :title => Proc.new {|o| "#{l(:label_wiki)}: #{o.title}"},
28 acts_as_event :title => Proc.new {|o| "#{l(:label_wiki)}: #{o.title}"},
29 :description => :text,
29 :description => :text,
30 :datetime => :created_on,
30 :datetime => :created_on,
31 :url => Proc.new {|o| {:controller => 'wiki', :id => o.wiki.project, :page => o.title}}
31 :url => Proc.new {|o| {:controller => 'wiki', :id => o.wiki.project, :page => o.title}}
32
32
33 acts_as_searchable :columns => ['title', 'text'],
33 acts_as_searchable :columns => ['title', 'text'],
34 :include => [{:wiki => :project}, :content],
34 :include => [{:wiki => :project}, :content],
35 :project_key => "#{Wiki.table_name}.project_id"
35 :project_key => "#{Wiki.table_name}.project_id"
36
36
37 attr_accessor :redirect_existing_links
37 attr_accessor :redirect_existing_links
38
38
39 validates_presence_of :title
39 validates_presence_of :title
40 validates_format_of :title, :with => /^[^,\.\/\?\;\|\s]*$/
40 validates_format_of :title, :with => /^[^,\.\/\?\;\|\s]*$/
41 validates_uniqueness_of :title, :scope => :wiki_id, :case_sensitive => false
41 validates_uniqueness_of :title, :scope => :wiki_id, :case_sensitive => false
42 validates_associated :content
42 validates_associated :content
43
43
44 # Wiki pages that are protected by default
45 DEFAULT_PROTECTED_PAGES = %w(sidebar)
46
47 def after_initialize
48 if new_record? && DEFAULT_PROTECTED_PAGES.include?(title.to_s.downcase)
49 self.protected = true
50 end
51 end
52
44 def visible?(user=User.current)
53 def visible?(user=User.current)
45 !user.nil? && user.allowed_to?(:view_wiki_pages, project)
54 !user.nil? && user.allowed_to?(:view_wiki_pages, project)
46 end
55 end
47
56
48 def title=(value)
57 def title=(value)
49 value = Wiki.titleize(value)
58 value = Wiki.titleize(value)
50 @previous_title = read_attribute(:title) if @previous_title.blank?
59 @previous_title = read_attribute(:title) if @previous_title.blank?
51 write_attribute(:title, value)
60 write_attribute(:title, value)
52 end
61 end
53
62
54 def before_save
63 def before_save
55 self.title = Wiki.titleize(title)
64 self.title = Wiki.titleize(title)
56 # Manage redirects if the title has changed
65 # Manage redirects if the title has changed
57 if !@previous_title.blank? && (@previous_title != title) && !new_record?
66 if !@previous_title.blank? && (@previous_title != title) && !new_record?
58 # Update redirects that point to the old title
67 # Update redirects that point to the old title
59 wiki.redirects.find_all_by_redirects_to(@previous_title).each do |r|
68 wiki.redirects.find_all_by_redirects_to(@previous_title).each do |r|
60 r.redirects_to = title
69 r.redirects_to = title
61 r.title == r.redirects_to ? r.destroy : r.save
70 r.title == r.redirects_to ? r.destroy : r.save
62 end
71 end
63 # Remove redirects for the new title
72 # Remove redirects for the new title
64 wiki.redirects.find_all_by_title(title).each(&:destroy)
73 wiki.redirects.find_all_by_title(title).each(&:destroy)
65 # Create a redirect to the new title
74 # Create a redirect to the new title
66 wiki.redirects << WikiRedirect.new(:title => @previous_title, :redirects_to => title) unless redirect_existing_links == "0"
75 wiki.redirects << WikiRedirect.new(:title => @previous_title, :redirects_to => title) unless redirect_existing_links == "0"
67 @previous_title = nil
76 @previous_title = nil
68 end
77 end
69 end
78 end
70
79
71 def before_destroy
80 def before_destroy
72 # Remove redirects to this page
81 # Remove redirects to this page
73 wiki.redirects.find_all_by_redirects_to(title).each(&:destroy)
82 wiki.redirects.find_all_by_redirects_to(title).each(&:destroy)
74 end
83 end
75
84
76 def pretty_title
85 def pretty_title
77 WikiPage.pretty_title(title)
86 WikiPage.pretty_title(title)
78 end
87 end
79
88
80 def content_for_version(version=nil)
89 def content_for_version(version=nil)
81 result = content.versions.find_by_version(version.to_i) if version
90 result = content.versions.find_by_version(version.to_i) if version
82 result ||= content
91 result ||= content
83 result
92 result
84 end
93 end
85
94
86 def diff(version_to=nil, version_from=nil)
95 def diff(version_to=nil, version_from=nil)
87 version_to = version_to ? version_to.to_i : self.content.version
96 version_to = version_to ? version_to.to_i : self.content.version
88 version_from = version_from ? version_from.to_i : version_to - 1
97 version_from = version_from ? version_from.to_i : version_to - 1
89 version_to, version_from = version_from, version_to unless version_from < version_to
98 version_to, version_from = version_from, version_to unless version_from < version_to
90
99
91 content_to = content.versions.find_by_version(version_to)
100 content_to = content.versions.find_by_version(version_to)
92 content_from = content.versions.find_by_version(version_from)
101 content_from = content.versions.find_by_version(version_from)
93
102
94 (content_to && content_from) ? WikiDiff.new(content_to, content_from) : nil
103 (content_to && content_from) ? WikiDiff.new(content_to, content_from) : nil
95 end
104 end
96
105
97 def annotate(version=nil)
106 def annotate(version=nil)
98 version = version ? version.to_i : self.content.version
107 version = version ? version.to_i : self.content.version
99 c = content.versions.find_by_version(version)
108 c = content.versions.find_by_version(version)
100 c ? WikiAnnotate.new(c) : nil
109 c ? WikiAnnotate.new(c) : nil
101 end
110 end
102
111
103 def self.pretty_title(str)
112 def self.pretty_title(str)
104 (str && str.is_a?(String)) ? str.tr('_', ' ') : str
113 (str && str.is_a?(String)) ? str.tr('_', ' ') : str
105 end
114 end
106
115
107 def project
116 def project
108 wiki.project
117 wiki.project
109 end
118 end
110
119
111 def text
120 def text
112 content.text if content
121 content.text if content
113 end
122 end
114
123
115 # Returns true if usr is allowed to edit the page, otherwise false
124 # Returns true if usr is allowed to edit the page, otherwise false
116 def editable_by?(usr)
125 def editable_by?(usr)
117 !protected? || usr.allowed_to?(:protect_wiki_pages, wiki.project)
126 !protected? || usr.allowed_to?(:protect_wiki_pages, wiki.project)
118 end
127 end
119
128
120 def attachments_deletable?(usr=User.current)
129 def attachments_deletable?(usr=User.current)
121 editable_by?(usr) && super(usr)
130 editable_by?(usr) && super(usr)
122 end
131 end
123
132
124 def parent_title
133 def parent_title
125 @parent_title || (self.parent && self.parent.pretty_title)
134 @parent_title || (self.parent && self.parent.pretty_title)
126 end
135 end
127
136
128 def parent_title=(t)
137 def parent_title=(t)
129 @parent_title = t
138 @parent_title = t
130 parent_page = t.blank? ? nil : self.wiki.find_page(t)
139 parent_page = t.blank? ? nil : self.wiki.find_page(t)
131 self.parent = parent_page
140 self.parent = parent_page
132 end
141 end
133
142
134 protected
143 protected
135
144
136 def validate
145 def validate
137 errors.add(:parent_title, :invalid) if !@parent_title.blank? && parent.nil?
146 errors.add(:parent_title, :invalid) if !@parent_title.blank? && parent.nil?
138 errors.add(:parent_title, :circular_dependency) if parent && (parent == self || parent.ancestors.include?(self))
147 errors.add(:parent_title, :circular_dependency) if parent && (parent == self || parent.ancestors.include?(self))
139 errors.add(:parent_title, :not_same_project) if parent && (parent.wiki_id != wiki_id)
148 errors.add(:parent_title, :not_same_project) if parent && (parent.wiki_id != wiki_id)
140 end
149 end
141 end
150 end
142
151
143 class WikiDiff
152 class WikiDiff
144 attr_reader :diff, :words, :content_to, :content_from
153 attr_reader :diff, :words, :content_to, :content_from
145
154
146 def initialize(content_to, content_from)
155 def initialize(content_to, content_from)
147 @content_to = content_to
156 @content_to = content_to
148 @content_from = content_from
157 @content_from = content_from
149 @words = content_to.text.split(/(\s+)/)
158 @words = content_to.text.split(/(\s+)/)
150 @words = @words.select {|word| word != ' '}
159 @words = @words.select {|word| word != ' '}
151 words_from = content_from.text.split(/(\s+)/)
160 words_from = content_from.text.split(/(\s+)/)
152 words_from = words_from.select {|word| word != ' '}
161 words_from = words_from.select {|word| word != ' '}
153 @diff = words_from.diff @words
162 @diff = words_from.diff @words
154 end
163 end
155 end
164 end
156
165
157 class WikiAnnotate
166 class WikiAnnotate
158 attr_reader :lines, :content
167 attr_reader :lines, :content
159
168
160 def initialize(content)
169 def initialize(content)
161 @content = content
170 @content = content
162 current = content
171 current = content
163 current_lines = current.text.split(/\r?\n/)
172 current_lines = current.text.split(/\r?\n/)
164 @lines = current_lines.collect {|t| [nil, nil, t]}
173 @lines = current_lines.collect {|t| [nil, nil, t]}
165 positions = []
174 positions = []
166 current_lines.size.times {|i| positions << i}
175 current_lines.size.times {|i| positions << i}
167 while (current.previous)
176 while (current.previous)
168 d = current.previous.text.split(/\r?\n/).diff(current.text.split(/\r?\n/)).diffs.flatten
177 d = current.previous.text.split(/\r?\n/).diff(current.text.split(/\r?\n/)).diffs.flatten
169 d.each_slice(3) do |s|
178 d.each_slice(3) do |s|
170 sign, line = s[0], s[1]
179 sign, line = s[0], s[1]
171 if sign == '+' && positions[line] && positions[line] != -1
180 if sign == '+' && positions[line] && positions[line] != -1
172 if @lines[positions[line]][0].nil?
181 if @lines[positions[line]][0].nil?
173 @lines[positions[line]][0] = current.version
182 @lines[positions[line]][0] = current.version
174 @lines[positions[line]][1] = current.author
183 @lines[positions[line]][1] = current.author
175 end
184 end
176 end
185 end
177 end
186 end
178 d.each_slice(3) do |s|
187 d.each_slice(3) do |s|
179 sign, line = s[0], s[1]
188 sign, line = s[0], s[1]
180 if sign == '-'
189 if sign == '-'
181 positions.insert(line, -1)
190 positions.insert(line, -1)
182 else
191 else
183 positions[line] = nil
192 positions[line] = nil
184 end
193 end
185 end
194 end
186 positions.compact!
195 positions.compact!
187 # Stop if every line is annotated
196 # Stop if every line is annotated
188 break unless @lines.detect { |line| line[0].nil? }
197 break unless @lines.detect { |line| line[0].nil? }
189 current = current.previous
198 current = current.previous
190 end
199 end
191 @lines.each { |line| line[0] ||= current.version }
200 @lines.each { |line| line[0] ||= current.version }
192 end
201 end
193 end
202 end
@@ -1,5 +1,9
1 <% if @wiki && @wiki.sidebar -%>
2 <%= textilizable @wiki.sidebar.content, :text %>
3 <% end -%>
4
1 <h3><%= l(:label_wiki) %></h3>
5 <h3><%= l(:label_wiki) %></h3>
2
6
3 <%= link_to l(:field_start_page), {:action => 'index', :page => nil} %><br />
7 <%= link_to l(:field_start_page), {:action => 'index', :page => nil} %><br />
4 <%= link_to l(:label_index_by_title), {:action => 'special', :page => 'Page_index'} %><br />
8 <%= link_to l(:label_index_by_title), {:action => 'special', :page => 'Page_index'} %><br />
5 <%= link_to l(:label_index_by_date), {:action => 'special', :page => 'Date_index'} %><br />
9 <%= link_to l(:label_index_by_date), {:action => 'special', :page => 'Date_index'} %><br />
@@ -1,414 +1,425
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
19 require 'wiki_controller'
19 require 'wiki_controller'
20
20
21 # Re-raise errors caught by the controller.
21 # Re-raise errors caught by the controller.
22 class WikiController; def rescue_action(e) raise e end; end
22 class WikiController; def rescue_action(e) raise e end; end
23
23
24 class WikiControllerTest < ActionController::TestCase
24 class WikiControllerTest < ActionController::TestCase
25 fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules, :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions, :attachments
25 fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules, :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions, :attachments
26
26
27 def setup
27 def setup
28 @controller = WikiController.new
28 @controller = WikiController.new
29 @request = ActionController::TestRequest.new
29 @request = ActionController::TestRequest.new
30 @response = ActionController::TestResponse.new
30 @response = ActionController::TestResponse.new
31 User.current = nil
31 User.current = nil
32 end
32 end
33
33
34 def test_index_routing
34 def test_index_routing
35 assert_routing(
35 assert_routing(
36 {:method => :get, :path => '/projects/567/wiki'},
36 {:method => :get, :path => '/projects/567/wiki'},
37 :controller => 'wiki', :action => 'index', :id => '567'
37 :controller => 'wiki', :action => 'index', :id => '567'
38 )
38 )
39 assert_routing(
39 assert_routing(
40 {:method => :get, :path => '/projects/567/wiki/lalala'},
40 {:method => :get, :path => '/projects/567/wiki/lalala'},
41 :controller => 'wiki', :action => 'index', :id => '567', :page => 'lalala'
41 :controller => 'wiki', :action => 'index', :id => '567', :page => 'lalala'
42 )
42 )
43 assert_generates(
43 assert_generates(
44 '/projects/567/wiki',
44 '/projects/567/wiki',
45 :controller => 'wiki', :action => 'index', :id => '567', :page => nil
45 :controller => 'wiki', :action => 'index', :id => '567', :page => nil
46 )
46 )
47 end
47 end
48
48
49 def test_show_start_page
49 def test_show_start_page
50 get :index, :id => 'ecookbook'
50 get :index, :id => 'ecookbook'
51 assert_response :success
51 assert_response :success
52 assert_template 'show'
52 assert_template 'show'
53 assert_tag :tag => 'h1', :content => /CookBook documentation/
53 assert_tag :tag => 'h1', :content => /CookBook documentation/
54
54
55 # child_pages macro
55 # child_pages macro
56 assert_tag :ul, :attributes => { :class => 'pages-hierarchy' },
56 assert_tag :ul, :attributes => { :class => 'pages-hierarchy' },
57 :child => { :tag => 'li',
57 :child => { :tag => 'li',
58 :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/Page_with_an_inline_image' },
58 :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/Page_with_an_inline_image' },
59 :content => 'Page with an inline image' } }
59 :content => 'Page with an inline image' } }
60 end
60 end
61
61
62 def test_show_page_with_name
62 def test_show_page_with_name
63 get :index, :id => 1, :page => 'Another_page'
63 get :index, :id => 1, :page => 'Another_page'
64 assert_response :success
64 assert_response :success
65 assert_template 'show'
65 assert_template 'show'
66 assert_tag :tag => 'h1', :content => /Another page/
66 assert_tag :tag => 'h1', :content => /Another page/
67 # Included page with an inline image
67 # Included page with an inline image
68 assert_tag :tag => 'p', :content => /This is an inline image/
68 assert_tag :tag => 'p', :content => /This is an inline image/
69 assert_tag :tag => 'img', :attributes => { :src => '/attachments/download/3',
69 assert_tag :tag => 'img', :attributes => { :src => '/attachments/download/3',
70 :alt => 'This is a logo' }
70 :alt => 'This is a logo' }
71 end
71 end
72
72
73 def test_show_with_sidebar
74 page = Project.find(1).wiki.pages.new(:title => 'Sidebar')
75 page.content = WikiContent.new(:text => 'Side bar content for test_show_with_sidebar')
76 page.save!
77
78 get :index, :id => 1, :page => 'Another_page'
79 assert_response :success
80 assert_tag :tag => 'div', :attributes => {:id => 'sidebar'},
81 :content => /Side bar content for test_show_with_sidebar/
82 end
83
73 def test_show_unexistent_page_without_edit_right
84 def test_show_unexistent_page_without_edit_right
74 get :index, :id => 1, :page => 'Unexistent page'
85 get :index, :id => 1, :page => 'Unexistent page'
75 assert_response 404
86 assert_response 404
76 end
87 end
77
88
78 def test_show_unexistent_page_with_edit_right
89 def test_show_unexistent_page_with_edit_right
79 @request.session[:user_id] = 2
90 @request.session[:user_id] = 2
80 get :index, :id => 1, :page => 'Unexistent page'
91 get :index, :id => 1, :page => 'Unexistent page'
81 assert_response :success
92 assert_response :success
82 assert_template 'edit'
93 assert_template 'edit'
83 end
94 end
84
95
85 def test_edit_routing
96 def test_edit_routing
86 assert_routing(
97 assert_routing(
87 {:method => :get, :path => '/projects/567/wiki/my_page/edit'},
98 {:method => :get, :path => '/projects/567/wiki/my_page/edit'},
88 :controller => 'wiki', :action => 'edit', :id => '567', :page => 'my_page'
99 :controller => 'wiki', :action => 'edit', :id => '567', :page => 'my_page'
89 )
100 )
90 assert_recognizes(#TODO: use PUT to page path, adjust forms accordingly
101 assert_recognizes(#TODO: use PUT to page path, adjust forms accordingly
91 {:controller => 'wiki', :action => 'edit', :id => '567', :page => 'my_page'},
102 {:controller => 'wiki', :action => 'edit', :id => '567', :page => 'my_page'},
92 {:method => :post, :path => '/projects/567/wiki/my_page/edit'}
103 {:method => :post, :path => '/projects/567/wiki/my_page/edit'}
93 )
104 )
94 end
105 end
95
106
96 def test_create_page
107 def test_create_page
97 @request.session[:user_id] = 2
108 @request.session[:user_id] = 2
98 post :edit, :id => 1,
109 post :edit, :id => 1,
99 :page => 'New page',
110 :page => 'New page',
100 :content => {:comments => 'Created the page',
111 :content => {:comments => 'Created the page',
101 :text => "h1. New page\n\nThis is a new page",
112 :text => "h1. New page\n\nThis is a new page",
102 :version => 0}
113 :version => 0}
103 assert_redirected_to :action => 'index', :id => 'ecookbook', :page => 'New_page'
114 assert_redirected_to :action => 'index', :id => 'ecookbook', :page => 'New_page'
104 page = Project.find(1).wiki.find_page('New page')
115 page = Project.find(1).wiki.find_page('New page')
105 assert !page.new_record?
116 assert !page.new_record?
106 assert_not_nil page.content
117 assert_not_nil page.content
107 assert_equal 'Created the page', page.content.comments
118 assert_equal 'Created the page', page.content.comments
108 end
119 end
109
120
110 def test_create_page_with_attachments
121 def test_create_page_with_attachments
111 @request.session[:user_id] = 2
122 @request.session[:user_id] = 2
112 assert_difference 'WikiPage.count' do
123 assert_difference 'WikiPage.count' do
113 assert_difference 'Attachment.count' do
124 assert_difference 'Attachment.count' do
114 post :edit, :id => 1,
125 post :edit, :id => 1,
115 :page => 'New page',
126 :page => 'New page',
116 :content => {:comments => 'Created the page',
127 :content => {:comments => 'Created the page',
117 :text => "h1. New page\n\nThis is a new page",
128 :text => "h1. New page\n\nThis is a new page",
118 :version => 0},
129 :version => 0},
119 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
130 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
120 end
131 end
121 end
132 end
122 page = Project.find(1).wiki.find_page('New page')
133 page = Project.find(1).wiki.find_page('New page')
123 assert_equal 1, page.attachments.count
134 assert_equal 1, page.attachments.count
124 assert_equal 'testfile.txt', page.attachments.first.filename
135 assert_equal 'testfile.txt', page.attachments.first.filename
125 end
136 end
126
137
127 def test_preview_routing
138 def test_preview_routing
128 assert_routing(
139 assert_routing(
129 {:method => :post, :path => '/projects/567/wiki/CookBook_documentation/preview'},
140 {:method => :post, :path => '/projects/567/wiki/CookBook_documentation/preview'},
130 :controller => 'wiki', :action => 'preview', :id => '567', :page => 'CookBook_documentation'
141 :controller => 'wiki', :action => 'preview', :id => '567', :page => 'CookBook_documentation'
131 )
142 )
132 end
143 end
133
144
134 def test_preview
145 def test_preview
135 @request.session[:user_id] = 2
146 @request.session[:user_id] = 2
136 xhr :post, :preview, :id => 1, :page => 'CookBook_documentation',
147 xhr :post, :preview, :id => 1, :page => 'CookBook_documentation',
137 :content => { :comments => '',
148 :content => { :comments => '',
138 :text => 'this is a *previewed text*',
149 :text => 'this is a *previewed text*',
139 :version => 3 }
150 :version => 3 }
140 assert_response :success
151 assert_response :success
141 assert_template 'common/_preview'
152 assert_template 'common/_preview'
142 assert_tag :tag => 'strong', :content => /previewed text/
153 assert_tag :tag => 'strong', :content => /previewed text/
143 end
154 end
144
155
145 def test_preview_new_page
156 def test_preview_new_page
146 @request.session[:user_id] = 2
157 @request.session[:user_id] = 2
147 xhr :post, :preview, :id => 1, :page => 'New page',
158 xhr :post, :preview, :id => 1, :page => 'New page',
148 :content => { :text => 'h1. New page',
159 :content => { :text => 'h1. New page',
149 :comments => '',
160 :comments => '',
150 :version => 0 }
161 :version => 0 }
151 assert_response :success
162 assert_response :success
152 assert_template 'common/_preview'
163 assert_template 'common/_preview'
153 assert_tag :tag => 'h1', :content => /New page/
164 assert_tag :tag => 'h1', :content => /New page/
154 end
165 end
155
166
156 def test_history_routing
167 def test_history_routing
157 assert_routing(
168 assert_routing(
158 {:method => :get, :path => '/projects/1/wiki/CookBook_documentation/history'},
169 {:method => :get, :path => '/projects/1/wiki/CookBook_documentation/history'},
159 :controller => 'wiki', :action => 'history', :id => '1', :page => 'CookBook_documentation'
170 :controller => 'wiki', :action => 'history', :id => '1', :page => 'CookBook_documentation'
160 )
171 )
161 end
172 end
162
173
163 def test_history
174 def test_history
164 get :history, :id => 1, :page => 'CookBook_documentation'
175 get :history, :id => 1, :page => 'CookBook_documentation'
165 assert_response :success
176 assert_response :success
166 assert_template 'history'
177 assert_template 'history'
167 assert_not_nil assigns(:versions)
178 assert_not_nil assigns(:versions)
168 assert_equal 3, assigns(:versions).size
179 assert_equal 3, assigns(:versions).size
169 assert_select "input[type=submit][name=commit]"
180 assert_select "input[type=submit][name=commit]"
170 end
181 end
171
182
172 def test_history_with_one_version
183 def test_history_with_one_version
173 get :history, :id => 1, :page => 'Another_page'
184 get :history, :id => 1, :page => 'Another_page'
174 assert_response :success
185 assert_response :success
175 assert_template 'history'
186 assert_template 'history'
176 assert_not_nil assigns(:versions)
187 assert_not_nil assigns(:versions)
177 assert_equal 1, assigns(:versions).size
188 assert_equal 1, assigns(:versions).size
178 assert_select "input[type=submit][name=commit]", false
189 assert_select "input[type=submit][name=commit]", false
179 end
190 end
180
191
181 def test_diff_routing
192 def test_diff_routing
182 assert_routing(
193 assert_routing(
183 {:method => :get, :path => '/projects/1/wiki/CookBook_documentation/diff/2/vs/1'},
194 {:method => :get, :path => '/projects/1/wiki/CookBook_documentation/diff/2/vs/1'},
184 :controller => 'wiki', :action => 'diff', :id => '1', :page => 'CookBook_documentation', :version => '2', :version_from => '1'
195 :controller => 'wiki', :action => 'diff', :id => '1', :page => 'CookBook_documentation', :version => '2', :version_from => '1'
185 )
196 )
186 end
197 end
187
198
188 def test_diff
199 def test_diff
189 get :diff, :id => 1, :page => 'CookBook_documentation', :version => 2, :version_from => 1
200 get :diff, :id => 1, :page => 'CookBook_documentation', :version => 2, :version_from => 1
190 assert_response :success
201 assert_response :success
191 assert_template 'diff'
202 assert_template 'diff'
192 assert_tag :tag => 'span', :attributes => { :class => 'diff_in'},
203 assert_tag :tag => 'span', :attributes => { :class => 'diff_in'},
193 :content => /updated/
204 :content => /updated/
194 end
205 end
195
206
196 def test_annotate_routing
207 def test_annotate_routing
197 assert_routing(
208 assert_routing(
198 {:method => :get, :path => '/projects/1/wiki/CookBook_documentation/annotate/2'},
209 {:method => :get, :path => '/projects/1/wiki/CookBook_documentation/annotate/2'},
199 :controller => 'wiki', :action => 'annotate', :id => '1', :page => 'CookBook_documentation', :version => '2'
210 :controller => 'wiki', :action => 'annotate', :id => '1', :page => 'CookBook_documentation', :version => '2'
200 )
211 )
201 end
212 end
202
213
203 def test_annotate
214 def test_annotate
204 get :annotate, :id => 1, :page => 'CookBook_documentation', :version => 2
215 get :annotate, :id => 1, :page => 'CookBook_documentation', :version => 2
205 assert_response :success
216 assert_response :success
206 assert_template 'annotate'
217 assert_template 'annotate'
207 # Line 1
218 # Line 1
208 assert_tag :tag => 'tr', :child => { :tag => 'th', :attributes => {:class => 'line-num'}, :content => '1' },
219 assert_tag :tag => 'tr', :child => { :tag => 'th', :attributes => {:class => 'line-num'}, :content => '1' },
209 :child => { :tag => 'td', :attributes => {:class => 'author'}, :content => /John Smith/ },
220 :child => { :tag => 'td', :attributes => {:class => 'author'}, :content => /John Smith/ },
210 :child => { :tag => 'td', :content => /h1\. CookBook documentation/ }
221 :child => { :tag => 'td', :content => /h1\. CookBook documentation/ }
211 # Line 2
222 # Line 2
212 assert_tag :tag => 'tr', :child => { :tag => 'th', :attributes => {:class => 'line-num'}, :content => '2' },
223 assert_tag :tag => 'tr', :child => { :tag => 'th', :attributes => {:class => 'line-num'}, :content => '2' },
213 :child => { :tag => 'td', :attributes => {:class => 'author'}, :content => /redMine Admin/ },
224 :child => { :tag => 'td', :attributes => {:class => 'author'}, :content => /redMine Admin/ },
214 :child => { :tag => 'td', :content => /Some updated \[\[documentation\]\] here/ }
225 :child => { :tag => 'td', :content => /Some updated \[\[documentation\]\] here/ }
215 end
226 end
216
227
217 def test_rename_routing
228 def test_rename_routing
218 assert_routing(
229 assert_routing(
219 {:method => :get, :path => '/projects/22/wiki/ladida/rename'},
230 {:method => :get, :path => '/projects/22/wiki/ladida/rename'},
220 :controller => 'wiki', :action => 'rename', :id => '22', :page => 'ladida'
231 :controller => 'wiki', :action => 'rename', :id => '22', :page => 'ladida'
221 )
232 )
222 assert_recognizes(
233 assert_recognizes(
223 #TODO: should be moved into a update action and use a PUT to the page URI
234 #TODO: should be moved into a update action and use a PUT to the page URI
224 {:controller => 'wiki', :action => 'rename', :id => '22', :page => 'ladida'},
235 {:controller => 'wiki', :action => 'rename', :id => '22', :page => 'ladida'},
225 {:method => :post, :path => '/projects/22/wiki/ladida/rename'}
236 {:method => :post, :path => '/projects/22/wiki/ladida/rename'}
226 )
237 )
227 end
238 end
228
239
229 def test_rename_with_redirect
240 def test_rename_with_redirect
230 @request.session[:user_id] = 2
241 @request.session[:user_id] = 2
231 post :rename, :id => 1, :page => 'Another_page',
242 post :rename, :id => 1, :page => 'Another_page',
232 :wiki_page => { :title => 'Another renamed page',
243 :wiki_page => { :title => 'Another renamed page',
233 :redirect_existing_links => 1 }
244 :redirect_existing_links => 1 }
234 assert_redirected_to :action => 'index', :id => 'ecookbook', :page => 'Another_renamed_page'
245 assert_redirected_to :action => 'index', :id => 'ecookbook', :page => 'Another_renamed_page'
235 wiki = Project.find(1).wiki
246 wiki = Project.find(1).wiki
236 # Check redirects
247 # Check redirects
237 assert_not_nil wiki.find_page('Another page')
248 assert_not_nil wiki.find_page('Another page')
238 assert_nil wiki.find_page('Another page', :with_redirect => false)
249 assert_nil wiki.find_page('Another page', :with_redirect => false)
239 end
250 end
240
251
241 def test_rename_without_redirect
252 def test_rename_without_redirect
242 @request.session[:user_id] = 2
253 @request.session[:user_id] = 2
243 post :rename, :id => 1, :page => 'Another_page',
254 post :rename, :id => 1, :page => 'Another_page',
244 :wiki_page => { :title => 'Another renamed page',
255 :wiki_page => { :title => 'Another renamed page',
245 :redirect_existing_links => "0" }
256 :redirect_existing_links => "0" }
246 assert_redirected_to :action => 'index', :id => 'ecookbook', :page => 'Another_renamed_page'
257 assert_redirected_to :action => 'index', :id => 'ecookbook', :page => 'Another_renamed_page'
247 wiki = Project.find(1).wiki
258 wiki = Project.find(1).wiki
248 # Check that there's no redirects
259 # Check that there's no redirects
249 assert_nil wiki.find_page('Another page')
260 assert_nil wiki.find_page('Another page')
250 end
261 end
251
262
252 def test_destroy_routing
263 def test_destroy_routing
253 assert_recognizes(
264 assert_recognizes(
254 #TODO: should use DELETE on page URI
265 #TODO: should use DELETE on page URI
255 {:controller => 'wiki', :action => 'destroy', :id => '22', :page => 'ladida'},
266 {:controller => 'wiki', :action => 'destroy', :id => '22', :page => 'ladida'},
256 {:method => :post, :path => 'projects/22/wiki/ladida/destroy'}
267 {:method => :post, :path => 'projects/22/wiki/ladida/destroy'}
257 )
268 )
258 end
269 end
259
270
260 def test_destroy_child
271 def test_destroy_child
261 @request.session[:user_id] = 2
272 @request.session[:user_id] = 2
262 post :destroy, :id => 1, :page => 'Child_1'
273 post :destroy, :id => 1, :page => 'Child_1'
263 assert_redirected_to :action => 'special', :id => 'ecookbook', :page => 'Page_index'
274 assert_redirected_to :action => 'special', :id => 'ecookbook', :page => 'Page_index'
264 end
275 end
265
276
266 def test_destroy_parent
277 def test_destroy_parent
267 @request.session[:user_id] = 2
278 @request.session[:user_id] = 2
268 assert_no_difference('WikiPage.count') do
279 assert_no_difference('WikiPage.count') do
269 post :destroy, :id => 1, :page => 'Another_page'
280 post :destroy, :id => 1, :page => 'Another_page'
270 end
281 end
271 assert_response :success
282 assert_response :success
272 assert_template 'destroy'
283 assert_template 'destroy'
273 end
284 end
274
285
275 def test_destroy_parent_with_nullify
286 def test_destroy_parent_with_nullify
276 @request.session[:user_id] = 2
287 @request.session[:user_id] = 2
277 assert_difference('WikiPage.count', -1) do
288 assert_difference('WikiPage.count', -1) do
278 post :destroy, :id => 1, :page => 'Another_page', :todo => 'nullify'
289 post :destroy, :id => 1, :page => 'Another_page', :todo => 'nullify'
279 end
290 end
280 assert_redirected_to :action => 'special', :id => 'ecookbook', :page => 'Page_index'
291 assert_redirected_to :action => 'special', :id => 'ecookbook', :page => 'Page_index'
281 assert_nil WikiPage.find_by_id(2)
292 assert_nil WikiPage.find_by_id(2)
282 end
293 end
283
294
284 def test_destroy_parent_with_cascade
295 def test_destroy_parent_with_cascade
285 @request.session[:user_id] = 2
296 @request.session[:user_id] = 2
286 assert_difference('WikiPage.count', -3) do
297 assert_difference('WikiPage.count', -3) do
287 post :destroy, :id => 1, :page => 'Another_page', :todo => 'destroy'
298 post :destroy, :id => 1, :page => 'Another_page', :todo => 'destroy'
288 end
299 end
289 assert_redirected_to :action => 'special', :id => 'ecookbook', :page => 'Page_index'
300 assert_redirected_to :action => 'special', :id => 'ecookbook', :page => 'Page_index'
290 assert_nil WikiPage.find_by_id(2)
301 assert_nil WikiPage.find_by_id(2)
291 assert_nil WikiPage.find_by_id(5)
302 assert_nil WikiPage.find_by_id(5)
292 end
303 end
293
304
294 def test_destroy_parent_with_reassign
305 def test_destroy_parent_with_reassign
295 @request.session[:user_id] = 2
306 @request.session[:user_id] = 2
296 assert_difference('WikiPage.count', -1) do
307 assert_difference('WikiPage.count', -1) do
297 post :destroy, :id => 1, :page => 'Another_page', :todo => 'reassign', :reassign_to_id => 1
308 post :destroy, :id => 1, :page => 'Another_page', :todo => 'reassign', :reassign_to_id => 1
298 end
309 end
299 assert_redirected_to :action => 'special', :id => 'ecookbook', :page => 'Page_index'
310 assert_redirected_to :action => 'special', :id => 'ecookbook', :page => 'Page_index'
300 assert_nil WikiPage.find_by_id(2)
311 assert_nil WikiPage.find_by_id(2)
301 assert_equal WikiPage.find(1), WikiPage.find_by_id(5).parent
312 assert_equal WikiPage.find(1), WikiPage.find_by_id(5).parent
302 end
313 end
303
314
304 def test_special_routing
315 def test_special_routing
305 assert_routing(
316 assert_routing(
306 {:method => :get, :path => '/projects/567/wiki/page_index'},
317 {:method => :get, :path => '/projects/567/wiki/page_index'},
307 :controller => 'wiki', :action => 'special', :id => '567', :page => 'page_index'
318 :controller => 'wiki', :action => 'special', :id => '567', :page => 'page_index'
308 )
319 )
309 assert_routing(
320 assert_routing(
310 {:method => :get, :path => '/projects/567/wiki/Page_Index'},
321 {:method => :get, :path => '/projects/567/wiki/Page_Index'},
311 :controller => 'wiki', :action => 'special', :id => '567', :page => 'Page_Index'
322 :controller => 'wiki', :action => 'special', :id => '567', :page => 'Page_Index'
312 )
323 )
313 assert_routing(
324 assert_routing(
314 {:method => :get, :path => '/projects/567/wiki/date_index'},
325 {:method => :get, :path => '/projects/567/wiki/date_index'},
315 :controller => 'wiki', :action => 'special', :id => '567', :page => 'date_index'
326 :controller => 'wiki', :action => 'special', :id => '567', :page => 'date_index'
316 )
327 )
317 assert_routing(
328 assert_routing(
318 {:method => :get, :path => '/projects/567/wiki/export'},
329 {:method => :get, :path => '/projects/567/wiki/export'},
319 :controller => 'wiki', :action => 'special', :id => '567', :page => 'export'
330 :controller => 'wiki', :action => 'special', :id => '567', :page => 'export'
320 )
331 )
321 end
332 end
322
333
323 def test_page_index
334 def test_page_index
324 get :special, :id => 'ecookbook', :page => 'Page_index'
335 get :special, :id => 'ecookbook', :page => 'Page_index'
325 assert_response :success
336 assert_response :success
326 assert_template 'special_page_index'
337 assert_template 'special_page_index'
327 pages = assigns(:pages)
338 pages = assigns(:pages)
328 assert_not_nil pages
339 assert_not_nil pages
329 assert_equal Project.find(1).wiki.pages.size, pages.size
340 assert_equal Project.find(1).wiki.pages.size, pages.size
330
341
331 assert_tag :ul, :attributes => { :class => 'pages-hierarchy' },
342 assert_tag :ul, :attributes => { :class => 'pages-hierarchy' },
332 :child => { :tag => 'li', :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/CookBook_documentation' },
343 :child => { :tag => 'li', :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/CookBook_documentation' },
333 :content => 'CookBook documentation' },
344 :content => 'CookBook documentation' },
334 :child => { :tag => 'ul',
345 :child => { :tag => 'ul',
335 :child => { :tag => 'li',
346 :child => { :tag => 'li',
336 :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/Page_with_an_inline_image' },
347 :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/Page_with_an_inline_image' },
337 :content => 'Page with an inline image' } } } },
348 :content => 'Page with an inline image' } } } },
338 :child => { :tag => 'li', :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/Another_page' },
349 :child => { :tag => 'li', :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/Another_page' },
339 :content => 'Another page' } }
350 :content => 'Another page' } }
340 end
351 end
341
352
342 def test_not_found
353 def test_not_found
343 get :index, :id => 999
354 get :index, :id => 999
344 assert_response 404
355 assert_response 404
345 end
356 end
346
357
347 def test_protect_routing
358 def test_protect_routing
348 assert_routing(
359 assert_routing(
349 {:method => :post, :path => 'projects/22/wiki/ladida/protect'},
360 {:method => :post, :path => 'projects/22/wiki/ladida/protect'},
350 {:controller => 'wiki', :action => 'protect', :id => '22', :page => 'ladida'}
361 {:controller => 'wiki', :action => 'protect', :id => '22', :page => 'ladida'}
351 )
362 )
352 end
363 end
353
364
354 def test_protect_page
365 def test_protect_page
355 page = WikiPage.find_by_wiki_id_and_title(1, 'Another_page')
366 page = WikiPage.find_by_wiki_id_and_title(1, 'Another_page')
356 assert !page.protected?
367 assert !page.protected?
357 @request.session[:user_id] = 2
368 @request.session[:user_id] = 2
358 post :protect, :id => 1, :page => page.title, :protected => '1'
369 post :protect, :id => 1, :page => page.title, :protected => '1'
359 assert_redirected_to :action => 'index', :id => 'ecookbook', :page => 'Another_page'
370 assert_redirected_to :action => 'index', :id => 'ecookbook', :page => 'Another_page'
360 assert page.reload.protected?
371 assert page.reload.protected?
361 end
372 end
362
373
363 def test_unprotect_page
374 def test_unprotect_page
364 page = WikiPage.find_by_wiki_id_and_title(1, 'CookBook_documentation')
375 page = WikiPage.find_by_wiki_id_and_title(1, 'CookBook_documentation')
365 assert page.protected?
376 assert page.protected?
366 @request.session[:user_id] = 2
377 @request.session[:user_id] = 2
367 post :protect, :id => 1, :page => page.title, :protected => '0'
378 post :protect, :id => 1, :page => page.title, :protected => '0'
368 assert_redirected_to :action => 'index', :id => 'ecookbook', :page => 'CookBook_documentation'
379 assert_redirected_to :action => 'index', :id => 'ecookbook', :page => 'CookBook_documentation'
369 assert !page.reload.protected?
380 assert !page.reload.protected?
370 end
381 end
371
382
372 def test_show_page_with_edit_link
383 def test_show_page_with_edit_link
373 @request.session[:user_id] = 2
384 @request.session[:user_id] = 2
374 get :index, :id => 1
385 get :index, :id => 1
375 assert_response :success
386 assert_response :success
376 assert_template 'show'
387 assert_template 'show'
377 assert_tag :tag => 'a', :attributes => { :href => '/projects/1/wiki/CookBook_documentation/edit' }
388 assert_tag :tag => 'a', :attributes => { :href => '/projects/1/wiki/CookBook_documentation/edit' }
378 end
389 end
379
390
380 def test_show_page_without_edit_link
391 def test_show_page_without_edit_link
381 @request.session[:user_id] = 4
392 @request.session[:user_id] = 4
382 get :index, :id => 1
393 get :index, :id => 1
383 assert_response :success
394 assert_response :success
384 assert_template 'show'
395 assert_template 'show'
385 assert_no_tag :tag => 'a', :attributes => { :href => '/projects/1/wiki/CookBook_documentation/edit' }
396 assert_no_tag :tag => 'a', :attributes => { :href => '/projects/1/wiki/CookBook_documentation/edit' }
386 end
397 end
387
398
388 def test_edit_unprotected_page
399 def test_edit_unprotected_page
389 # Non members can edit unprotected wiki pages
400 # Non members can edit unprotected wiki pages
390 @request.session[:user_id] = 4
401 @request.session[:user_id] = 4
391 get :edit, :id => 1, :page => 'Another_page'
402 get :edit, :id => 1, :page => 'Another_page'
392 assert_response :success
403 assert_response :success
393 assert_template 'edit'
404 assert_template 'edit'
394 end
405 end
395
406
396 def test_edit_protected_page_by_nonmember
407 def test_edit_protected_page_by_nonmember
397 # Non members can't edit protected wiki pages
408 # Non members can't edit protected wiki pages
398 @request.session[:user_id] = 4
409 @request.session[:user_id] = 4
399 get :edit, :id => 1, :page => 'CookBook_documentation'
410 get :edit, :id => 1, :page => 'CookBook_documentation'
400 assert_response 403
411 assert_response 403
401 end
412 end
402
413
403 def test_edit_protected_page_by_member
414 def test_edit_protected_page_by_member
404 @request.session[:user_id] = 2
415 @request.session[:user_id] = 2
405 get :edit, :id => 1, :page => 'CookBook_documentation'
416 get :edit, :id => 1, :page => 'CookBook_documentation'
406 assert_response :success
417 assert_response :success
407 assert_template 'edit'
418 assert_template 'edit'
408 end
419 end
409
420
410 def test_history_of_non_existing_page_should_return_404
421 def test_history_of_non_existing_page_should_return_404
411 get :history, :id => 1, :page => 'Unknown_page'
422 get :history, :id => 1, :page => 'Unknown_page'
412 assert_response 404
423 assert_response 404
413 end
424 end
414 end
425 end
@@ -1,117 +1,124
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
19
19
20 class WikiPageTest < ActiveSupport::TestCase
20 class WikiPageTest < ActiveSupport::TestCase
21 fixtures :projects, :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions
21 fixtures :projects, :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions
22
22
23 def setup
23 def setup
24 @wiki = Wiki.find(1)
24 @wiki = Wiki.find(1)
25 @page = @wiki.pages.first
25 @page = @wiki.pages.first
26 end
26 end
27
27
28 def test_create
28 def test_create
29 page = WikiPage.new(:wiki => @wiki)
29 page = WikiPage.new(:wiki => @wiki)
30 assert !page.save
30 assert !page.save
31 assert_equal 1, page.errors.count
31 assert_equal 1, page.errors.count
32
32
33 page.title = "Page"
33 page.title = "Page"
34 assert page.save
34 assert page.save
35 page.reload
35 page.reload
36 assert !page.protected?
36
37
37 @wiki.reload
38 @wiki.reload
38 assert @wiki.pages.include?(page)
39 assert @wiki.pages.include?(page)
39 end
40 end
40
41
42 def test_sidebar_should_be_protected_by_default
43 page = @wiki.find_or_new_page('sidebar')
44 assert page.new_record?
45 assert page.protected?
46 end
47
41 def test_find_or_new_page
48 def test_find_or_new_page
42 page = @wiki.find_or_new_page("CookBook documentation")
49 page = @wiki.find_or_new_page("CookBook documentation")
43 assert_kind_of WikiPage, page
50 assert_kind_of WikiPage, page
44 assert !page.new_record?
51 assert !page.new_record?
45
52
46 page = @wiki.find_or_new_page("Non existing page")
53 page = @wiki.find_or_new_page("Non existing page")
47 assert_kind_of WikiPage, page
54 assert_kind_of WikiPage, page
48 assert page.new_record?
55 assert page.new_record?
49 end
56 end
50
57
51 def test_parent_title
58 def test_parent_title
52 page = WikiPage.find_by_title('Another_page')
59 page = WikiPage.find_by_title('Another_page')
53 assert_nil page.parent_title
60 assert_nil page.parent_title
54
61
55 page = WikiPage.find_by_title('Page_with_an_inline_image')
62 page = WikiPage.find_by_title('Page_with_an_inline_image')
56 assert_equal 'CookBook documentation', page.parent_title
63 assert_equal 'CookBook documentation', page.parent_title
57 end
64 end
58
65
59 def test_assign_parent
66 def test_assign_parent
60 page = WikiPage.find_by_title('Another_page')
67 page = WikiPage.find_by_title('Another_page')
61 page.parent_title = 'CookBook documentation'
68 page.parent_title = 'CookBook documentation'
62 assert page.save
69 assert page.save
63 page.reload
70 page.reload
64 assert_equal WikiPage.find_by_title('CookBook_documentation'), page.parent
71 assert_equal WikiPage.find_by_title('CookBook_documentation'), page.parent
65 end
72 end
66
73
67 def test_unassign_parent
74 def test_unassign_parent
68 page = WikiPage.find_by_title('Page_with_an_inline_image')
75 page = WikiPage.find_by_title('Page_with_an_inline_image')
69 page.parent_title = ''
76 page.parent_title = ''
70 assert page.save
77 assert page.save
71 page.reload
78 page.reload
72 assert_nil page.parent
79 assert_nil page.parent
73 end
80 end
74
81
75 def test_parent_validation
82 def test_parent_validation
76 page = WikiPage.find_by_title('CookBook_documentation')
83 page = WikiPage.find_by_title('CookBook_documentation')
77
84
78 # A page that doesn't exist
85 # A page that doesn't exist
79 page.parent_title = 'Unknown title'
86 page.parent_title = 'Unknown title'
80 assert !page.save
87 assert !page.save
81 assert_equal I18n.translate('activerecord.errors.messages.invalid'), page.errors.on(:parent_title)
88 assert_equal I18n.translate('activerecord.errors.messages.invalid'), page.errors.on(:parent_title)
82 # A child page
89 # A child page
83 page.parent_title = 'Page_with_an_inline_image'
90 page.parent_title = 'Page_with_an_inline_image'
84 assert !page.save
91 assert !page.save
85 assert_equal I18n.translate('activerecord.errors.messages.circular_dependency'), page.errors.on(:parent_title)
92 assert_equal I18n.translate('activerecord.errors.messages.circular_dependency'), page.errors.on(:parent_title)
86 # The page itself
93 # The page itself
87 page.parent_title = 'CookBook_documentation'
94 page.parent_title = 'CookBook_documentation'
88 assert !page.save
95 assert !page.save
89 assert_equal I18n.translate('activerecord.errors.messages.circular_dependency'), page.errors.on(:parent_title)
96 assert_equal I18n.translate('activerecord.errors.messages.circular_dependency'), page.errors.on(:parent_title)
90
97
91 page.parent_title = 'Another_page'
98 page.parent_title = 'Another_page'
92 assert page.save
99 assert page.save
93 end
100 end
94
101
95 def test_destroy
102 def test_destroy
96 page = WikiPage.find(1)
103 page = WikiPage.find(1)
97 page.destroy
104 page.destroy
98 assert_nil WikiPage.find_by_id(1)
105 assert_nil WikiPage.find_by_id(1)
99 # make sure that page content and its history are deleted
106 # make sure that page content and its history are deleted
100 assert WikiContent.find_all_by_page_id(1).empty?
107 assert WikiContent.find_all_by_page_id(1).empty?
101 assert WikiContent.versioned_class.find_all_by_page_id(1).empty?
108 assert WikiContent.versioned_class.find_all_by_page_id(1).empty?
102 end
109 end
103
110
104 def test_destroy_should_not_nullify_children
111 def test_destroy_should_not_nullify_children
105 page = WikiPage.find(2)
112 page = WikiPage.find(2)
106 child_ids = page.child_ids
113 child_ids = page.child_ids
107 assert child_ids.any?
114 assert child_ids.any?
108 page.destroy
115 page.destroy
109 assert_nil WikiPage.find_by_id(2)
116 assert_nil WikiPage.find_by_id(2)
110
117
111 children = WikiPage.find_all_by_id(child_ids)
118 children = WikiPage.find_all_by_id(child_ids)
112 assert_equal child_ids.size, children.size
119 assert_equal child_ids.size, children.size
113 children.each do |child|
120 children.each do |child|
114 assert_nil child.parent_id
121 assert_nil child.parent_id
115 end
122 end
116 end
123 end
117 end
124 end
@@ -1,46 +1,65
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # redMine - project management software
3 # redMine - project management software
4 # Copyright (C) 2006-2007 Jean-Philippe Lang
4 # Copyright (C) 2006-2007 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 require File.dirname(__FILE__) + '/../test_helper'
20 require File.dirname(__FILE__) + '/../test_helper'
21
21
22 class WikiTest < ActiveSupport::TestCase
22 class WikiTest < ActiveSupport::TestCase
23 fixtures :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions
23 fixtures :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions
24
24
25 def test_create
25 def test_create
26 wiki = Wiki.new(:project => Project.find(2))
26 wiki = Wiki.new(:project => Project.find(2))
27 assert !wiki.save
27 assert !wiki.save
28 assert_equal 1, wiki.errors.count
28 assert_equal 1, wiki.errors.count
29
29
30 wiki.start_page = "Start page"
30 wiki.start_page = "Start page"
31 assert wiki.save
31 assert wiki.save
32 end
32 end
33
33
34 def test_update
34 def test_update
35 @wiki = Wiki.find(1)
35 @wiki = Wiki.find(1)
36 @wiki.start_page = "Another start page"
36 @wiki.start_page = "Another start page"
37 assert @wiki.save
37 assert @wiki.save
38 @wiki.reload
38 @wiki.reload
39 assert_equal "Another start page", @wiki.start_page
39 assert_equal "Another start page", @wiki.start_page
40 end
40 end
41
41
42 def test_titleize
42 def test_titleize
43 assert_equal 'Page_title_with_CAPITALES', Wiki.titleize('page title with CAPITALES')
43 assert_equal 'Page_title_with_CAPITALES', Wiki.titleize('page title with CAPITALES')
44 assert_equal 'テスト', Wiki.titleize('テスト')
44 assert_equal 'テスト', Wiki.titleize('テスト')
45 end
45 end
46
47 context "#sidebar" do
48 setup do
49 @wiki = Wiki.find(1)
50 end
51
52 should "return nil if undefined" do
53 assert_nil @wiki.sidebar
54 end
55
56 should "return a WikiPage if defined" do
57 page = @wiki.pages.new(:title => 'Sidebar')
58 page.content = WikiContent.new(:text => 'Side bar content for test_show_with_sidebar')
59 page.save!
60
61 assert_kind_of WikiPage, @wiki.sidebar
62 assert_equal 'Sidebar', @wiki.sidebar.title
63 end
64 end
46 end
65 end
General Comments 0
You need to be logged in to leave comments. Login now