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