##// END OF EJS Templates
Fixed that moving a wiki page with a child raises an error when target wiki contains a page with the same name as the child (#21900)....
Jean-Philippe Lang -
r14748:f4bcb1286e21
parent child
Show More
@@ -1,297 +1,297
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2015 Jean-Philippe Lang
2 # Copyright (C) 2006-2015 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 has_one :content_without_text, lambda {without_text.readonly}, :class_name => 'WikiContent', :foreign_key => 'page_id'
26 has_one :content_without_text, lambda {without_text.readonly}, :class_name => 'WikiContent', :foreign_key => 'page_id'
27
27
28 acts_as_attachable :delete_permission => :delete_wiki_pages_attachments
28 acts_as_attachable :delete_permission => :delete_wiki_pages_attachments
29 acts_as_tree :dependent => :nullify, :order => 'title'
29 acts_as_tree :dependent => :nullify, :order => 'title'
30
30
31 acts_as_watchable
31 acts_as_watchable
32 acts_as_event :title => Proc.new {|o| "#{l(:label_wiki)}: #{o.title}"},
32 acts_as_event :title => Proc.new {|o| "#{l(:label_wiki)}: #{o.title}"},
33 :description => :text,
33 :description => :text,
34 :datetime => :created_on,
34 :datetime => :created_on,
35 :url => Proc.new {|o| {:controller => 'wiki', :action => 'show', :project_id => o.wiki.project, :id => o.title}}
35 :url => Proc.new {|o| {:controller => 'wiki', :action => 'show', :project_id => o.wiki.project, :id => o.title}}
36
36
37 acts_as_searchable :columns => ['title', "#{WikiContent.table_name}.text"],
37 acts_as_searchable :columns => ['title', "#{WikiContent.table_name}.text"],
38 :scope => joins(:content, {:wiki => :project}),
38 :scope => joins(:content, {:wiki => :project}),
39 :preload => [:content, {:wiki => :project}],
39 :preload => [:content, {:wiki => :project}],
40 :permission => :view_wiki_pages,
40 :permission => :view_wiki_pages,
41 :project_key => "#{Wiki.table_name}.project_id"
41 :project_key => "#{Wiki.table_name}.project_id"
42
42
43 attr_accessor :redirect_existing_links
43 attr_accessor :redirect_existing_links
44
44
45 validates_presence_of :title
45 validates_presence_of :title
46 validates_format_of :title, :with => /\A[^,\.\/\?\;\|\s]*\z/
46 validates_format_of :title, :with => /\A[^,\.\/\?\;\|\s]*\z/
47 validates_uniqueness_of :title, :scope => :wiki_id, :case_sensitive => false
47 validates_uniqueness_of :title, :scope => :wiki_id, :case_sensitive => false
48 validates_associated :content
48 validates_associated :content
49 attr_protected :id
49 attr_protected :id
50
50
51 validate :validate_parent_title
51 validate :validate_parent_title
52 before_destroy :delete_redirects
52 before_destroy :delete_redirects
53 before_save :handle_rename_or_move
53 before_save :handle_rename_or_move
54 after_save :handle_children_move
54 after_save :handle_children_move
55
55
56 # eager load information about last updates, without loading text
56 # eager load information about last updates, without loading text
57 scope :with_updated_on, lambda { preload(:content_without_text) }
57 scope :with_updated_on, lambda { preload(:content_without_text) }
58
58
59 # Wiki pages that are protected by default
59 # Wiki pages that are protected by default
60 DEFAULT_PROTECTED_PAGES = %w(sidebar)
60 DEFAULT_PROTECTED_PAGES = %w(sidebar)
61
61
62 safe_attributes 'parent_id', 'parent_title', 'title', 'redirect_existing_links', 'wiki_id',
62 safe_attributes 'parent_id', 'parent_title', 'title', 'redirect_existing_links', 'wiki_id',
63 :if => lambda {|page, user| page.new_record? || user.allowed_to?(:rename_wiki_pages, page.project)}
63 :if => lambda {|page, user| page.new_record? || user.allowed_to?(:rename_wiki_pages, page.project)}
64
64
65 def initialize(attributes=nil, *args)
65 def initialize(attributes=nil, *args)
66 super
66 super
67 if new_record? && DEFAULT_PROTECTED_PAGES.include?(title.to_s.downcase)
67 if new_record? && DEFAULT_PROTECTED_PAGES.include?(title.to_s.downcase)
68 self.protected = true
68 self.protected = true
69 end
69 end
70 end
70 end
71
71
72 def visible?(user=User.current)
72 def visible?(user=User.current)
73 !user.nil? && user.allowed_to?(:view_wiki_pages, project)
73 !user.nil? && user.allowed_to?(:view_wiki_pages, project)
74 end
74 end
75
75
76 def title=(value)
76 def title=(value)
77 value = Wiki.titleize(value)
77 value = Wiki.titleize(value)
78 write_attribute(:title, value)
78 write_attribute(:title, value)
79 end
79 end
80
80
81 def safe_attributes=(attrs, user=User.current)
81 def safe_attributes=(attrs, user=User.current)
82 return unless attrs.is_a?(Hash)
82 return unless attrs.is_a?(Hash)
83 attrs = attrs.deep_dup
83 attrs = attrs.deep_dup
84
84
85 # Project and Tracker must be set before since new_statuses_allowed_to depends on it.
85 # Project and Tracker must be set before since new_statuses_allowed_to depends on it.
86 if (w_id = attrs.delete('wiki_id')) && safe_attribute?('wiki_id')
86 if (w_id = attrs.delete('wiki_id')) && safe_attribute?('wiki_id')
87 if (w = Wiki.find_by_id(w_id)) && w.project && user.allowed_to?(:rename_wiki_pages, w.project)
87 if (w = Wiki.find_by_id(w_id)) && w.project && user.allowed_to?(:rename_wiki_pages, w.project)
88 self.wiki = w
88 self.wiki = w
89 end
89 end
90 end
90 end
91
91
92 super attrs, user
92 super attrs, user
93 end
93 end
94
94
95 # Manages redirects if page is renamed or moved
95 # Manages redirects if page is renamed or moved
96 def handle_rename_or_move
96 def handle_rename_or_move
97 if !new_record? && (title_changed? || wiki_id_changed?)
97 if !new_record? && (title_changed? || wiki_id_changed?)
98 # Update redirects that point to the old title
98 # Update redirects that point to the old title
99 WikiRedirect.where(:redirects_to => title_was, :redirects_to_wiki_id => wiki_id_was).each do |r|
99 WikiRedirect.where(:redirects_to => title_was, :redirects_to_wiki_id => wiki_id_was).each do |r|
100 r.redirects_to = title
100 r.redirects_to = title
101 r.redirects_to_wiki_id = wiki_id
101 r.redirects_to_wiki_id = wiki_id
102 (r.title == r.redirects_to && r.wiki_id == r.redirects_to_wiki_id) ? r.destroy : r.save
102 (r.title == r.redirects_to && r.wiki_id == r.redirects_to_wiki_id) ? r.destroy : r.save
103 end
103 end
104 # Remove redirects for the new title
104 # Remove redirects for the new title
105 WikiRedirect.where(:wiki_id => wiki_id, :title => title).delete_all
105 WikiRedirect.where(:wiki_id => wiki_id, :title => title).delete_all
106 # Create a redirect to the new title
106 # Create a redirect to the new title
107 unless redirect_existing_links == "0"
107 unless redirect_existing_links == "0"
108 WikiRedirect.create(
108 WikiRedirect.create(
109 :wiki_id => wiki_id_was, :title => title_was,
109 :wiki_id => wiki_id_was, :title => title_was,
110 :redirects_to_wiki_id => wiki_id, :redirects_to => title
110 :redirects_to_wiki_id => wiki_id, :redirects_to => title
111 )
111 )
112 end
112 end
113 end
113 end
114 if !new_record? && wiki_id_changed? && parent.present?
114 if !new_record? && wiki_id_changed? && parent.present?
115 unless parent.wiki_id == wiki_id
115 unless parent.wiki_id == wiki_id
116 self.parent_id = nil
116 self.parent_id = nil
117 end
117 end
118 end
118 end
119 end
119 end
120 private :handle_rename_or_move
120 private :handle_rename_or_move
121
121
122 # Moves child pages if page was moved
122 # Moves child pages if page was moved
123 def handle_children_move
123 def handle_children_move
124 if !new_record? && wiki_id_changed?
124 if !new_record? && wiki_id_changed?
125 children.each do |child|
125 children.each do |child|
126 child.wiki_id = wiki_id
126 child.wiki_id = wiki_id
127 child.redirect_existing_links = redirect_existing_links
127 child.redirect_existing_links = redirect_existing_links
128 unless child.save
128 unless child.save
129 WikiPage.where(:id => child.id).update_all :parent_nil => nil
129 WikiPage.where(:id => child.id).update_all :parent_id => nil
130 end
130 end
131 end
131 end
132 end
132 end
133 end
133 end
134 private :handle_children_move
134 private :handle_children_move
135
135
136 # Deletes redirects to this page
136 # Deletes redirects to this page
137 def delete_redirects
137 def delete_redirects
138 WikiRedirect.where(:redirects_to_wiki_id => wiki_id, :redirects_to => title).delete_all
138 WikiRedirect.where(:redirects_to_wiki_id => wiki_id, :redirects_to => title).delete_all
139 end
139 end
140
140
141 def pretty_title
141 def pretty_title
142 WikiPage.pretty_title(title)
142 WikiPage.pretty_title(title)
143 end
143 end
144
144
145 def content_for_version(version=nil)
145 def content_for_version(version=nil)
146 if content
146 if content
147 result = content.versions.find_by_version(version.to_i) if version
147 result = content.versions.find_by_version(version.to_i) if version
148 result ||= content
148 result ||= content
149 result
149 result
150 end
150 end
151 end
151 end
152
152
153 def diff(version_to=nil, version_from=nil)
153 def diff(version_to=nil, version_from=nil)
154 version_to = version_to ? version_to.to_i : self.content.version
154 version_to = version_to ? version_to.to_i : self.content.version
155 content_to = content.versions.find_by_version(version_to)
155 content_to = content.versions.find_by_version(version_to)
156 content_from = version_from ? content.versions.find_by_version(version_from.to_i) : content_to.try(:previous)
156 content_from = version_from ? content.versions.find_by_version(version_from.to_i) : content_to.try(:previous)
157 return nil unless content_to && content_from
157 return nil unless content_to && content_from
158
158
159 if content_from.version > content_to.version
159 if content_from.version > content_to.version
160 content_to, content_from = content_from, content_to
160 content_to, content_from = content_from, content_to
161 end
161 end
162
162
163 (content_to && content_from) ? WikiDiff.new(content_to, content_from) : nil
163 (content_to && content_from) ? WikiDiff.new(content_to, content_from) : nil
164 end
164 end
165
165
166 def annotate(version=nil)
166 def annotate(version=nil)
167 version = version ? version.to_i : self.content.version
167 version = version ? version.to_i : self.content.version
168 c = content.versions.find_by_version(version)
168 c = content.versions.find_by_version(version)
169 c ? WikiAnnotate.new(c) : nil
169 c ? WikiAnnotate.new(c) : nil
170 end
170 end
171
171
172 def self.pretty_title(str)
172 def self.pretty_title(str)
173 (str && str.is_a?(String)) ? str.tr('_', ' ') : str
173 (str && str.is_a?(String)) ? str.tr('_', ' ') : str
174 end
174 end
175
175
176 def project
176 def project
177 wiki.try(:project)
177 wiki.try(:project)
178 end
178 end
179
179
180 def text
180 def text
181 content.text if content
181 content.text if content
182 end
182 end
183
183
184 def updated_on
184 def updated_on
185 content_attribute(:updated_on)
185 content_attribute(:updated_on)
186 end
186 end
187
187
188 def version
188 def version
189 content_attribute(:version)
189 content_attribute(:version)
190 end
190 end
191
191
192 # Returns true if usr is allowed to edit the page, otherwise false
192 # Returns true if usr is allowed to edit the page, otherwise false
193 def editable_by?(usr)
193 def editable_by?(usr)
194 !protected? || usr.allowed_to?(:protect_wiki_pages, wiki.project)
194 !protected? || usr.allowed_to?(:protect_wiki_pages, wiki.project)
195 end
195 end
196
196
197 def attachments_deletable?(usr=User.current)
197 def attachments_deletable?(usr=User.current)
198 editable_by?(usr) && super(usr)
198 editable_by?(usr) && super(usr)
199 end
199 end
200
200
201 def parent_title
201 def parent_title
202 @parent_title || (self.parent && self.parent.pretty_title)
202 @parent_title || (self.parent && self.parent.pretty_title)
203 end
203 end
204
204
205 def parent_title=(t)
205 def parent_title=(t)
206 @parent_title = t
206 @parent_title = t
207 parent_page = t.blank? ? nil : self.wiki.find_page(t)
207 parent_page = t.blank? ? nil : self.wiki.find_page(t)
208 self.parent = parent_page
208 self.parent = parent_page
209 end
209 end
210
210
211 # Saves the page and its content if text was changed
211 # Saves the page and its content if text was changed
212 # Return true if the page was saved
212 # Return true if the page was saved
213 def save_with_content(content)
213 def save_with_content(content)
214 ret = nil
214 ret = nil
215 transaction do
215 transaction do
216 ret = save
216 ret = save
217 if content.text_changed?
217 if content.text_changed?
218 begin
218 begin
219 self.content = content
219 self.content = content
220 ret = ret && content.changed?
220 ret = ret && content.changed?
221 rescue ActiveRecord::RecordNotSaved
221 rescue ActiveRecord::RecordNotSaved
222 ret = false
222 ret = false
223 end
223 end
224 end
224 end
225 raise ActiveRecord::Rollback unless ret
225 raise ActiveRecord::Rollback unless ret
226 end
226 end
227 ret
227 ret
228 end
228 end
229
229
230 protected
230 protected
231
231
232 def validate_parent_title
232 def validate_parent_title
233 errors.add(:parent_title, :invalid) if !@parent_title.blank? && parent.nil?
233 errors.add(:parent_title, :invalid) if !@parent_title.blank? && parent.nil?
234 errors.add(:parent_title, :circular_dependency) if parent && (parent == self || parent.ancestors.include?(self))
234 errors.add(:parent_title, :circular_dependency) if parent && (parent == self || parent.ancestors.include?(self))
235 if parent_id_changed? && parent && (parent.wiki_id != wiki_id)
235 if parent_id_changed? && parent && (parent.wiki_id != wiki_id)
236 errors.add(:parent_title, :not_same_project)
236 errors.add(:parent_title, :not_same_project)
237 end
237 end
238 end
238 end
239
239
240 private
240 private
241
241
242 def content_attribute(name)
242 def content_attribute(name)
243 (association(:content).loaded? ? content : content_without_text).try(name)
243 (association(:content).loaded? ? content : content_without_text).try(name)
244 end
244 end
245 end
245 end
246
246
247 class WikiDiff < Redmine::Helpers::Diff
247 class WikiDiff < Redmine::Helpers::Diff
248 attr_reader :content_to, :content_from
248 attr_reader :content_to, :content_from
249
249
250 def initialize(content_to, content_from)
250 def initialize(content_to, content_from)
251 @content_to = content_to
251 @content_to = content_to
252 @content_from = content_from
252 @content_from = content_from
253 super(content_to.text, content_from.text)
253 super(content_to.text, content_from.text)
254 end
254 end
255 end
255 end
256
256
257 class WikiAnnotate
257 class WikiAnnotate
258 attr_reader :lines, :content
258 attr_reader :lines, :content
259
259
260 def initialize(content)
260 def initialize(content)
261 @content = content
261 @content = content
262 current = content
262 current = content
263 current_lines = current.text.split(/\r?\n/)
263 current_lines = current.text.split(/\r?\n/)
264 @lines = current_lines.collect {|t| [nil, nil, t]}
264 @lines = current_lines.collect {|t| [nil, nil, t]}
265 positions = []
265 positions = []
266 current_lines.size.times {|i| positions << i}
266 current_lines.size.times {|i| positions << i}
267 while (current.previous)
267 while (current.previous)
268 d = current.previous.text.split(/\r?\n/).diff(current.text.split(/\r?\n/)).diffs.flatten
268 d = current.previous.text.split(/\r?\n/).diff(current.text.split(/\r?\n/)).diffs.flatten
269 d.each_slice(3) do |s|
269 d.each_slice(3) do |s|
270 sign, line = s[0], s[1]
270 sign, line = s[0], s[1]
271 if sign == '+' && positions[line] && positions[line] != -1
271 if sign == '+' && positions[line] && positions[line] != -1
272 if @lines[positions[line]][0].nil?
272 if @lines[positions[line]][0].nil?
273 @lines[positions[line]][0] = current.version
273 @lines[positions[line]][0] = current.version
274 @lines[positions[line]][1] = current.author
274 @lines[positions[line]][1] = current.author
275 end
275 end
276 end
276 end
277 end
277 end
278 d.each_slice(3) do |s|
278 d.each_slice(3) do |s|
279 sign, line = s[0], s[1]
279 sign, line = s[0], s[1]
280 if sign == '-'
280 if sign == '-'
281 positions.insert(line, -1)
281 positions.insert(line, -1)
282 else
282 else
283 positions[line] = nil
283 positions[line] = nil
284 end
284 end
285 end
285 end
286 positions.compact!
286 positions.compact!
287 # Stop if every line is annotated
287 # Stop if every line is annotated
288 break unless @lines.detect { |line| line[0].nil? }
288 break unless @lines.detect { |line| line[0].nil? }
289 current = current.previous
289 current = current.previous
290 end
290 end
291 @lines.each { |line|
291 @lines.each { |line|
292 line[0] ||= current.version
292 line[0] ||= current.version
293 # if the last known version is > 1 (eg. history was cleared), we don't know the author
293 # if the last known version is > 1 (eg. history was cleared), we don't know the author
294 line[1] ||= current.author if current.version == 1
294 line[1] ||= current.author if current.version == 1
295 }
295 }
296 end
296 end
297 end
297 end
@@ -1,183 +1,201
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2015 Jean-Philippe Lang
2 # Copyright (C) 2006-2015 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 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 assert !page.protected?
37
37
38 @wiki.reload
38 @wiki.reload
39 assert @wiki.pages.include?(page)
39 assert @wiki.pages.include?(page)
40 end
40 end
41
41
42 def test_sidebar_should_be_protected_by_default
42 def test_sidebar_should_be_protected_by_default
43 page = @wiki.find_or_new_page('sidebar')
43 page = @wiki.find_or_new_page('sidebar')
44 assert page.new_record?
44 assert page.new_record?
45 assert page.protected?
45 assert page.protected?
46 end
46 end
47
47
48 def test_find_or_new_page
48 def test_find_or_new_page
49 page = @wiki.find_or_new_page("CookBook documentation")
49 page = @wiki.find_or_new_page("CookBook documentation")
50 assert_kind_of WikiPage, page
50 assert_kind_of WikiPage, page
51 assert !page.new_record?
51 assert !page.new_record?
52
52
53 page = @wiki.find_or_new_page("Non existing page")
53 page = @wiki.find_or_new_page("Non existing page")
54 assert_kind_of WikiPage, page
54 assert_kind_of WikiPage, page
55 assert page.new_record?
55 assert page.new_record?
56 end
56 end
57
57
58 def test_parent_title
58 def test_parent_title
59 page = WikiPage.find_by_title('Another_page')
59 page = WikiPage.find_by_title('Another_page')
60 assert_nil page.parent_title
60 assert_nil page.parent_title
61
61
62 page = WikiPage.find_by_title('Page_with_an_inline_image')
62 page = WikiPage.find_by_title('Page_with_an_inline_image')
63 assert_equal 'CookBook documentation', page.parent_title
63 assert_equal 'CookBook documentation', page.parent_title
64 end
64 end
65
65
66 def test_assign_parent
66 def test_assign_parent
67 page = WikiPage.find_by_title('Another_page')
67 page = WikiPage.find_by_title('Another_page')
68 page.parent_title = 'CookBook documentation'
68 page.parent_title = 'CookBook documentation'
69 assert page.save
69 assert page.save
70 page.reload
70 page.reload
71 assert_equal WikiPage.find_by_title('CookBook_documentation'), page.parent
71 assert_equal WikiPage.find_by_title('CookBook_documentation'), page.parent
72 end
72 end
73
73
74 def test_unassign_parent
74 def test_unassign_parent
75 page = WikiPage.find_by_title('Page_with_an_inline_image')
75 page = WikiPage.find_by_title('Page_with_an_inline_image')
76 page.parent_title = ''
76 page.parent_title = ''
77 assert page.save
77 assert page.save
78 page.reload
78 page.reload
79 assert_nil page.parent
79 assert_nil page.parent
80 end
80 end
81
81
82 def test_parent_validation
82 def test_parent_validation
83 page = WikiPage.find_by_title('CookBook_documentation')
83 page = WikiPage.find_by_title('CookBook_documentation')
84
84
85 # A page that doesn't exist
85 # A page that doesn't exist
86 page.parent_title = 'Unknown title'
86 page.parent_title = 'Unknown title'
87 assert !page.save
87 assert !page.save
88 assert_include I18n.translate('activerecord.errors.messages.invalid'),
88 assert_include I18n.translate('activerecord.errors.messages.invalid'),
89 page.errors[:parent_title]
89 page.errors[:parent_title]
90 # A child page
90 # A child page
91 page.parent_title = 'Page_with_an_inline_image'
91 page.parent_title = 'Page_with_an_inline_image'
92 assert !page.save
92 assert !page.save
93 assert_include I18n.translate('activerecord.errors.messages.circular_dependency'),
93 assert_include I18n.translate('activerecord.errors.messages.circular_dependency'),
94 page.errors[:parent_title]
94 page.errors[:parent_title]
95 # The page itself
95 # The page itself
96 page.parent_title = 'CookBook_documentation'
96 page.parent_title = 'CookBook_documentation'
97 assert !page.save
97 assert !page.save
98 assert_include I18n.translate('activerecord.errors.messages.circular_dependency'),
98 assert_include I18n.translate('activerecord.errors.messages.circular_dependency'),
99 page.errors[:parent_title]
99 page.errors[:parent_title]
100 page.parent_title = 'Another_page'
100 page.parent_title = 'Another_page'
101 assert page.save
101 assert page.save
102 end
102 end
103
103
104 def test_move_child_should_clear_parent
104 def test_move_child_should_clear_parent
105 parent = WikiPage.create!(:wiki_id => 1, :title => 'Parent')
105 parent = WikiPage.create!(:wiki_id => 1, :title => 'Parent')
106 child = WikiPage.create!(:wiki_id => 1, :title => 'Child', :parent => parent)
106 child = WikiPage.create!(:wiki_id => 1, :title => 'Child', :parent => parent)
107
107
108 child.wiki_id = 2
108 child.wiki_id = 2
109 child.save!
109 child.save!
110 assert_equal nil, child.reload.parent_id
110 assert_equal nil, child.reload.parent_id
111 end
111 end
112
112
113 def test_move_parent_should_move_child_page
113 def test_move_parent_should_move_child_page
114 parent = WikiPage.create!(:wiki_id => 1, :title => 'Parent')
114 parent = WikiPage.create!(:wiki_id => 1, :title => 'Parent')
115 child = WikiPage.create!(:wiki_id => 1, :title => 'Child', :parent => parent)
115 child = WikiPage.create!(:wiki_id => 1, :title => 'Child', :parent => parent)
116 parent.reload
116 parent.reload
117
117
118 parent.wiki_id = 2
118 parent.wiki_id = 2
119 parent.save!
119 parent.save!
120 assert_equal 2, child.reload.wiki_id
120 assert_equal 2, child.reload.wiki_id
121 assert_equal parent, child.parent
121 assert_equal parent, child.parent
122 end
122 end
123
123
124 def test_move_parent_with_child_with_duplicate_name_should_not_move_child
125 parent = WikiPage.create!(:wiki_id => 1, :title => 'Parent')
126 child = WikiPage.create!(:wiki_id => 1, :title => 'Child', :parent_id => parent.id)
127 parent.reload
128 # page with the same name as the child in the target wiki
129 WikiPage.create!(:wiki_id => 2, :title => 'Child')
130
131 parent.wiki_id = 2
132 parent.save!
133
134 parent.reload
135 assert_equal 2, parent.wiki_id
136
137 child.reload
138 assert_equal 1, child.wiki_id
139 assert_nil child.parent_id
140 end
141
124 def test_destroy
142 def test_destroy
125 page = WikiPage.find(1)
143 page = WikiPage.find(1)
126 page.destroy
144 page.destroy
127 assert_nil WikiPage.find_by_id(1)
145 assert_nil WikiPage.find_by_id(1)
128 # make sure that page content and its history are deleted
146 # make sure that page content and its history are deleted
129 assert_equal 0, WikiContent.where(:page_id => 1).count
147 assert_equal 0, WikiContent.where(:page_id => 1).count
130 assert_equal 0, WikiContent.versioned_class.where(:page_id => 1).count
148 assert_equal 0, WikiContent.versioned_class.where(:page_id => 1).count
131 end
149 end
132
150
133 def test_destroy_should_not_nullify_children
151 def test_destroy_should_not_nullify_children
134 page = WikiPage.find(2)
152 page = WikiPage.find(2)
135 child_ids = page.child_ids
153 child_ids = page.child_ids
136 assert child_ids.any?
154 assert child_ids.any?
137 page.destroy
155 page.destroy
138 assert_nil WikiPage.find_by_id(2)
156 assert_nil WikiPage.find_by_id(2)
139
157
140 children = WikiPage.where(:id => child_ids)
158 children = WikiPage.where(:id => child_ids)
141 assert_equal child_ids.size, children.count
159 assert_equal child_ids.size, children.count
142 children.each do |child|
160 children.each do |child|
143 assert_nil child.parent_id
161 assert_nil child.parent_id
144 end
162 end
145 end
163 end
146
164
147 def test_with_updated_on_scope_should_preload_updated_on_and_version
165 def test_with_updated_on_scope_should_preload_updated_on_and_version
148 page = WikiPage.with_updated_on.where(:id => 1).first
166 page = WikiPage.with_updated_on.where(:id => 1).first
149 # make the assertions fail if attributes are not preloaded
167 # make the assertions fail if attributes are not preloaded
150 WikiContent.update_all(:updated_on => '2001-01-01 10:00:00', :version => 1)
168 WikiContent.update_all(:updated_on => '2001-01-01 10:00:00', :version => 1)
151
169
152 assert_equal Time.gm(2007, 3, 6, 23, 10, 51), page.updated_on
170 assert_equal Time.gm(2007, 3, 6, 23, 10, 51), page.updated_on
153 assert_equal 3, page.version
171 assert_equal 3, page.version
154 end
172 end
155
173
156 def test_descendants
174 def test_descendants
157 page = WikiPage.create!(:wiki => @wiki, :title => 'Parent')
175 page = WikiPage.create!(:wiki => @wiki, :title => 'Parent')
158 child1 = WikiPage.create!(:wiki => @wiki, :title => 'Child1', :parent => page)
176 child1 = WikiPage.create!(:wiki => @wiki, :title => 'Child1', :parent => page)
159 child11 = WikiPage.create!(:wiki => @wiki, :title => 'Child11', :parent => child1)
177 child11 = WikiPage.create!(:wiki => @wiki, :title => 'Child11', :parent => child1)
160 child111 = WikiPage.create!(:wiki => @wiki, :title => 'Child111', :parent => child11)
178 child111 = WikiPage.create!(:wiki => @wiki, :title => 'Child111', :parent => child11)
161 child2 = WikiPage.create!(:wiki => @wiki, :title => 'Child2', :parent => page)
179 child2 = WikiPage.create!(:wiki => @wiki, :title => 'Child2', :parent => page)
162
180
163 assert_equal %w(Child1 Child11 Child111 Child2), page.descendants.map(&:title).sort
181 assert_equal %w(Child1 Child11 Child111 Child2), page.descendants.map(&:title).sort
164 assert_equal %w(Child1 Child11 Child111 Child2), page.descendants(nil).map(&:title).sort
182 assert_equal %w(Child1 Child11 Child111 Child2), page.descendants(nil).map(&:title).sort
165 assert_equal %w(Child1 Child11 Child2), page.descendants(2).map(&:title).sort
183 assert_equal %w(Child1 Child11 Child2), page.descendants(2).map(&:title).sort
166 assert_equal %w(Child1 Child2), page.descendants(1).map(&:title).sort
184 assert_equal %w(Child1 Child2), page.descendants(1).map(&:title).sort
167
185
168 assert_equal %w(Child1 Child11 Child111 Child2 Parent), page.self_and_descendants.map(&:title).sort
186 assert_equal %w(Child1 Child11 Child111 Child2 Parent), page.self_and_descendants.map(&:title).sort
169 assert_equal %w(Child1 Child11 Child111 Child2 Parent), page.self_and_descendants(nil).map(&:title).sort
187 assert_equal %w(Child1 Child11 Child111 Child2 Parent), page.self_and_descendants(nil).map(&:title).sort
170 assert_equal %w(Child1 Child11 Child2 Parent), page.self_and_descendants(2).map(&:title).sort
188 assert_equal %w(Child1 Child11 Child2 Parent), page.self_and_descendants(2).map(&:title).sort
171 assert_equal %w(Child1 Child2 Parent), page.self_and_descendants(1).map(&:title).sort
189 assert_equal %w(Child1 Child2 Parent), page.self_and_descendants(1).map(&:title).sort
172 end
190 end
173
191
174 def test_diff_for_page_with_deleted_version_should_pick_the_previous_available_version
192 def test_diff_for_page_with_deleted_version_should_pick_the_previous_available_version
175 WikiContent::Version.find_by_page_id_and_version(1, 2).destroy
193 WikiContent::Version.find_by_page_id_and_version(1, 2).destroy
176
194
177 page = WikiPage.find(1)
195 page = WikiPage.find(1)
178 diff = page.diff(3)
196 diff = page.diff(3)
179 assert_not_nil diff
197 assert_not_nil diff
180 assert_equal 3, diff.content_to.version
198 assert_equal 3, diff.content_to.version
181 assert_equal 1, diff.content_from.version
199 assert_equal 1, diff.content_from.version
182 end
200 end
183 end
201 end
General Comments 0
You need to be logged in to leave comments. Login now