##// END OF EJS Templates
Fixed test failures on wiki_page updated_on eager loading (#10996)....
Jean-Philippe Lang -
r9544:9aef7a32425d
parent child
Show More
@@ -1,234 +1,234
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'diff'
19 19 require 'enumerator'
20 20
21 21 class WikiPage < ActiveRecord::Base
22 22 include Redmine::SafeAttributes
23 23
24 24 belongs_to :wiki
25 25 has_one :content, :class_name => 'WikiContent', :foreign_key => 'page_id', :dependent => :destroy
26 26 acts_as_attachable :delete_permission => :delete_wiki_pages_attachments
27 27 acts_as_tree :dependent => :nullify, :order => 'title'
28 28
29 29 acts_as_watchable
30 30 acts_as_event :title => Proc.new {|o| "#{l(:label_wiki)}: #{o.title}"},
31 31 :description => :text,
32 32 :datetime => :created_on,
33 33 :url => Proc.new {|o| {:controller => 'wiki', :action => 'show', :project_id => o.wiki.project, :id => o.title}}
34 34
35 35 acts_as_searchable :columns => ['title', "#{WikiContent.table_name}.text"],
36 36 :include => [{:wiki => :project}, :content],
37 37 :permission => :view_wiki_pages,
38 38 :project_key => "#{Wiki.table_name}.project_id"
39 39
40 40 attr_accessor :redirect_existing_links
41 41
42 42 validates_presence_of :title
43 43 validates_format_of :title, :with => /^[^,\.\/\?\;\|\s]*$/
44 44 validates_uniqueness_of :title, :scope => :wiki_id, :case_sensitive => false
45 45 validates_associated :content
46 46
47 47 validate :validate_parent_title
48 48 before_destroy :remove_redirects
49 49 before_save :handle_redirects
50 50
51 51 # eager load information about last updates, without loading text
52 52 scope :with_updated_on, {
53 53 :select => "#{WikiPage.table_name}.*, #{WikiContent.table_name}.updated_on",
54 54 :joins => "LEFT JOIN #{WikiContent.table_name} ON #{WikiContent.table_name}.page_id = #{WikiPage.table_name}.id"
55 55 }
56 56
57 57 # Wiki pages that are protected by default
58 58 DEFAULT_PROTECTED_PAGES = %w(sidebar)
59 59
60 60 safe_attributes 'parent_id',
61 61 :if => lambda {|page, user| page.new_record? || user.allowed_to?(:rename_wiki_pages, page.project)}
62 62
63 63 def initialize(attributes=nil, *args)
64 64 super
65 65 if new_record? && DEFAULT_PROTECTED_PAGES.include?(title.to_s.downcase)
66 66 self.protected = true
67 67 end
68 68 end
69 69
70 70 def visible?(user=User.current)
71 71 !user.nil? && user.allowed_to?(:view_wiki_pages, project)
72 72 end
73 73
74 74 def title=(value)
75 75 value = Wiki.titleize(value)
76 76 @previous_title = read_attribute(:title) if @previous_title.blank?
77 77 write_attribute(:title, value)
78 78 end
79 79
80 80 def handle_redirects
81 81 self.title = Wiki.titleize(title)
82 82 # Manage redirects if the title has changed
83 83 if !@previous_title.blank? && (@previous_title != title) && !new_record?
84 84 # Update redirects that point to the old title
85 85 wiki.redirects.find_all_by_redirects_to(@previous_title).each do |r|
86 86 r.redirects_to = title
87 87 r.title == r.redirects_to ? r.destroy : r.save
88 88 end
89 89 # Remove redirects for the new title
90 90 wiki.redirects.find_all_by_title(title).each(&:destroy)
91 91 # Create a redirect to the new title
92 92 wiki.redirects << WikiRedirect.new(:title => @previous_title, :redirects_to => title) unless redirect_existing_links == "0"
93 93 @previous_title = nil
94 94 end
95 95 end
96 96
97 97 def remove_redirects
98 98 # Remove redirects to this page
99 99 wiki.redirects.find_all_by_redirects_to(title).each(&:destroy)
100 100 end
101 101
102 102 def pretty_title
103 103 WikiPage.pretty_title(title)
104 104 end
105 105
106 106 def content_for_version(version=nil)
107 107 result = content.versions.find_by_version(version.to_i) if version
108 108 result ||= content
109 109 result
110 110 end
111 111
112 112 def diff(version_to=nil, version_from=nil)
113 113 version_to = version_to ? version_to.to_i : self.content.version
114 114 version_from = version_from ? version_from.to_i : version_to - 1
115 115 version_to, version_from = version_from, version_to unless version_from < version_to
116 116
117 117 content_to = content.versions.find_by_version(version_to)
118 118 content_from = content.versions.find_by_version(version_from)
119 119
120 120 (content_to && content_from) ? WikiDiff.new(content_to, content_from) : nil
121 121 end
122 122
123 123 def annotate(version=nil)
124 124 version = version ? version.to_i : self.content.version
125 125 c = content.versions.find_by_version(version)
126 126 c ? WikiAnnotate.new(c) : nil
127 127 end
128 128
129 129 def self.pretty_title(str)
130 130 (str && str.is_a?(String)) ? str.tr('_', ' ') : str
131 131 end
132 132
133 133 def project
134 134 wiki.project
135 135 end
136 136
137 137 def text
138 138 content.text if content
139 139 end
140 140
141 141 def updated_on
142 142 unless @updated_on
143 143 if time = read_attribute(:updated_on)
144 144 # content updated_on was eager loaded with the page
145 145 begin
146 @updated_on = Time.zone ? Time.zone.parse(time.to_s) : Time.parse(time.to_s)
146 @updated_on = (self.class.default_timezone == :utc ? Time.parse(time.to_s).utc : Time.parse(time.to_s).localtime)
147 147 rescue
148 148 end
149 149 else
150 150 @updated_on = content && content.updated_on
151 151 end
152 152 end
153 153 @updated_on
154 154 end
155 155
156 156 # Returns true if usr is allowed to edit the page, otherwise false
157 157 def editable_by?(usr)
158 158 !protected? || usr.allowed_to?(:protect_wiki_pages, wiki.project)
159 159 end
160 160
161 161 def attachments_deletable?(usr=User.current)
162 162 editable_by?(usr) && super(usr)
163 163 end
164 164
165 165 def parent_title
166 166 @parent_title || (self.parent && self.parent.pretty_title)
167 167 end
168 168
169 169 def parent_title=(t)
170 170 @parent_title = t
171 171 parent_page = t.blank? ? nil : self.wiki.find_page(t)
172 172 self.parent = parent_page
173 173 end
174 174
175 175 protected
176 176
177 177 def validate_parent_title
178 178 errors.add(:parent_title, :invalid) if !@parent_title.blank? && parent.nil?
179 179 errors.add(:parent_title, :circular_dependency) if parent && (parent == self || parent.ancestors.include?(self))
180 180 errors.add(:parent_title, :not_same_project) if parent && (parent.wiki_id != wiki_id)
181 181 end
182 182 end
183 183
184 184 class WikiDiff < Redmine::Helpers::Diff
185 185 attr_reader :content_to, :content_from
186 186
187 187 def initialize(content_to, content_from)
188 188 @content_to = content_to
189 189 @content_from = content_from
190 190 super(content_to.text, content_from.text)
191 191 end
192 192 end
193 193
194 194 class WikiAnnotate
195 195 attr_reader :lines, :content
196 196
197 197 def initialize(content)
198 198 @content = content
199 199 current = content
200 200 current_lines = current.text.split(/\r?\n/)
201 201 @lines = current_lines.collect {|t| [nil, nil, t]}
202 202 positions = []
203 203 current_lines.size.times {|i| positions << i}
204 204 while (current.previous)
205 205 d = current.previous.text.split(/\r?\n/).diff(current.text.split(/\r?\n/)).diffs.flatten
206 206 d.each_slice(3) do |s|
207 207 sign, line = s[0], s[1]
208 208 if sign == '+' && positions[line] && positions[line] != -1
209 209 if @lines[positions[line]][0].nil?
210 210 @lines[positions[line]][0] = current.version
211 211 @lines[positions[line]][1] = current.author
212 212 end
213 213 end
214 214 end
215 215 d.each_slice(3) do |s|
216 216 sign, line = s[0], s[1]
217 217 if sign == '-'
218 218 positions.insert(line, -1)
219 219 else
220 220 positions[line] = nil
221 221 end
222 222 end
223 223 positions.compact!
224 224 # Stop if every line is annotated
225 225 break unless @lines.detect { |line| line[0].nil? }
226 226 current = current.previous
227 227 end
228 228 @lines.each { |line|
229 229 line[0] ||= current.version
230 230 # if the last known version is > 1 (eg. history was cleared), we don't know the author
231 231 line[1] ||= current.author if current.version == 1
232 232 }
233 233 end
234 234 end
General Comments 0
You need to be logged in to leave comments. Login now