##// END OF EJS Templates
Rails3: replace deprecated 'after_create' to declared method at Repository model....
Toshi MARUYAMA -
r6620:80c5f34c07a5
parent child
Show More
@@ -1,298 +1,300
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 'iconv'
19 19
20 20 class Changeset < ActiveRecord::Base
21 21 belongs_to :repository
22 22 belongs_to :user
23 23 has_many :changes, :dependent => :delete_all
24 24 has_and_belongs_to_many :issues
25 25
26 26 acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{o.format_identifier}" + (o.short_comments.blank? ? '' : (': ' + o.short_comments))},
27 27 :description => :long_comments,
28 28 :datetime => :committed_on,
29 29 :url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :rev => o.identifier}}
30 30
31 31 acts_as_searchable :columns => 'comments',
32 32 :include => {:repository => :project},
33 33 :project_key => "#{Repository.table_name}.project_id",
34 34 :date_column => 'committed_on'
35 35
36 36 acts_as_activity_provider :timestamp => "#{table_name}.committed_on",
37 37 :author_key => :user_id,
38 38 :find_options => {:include => [:user, {:repository => :project}]}
39 39
40 40 validates_presence_of :repository_id, :revision, :committed_on, :commit_date
41 41 validates_uniqueness_of :revision, :scope => :repository_id
42 42 validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
43 43
44 44 named_scope :visible, lambda {|*args| { :include => {:repository => :project},
45 45 :conditions => Project.allowed_to_condition(args.shift || User.current, :view_changesets, *args) } }
46 46
47 after_create :scan_for_issues
48
47 49 def revision=(r)
48 50 write_attribute :revision, (r.nil? ? nil : r.to_s)
49 51 end
50 52
51 53 # Returns the identifier of this changeset; depending on repository backends
52 54 def identifier
53 55 if repository.class.respond_to? :changeset_identifier
54 56 repository.class.changeset_identifier self
55 57 else
56 58 revision.to_s
57 59 end
58 60 end
59 61
60 62 def committed_on=(date)
61 63 self.commit_date = date
62 64 super
63 65 end
64 66
65 67 # Returns the readable identifier
66 68 def format_identifier
67 69 if repository.class.respond_to? :format_changeset_identifier
68 70 repository.class.format_changeset_identifier self
69 71 else
70 72 identifier
71 73 end
72 74 end
73 75
74 76 def project
75 77 repository.project
76 78 end
77 79
78 80 def author
79 81 user || committer.to_s.split('<').first
80 82 end
81 83
82 84 def before_create
83 85 self.committer = self.class.to_utf8(self.committer, repository.repo_log_encoding)
84 86 self.comments = self.class.normalize_comments(
85 87 self.comments, repository.repo_log_encoding)
86 88 self.user = repository.find_committer_user(self.committer)
87 89 end
88 90
89 def after_create
91 def scan_for_issues
90 92 scan_comment_for_issue_ids
91 93 end
92 94
93 95 TIMELOG_RE = /
94 96 (
95 97 ((\d+)(h|hours?))((\d+)(m|min)?)?
96 98 |
97 99 ((\d+)(h|hours?|m|min))
98 100 |
99 101 (\d+):(\d+)
100 102 |
101 103 (\d+([\.,]\d+)?)h?
102 104 )
103 105 /x
104 106
105 107 def scan_comment_for_issue_ids
106 108 return if comments.blank?
107 109 # keywords used to reference issues
108 110 ref_keywords = Setting.commit_ref_keywords.downcase.split(",").collect(&:strip)
109 111 ref_keywords_any = ref_keywords.delete('*')
110 112 # keywords used to fix issues
111 113 fix_keywords = Setting.commit_fix_keywords.downcase.split(",").collect(&:strip)
112 114
113 115 kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw)}.join("|")
114 116
115 117 referenced_issues = []
116 118
117 119 comments.scan(/([\s\(\[,-]|^)((#{kw_regexp})[\s:]+)?(#\d+(\s+@#{TIMELOG_RE})?([\s,;&]+#\d+(\s+@#{TIMELOG_RE})?)*)(?=[[:punct:]]|\s|<|$)/i) do |match|
118 120 action, refs = match[2], match[3]
119 121 next unless action.present? || ref_keywords_any
120 122
121 123 refs.scan(/#(\d+)(\s+@#{TIMELOG_RE})?/).each do |m|
122 124 issue, hours = find_referenced_issue_by_id(m[0].to_i), m[2]
123 125 if issue
124 126 referenced_issues << issue
125 127 fix_issue(issue) if fix_keywords.include?(action.to_s.downcase)
126 128 log_time(issue, hours) if hours && Setting.commit_logtime_enabled?
127 129 end
128 130 end
129 131 end
130 132
131 133 referenced_issues.uniq!
132 134 self.issues = referenced_issues unless referenced_issues.empty?
133 135 end
134 136
135 137 def short_comments
136 138 @short_comments || split_comments.first
137 139 end
138 140
139 141 def long_comments
140 142 @long_comments || split_comments.last
141 143 end
142 144
143 145 def text_tag
144 146 if scmid?
145 147 "commit:#{scmid}"
146 148 else
147 149 "r#{revision}"
148 150 end
149 151 end
150 152
151 153 # Returns the previous changeset
152 154 def previous
153 155 @previous ||= Changeset.find(:first,
154 156 :conditions => ['id < ? AND repository_id = ?',
155 157 self.id, self.repository_id],
156 158 :order => 'id DESC')
157 159 end
158 160
159 161 # Returns the next changeset
160 162 def next
161 163 @next ||= Changeset.find(:first,
162 164 :conditions => ['id > ? AND repository_id = ?',
163 165 self.id, self.repository_id],
164 166 :order => 'id ASC')
165 167 end
166 168
167 169 # Creates a new Change from it's common parameters
168 170 def create_change(change)
169 171 Change.create(:changeset => self,
170 172 :action => change[:action],
171 173 :path => change[:path],
172 174 :from_path => change[:from_path],
173 175 :from_revision => change[:from_revision])
174 176 end
175 177
176 178 private
177 179
178 180 # Finds an issue that can be referenced by the commit message
179 181 # i.e. an issue that belong to the repository project, a subproject or a parent project
180 182 def find_referenced_issue_by_id(id)
181 183 return nil if id.blank?
182 184 issue = Issue.find_by_id(id.to_i, :include => :project)
183 185 if issue
184 186 unless issue.project &&
185 187 (project == issue.project || project.is_ancestor_of?(issue.project) ||
186 188 project.is_descendant_of?(issue.project))
187 189 issue = nil
188 190 end
189 191 end
190 192 issue
191 193 end
192 194
193 195 def fix_issue(issue)
194 196 status = IssueStatus.find_by_id(Setting.commit_fix_status_id.to_i)
195 197 if status.nil?
196 198 logger.warn("No status macthes commit_fix_status_id setting (#{Setting.commit_fix_status_id})") if logger
197 199 return issue
198 200 end
199 201
200 202 # the issue may have been updated by the closure of another one (eg. duplicate)
201 203 issue.reload
202 204 # don't change the status is the issue is closed
203 205 return if issue.status && issue.status.is_closed?
204 206
205 207 journal = issue.init_journal(user || User.anonymous, ll(Setting.default_language, :text_status_changed_by_changeset, text_tag))
206 208 issue.status = status
207 209 unless Setting.commit_fix_done_ratio.blank?
208 210 issue.done_ratio = Setting.commit_fix_done_ratio.to_i
209 211 end
210 212 Redmine::Hook.call_hook(:model_changeset_scan_commit_for_issue_ids_pre_issue_update,
211 213 { :changeset => self, :issue => issue })
212 214 unless issue.save
213 215 logger.warn("Issue ##{issue.id} could not be saved by changeset #{id}: #{issue.errors.full_messages}") if logger
214 216 end
215 217 issue
216 218 end
217 219
218 220 def log_time(issue, hours)
219 221 time_entry = TimeEntry.new(
220 222 :user => user,
221 223 :hours => hours,
222 224 :issue => issue,
223 225 :spent_on => commit_date,
224 226 :comments => l(:text_time_logged_by_changeset, :value => text_tag,
225 227 :locale => Setting.default_language)
226 228 )
227 229 time_entry.activity = log_time_activity unless log_time_activity.nil?
228 230
229 231 unless time_entry.save
230 232 logger.warn("TimeEntry could not be created by changeset #{id}: #{time_entry.errors.full_messages}") if logger
231 233 end
232 234 time_entry
233 235 end
234 236
235 237 def log_time_activity
236 238 if Setting.commit_logtime_activity_id.to_i > 0
237 239 TimeEntryActivity.find_by_id(Setting.commit_logtime_activity_id.to_i)
238 240 end
239 241 end
240 242
241 243 def split_comments
242 244 comments =~ /\A(.+?)\r?\n(.*)$/m
243 245 @short_comments = $1 || comments
244 246 @long_comments = $2.to_s.strip
245 247 return @short_comments, @long_comments
246 248 end
247 249
248 250 public
249 251
250 252 # Strips and reencodes a commit log before insertion into the database
251 253 def self.normalize_comments(str, encoding)
252 254 Changeset.to_utf8(str.to_s.strip, encoding)
253 255 end
254 256
255 257 def self.to_utf8(str, encoding)
256 258 return str if str.nil?
257 259 str.force_encoding("ASCII-8BIT") if str.respond_to?(:force_encoding)
258 260 if str.empty?
259 261 str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
260 262 return str
261 263 end
262 264 enc = encoding.blank? ? "UTF-8" : encoding
263 265 if str.respond_to?(:force_encoding)
264 266 if enc.upcase != "UTF-8"
265 267 str.force_encoding(enc)
266 268 str = str.encode("UTF-8", :invalid => :replace,
267 269 :undef => :replace, :replace => '?')
268 270 else
269 271 str.force_encoding("UTF-8")
270 272 if ! str.valid_encoding?
271 273 str = str.encode("US-ASCII", :invalid => :replace,
272 274 :undef => :replace, :replace => '?').encode("UTF-8")
273 275 end
274 276 end
275 277 elsif RUBY_PLATFORM == 'java'
276 278 begin
277 279 ic = Iconv.new('UTF-8', enc)
278 280 str = ic.iconv(str)
279 281 rescue
280 282 str = str.gsub(%r{[^\r\n\t\x20-\x7e]}, '?')
281 283 end
282 284 else
283 285 ic = Iconv.new('UTF-8', enc)
284 286 txtar = ""
285 287 begin
286 288 txtar += ic.iconv(str)
287 289 rescue Iconv::IllegalSequence
288 290 txtar += $!.success
289 291 str = '?' + $!.failed[1,$!.failed.length]
290 292 retry
291 293 rescue
292 294 txtar += $!.success
293 295 end
294 296 str = txtar
295 297 end
296 298 str
297 299 end
298 300 end
General Comments 0
You need to be logged in to leave comments. Login now