Feature #1518 » patch_commit_messages2.diff
| test/unit/changeset_test.rb (working copy) | ||
|---|---|---|
| 32 | 32 |
c = Changeset.new(:repository => Project.find(1).repository, |
| 33 | 33 |
:committed_on => Time.now, |
| 34 | 34 |
:comments => 'New commit (#2). Fixes #1') |
| 35 |
c.scan_comment_for_issue_ids |
|
| 36 |
|
|
| 35 | ||
| 36 |
c.parse_comment |
|
| 37 | ||
| 37 | 38 |
assert_equal [1, 2], c.issue_ids.sort |
| 38 | 39 |
fixed = Issue.find(1) |
| 39 | 40 |
assert fixed.closed? |
| 40 | 41 |
assert_equal 90, fixed.done_ratio |
| 41 | 42 |
end |
| 43 | ||
| 44 |
def test_not_associate_any_mentioned_tickets |
|
| 45 |
Setting.commit_ref_keywords = 'key' |
|
| 46 |
|
|
| 47 |
c = Changeset.new(:repository => Project.find(1).repository, |
|
| 48 |
:committed_on => Time.now, |
|
| 49 |
:comments => 'New commit (#2). #1') |
|
| 50 | ||
| 51 |
c.parse_comment |
|
| 52 | ||
| 53 |
assert_equal [], c.issue_ids |
|
| 54 |
end |
|
| 42 | 55 |
|
| 56 |
def test_fixes_multiple_tickets |
|
| 57 |
Setting.commit_fix_status_id = IssueStatus.find(:first, :conditions => ["is_closed = ?", true]).id |
|
| 58 |
Setting.commit_fix_keywords = 'fixes , closes' |
|
| 59 |
|
|
| 60 |
c = Changeset.new(:repository => Project.find(1).repository, |
|
| 61 |
:committed_on => Time.now, |
|
| 62 |
:comments => 'Fixes #1,#2') |
|
| 63 | ||
| 64 |
c.parse_comment |
|
| 65 | ||
| 66 |
assert_equal [1, 2], c.issue_ids.sort |
|
| 67 |
end |
|
| 68 |
|
|
| 43 | 69 |
def test_ref_keywords_any_line_start |
| 44 | 70 |
Setting.commit_ref_keywords = '*' |
| 45 | 71 | |
| 46 | 72 |
c = Changeset.new(:repository => Project.find(1).repository, |
| 47 | 73 |
:committed_on => Time.now, |
| 48 | 74 |
:comments => '#1 is the reason of this commit') |
| 49 |
c.scan_comment_for_issue_ids
|
|
| 75 |
c.parse_comment
|
|
| 50 | 76 | |
| 51 | 77 |
assert_equal [1], c.issue_ids.sort |
| 52 | 78 |
end |
| 53 | 79 | |
| 80 |
def test_log_time_without_issues_should_do_nothing |
|
| 81 |
count = TimeEntry.count |
|
| 82 |
Setting.advanced_commit_parsing = 1 |
|
| 83 |
Setting.commit_ref_keywords = '*' |
|
| 84 | ||
| 85 |
c = Changeset.new(:repository => Project.find(1).repository, |
|
| 86 |
:committed_on => Time.now, |
|
| 87 |
:comments => 'time 3,5') |
|
| 88 |
c.parse_comment |
|
| 89 | ||
| 90 |
assert_equal count, TimeEntry.count |
|
| 91 |
end |
|
| 92 | ||
| 93 |
def test_log_time_should_work |
|
| 94 |
count = TimeEntry.count |
|
| 95 |
Setting.advanced_commit_parsing = 1 |
|
| 96 |
Setting.commit_ref_keywords = '*' |
|
| 97 |
|
|
| 98 |
comment = <<-EOL |
|
| 99 |
#1 |
|
| 100 |
time 3,5 |
|
| 101 |
EOL |
|
| 102 |
|
|
| 103 |
c = Changeset.new(:repository => Project.find(1).repository, |
|
| 104 |
:committed_on => Time.now, |
|
| 105 |
:comments => comment) |
|
| 106 | ||
| 107 |
c.parse_comment |
|
| 108 | ||
| 109 |
assert_equal count+1, TimeEntry.count |
|
| 110 |
end |
|
| 111 | ||
| 112 |
def test_log_time_splits_the_time_equally |
|
| 113 |
Setting.commit_fix_keywords = 'fixes , closes' |
|
| 114 |
Setting.advanced_commit_parsing = 1 |
|
| 115 |
|
|
| 116 |
comment = <<-EOL |
|
| 117 |
fixes #1,#2 |
|
| 118 |
time 3 |
|
| 119 | ||
| 120 |
the comment |
|
| 121 |
EOL |
|
| 122 |
|
|
| 123 |
c = Changeset.new(:repository => Project.find(1).repository, |
|
| 124 |
:committed_on => Time.now, |
|
| 125 |
:comments => comment) |
|
| 126 | ||
| 127 |
c.parse_comment |
|
| 128 | ||
| 129 |
time_entry = TimeEntry.find(:first, :order => 'id DESC') |
|
| 130 |
assert_equal 1.5, time_entry.hours |
|
| 131 |
assert_equal 'the comment', time_entry.comments |
|
| 132 | ||
| 133 |
end |
|
| 134 | ||
| 135 |
def test_extract_time |
|
| 136 |
c = Changeset.new |
|
| 137 |
time_formats = [ "2", "21.1", "2,1","7:12", "10h", "10 h", "45m", "45 m", "3h15", "3h 15", "3 h 15", "3 h 15m", "3 h 15 m"] |
|
| 138 |
|
|
| 139 |
time_formats.each do |format| |
|
| 140 |
assert_equal format, c.send(:extract_time!, "time #{format}")
|
|
| 141 |
end |
|
| 142 |
end |
|
| 143 | ||
| 144 |
def test_set_done_ratio |
|
| 145 |
Setting.commit_ref_keywords = '*' |
|
| 146 |
Setting.advanced_commit_parsing = 1 |
|
| 147 | ||
| 148 |
comment = <<-EOL |
|
| 149 |
#1 |
|
| 150 |
done 50% |
|
| 151 |
#2 |
|
| 152 |
done 40 |
|
| 153 |
EOL |
|
| 154 |
|
|
| 155 |
c = Changeset.new(:repository => Project.find(1).repository, |
|
| 156 |
:committed_on => Time.now, |
|
| 157 |
:comments => comment) |
|
| 158 | ||
| 159 |
c.parse_comment |
|
| 160 | ||
| 161 |
assert_equal [1, 2], c.issue_ids.sort |
|
| 162 |
assert_equal 50, Issue.find(1).done_ratio |
|
| 163 |
assert_equal 40, Issue.find(2).done_ratio |
|
| 164 |
end |
|
| 165 | ||
| 166 |
def test_set_committer_identified_by_email |
|
| 167 |
Setting.commit_fix_keywords = 'fixes' |
|
| 168 |
user = User.find(:first) |
|
| 169 |
|
|
| 170 |
c = Changeset.new(:repository => Project.find(1).repository, |
|
| 171 |
:committed_on => Time.now, |
|
| 172 |
:committer => "arnie<#{user.mail}>",
|
|
| 173 |
:comments => 'Fixes #1') |
|
| 174 | ||
| 175 |
c.parse_comment |
|
| 176 | ||
| 177 |
fixed = Issue.find(1) |
|
| 178 |
assert fixed.closed? |
|
| 179 |
assert_equal user, fixed.journals.find(:first, :order => 'id DESC').user |
|
| 180 |
end |
|
| 181 | ||
| 182 |
def test_set_committer_identified_by_login |
|
| 183 |
Setting.commit_fix_keywords = 'fixes' |
|
| 184 |
user = User.find(:first) |
|
| 185 |
|
|
| 186 |
c = Changeset.new(:repository => Project.find(1).repository, |
|
| 187 |
:committed_on => Time.now, |
|
| 188 |
:committer => user.login, |
|
| 189 |
:comments => 'Fixes #1') |
|
| 190 | ||
| 191 |
c.parse_comment |
|
| 192 | ||
| 193 |
fixed = Issue.find(1) |
|
| 194 |
assert fixed.closed? |
|
| 195 |
assert_equal user, fixed.journals.find(:first, :order => 'id DESC').user |
|
| 196 |
end |
|
| 197 | ||
| 198 |
def test_set_annonymous_if_committer_unknown |
|
| 199 |
Setting.commit_fix_keywords = 'fixes' |
|
| 200 | ||
| 201 |
c = Changeset.new(:repository => Project.find(1).repository, |
|
| 202 |
:committed_on => Time.now, |
|
| 203 |
:committer => 'arnie', |
|
| 204 |
:comments => 'Fixes #1') |
|
| 205 | ||
| 206 |
c.parse_comment |
|
| 207 | ||
| 208 |
fixed = Issue.find(1) |
|
| 209 |
assert fixed.closed? |
|
| 210 |
assert_equal User.anonymous, fixed.journals.find(:first, :order => 'id DESC').user |
|
| 211 |
end |
|
| 212 | ||
| 213 |
def test_mail_deliveries |
|
| 214 |
ActionMailer::Base.deliveries.clear |
|
| 215 | ||
| 216 |
Setting.commit_fix_keywords = 'fixes' |
|
| 217 |
|
|
| 218 |
c = Changeset.new(:repository => Project.find(1).repository, |
|
| 219 |
:committed_on => Time.now, |
|
| 220 |
:comments => 'Fixes #1') |
|
| 221 | ||
| 222 |
c.parse_comment |
|
| 223 |
|
|
| 224 |
assert_equal 1, ActionMailer::Base.deliveries.size |
|
| 225 |
end |
|
| 226 | ||
| 227 |
def test_ignore_cross_refrenced_issue_ids |
|
| 228 |
Setting.commit_fix_keywords = 'fixes' |
|
| 229 | ||
| 230 |
c = Changeset.new(:repository => Project.find(1).repository, |
|
| 231 |
:committed_on => Time.now, |
|
| 232 |
:comments => 'Fixes #1234') |
|
| 233 | ||
| 234 |
c.parse_comment |
|
| 235 | ||
| 236 |
assert_equal [], c.issue_ids.sort |
|
| 237 |
end |
|
| 238 | ||
| 54 | 239 |
def test_previous |
| 55 | 240 |
changeset = Changeset.find_by_revision('3')
|
| 56 | 241 |
assert_equal Changeset.find_by_revision('2'), changeset.previous
|
| ... | ... | |
| 70 | 255 |
changeset = Changeset.find_by_revision('4')
|
| 71 | 256 |
assert_nil changeset.next |
| 72 | 257 |
end |
| 258 | ||
| 259 |
def test_for_changeset_comments_strip |
|
| 260 |
comment = <<-COMMENT |
|
| 261 |
This is a loooooooooooooooooooooooooooong comment |
|
| 262 |
|
|
| 263 |
|
|
| 264 |
COMMENT |
|
| 265 |
changeset = Changeset.new :comments => comment |
|
| 266 |
assert_equal( 'This is a loooooooooooooooooooooooooooong comment', changeset.comments ) |
|
| 267 |
end |
|
| 268 |
|
|
| 269 | ||
| 73 | 270 |
end |
| test/unit/repository_test.rb (working copy) | ||
|---|---|---|
| 55 | 55 |
Setting.delete_all |
| 56 | 56 |
end |
| 57 | 57 |
|
| 58 |
def test_scan_changesets_for_issue_ids |
|
| 59 |
# choosing a status to apply to fix issues |
|
| 60 |
Setting.commit_fix_status_id = IssueStatus.find(:first, :conditions => ["is_closed = ?", true]).id |
|
| 61 |
Setting.commit_fix_done_ratio = "90" |
|
| 62 |
Setting.commit_ref_keywords = 'refs , references, IssueID' |
|
| 63 |
Setting.commit_fix_keywords = 'fixes , closes' |
|
| 64 |
Setting.default_language = 'en' |
|
| 65 |
ActionMailer::Base.deliveries.clear |
|
| 66 |
|
|
| 67 |
# make sure issue 1 is not already closed |
|
| 68 |
fixed_issue = Issue.find(1) |
|
| 69 |
assert !fixed_issue.status.is_closed? |
|
| 70 |
old_status = fixed_issue.status |
|
| 71 |
|
|
| 72 |
Repository.scan_changesets_for_issue_ids |
|
| 73 |
assert_equal [101, 102], Issue.find(3).changeset_ids |
|
| 74 |
|
|
| 75 |
# fixed issues |
|
| 76 |
fixed_issue.reload |
|
| 77 |
assert fixed_issue.status.is_closed? |
|
| 78 |
assert_equal 90, fixed_issue.done_ratio |
|
| 79 |
assert_equal [101], fixed_issue.changeset_ids |
|
| 80 |
|
|
| 81 |
# issue change |
|
| 82 |
journal = fixed_issue.journals.find(:first, :order => 'created_on desc') |
|
| 83 |
assert_equal User.find_by_login('dlopper'), journal.user
|
|
| 84 |
assert_equal 'Applied in changeset r2.', journal.notes |
|
| 85 |
|
|
| 86 |
# 2 email notifications |
|
| 87 |
assert_equal 2, ActionMailer::Base.deliveries.size |
|
| 88 |
mail = ActionMailer::Base.deliveries.first |
|
| 89 |
assert_kind_of TMail::Mail, mail |
|
| 90 |
assert mail.subject.starts_with?("[#{fixed_issue.project.name} - #{fixed_issue.tracker.name} ##{fixed_issue.id}]")
|
|
| 91 |
assert mail.body.include?("Status changed from #{old_status} to #{fixed_issue.status}")
|
|
| 92 |
|
|
| 93 |
# ignoring commits referencing an issue of another project |
|
| 94 |
assert_equal [], Issue.find(4).changesets |
|
| 95 |
end |
|
| 96 |
|
|
| 97 |
def test_for_changeset_comments_strip |
|
| 98 |
repository = Repository::Mercurial.create( :project => Project.find( 4 ), :url => '/foo/bar/baz' ) |
|
| 99 |
comment = <<-COMMENT |
|
| 100 |
This is a loooooooooooooooooooooooooooong comment |
|
| 101 |
|
|
| 102 |
|
|
| 103 |
COMMENT |
|
| 104 |
changeset = Changeset.new( |
|
| 105 |
:comments => comment, :commit_date => Time.now, :revision => 0, :scmid => 'f39b7922fb3c', |
|
| 106 |
:committer => 'foo <foo@example.com>', :committed_on => Time.now, :repository => repository ) |
|
| 107 |
assert( changeset.save ) |
|
| 108 |
assert_not_equal( comment, changeset.comments ) |
|
| 109 |
assert_equal( 'This is a loooooooooooooooooooooooooooong comment', changeset.comments ) |
|
| 110 |
end |
|
| 111 |
|
|
| 112 | 58 |
def test_for_urls_strip |
| 113 | 59 |
repository = Repository::Cvs.create(:project => Project.find(4), :url => ' :pserver:login:password@host:/path/to/the/repository', |
| 114 | 60 |
:root_url => 'foo ') |
| app/models/repository.rb (working copy) | ||
|---|---|---|
| 84 | 84 |
@latest_changeset ||= changesets.find(:first) |
| 85 | 85 |
end |
| 86 | 86 |
|
| 87 |
def scan_changesets_for_issue_ids |
|
| 88 |
self.changesets.each(&:scan_comment_for_issue_ids) |
|
| 89 |
end |
|
| 90 |
|
|
| 91 | 87 |
# fetch new changesets for all repositories |
| 92 | 88 |
# can be called periodically by an external script |
| 93 | 89 |
# eg. ruby script/runner "Repository.fetch_changesets" |
| ... | ... | |
| 95 | 91 |
find(:all).each(&:fetch_changesets) |
| 96 | 92 |
end |
| 97 | 93 |
|
| 98 |
# scan changeset comments to find related and fixed issues for all repositories |
|
| 99 |
def self.scan_changesets_for_issue_ids |
|
| 100 |
find(:all).each(&:scan_changesets_for_issue_ids) |
|
| 101 |
end |
|
| 102 | ||
| 103 | 94 |
def self.scm_name |
| 104 | 95 |
'Abstract' |
| 105 | 96 |
end |
| app/models/changeset.rb (working copy) | ||
|---|---|---|
| 34 | 34 |
validates_presence_of :repository_id, :revision, :committed_on, :commit_date |
| 35 | 35 |
validates_uniqueness_of :revision, :scope => :repository_id |
| 36 | 36 |
validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true |
| 37 | ||
| 38 |
after_create :parse_comment |
|
| 37 | 39 |
|
| 38 | 40 |
def revision=(r) |
| 39 | 41 |
write_attribute :revision, (r.nil? ? nil : r.to_s) |
| ... | ... | |
| 52 | 54 |
repository.project |
| 53 | 55 |
end |
| 54 | 56 |
|
| 55 |
def after_create |
|
| 56 |
scan_comment_for_issue_ids |
|
| 57 |
# This starts the comment parsing. Executed by an after_save filter |
|
| 58 |
def parse_comment |
|
| 59 |
return if comments.blank? |
|
| 60 | ||
| 61 |
keywords = (ref_keywords + fix_keywords) |
|
| 62 |
return if keywords.blank? |
|
| 63 | ||
| 64 |
process_issues_marked_by(keywords) |
|
| 57 | 65 |
end |
| 58 |
require 'pp' |
|
| 59 |
|
|
| 60 |
def scan_comment_for_issue_ids
|
|
| 61 |
return if comments.blank?
|
|
| 62 |
# keywords used to reference issues
|
|
| 63 |
ref_keywords = Setting.commit_ref_keywords.downcase.split(",").collect(&:strip)
|
|
| 64 |
# keywords used to fix issues
|
|
| 65 |
fix_keywords = Setting.commit_fix_keywords.downcase.split(",").collect(&:strip)
|
|
| 66 |
# status and optional done ratio applied
|
|
| 67 |
fix_status = IssueStatus.find_by_id(Setting.commit_fix_status_id)
|
|
| 68 |
done_ratio = Setting.commit_fix_done_ratio.blank? ? nil : Setting.commit_fix_done_ratio.to_i |
|
| 69 |
|
|
| 70 |
kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw)}.join("|")
|
|
| 71 |
return if kw_regexp.blank?
|
|
| 72 |
|
|
| 66 | ||
| 67 |
# Returns the previous changeset |
|
| 68 |
def previous
|
|
| 69 |
@previous ||= Changeset.find(:first, :conditions => ['id < ? AND repository_id = ?', self.id, self.repository_id], :order => 'id DESC')
|
|
| 70 |
end
|
|
| 71 | ||
| 72 |
# Returns the next changeset
|
|
| 73 |
def next
|
|
| 74 |
@next ||= Changeset.find(:first, :conditions => ['id > ? AND repository_id = ?', self.id, self.repository_id], :order => 'id ASC')
|
|
| 75 |
end
|
|
| 76 | ||
| 77 |
protected |
|
| 78 | ||
| 79 |
# This parses the whole comment. Therefore the comment gets split into parts.
|
|
| 80 |
def process_issues_marked_by(ticket_keywords) |
|
| 73 | 81 |
referenced_issues = [] |
| 74 |
|
|
| 75 |
if ref_keywords.delete('*')
|
|
| 76 |
# find any issue ID in the comments |
|
| 77 |
target_issue_ids = [] |
|
| 78 |
comments.scan(%r{([\s\(,-]|^)#(\d+)(?=[[:punct:]]|\s|<|$)}).each { |m| target_issue_ids << m[1] }
|
|
| 79 |
referenced_issues += repository.project.issues.find_all_by_id(target_issue_ids) |
|
| 80 |
end |
|
| 81 |
|
|
| 82 |
comments.scan(Regexp.new("(#{kw_regexp})[\s:]+(([\s,;&]*#?\\d+)+)", Regexp::IGNORECASE)).each do |match|
|
|
| 82 |
comments.scan( splitting_regexp(ticket_keywords) ).each do |match| |
|
| 83 | 83 |
action = match[0] |
| 84 | 84 |
target_issue_ids = match[1].scan(/\d+/) |
| 85 |
rest = match.last |
|
| 86 | ||
| 85 | 87 |
target_issues = repository.project.issues.find_all_by_id(target_issue_ids) |
| 86 |
if fix_status && fix_keywords.include?(action.downcase) |
|
| 88 |
process_part(action, target_issues, rest) |
|
| 89 | ||
| 90 |
referenced_issues += target_issues |
|
| 91 |
end |
|
| 92 | ||
| 93 |
self.issues = referenced_issues.uniq |
|
| 94 |
end |
|
| 95 | ||
| 96 |
# returns a regexp that splits the long comment into parts |
|
| 97 |
# |
|
| 98 |
# Each part starts with a valid ticket reference and |
|
| 99 |
# either ends with one or ends at the end of the comment |
|
| 100 |
def splitting_regexp(ticket_keywords) |
|
| 101 |
ref_any = ticket_keywords.delete('*')
|
|
| 102 |
joined_kw = ticket_keywords.join("|")
|
|
| 103 |
first = "(#{joined_kw})#{ref_any ? '*' : '+' }"
|
|
| 104 |
second = joined_kw + (ref_any ? '|#' : '') |
|
| 105 |
/#{first}[\s:]*(([\s,;&]*#?\d+)+)(.*?)(?=#{second}|\Z)/im
|
|
| 106 |
end |
|
| 107 | ||
| 108 |
# Process_part analyses the part and executes ticket changes, time logs etc. |
|
| 109 |
def process_part(action,target_issues,rest) |
|
| 110 |
if Setting.advanced_commit_parsing? |
|
| 111 |
time = extract_time!(rest) |
|
| 112 |
ratio = extract_ratio!(rest) |
|
| 113 |
end |
|
| 114 | ||
| 115 |
target_issues.each do |issue| |
|
| 116 |
if fix_status && action && fix_keywords.include?(action.downcase) |
|
| 87 | 117 |
# update status of issues |
| 88 | 118 |
logger.debug "Issues fixed by changeset #{self.revision}: #{issue_ids.join(', ')}." if logger && logger.debug?
|
| 89 |
target_issues.each do |issue| |
|
| 90 |
# the issue may have been updated by the closure of another one (eg. duplicate) |
|
| 91 |
issue.reload |
|
| 92 |
# don't change the status is the issue is closed |
|
| 93 |
next if issue.status.is_closed? |
|
| 94 |
user = committer_user || User.anonymous |
|
| 95 |
csettext = "r#{self.revision}"
|
|
| 96 |
if self.scmid && (! (csettext =~ /^r[0-9]+$/)) |
|
| 97 |
csettext = "commit:\"#{self.scmid}\""
|
|
| 98 |
end |
|
| 99 |
journal = issue.init_journal(user, l(:text_status_changed_by_changeset, csettext)) |
|
| 100 |
issue.status = fix_status |
|
| 101 |
issue.done_ratio = done_ratio if done_ratio |
|
| 102 |
issue.save |
|
| 103 |
Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
|
|
| 119 |
# the issue may have been updated by the closure of another one (eg. duplicate) |
|
| 120 |
issue.reload |
|
| 121 |
# don't change the status is the issue is closed |
|
| 122 |
next if issue.status.is_closed? |
|
| 123 |
issue.status = fix_status |
|
| 124 |
issue.done_ratio = done_ratio if done_ratio |
|
| 125 |
update_journal(issue) |
|
| 126 |
else # elsif Setting.extended_comment_parsing |
|
| 127 |
if ratio |
|
| 128 |
issue.done_ratio = ratio |
|
| 129 |
update_journal(issue) |
|
| 104 | 130 |
end |
| 105 | 131 |
end |
| 106 |
referenced_issues += target_issues |
|
| 132 |
if time |
|
| 133 |
time_entry = TimeEntry.new( :hours => time, |
|
| 134 |
:spent_on => Time.now, |
|
| 135 |
:comments => rest[0..254], |
|
| 136 |
:user => committer_user) |
|
| 137 |
time_entry.hours /= target_issues.length |
|
| 138 |
issue.time_entries << time_entry |
|
| 139 |
end |
|
| 140 |
issue.save |
|
| 107 | 141 |
end |
| 108 |
|
|
| 109 |
self.issues = referenced_issues.uniq |
|
| 110 | 142 |
end |
| 111 | 143 | |
| 144 |
# This updates the journal of an Issue and sends an update email if necessary |
|
| 145 |
def update_journal(issue) |
|
| 146 |
user = committer_user |
|
| 147 |
csettext = "r#{self.revision}"
|
|
| 148 |
if self.scmid && (! (csettext =~ /^r[0-9]+$/)) |
|
| 149 |
csettext = "commit:\"#{self.scmid}\""
|
|
| 150 |
end |
|
| 151 |
journal = issue.init_journal(user, l(:text_status_changed_by_changeset, csettext)) |
|
| 152 |
Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
|
|
| 153 |
end |
|
| 154 | ||
| 155 |
# extracts the time |
|
| 156 |
def extract_time!(string) |
|
| 157 |
extract!(/(?:#{time_keywords.join("|")})[\s:]+(\d+[.,:hm ]*\d*[m ]*)/,string)
|
|
| 158 |
end |
|
| 159 | ||
| 160 |
# extracts the ratio |
|
| 161 |
def extract_ratio!(string) |
|
| 162 |
extract!(/(?:#{ratio_keywords.join("|")})[\s:]+(\d+)%?/,string)
|
|
| 163 |
end |
|
| 164 | ||
| 165 |
# generic extract function. Notice the !. The original string is silently manipulated |
|
| 166 |
def extract!(regexp,string) |
|
| 167 |
if match = string.match(/(.*?)#{regexp}(.*)/mi)
|
|
| 168 |
replacement = if match[1] && !match[1].strip.empty? |
|
| 169 |
match[1].strip + ' ' + match[3].strip |
|
| 170 |
else |
|
| 171 |
match[3].strip |
|
| 172 |
end |
|
| 173 |
string.replace(replacement) |
|
| 174 |
match[2] |
|
| 175 |
end |
|
| 176 |
end |
|
| 177 | ||
| 178 |
# keywords used to reference issues |
|
| 179 |
def ref_keywords |
|
| 180 |
@ref_keywords ||= Setting.commit_ref_keywords.downcase.split(",").collect(&:strip)
|
|
| 181 |
end |
|
| 182 | ||
| 183 |
# keywords used to fix issues |
|
| 184 |
def fix_keywords |
|
| 185 |
@fix_keywords ||= Setting.commit_fix_keywords.downcase.split(",").collect(&:strip)
|
|
| 186 |
end |
|
| 187 | ||
| 188 |
# keywords used to set the ratio of the issues |
|
| 189 |
def ratio_keywords |
|
| 190 |
@ratio_keywords ||= Setting.commit_ratio_keywords.downcase.split(',').collect(&:strip)
|
|
| 191 |
end |
|
| 192 | ||
| 193 |
# keywords used to log time of an issue |
|
| 194 |
def time_keywords |
|
| 195 |
@time_keywords ||= Setting.commit_time_keywords.downcase.split(',').collect(&:strip)
|
|
| 196 |
end |
|
| 197 | ||
| 198 |
# status if an issue is fixed |
|
| 199 |
def fix_status |
|
| 200 |
@fix_status ||= IssueStatus.find_by_id(Setting.commit_fix_status_id) |
|
| 201 |
end |
|
| 202 | ||
| 203 |
# the ratio if an issue is fixed |
|
| 204 |
def done_ratio |
|
| 205 |
@done_ratio ||= Setting.commit_fix_done_ratio.blank? ? nil : Setting.commit_fix_done_ratio.to_i |
|
| 206 |
end |
|
| 207 | ||
| 112 | 208 |
# Returns the Redmine User corresponding to the committer |
| 209 |
# or the anonymous user |
|
| 113 | 210 |
def committer_user |
| 114 | 211 |
if committer && committer.strip =~ /^([^<]+)(<(.*)>)?$/ |
| 115 | 212 |
username, email = $1.strip, $3 |
| 116 | 213 |
u = User.find_by_login(username) |
| 117 | 214 |
u ||= User.find_by_mail(email) unless email.blank? |
| 118 | 215 |
u |
| 119 |
end |
|
| 216 |
end || User.anonymous
|
|
| 120 | 217 |
end |
| 121 | 218 |
|
| 122 |
# Returns the previous changeset |
|
| 123 |
def previous |
|
| 124 |
@previous ||= Changeset.find(:first, :conditions => ['id < ? AND repository_id = ?', self.id, self.repository_id], :order => 'id DESC') |
|
| 125 |
end |
|
| 126 | ||
| 127 |
# Returns the next changeset |
|
| 128 |
def next |
|
| 129 |
@next ||= Changeset.find(:first, :conditions => ['id > ? AND repository_id = ?', self.id, self.repository_id], :order => 'id ASC') |
|
| 130 |
end |
|
| 131 | 219 |
end |
| app/views/settings/_repositories.rhtml (working copy) | ||
|---|---|---|
| 29 | 29 |
<br /><em><%= l(:text_comma_separated) %></em></p> |
| 30 | 30 |
</fieldset> |
| 31 | 31 | |
| 32 |
<fieldset class="box tabular settings"><legend><%= l(:text_issues_advanced_commit_message_keywords) %></legend> |
|
| 33 |
<p><label><%= l(:setting_advanced_commit_keywords) %></label> |
|
| 34 |
<%= check_box_tag 'settings[advanced_commit_parsing]', 1, Setting.advanced_commit_parsing?, :onclick=>"Element.toggle('advanced_keywords'); return true;" %><%= hidden_field_tag 'settings[advanced_commit_parsing]', 0 %></p>
|
|
| 35 | ||
| 36 |
<div id="advanced_keywords" <%= Setting.advanced_commit_parsing? ? '' : 'style="display:none"' %>> |
|
| 37 |
<p><label><%= l(:setting_commit_time_keywords) %></label> |
|
| 38 |
<%= text_field_tag 'settings[commit_time_keywords]', Setting.commit_time_keywords, :size => 30 %> |
|
| 39 |
<br /><em><%= l(:text_comma_separated) %></em></p> |
|
| 40 |
<p><label><%= l(:setting_commit_ratio_keywords) %></label> |
|
| 41 |
<%= text_field_tag 'settings[commit_ratio_keywords]', Setting.commit_ratio_keywords, :size => 30 %> |
|
| 42 |
<br /><em><%= l(:text_comma_separated) %></em></p> |
|
| 43 |
</div> |
|
| 44 |
</fieldset> |
|
| 45 | ||
| 32 | 46 |
<%= submit_tag l(:button_save) %> |
| 33 | 47 |
<% end %> |
| lang/en.yml (working copy) | ||
|---|---|---|
| 202 | 202 |
setting_sys_api_enabled: Enable WS for repository management |
| 203 | 203 |
setting_commit_ref_keywords: Referencing keywords |
| 204 | 204 |
setting_commit_fix_keywords: Fixing keywords |
| 205 |
setting_advanced_commit_keywords: Enable advanced keywords |
|
| 206 |
setting_commit_time_keywords: Time logging keywords |
|
| 207 |
setting_commit_ratio_keywords: Done ratio keywords |
|
| 205 | 208 |
setting_autologin: Autologin |
| 206 | 209 |
setting_date_format: Date format |
| 207 | 210 |
setting_time_format: Time format |
| ... | ... | |
| 584 | 587 |
text_unallowed_characters: Unallowed characters |
| 585 | 588 |
text_comma_separated: Multiple values allowed (comma separated). |
| 586 | 589 |
text_issues_ref_in_commit_messages: Referencing and fixing issues in commit messages |
| 590 |
text_issues_advanced_commit_message_keywords: Logging time and setting issue ratios via commit messages |
|
| 587 | 591 |
text_issue_added: Issue %s has been reported by %s. |
| 588 | 592 |
text_issue_updated: Issue %s has been updated by %s. |
| 589 | 593 |
text_wiki_destroy_confirmation: Are you sure you want to delete this wiki and all its content ? |
| config/settings.yml (working copy) | ||
|---|---|---|
| 81 | 81 |
default: 0 |
| 82 | 82 |
commit_fix_done_ratio: |
| 83 | 83 |
default: 100 |
| 84 |
advanced_commit_parsing: |
|
| 85 |
default: 0 |
|
| 86 |
commit_time_keywords: |
|
| 87 |
default: 'time,log' |
|
| 88 |
commit_ratio_keywords: |
|
| 89 |
default: 'done,ratio' |
|
| 84 | 90 |
# autologin duration in days |
| 85 | 91 |
# 0 means autologin is disabled |
| 86 | 92 |
autologin: |