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