]> source.dussan.org Git - redmine.git/commitdiff
Ability to define commit keywords per tracker (#7590).
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Sun, 13 Oct 2013 07:37:49 +0000 (07:37 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Sun, 13 Oct 2013 07:37:49 +0000 (07:37 +0000)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@12208 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/controllers/settings_controller.rb
app/models/changeset.rb
app/models/setting.rb
app/views/settings/_repositories.html.erb
config/settings.yml
test/functional/settings_controller_test.rb
test/object_helpers.rb
test/unit/changeset_test.rb
test/unit/repository_test.rb

index 05550bc65f1301d3ad0a3942571f927c6c395ef6..fdd6bf4f05af12b80955a551b8a3cc1862c4812b 100644 (file)
@@ -47,7 +47,7 @@ class SettingsController < ApplicationController
       @guessed_host_and_path << ('/'+ Redmine::Utils.relative_url_root.gsub(%r{^\/}, '')) unless Redmine::Utils.relative_url_root.blank?
 
       @commit_update_keywords = Setting.commit_update_keywords.dup
-      @commit_update_keywords[''] = {} if @commit_update_keywords.blank?
+      @commit_update_keywords << {} if @commit_update_keywords.blank?
 
       Redmine::Themes.rescan
     end
index 2c574f75e14aa446a1e104cce032dfa9a40ac5a4..4198e7e02e510aabb3abd1c54eb53a0dd3b50fc6 100644 (file)
@@ -118,7 +118,7 @@ class Changeset < ActiveRecord::Base
     ref_keywords = Setting.commit_ref_keywords.downcase.split(",").collect(&:strip)
     ref_keywords_any = ref_keywords.delete('*')
     # keywords used to fix issues
-    fix_keywords = Setting.commit_update_by_keyword.keys
+    fix_keywords = Setting.commit_update_keywords_array.map {|r| r['keywords']}.flatten.compact
 
     kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw)}.join("|")
 
@@ -215,16 +215,18 @@ class Changeset < ActiveRecord::Base
 
   # Updates the +issue+ according to +action+
   def fix_issue(issue, action)
-    updates = Setting.commit_update_by_keyword[action]
-    return unless updates.is_a?(Hash)
-
     # the issue may have been updated by the closure of another one (eg. duplicate)
     issue.reload
     # don't change the status is the issue is closed
     return if issue.status && issue.status.is_closed?
 
     journal = issue.init_journal(user || User.anonymous, ll(Setting.default_language, :text_status_changed_by_changeset, text_tag(issue.project)))
-    issue.assign_attributes updates.slice(*Issue.attribute_names)
+    rule = Setting.commit_update_keywords_array.detect do |rule|
+      rule['keywords'].include?(action) && (rule['if_tracker_id'].blank? || rule['if_tracker_id'] == issue.tracker_id.to_s)
+    end
+    if rule
+      issue.assign_attributes rule.slice(*Issue.attribute_names)
+    end
     Redmine::Hook.call_hook(:model_changeset_scan_commit_for_issue_ids_pre_issue_update,
                             { :changeset => self, :issue => issue, :action => action })
     unless issue.save
index 71ef152083bd060c2a87e6d5b9377fbd3fd31545..444f45e207b19fde294f7ae2c7e79985ea6d67f5 100644 (file)
@@ -154,18 +154,18 @@ END_SRC
   # Example:
   # params = {:keywords => ['fixes', 'closes'], :status_id => ["3", "5"], :done_ratio => ["", "100"]}
   # Setting.commit_update_keywords_from_params(params)
-  # # => {'fixes' => {'status_id' => "3"}, 'closes' => {'status_id' => "5", 'done_ratio' => "100"}}
+  # # => [{'keywords => 'fixes', 'status_id' => "3"}, {'keywords => 'closes', 'status_id' => "5", 'done_ratio' => "100"}]
   def self.commit_update_keywords_from_params(params)
