##// END OF EJS Templates
Rails3: model: replace deprecated 'before_destroy' method at WikiPage model...
Toshi MARUYAMA -
r7317:ec1b9f38f3d2
parent child
Show More
@@ -1,223 +1,224
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 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', :action => 'show', :project_id => o.wiki.project, :id => o.title}}
31 :url => Proc.new {|o| {:controller => 'wiki', :action => 'show', :project_id => o.wiki.project, :id => 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 :permission => :view_wiki_pages,
35 :permission => :view_wiki_pages,
36 :project_key => "#{Wiki.table_name}.project_id"
36 :project_key => "#{Wiki.table_name}.project_id"
37
37
38 attr_accessor :redirect_existing_links
38 attr_accessor :redirect_existing_links
39
39
40 validates_presence_of :title
40 validates_presence_of :title
41 validates_format_of :title, :with => /^[^,\.\/\?\;\|\s]*$/
41 validates_format_of :title, :with => /^[^,\.\/\?\;\|\s]*$/
42 validates_uniqueness_of :title, :scope => :wiki_id, :case_sensitive => false
42 validates_uniqueness_of :title, :scope => :wiki_id, :case_sensitive => false
43 validates_associated :content
43 validates_associated :content
44
44
45 validate :validate_parent_title
45 validate :validate_parent_title
46 before_destroy :remove_redirects
46
47
47 # eager load information about last updates, without loading text
48 # eager load information about last updates, without loading text
48 named_scope :with_updated_on, {
49 named_scope :with_updated_on, {
49 :select => "#{WikiPage.table_name}.*, #{WikiContent.table_name}.updated_on",
50 :select => "#{WikiPage.table_name}.*, #{WikiContent.table_name}.updated_on",
50 :joins => "LEFT JOIN #{WikiContent.table_name} ON #{WikiContent.table_name}.page_id = #{WikiPage.table_name}.id"
51 :joins => "LEFT JOIN #{WikiContent.table_name} ON #{WikiContent.table_name}.page_id = #{WikiPage.table_name}.id"
51 }
52 }
52
53
53 # Wiki pages that are protected by default
54 # Wiki pages that are protected by default
54 DEFAULT_PROTECTED_PAGES = %w(sidebar)
55 DEFAULT_PROTECTED_PAGES = %w(sidebar)
55
56
56 def after_initialize
57 def after_initialize
57 if new_record? && DEFAULT_PROTECTED_PAGES.include?(title.to_s.downcase)
58 if new_record? && DEFAULT_PROTECTED_PAGES.include?(title.to_s.downcase)
58 self.protected = true
59 self.protected = true
59 end
60 end
60 end
61 end
61
62
62 def visible?(user=User.current)
63 def visible?(user=User.current)
63 !user.nil? && user.allowed_to?(:view_wiki_pages, project)
64 !user.nil? && user.allowed_to?(:view_wiki_pages, project)
64 end
65 end
65
66
66 def title=(value)
67 def title=(value)
67 value = Wiki.titleize(value)
68 value = Wiki.titleize(value)
68 @previous_title = read_attribute(:title) if @previous_title.blank?
69 @previous_title = read_attribute(:title) if @previous_title.blank?
69 write_attribute(:title, value)
70 write_attribute(:title, value)
70 end
71 end
71
72
72 def before_save
73 def before_save
73 self.title = Wiki.titleize(title)
74 self.title = Wiki.titleize(title)
74 # Manage redirects if the title has changed
75 # Manage redirects if the title has changed
75 if !@previous_title.blank? && (@previous_title != title) && !new_record?
76 if !@previous_title.blank? && (@previous_title != title) && !new_record?
76 # Update redirects that point to the old title
77 # Update redirects that point to the old title
77 wiki.redirects.find_all_by_redirects_to(@previous_title).each do |r|
78 wiki.redirects.find_all_by_redirects_to(@previous_title).each do |r|
78 r.redirects_to = title
79 r.redirects_to = title
79 r.title == r.redirects_to ? r.destroy : r.save
80 r.title == r.redirects_to ? r.destroy : r.save
80 end
81 end
81 # Remove redirects for the new title
82 # Remove redirects for the new title
82 wiki.redirects.find_all_by_title(title).each(&:destroy)
83 wiki.redirects.find_all_by_title(title).each(&:destroy)
83 # Create a redirect to the new title
84 # Create a redirect to the new title
84 wiki.redirects << WikiRedirect.new(:title => @previous_title, :redirects_to => title) unless redirect_existing_links == "0"
85 wiki.redirects << WikiRedirect.new(:title => @previous_title, :redirects_to => title) unless redirect_existing_links == "0"
85 @previous_title = nil
86 @previous_title = nil
86 end
87 end
87 end
88 end
88
89
89 def before_destroy
90 def remove_redirects
90 # Remove redirects to this page
91 # Remove redirects to this page
91 wiki.redirects.find_all_by_redirects_to(title).each(&:destroy)
92 wiki.redirects.find_all_by_redirects_to(title).each(&:destroy)
92 end
93 end
93
94
94 def pretty_title
95 def pretty_title
95 WikiPage.pretty_title(title)
96 WikiPage.pretty_title(title)
96 end
97 end
97
98
98 def content_for_version(version=nil)
99 def content_for_version(version=nil)
99 result = content.versions.find_by_version(version.to_i) if version
100 result = content.versions.find_by_version(version.to_i) if version
100 result ||= content
101 result ||= content
101 result
102 result
102 end
103 end
103
104
104 def diff(version_to=nil, version_from=nil)
105 def diff(version_to=nil, version_from=nil)
105 version_to = version_to ? version_to.to_i : self.content.version
106 version_to = version_to ? version_to.to_i : self.content.version
106 version_from = version_from ? version_from.to_i : version_to - 1
107 version_from = version_from ? version_from.to_i : version_to - 1
107 version_to, version_from = version_from, version_to unless version_from < version_to
108 version_to, version_from = version_from, version_to unless version_from < version_to
108
109
109 content_to = content.versions.find_by_version(version_to)
110 content_to = content.versions.find_by_version(version_to)
110 content_from = content.versions.find_by_version(version_from)
111 content_from = content.versions.find_by_version(version_from)
111
112
112 (content_to && content_from) ? WikiDiff.new(content_to, content_from) : nil
113 (content_to && content_from) ? WikiDiff.new(content_to, content_from) : nil
113 end
114 end
114
115
115 def annotate(version=nil)
116 def annotate(version=nil)
116 version = version ? version.to_i : self.content.version
117 version = version ? version.to_i : self.content.version
117 c = content.versions.find_by_version(version)
118 c = content.versions.find_by_version(version)
118 c ? WikiAnnotate.new(c) : nil
119 c ? WikiAnnotate.new(c) : nil
119 end
120 end
120
121
121 def self.pretty_title(str)
122 def self.pretty_title(str)
122 (str && str.is_a?(String)) ? str.tr('_', ' ') : str
123 (str && str.is_a?(String)) ? str.tr('_', ' ') : str
123 end
124 end
124
125
125 def project
126 def project
126 wiki.project
127 wiki.project
127 end
128 end
128
129
129 def text
130 def text
130 content.text if content
131 content.text if content
131 end
132 end
132
133
133 def updated_on
134 def updated_on
134 unless @updated_on
135 unless @updated_on
135 if time = read_attribute(:updated_on)
136 if time = read_attribute(:updated_on)
136 # content updated_on was eager loaded with the page
137 # content updated_on was eager loaded with the page
137 @updated_on = Time.parse(time) rescue nil
138 @updated_on = Time.parse(time) rescue nil
138 else
139 else
139 @updated_on = content && content.updated_on
140 @updated_on = content && content.updated_on
140 end
141 end
141 end
142 end
142 @updated_on
143 @updated_on
143 end
144 end
144
145
145 # Returns true if usr is allowed to edit the page, otherwise false
146 # Returns true if usr is allowed to edit the page, otherwise false
146 def editable_by?(usr)
147 def editable_by?(usr)
147 !protected? || usr.allowed_to?(:protect_wiki_pages, wiki.project)
148 !protected? || usr.allowed_to?(:protect_wiki_pages, wiki.project)
148 end
149 end
149
150
150 def attachments_deletable?(usr=User.current)
151 def attachments_deletable?(usr=User.current)
151 editable_by?(usr) && super(usr)
152 editable_by?(usr) && super(usr)
152 end
153 end
153
154
154 def parent_title
155 def parent_title
155 @parent_title || (self.parent && self.parent.pretty_title)
156 @parent_title || (self.parent && self.parent.pretty_title)
156 end
157 end
157
158
158 def parent_title=(t)
159 def parent_title=(t)
159 @parent_title = t
160 @parent_title = t
160 parent_page = t.blank? ? nil : self.wiki.find_page(t)
161 parent_page = t.blank? ? nil : self.wiki.find_page(t)
161 self.parent = parent_page
162 self.parent = parent_page
162 end
163 end
163
164
164 protected
165 protected
165
166
166 def validate_parent_title
167 def validate_parent_title
167 errors.add(:parent_title, :invalid) if !@parent_title.blank? && parent.nil?
168 errors.add(:parent_title, :invalid) if !@parent_title.blank? && parent.nil?
168 errors.add(:parent_title, :circular_dependency) if parent && (parent == self || parent.ancestors.include?(self))
169 errors.add(:parent_title, :circular_dependency) if parent && (parent == self || parent.ancestors.include?(self))
169 errors.add(:parent_title, :not_same_project) if parent && (parent.wiki_id != wiki_id)
170 errors.add(:parent_title, :not_same_project) if parent && (parent.wiki_id != wiki_id)
170 end
171 end
171 end
172 end
172
173
173 class WikiDiff < Redmine::Helpers::Diff
174 class WikiDiff < Redmine::Helpers::Diff
174 attr_reader :content_to, :content_from
175 attr_reader :content_to, :content_from
175
176
176 def initialize(content_to, content_from)
177 def initialize(content_to, content_from)
177 @content_to = content_to
178 @content_to = content_to
178 @content_from = content_from
179 @content_from = content_from
179 super(content_to.text, content_from.text)
180 super(content_to.text, content_from.text)
180 end
181 end
181 end
182 end
182
183
183 class WikiAnnotate
184 class WikiAnnotate
184 attr_reader :lines, :content
185 attr_reader :lines, :content
185
186
186 def initialize(content)
187 def initialize(content)
187 @content = content
188 @content = content
188 current = content
189 current = content
189 current_lines = current.text.split(/\r?\n/)
190 current_lines = current.text.split(/\r?\n/)
190 @lines = current_lines.collect {|t| [nil, nil, t]}
191 @lines = current_lines.collect {|t| [nil, nil, t]}
191 positions = []
192 positions = []
192 current_lines.size.times {|i| positions << i}
193 current_lines.size.times {|i| positions << i}
193 while (current.previous)
194 while (current.previous)
194 d = current.previous.text.split(/\r?\n/).diff(current.text.split(/\r?\n/)).diffs.flatten
195 d = current.previous.text.split(/\r?\n/).diff(current.text.split(/\r?\n/)).diffs.flatten
195 d.each_slice(3) do |s|
196 d.each_slice(3) do |s|
196 sign, line = s[0], s[1]
197 sign, line = s[0], s[1]
197 if sign == '+' && positions[line] && positions[line] != -1
198 if sign == '+' && positions[line] && positions[line] != -1
198 if @lines[positions[line]][0].nil?
199 if @lines[positions[line]][0].nil?
199 @lines[positions[line]][0] = current.version
200 @lines[positions[line]][0] = current.version
200 @lines[positions[line]][1] = current.author
201 @lines[positions[line]][1] = current.author
201 end
202 end
202 end
203 end
203 end
204 end
204 d.each_slice(3) do |s|
205 d.each_slice(3) do |s|
205 sign, line = s[0], s[1]
206 sign, line = s[0], s[1]
206 if sign == '-'
207 if sign == '-'
207 positions.insert(line, -1)
208 positions.insert(line, -1)
208 else
209 else
209 positions[line] = nil
210 positions[line] = nil
210 end
211 end
211 end
212 end
212 positions.compact!
213 positions.compact!
213 # Stop if every line is annotated
214 # Stop if every line is annotated
214 break unless @lines.detect { |line| line[0].nil? }
215 break unless @lines.detect { |line| line[0].nil? }
215 current = current.previous
216 current = current.previous
216 end
217 end
217 @lines.each { |line|
218 @lines.each { |line|
218 line[0] ||= current.version
219 line[0] ||= current.version
219 # if the last known version is > 1 (eg. history was cleared), we don't know the author
220 # if the last known version is > 1 (eg. history was cleared), we don't know the author
220 line[1] ||= current.author if current.version == 1
221 line[1] ||= current.author if current.version == 1
221 }
222 }
222 end
223 end
223 end
224 end
General Comments 0
You need to be logged in to leave comments. Login now