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: |