-    s = {}
+    s = []
     if params.is_a?(Hash) && params.key?(:keywords) && params.values.all? {|v| v.is_a? Array}
       attributes = params.except(:keywords).keys
       params[:keywords].each_with_index do |keywords, i|
         next if keywords.blank?
-        s[keywords] = attributes.inject({}) {|h, a|
+        s << attributes.inject({}) {|h, a|
           value = params[a][i].to_s
           h[a.to_s] = value if value.present?
           h
-        }
+        }.merge('keywords' => keywords)
       end
     end
     s
@@ -177,39 +177,39 @@ END_SRC
   end
 
   # Helper that returns a Hash with single update keywords as keys
-  def self.commit_update_by_keyword
-    h = {}
-    if commit_update_keywords.is_a?(Hash)
-      commit_update_keywords.each do |keywords, attribute_updates|
-        next unless attribute_updates.is_a?(Hash)
-        attribute_updates = attribute_updates.dup
-        attribute_updates.delete_if {|k, v| v.blank?}
-        keywords.to_s.split(",").map(&:strip).reject(&:blank?).each do |keyword|
-          h[keyword.downcase] = attribute_updates
-        end
+  def self.commit_update_keywords_array
+    a = []
+    if commit_update_keywords.is_a?(Array)
+      commit_update_keywords.each do |rule|
+        next unless rule.is_a?(Hash)
+        rule = rule.dup
+        rule.delete_if {|k, v| v.blank?}
+        keywords = rule['keywords'].to_s.downcase.split(",").map(&:strip).reject(&:blank?)
+        next if keywords.empty?
+        a << rule.merge('keywords' => keywords)
       end
     end
-    h
+    a
   end
 
   def self.commit_fix_keywords
-     ActiveSupport::Deprecation.warn "Setting.commit_fix_keywords is deprecated and will be removed in Redmine 3"
-    if commit_update_keywords.is_a?(Hash)
-      commit_update_keywords.keys.first
+    ActiveSupport::Deprecation.warn "Setting.commit_fix_keywords is deprecated and will be removed in Redmine 3"
+    if commit_update_keywords.is_a?(Array)
+      commit_update_keywords.first && commit_update_keywords.first['keywords']
     end
   end
 
   def self.commit_fix_status_id
-     ActiveSupport::Deprecation.warn "Setting.commit_fix_status_id is deprecated and will be removed in Redmine 3"
-    if commit_update_keywords.is_a?(Hash)
-      commit_update_keywords[commit_fix_keywords]['status_id']
+    ActiveSupport::Deprecation.warn "Setting.commit_fix_status_id is deprecated and will be removed in Redmine 3"
+    if commit_update_keywords.is_a?(Array)
+      commit_update_keywords.first && commit_update_keywords.first['status_id']
     end
   end
 
   def self.commit_fix_done_ratio
-     ActiveSupport::Deprecation.warn "Setting.commit_fix_done_ratio is deprecated and will be removed in Redmine 3"
-    if commit_update_keywords.is_a?(Hash)
-      commit_update_keywords[commit_fix_keywords]['done_ratio']
+    ActiveSupport::Deprecation.warn "Setting.commit_fix_done_ratio is deprecated and will be removed in Redmine 3"
+    if commit_update_keywords.is_a?(Array)
+      commit_update_keywords.first && commit_update_keywords.first['done_ratio']
     end
   end
 
index d90c212ec61c68d9421041301fa5e8681a9cd629..e95a3fee167c93915bfda07b79576236de30830f 100644 (file)
@@ -80,6 +80,7 @@
 <table class="list" id="commit-keywords">
   <thead>
     <tr>
+      <th><%= l(:label_tracker) %></th>
       <th><%= l(:setting_commit_fix_keywords) %></th>
       <th><%= l(:label_applied_status) %></th>
       <th><%= l(:field_done_ratio) %></th>
     </tr>
   </thead>
   <tbody>
-    <% @commit_update_keywords.each do |keywords, updates| %>
+    <% @commit_update_keywords.each do |rule| %>
     <tr class="commit-keywords">
