comparison app/models/changeset.rb @ 1464:261b3d9a4903 redmine-2.4

Update to Redmine 2.4 branch rev 12663
author Chris Cannam
date Tue, 14 Jan 2014 14:37:42 +0000
parents 433d4f72a19b
children e248c7af89ec
comparison
equal deleted inserted replaced
1296:038ba2d95de8 1464:261b3d9a4903
1 # Redmine - project management software 1 # Redmine - project management software
2 # Copyright (C) 2006-2012 Jean-Philippe Lang 2 # Copyright (C) 2006-2013 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.
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 'iconv'
19
20 class Changeset < ActiveRecord::Base 18 class Changeset < ActiveRecord::Base
21 belongs_to :repository 19 belongs_to :repository
22 belongs_to :user 20 belongs_to :user
23 has_many :filechanges, :class_name => 'Change', :dependent => :delete_all 21 has_many :filechanges, :class_name => 'Change', :dependent => :delete_all
24 has_and_belongs_to_many :issues 22 has_and_belongs_to_many :issues
47 45
48 validates_presence_of :repository_id, :revision, :committed_on, :commit_date 46 validates_presence_of :repository_id, :revision, :committed_on, :commit_date
49 validates_uniqueness_of :revision, :scope => :repository_id 47 validates_uniqueness_of :revision, :scope => :repository_id
50 validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true 48 validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
51 49
52 scope :visible, 50 scope :visible, lambda {|*args|
53 lambda {|*args| { :include => {:repository => :project}, 51 includes(:repository => :project).where(Project.allowed_to_condition(args.shift || User.current, :view_changesets, *args))
54 :conditions => Project.allowed_to_condition(args.shift || User.current, :view_changesets, *args) } } 52 }
55 53
56 after_create :scan_for_issues 54 after_create :scan_for_issues
57 before_create :before_create_cs 55 before_create :before_create_cs
58 56
59 def revision=(r) 57 def revision=(r)
118 return if comments.blank? 116 return if comments.blank?
119 # keywords used to reference issues 117 # keywords used to reference issues
120 ref_keywords = Setting.commit_ref_keywords.downcase.split(",").collect(&:strip) 118 ref_keywords = Setting.commit_ref_keywords.downcase.split(",").collect(&:strip)
121 ref_keywords_any = ref_keywords.delete('*') 119 ref_keywords_any = ref_keywords.delete('*')
122 # keywords used to fix issues 120 # keywords used to fix issues
123 fix_keywords = Setting.commit_fix_keywords.downcase.split(",").collect(&:strip) 121 fix_keywords = Setting.commit_update_keywords_array.map {|r| r['keywords']}.flatten.compact
124 122
125 kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw)}.join("|") 123 kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw)}.join("|")
126 124
127 referenced_issues = [] 125 referenced_issues = []
128 126
129 comments.scan(/([\s\(\[,-]|^)((#{kw_regexp})[\s:]+)?(#\d+(\s+@#{TIMELOG_RE})?([\s,;&]+#\d+(\s+@#{TIMELOG_RE})?)*)(?=[[:punct:]]|\s|<|$)/i) do |match| 127 comments.scan(/([\s\(\[,-]|^)((#{kw_regexp})[\s:]+)?(#\d+(\s+@#{TIMELOG_RE})?([\s,;&]+#\d+(\s+@#{TIMELOG_RE})?)*)(?=[[:punct:]]|\s|<|$)/i) do |match|
130 action, refs = match[2], match[3] 128 action, refs = match[2].to_s.downcase, match[3]
131 next unless action.present? || ref_keywords_any 129 next unless action.present? || ref_keywords_any
132 130
133 refs.scan(/#(\d+)(\s+@#{TIMELOG_RE})?/).each do |m| 131 refs.scan(/#(\d+)(\s+@#{TIMELOG_RE})?/).each do |m|
134 issue, hours = find_referenced_issue_by_id(m[0].to_i), m[2] 132 issue, hours = find_referenced_issue_by_id(m[0].to_i), m[2]
135 if issue 133 if issue
136 referenced_issues << issue 134 referenced_issues << issue
137 fix_issue(issue) if fix_keywords.include?(action.to_s.downcase) 135 # Don't update issues or log time when importing old commits
138 log_time(issue, hours) if hours && Setting.commit_logtime_enabled? 136 unless repository.created_on && committed_on && committed_on < repository.created_on
137 fix_issue(issue, action) if fix_keywords.include?(action)
138 log_time(issue, hours) if hours && Setting.commit_logtime_enabled?
139 end
139 end 140 end
140 end 141 end
141 end 142 end
142 143
143 referenced_issues.uniq! 144 referenced_issues.uniq!
210 issue 211 issue
211 end 212 end
212 213
213 private 214 private
214 215
215 def fix_issue(issue) 216 # Updates the +issue+ according to +action+
216 status = IssueStatus.find_by_id(Setting.commit_fix_status_id.to_i) 217 def fix_issue(issue, action)
217 if status.nil?
218 logger.warn("No status matches commit_fix_status_id setting (#{Setting.commit_fix_status_id})") if logger
219 return issue
220 end
221
222 # the issue may have been updated by the closure of another one (eg. duplicate) 218 # the issue may have been updated by the closure of another one (eg. duplicate)
223 issue.reload 219 issue.reload
224 # don't change the status is the issue is closed 220 # don't change the status is the issue is closed
225 return if issue.status && issue.status.is_closed? 221 return if issue.status && issue.status.is_closed?
226 222
227 journal = issue.init_journal(user || User.anonymous, ll(Setting.default_language, :text_status_changed_by_changeset, text_tag(issue.project))) 223 journal = issue.init_journal(user || User.anonymous,
228 issue.status = status 224 ll(Setting.default_language,
229 unless Setting.commit_fix_done_ratio.blank? 225 :text_status_changed_by_changeset,
230 issue.done_ratio = Setting.commit_fix_done_ratio.to_i 226 text_tag(issue.project)))
227 rule = Setting.commit_update_keywords_array.detect do |rule|
228 rule['keywords'].include?(action) &&
229 (rule['if_tracker_id'].blank? || rule['if_tracker_id'] == issue.tracker_id.to_s)
230 end
231 if rule
232 issue.assign_attributes rule.slice(*Issue.attribute_names)
231 end 233 end
232 Redmine::Hook.call_hook(:model_changeset_scan_commit_for_issue_ids_pre_issue_update, 234 Redmine::Hook.call_hook(:model_changeset_scan_commit_for_issue_ids_pre_issue_update,
233 { :changeset => self, :issue => issue }) 235 { :changeset => self, :issue => issue, :action => action })
234 unless issue.save 236 unless issue.save
235 logger.warn("Issue ##{issue.id} could not be saved by changeset #{id}: #{issue.errors.full_messages}") if logger 237 logger.warn("Issue ##{issue.id} could not be saved by changeset #{id}: #{issue.errors.full_messages}") if logger
236 end 238 end
237 issue 239 issue
238 end 240 end