-      <td><%= text_field_tag "settings[commit_update_keywords][keywords][]", keywords, :size => 30 %></td>
-      <td><%= select_tag "settings[commit_update_keywords][status_id][]", options_for_select([["", 0]] + IssueStatus.sorted.all.collect{|status| [status.name, status.id.to_s]}, updates['status_id']) %></td>
-      <td><%= select_tag "settings[commit_update_keywords][done_ratio][]", options_for_select([["", ""]] + (0..10).to_a.collect {|r| ["#{r*10} %", "#{r*10}"] }, updates['done_ratio']) %></td>
+      <td><%= select_tag "settings[commit_update_keywords][if_tracker_id][]", options_for_select([[l(:label_all), ""]] + Tracker.sorted.all.map {|t| [t.name, t.id.to_s]}, rule['if_tracker_id']) %></td>
+      <td><%= text_field_tag "settings[commit_update_keywords][keywords][]", rule['keywords'], :size => 30 %></td>
+      <td><%= select_tag "settings[commit_update_keywords][status_id][]", options_for_select([["", 0]] + IssueStatus.sorted.all.collect{|status| [status.name, status.id.to_s]}, rule['status_id']) %></td>
+      <td><%= select_tag "settings[commit_update_keywords][done_ratio][]", options_for_select([["", ""]] + (0..10).to_a.collect {|r| ["#{r*10} %", "#{r*10}"] }, rule['done_ratio']) %></td>
       <td class="buttons"><%= link_to image_tag('delete.png'), '#', :class => 'delete-commit-keywords' %></td>
     </tr>
     <% end %>
     <tr>
+      <td></td>
       <td><em class="info"><%= l(:text_comma_separated) %></em></td>
       <td></td>
       <td></td>
index 05779acccf46b2a8bfb2cf0f881602cb3d0ab97b..66c67bd0130a45d5efcee8b11ba9e7f9f51fc97a 100644 (file)
@@ -108,7 +108,7 @@ commit_ref_keywords:
   default: 'refs,references,IssueID'
 commit_update_keywords:
   serialized: true
-  default: {}
+  default: []
 commit_logtime_enabled:
   default: 0
 commit_logtime_activity_id:
index b3cf7bd5abc5b4a2d92dda597e1c0bec0d85152b..f5d4377f15b5c061c838ff103d4afa95b8d6da2d 100644 (file)
@@ -81,38 +81,41 @@ class SettingsControllerTest < ActionController::TestCase
   end
 
   def test_edit_commit_update_keywords
-    with_settings :commit_update_keywords => {
-      "fixes, resolves" => {"status_id" => "3"},
-      "closes" => {"status_id" => "5", "done_ratio" => "100"}
-    } do
+    with_settings :commit_update_keywords => [
+      {"keywords" => "fixes, resolves", "status_id" => "3"},
+      {"keywords" => "closes", "status_id" => "5", "done_ratio" => "100", "if_tracker_id" => "2"}
+    ] do
       get :edit
     end
     assert_response :success
     assert_select 'tr.commit-keywords', 2
-    assert_select 'tr.commit-keywords' do
+    assert_select 'tr.commit-keywords:nth-child(1)' do
       assert_select 'input[name=?][value=?]', 'settings[commit_update_keywords][keywords][]', 'fixes, resolves'
       assert_select 'select[name=?]', 'settings[commit_update_keywords][status_id][]' do
         assert_select 'option[value=3][selected=selected]'
       end
     end
-    assert_select 'tr.commit-keywords' do
+    assert_select 'tr.commit-keywords:nth-child(2)' do
       assert_select 'input[name=?][value=?]', 'settings[commit_update_keywords][keywords][]', 'closes'
       assert_select 'select[name=?]', 'settings[commit_update_keywords][status_id][]' do
-        assert_select 'option[value=5][selected=selected]'
+        assert_select 'option[value=5][selected=selected]', :text => 'Closed'
       end
       assert_select 'select[name=?]', 'settings[commit_update_keywords][done_ratio][]' do
-        assert_select 'option[value=100][selected=selected]'
+        assert_select 'option[value=100][selected=selected]', :text => '100 %'
+      end
+      assert_select 'select[name=?]', 'settings[commit_update_keywords][if_tracker_id][]' do
+        assert_select 'option[value=2][selected=selected]', :text => 'Feature request'
       end
     end
   end
 
   def test_edit_without_commit_update_keywords_should_show_blank_line
-    with_settings :commit_update_keywords => {} do
+    with_settings :commit_update_keywords => [] do
       get :edit
     end
     assert_response :success
     assert_select 'tr.commit-keywords', 1 do
-      assert_select 'input[name=?][value=?]', 'settings[commit_update_keywords][keywords][]', ''
+      assert_select 'input[name=?]:not([value])', 'settings[commit_update_keywords][keywords][]'
     end
   end
 
@@ -121,14 +124,15 @@ class SettingsControllerTest < ActionController::TestCase
       :commit_update_keywords => {
         :keywords => ["resolves", "closes"],
         :status_id => ["3", "5"],
-        :done_ratio => ["", "100"]
+        :done_ratio => ["", "100"],
+        :if_tracker_id => ["", "2"]
       }
     }
     assert_redirected_to '/settings'
-    assert_equal({
-      "resolves" => {"status_id" => "3"},
-      "closes" => {"status_id" => "5", "done_ratio" => "100"}
-    }, Setting.commit_update_keywords)
+    assert_equal([
+      {"keywords" => "resolves", "status_id" => "3"},
+      {"keywords" => "closes", "status_id" => "5", "done_ratio" => "100", "if_tracker_id" => "2"}
+    ], Setting.commit_update_keywords)
   end
 
   def test_get_plugin_settings
index 2f7559249ccbe08c019885ad6ddb7324a1fdf5a3..9cb0916f732e640d3458aedd333e5a41630460b8 100644 (file)
@@ -166,4 +166,16 @@ module ObjectHelpers
     field.save!
     field
   end
+
+  def Changeset.generate!(attributes={})
+    @generated_changeset_rev ||= '123456'
+    @generated_changeset_rev.succ!
+    changeset = new(attributes)
+    changeset.repository ||= Project.find(1).repository
+    changeset.revision ||= @generated_changeset_rev
+    changeset.committed_on ||= Time.now
+    yield changeset if block_given?
+    changeset.save!
+    changeset
+  end
 end
index 93bd7bba51802ef3e168a6387c56f458928a527c..0eea5f88b7e078f7bdcf05ad83cffff8e6544dd7 100644 (file)
@@ -31,7 +31,7 @@ class ChangesetTest < ActiveSupport::TestCase
   def test_ref_keywords_any
     ActionMailer::Base.deliveries.clear
     Setting.commit_ref_keywords = '*'
-    Setting.commit_update_keywords = {'fixes , closes' => {'status_id' => '5', 'done_ratio' => '90'}}
+    Setting.commit_update_keywords = [{'keywords' => 'fixes , closes', 'status_id' => '5', 'done_ratio' => '90'}]
 
     c = Changeset.new(:repository   => Project.find(1).repository,
                       :committed_on => Time.now,
@@ -111,11 +111,7 @@ class ChangesetTest < ActiveSupport::TestCase
 
   def test_ref_keywords_closing_with_timelog
     Setting.commit_ref_keywords = '*'
-    Setting.commit_update_keywords = {
-      'fixes , closes' => {
-        'status_id' => IssueStatus.where(:is_closed => true).first.id.to_s
-      }
-    }
+    Setting.commit_update_keywords = [{'keywords' => 'fixes , closes', 'status_id' => IssueStatus.where(:is_closed => true).first.id.to_s}]
     Setting.commit_logtime_enabled = '1'
 
     c = Changeset.new(:repository   => Project.find(1).repository,
@@ -165,20 +161,45 @@ class ChangesetTest < ActiveSupport::TestCase
   end
 
   def test_update_keywords_with_multiple_rules
-    Setting.commit_update_keywords = {
-      'fixes, closes' => {'status_id' => '5'},
-      'resolves' => {'status_id' => '3'}
-    }
-    issue1 = Issue.generate!
-    issue2 = Issue.generate!
+    with_settings :commit_update_keywords => [
+      {'keywords' => 'fixes, closes', 'status_id' => '5'},
+      {'keywords' => 'resolves', 'status_id' => '3'}
+    ] do
+
+      issue1 = Issue.generate!
+      issue2 = Issue.generate!
+      Changeset.generate!(:comments => "Closes ##{issue1.id}\nResolves ##{issue2.id}")
+      assert_equal 5, issue1.reload.status_id
+      assert_equal 3, issue2.reload.status_id
+    end
+  end
 
-    c = Changeset.new(:repository   => Project.find(1).repository,
-                      :committed_on => Time.now,
-                      :comments     => "Closes ##{issue1.id}\nResolves ##{issue2.id}",
-                      :revision     => '12345')
-    assert c.save
-    assert_equal 5, issue1.reload.status_id
-    assert_equal 3, issue2.reload.status_id
+  def test_update_keywords_with_multiple_rules_should_match_tracker
+    with_settings :commit_update_keywords => [
+      {'keywords' => 'fixes', 'status_id' => '5', 'if_tracker_id' => '2'},
+      {'keywords' => 'fixes', 'status_id' => '3', 'if_tracker_id' => ''}
+    ] do
+
+      issue1 = Issue.generate!(:tracker_id => 2)
+      issue2 = Issue.generate!
+      Changeset.generate!(:comments => "Fixes ##{issue1.id}, ##{issue2.id}")
+      assert_equal 5, issue1.reload.status_id
+      assert_equal 3, issue2.reload.status_id
+    end
+  end
+
+  def test_update_keywords_with_multiple_rules_and_no_match
+    with_settings :commit_update_keywords => [
+      {'keywords' => 'fixes', 'status_id' => '5', 'if_tracker_id' => '2'},
+      {'keywords' => 'fixes', 'status_id' => '3', 'if_tracker_id' => '3'}
+    ] do
+
+      issue1 = Issue.generate!(:tracker_id => 2)
+      issue2 = Issue.generate!
+      Changeset.generate!(:comments => "Fixes ##{issue1.id}, ##{issue2.id}")
+      assert_equal 5, issue1.reload.status_id
+      assert_equal 1, issue2.reload.status_id # no updates
+    end
   end
 
   def test_commit_referencing_a_subproject_issue
@@ -192,7 +213,7 @@ class ChangesetTest < ActiveSupport::TestCase
   end
 
   def test_commit_closing_a_subproject_issue
-    with_settings :commit_update_keywords => {'closes' => {'status_id' => '5'}},
+    with_settings :commit_update_keywords => [{'keywords' => 'closes', 'status_id' => '5'}],
                   :default_language => 'en' do
       issue = Issue.find(5)
       assert !issue.closed?
index 3ff9fc51ecc4fd4c431c303ab9f9a8428ad7091e..c269df7bb7f7192d1a3633843e4a3389d4a3f1fc 100644 (file)
@@ -183,9 +183,9 @@ class RepositoryTest < ActiveSupport::TestCase
     Setting.default_language = 'en'
 
     Setting.commit_ref_keywords = 'refs , references, IssueID'
-    Setting.commit_update_keywords = {
-      'fixes , closes' => {'status_id' => IssueStatus.where(:is_closed => true).first.id, 'done_ratio' => '90'}
-    }
+    Setting.commit_update_keywords = [
+      {'keywords' => 'fixes , closes', 'status_id' => IssueStatus.where(:is_closed => true).first.id, 'done_ratio' => '90'}
+    ]
     Setting.default_language = 'en'
     ActionMailer::Base.deliveries.clear