##// END OF EJS Templates
Bulk edit workflows for multiple trackers/roles (#16164)....
Jean-Philippe Lang -
r12649:b6c794d16b47
parent child
Show More
@@ -0,0 +1,93
1 # Redmine - project management software
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 require File.expand_path('../../test_helper', __FILE__)
19
20 class WorkflowTransitionTest < ActiveSupport::TestCase
21 fixtures :roles, :trackers, :issue_statuses
22
23 def setup
24 WorkflowTransition.delete_all
25 end
26
27 def test_replace_transitions_should_create_enabled_transitions
28 w = WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 2)
29
30 transitions = {'1' => {
31 '2' => {'always' => '1'},
32 '3' => {'always' => '1'}
33 }}
34 assert_difference 'WorkflowTransition.count' do
35 WorkflowTransition.replace_transitions(Tracker.find(1), Role.find(1), transitions)
36 end
37 assert WorkflowTransition.where(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3).exists?
38 end
39
40 def test_replace_transitions_should_delete_disabled_transitions
41 w1 = WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 2)
42 w2 = WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3)
43
44 transitions = {'1' => {
45 '2' => {'always' => '0'},
46 '3' => {'always' => '1'}
47 }}
48 assert_difference 'WorkflowTransition.count', -1 do
49 WorkflowTransition.replace_transitions(Tracker.find(1), Role.find(1), transitions)
50 end
51 assert !WorkflowTransition.exists?(w1.id)
52 end
53
54 def test_replace_transitions_should_create_enabled_additional_transitions
55 transitions = {'1' => {
56 '2' => {'always' => '0', 'assignee' => '0', 'author' => '1'}
57 }}
58 assert_difference 'WorkflowTransition.count' do
59 WorkflowTransition.replace_transitions(Tracker.find(1), Role.find(1), transitions)
60 end
61 w = WorkflowTransition.where(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 2).first
62 assert w
63 assert_equal false, w.assignee
64 assert_equal true, w.author
65 end
66
67 def test_replace_transitions_should_delete_disabled_additional_transitions
68 w = WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 2, :assignee => true)
69
70 transitions = {'1' => {
71 '2' => {'always' => '0', 'assignee' => '0', 'author' => '0'}
72 }}
73 assert_difference 'WorkflowTransition.count', -1 do
74 WorkflowTransition.replace_transitions(Tracker.find(1), Role.find(1), transitions)
75 end
76 assert !WorkflowTransition.exists?(w.id)
77 end
78
79 def test_replace_transitions_should_update_additional_transitions
80 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 2, :assignee => true)
81
82 transitions = {'1' => {
83 '2' => {'always' => '0', 'assignee' => '0', 'author' => '1'}
84 }}
85 assert_no_difference 'WorkflowTransition.count' do
86 WorkflowTransition.replace_transitions(Tracker.find(1), Role.find(1), transitions)
87 end
88 w = WorkflowTransition.where(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 2).first
89 assert w
90 assert_equal false, w.assignee
91 assert_equal true, w.author
92 end
93 end
@@ -18,40 +18,30
18 18 class WorkflowsController < ApplicationController
19 19 layout 'admin'
20 20
21 before_filter :require_admin, :find_roles, :find_trackers
21 before_filter :require_admin
22 22
23 23 def index
24 24 @workflow_counts = WorkflowTransition.count_by_tracker_and_role
25 25 end
26 26
27 27 def edit
28 @role = Role.find_by_id(params[:role_id]) if params[:role_id]
29 @tracker = Tracker.find_by_id(params[:tracker_id]) if params[:tracker_id]
28 find_trackers_roles_and_statuses_for_edit
30 29
31 if request.post?
32 WorkflowTransition.destroy_all( ["role_id=? and tracker_id=?", @role.id, @tracker.id])
33 (params[:issue_status] || []).each { |status_id, transitions|
34 transitions.each { |new_status_id, options|
35 author = options.is_a?(Array) && options.include?('author') && !options.include?('always')
36 assignee = options.is_a?(Array) && options.include?('assignee') && !options.include?('always')
37 WorkflowTransition.create(:role_id => @role.id, :tracker_id => @tracker.id, :old_status_id => status_id, :new_status_id => new_status_id, :author => author, :assignee => assignee)
38 }
39 }
40 if @role.save
41 flash[:notice] = l(:notice_successful_update)
42 redirect_to workflows_edit_path(:role_id => @role, :tracker_id => @tracker, :used_statuses_only => params[:used_statuses_only])
43 return
30 if request.post? && @roles && @trackers && params[:transitions]
31 transitions = params[:transitions].deep_dup
32 transitions.each do |old_status_id, transitions_by_new_status|
33 transitions_by_new_status.each do |new_status_id, transition_by_rule|
34 transition_by_rule.reject! {|rule, transition| transition == 'no_change'}
44 35 end
45 36 end
46
47 @used_statuses_only = (params[:used_statuses_only] == '0' ? false : true)
48 if @tracker && @used_statuses_only && @tracker.issue_statuses.any?
49 @statuses = @tracker.issue_statuses
37 WorkflowTransition.replace_transitions(@trackers, @roles, transitions)
38 flash[:notice] = l(:notice_successful_update)
39 redirect_to_referer_or workflows_edit_path
40 return
50 41 end
51 @statuses ||= IssueStatus.sorted.all
52 42
53 if @tracker && @role && @statuses.any?
54 workflows = WorkflowTransition.where(:role_id => @role.id, :tracker_id => @tracker.id).all
43 if @trackers && @roles && @statuses.any?
44 workflows = WorkflowTransition.where(:role_id => @roles.map(&:id), :tracker_id => @trackers.map(&:id)).all
55 45 @workflows = {}
56 46 @workflows['always'] = workflows.select {|w| !w.author && !w.assignee}
57 47 @workflows['author'] = workflows.select {|w| w.author}
@@ -60,36 +50,31 class WorkflowsController < ApplicationController
60 50 end
61 51
62 52 def permissions
63 @role = Role.find_by_id(params[:role_id]) if params[:role_id]
64 @tracker = Tracker.find_by_id(params[:tracker_id]) if params[:tracker_id]
53 find_trackers_roles_and_statuses_for_edit
65 54
66 if request.post? && @role && @tracker
67 WorkflowPermission.replace_permissions(@tracker, @role, params[:permissions] || {})
55 if request.post? && @roles && @trackers && params[:permissions]
56 permissions = params[:permissions].deep_dup
57 permissions.each { |field, rule_by_status_id|
58 rule_by_status_id.reject! {|status_id, rule| rule == 'no_change'}
59 }
60 WorkflowPermission.replace_permissions(@trackers, @roles, permissions)
68 61 flash[:notice] = l(:notice_successful_update)
69 redirect_to workflows_permissions_path(:role_id => @role, :tracker_id => @tracker, :used_statuses_only => params[:used_statuses_only])
62 redirect_to_referer_or workflows_permissions_path
70 63 return
71 64 end
72 65
73 @used_statuses_only = (params[:used_statuses_only] == '0' ? false : true)
74 if @tracker && @used_statuses_only && @tracker.issue_statuses.any?
75 @statuses = @tracker.issue_statuses
76 end
77 @statuses ||= IssueStatus.sorted.all
78
79 if @role && @tracker
80 @fields = (Tracker::CORE_FIELDS_ALL - @tracker.disabled_core_fields).map {|field| [field, l("field_"+field.sub(/_id$/, ''))]}
81 @custom_fields = @tracker.custom_fields
82 @permissions = WorkflowPermission.
83 where(:tracker_id => @tracker.id, :role_id => @role.id).inject({}) do |h, w|
84 h[w.old_status_id] ||= {}
85 h[w.old_status_id][w.field_name] = w.rule
86 h
87 end
66 if @roles && @trackers
67 @fields = (Tracker::CORE_FIELDS_ALL - @trackers.map(&:disabled_core_fields).reduce(:&)).map {|field| [field, l("field_"+field.sub(/_id$/, ''))]}
68 @custom_fields = @trackers.map(&:custom_fields).flatten.uniq.sort
69 @permissions = WorkflowPermission.rules_by_status_id(@trackers, @roles)
88 70 @statuses.each {|status| @permissions[status.id] ||= {}}
89 71 end
90 72 end
91 73
92 74 def copy
75 @roles = Role.sorted
76 @trackers = Tracker.sorted
77
93 78 if params[:source_tracker_id].blank? || params[:source_tracker_id] == 'any'
94 79 @source_tracker = nil
95 80 else
@@ -119,11 +104,37 class WorkflowsController < ApplicationController
119 104
120 105 private
121 106
107 def find_trackers_roles_and_statuses_for_edit
108 find_roles
109 find_trackers
110 find_statuses
111 end
112
122 113 def find_roles
114 ids = Array.wrap(params[:role_id])
115 if ids == ['all']
123 116 @roles = Role.sorted.all
117 elsif ids.present?
118 @roles = Role.where(:id => ids).all
119 end
120 @roles = nil if @roles.blank?
124 121 end
125 122
126 123 def find_trackers
124 ids = Array.wrap(params[:tracker_id])
125 if ids == ['all']
127 126 @trackers = Tracker.sorted.all
127 elsif ids.present?
128 @trackers = Tracker.where(:id => ids).all
129 end
130 @trackers = nil if @trackers.blank?
131 end
132
133 def find_statuses
134 @used_statuses_only = (params[:used_statuses_only] == '0' ? false : true)
135 if @trackers && @used_statuses_only
136 @statuses = @trackers.map(&:issue_statuses).flatten.uniq.sort.presence
137 end
138 @statuses ||= IssueStatus.sorted.all
128 139 end
129 140 end
@@ -18,24 +18,74
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 19
20 20 module WorkflowsHelper
21 def options_for_workflow_select(name, objects, selected, options={})
22 option_tags = ''.html_safe
23 multiple = false
24 if selected
25 if selected.size == objects.size
26 selected = 'all'
27 else
28 selected = selected.map(&:id)
29 if selected.size > 1
30 multiple = true
31 end
32 end
33 else
34 selected = objects.first.try(:id)
35 end
36 all_tag_options = {:value => 'all', :selected => (selected == 'all')}
37 if multiple
38 all_tag_options.merge!(:style => "display:none;")
39 end
40 option_tags << content_tag('option', 'All', all_tag_options)
41 option_tags << options_from_collection_for_select(objects, "id", "name", selected)
42 select_tag name, option_tags, {:multiple => multiple}.merge(options)
43 end
44
21 45 def field_required?(field)
22 46 field.is_a?(CustomField) ? field.is_required? : %w(project_id tracker_id subject priority_id is_private).include?(field)
23 47 end
24 48
25 def field_permission_tag(permissions, status, field, role)
49 def field_permission_tag(permissions, status, field, roles)
26 50 name = field.is_a?(CustomField) ? field.id.to_s : field
27 51 options = [["", ""], [l(:label_readonly), "readonly"]]
28 52 options << [l(:label_required), "required"] unless field_required?(field)
29 53 html_options = {}
30 selected = permissions[status.id][name]
31 54
32 hidden = field.is_a?(CustomField) && !field.visible? && !role.custom_fields.to_a.include?(field)
55 if perm = permissions[status.id][name]
56 if perm.uniq.size > 1 || perm.size < @roles.size * @trackers.size
57 options << [l(:label_no_change_option), "no_change"]
58 selected = 'no_change'
59 else
60 selected = perm.first
61 end
62 end
63
64 hidden = field.is_a?(CustomField) &&
65 !field.visible? &&
66 !roles.detect {|role| role.custom_fields.to_a.include?(field)}
67
33 68 if hidden
34 69 options[0][0] = l(:label_hidden)
35 70 selected = ''
36 71 html_options[:disabled] = true
37 72 end
38 73
39 select_tag("permissions[#{name}][#{status.id}]", options_for_select(options, selected), html_options)
74 select_tag("permissions[#{status.id}][#{name}]", options_for_select(options, selected), html_options)
75 end
76
77 def transition_tag(workflows, old_status, new_status, name)
78 w = workflows.select {|w| w.old_status_id == old_status.id && w.new_status_id == new_status.id}.size
79
80 tag_name = "transitions[#{ old_status.id }][#{new_status.id}][#{name}]"
81 if w == 0 || w == @roles.size * @trackers.size
82
83 hidden_field_tag(tag_name, "0") +
84 check_box_tag(tag_name, "1", w != 0,
85 :class => "old-status-#{old_status.id} new-status-#{new_status.id}")
86 else
87 select_tag tag_name,
88 options_for_select([["Oui", "1"], ["Non", "0"], [l(:label_no_change_option), "no_change"]], "no_change")
89 end
40 90 end
41 91 end
@@ -19,21 +19,44 class WorkflowPermission < WorkflowRule
19 19 validates_inclusion_of :rule, :in => %w(readonly required)
20 20 validate :validate_field_name
21 21
22 # Replaces the workflow permissions for the given tracker and role
22 # Returns the workflow permissions for the given trackers and roles
23 # grouped by status_id
23 24 #
24 25 # Example:
25 # WorkflowPermission.replace_permissions role, tracker, {'due_date' => {'1' => 'readonly', '2' => 'required'}}
26 def self.replace_permissions(tracker, role, permissions)
27 destroy_all(:tracker_id => tracker.id, :role_id => role.id)
26 # WorkflowPermission.rules_by_status_id trackers, roles
27 # # => {1 => {'start_date' => 'required', 'due_date' => 'readonly'}}
28 def self.rules_by_status_id(trackers, roles)
29 WorkflowPermission.where(:tracker_id => trackers.map(&:id), :role_id => roles.map(&:id)).inject({}) do |h, w|
30 h[w.old_status_id] ||= {}
31 h[w.old_status_id][w.field_name] ||= []
32 h[w.old_status_id][w.field_name] << w.rule
33 h
34 end
35 end
36
37 # Replaces the workflow permissions for the given trackers and roles
38 #
39 # Example:
40 # WorkflowPermission.replace_permissions trackers, roles, {'1' => {'start_date' => 'required', 'due_date' => 'readonly'}}
41 def self.replace_permissions(trackers, roles, permissions)
42 trackers = Array.wrap trackers
43 roles = Array.wrap roles
28 44
29 permissions.each { |field, rule_by_status_id|
30 rule_by_status_id.each { |status_id, rule|
45 transaction do
46 permissions.each { |status_id, rule_by_field|
47 rule_by_field.each { |field, rule|
48 destroy_all(:tracker_id => trackers.map(&:id), :role_id => roles.map(&:id), :old_status_id => status_id, :field_name => field)
31 49 if rule.present?
50 trackers.each do |tracker|
51 roles.each do |role|
32 52 WorkflowPermission.create(:role_id => role.id, :tracker_id => tracker.id, :old_status_id => status_id, :field_name => field, :rule => rule)
33 53 end
54 end
55 end
34 56 }
35 57 }
36 58 end
59 end
37 60
38 61 protected
39 62
@@ -36,4 +36,69 class WorkflowTransition < WorkflowRule
36 36
37 37 result
38 38 end
39
40 def self.replace_transitions(trackers, roles, transitions)
41 trackers = Array.wrap trackers
42 roles = Array.wrap roles
43
44 transaction do
45 records = WorkflowTransition.where(:tracker_id => trackers.map(&:id), :role_id => roles.map(&:id)).all
46
47 transitions.each do |old_status_id, transitions_by_new_status|
48 transitions_by_new_status.each do |new_status_id, transition_by_rule|
49 transition_by_rule.each do |rule, transition|
50 trackers.each do |tracker|
51 roles.each do |role|
52 w = records.select {|r|
53 r.old_status_id == old_status_id.to_i &&
54 r.new_status_id == new_status_id.to_i &&
55 r.tracker_id == tracker.id &&
56 r.role_id == role.id &&
57 !r.destroyed?
58 }
59
60 if rule == 'always'
61 w = w.select {|r| !r.author && !r.assignee}
62 else
63 w = w.select {|r| r.author || r.assignee}
64 end
65 if w.size > 1
66 w[1..-1].each(&:destroy)
67 end
68 w = w.first
69
70 if transition == "1" || transition == true
71 unless w
72 w = WorkflowTransition.new(:old_status_id => old_status_id, :new_status_id => new_status_id, :tracker_id => tracker.id, :role_id => role.id)
73 records << w
74 end
75 w.author = true if rule == "author"
76 w.assignee = true if rule == "assignee"
77 w.save if w.changed?
78 elsif w
79 if rule == 'always'
80 w.destroy
81 elsif rule == 'author'
82 if w.assignee
83 w.author = false
84 w.save if w.changed?
85 else
86 w.destroy
87 end
88 elsif rule == 'assignee'
89 if w.author
90 w.assignee = false
91 w.save if w.changed?
92 else
93 w.destroy
94 end
95 end
96 end
97 end
98 end
99 end
100 end
101 end
102 end
103 end
39 104 end
@@ -1,4 +1,4
1 <table class="list transitions transitions-<%= name %>">
1 <table class="list workflows transitions transitions-<%= name %>">
2 2 <thead>
3 3 <tr>
4 4 <th>
@@ -31,8 +31,7
31 31 <% for new_status in @statuses -%>
32 32 <% checked = workflows.detect {|w| w.old_status_id == old_status.id && w.new_status_id == new_status.id} %>
33 33 <td class="<%= checked ? 'enabled' : '' %>">
34 <%= check_box_tag "issue_status[#{ old_status.id }][#{new_status.id}][]", name, checked,
35 :class => "old-status-#{old_status.id} new-status-#{new_status.id}" %>
34 <%= transition_tag workflows, old_status, new_status, name %>
36 35 </td>
37 36 <% end -%>
38 37 </tr>
@@ -4,8 +4,8
4 4
5 5 <div class="tabs">
6 6 <ul>
7 <li><%= link_to l(:label_status_transitions), {:action => 'edit', :role_id => @role, :tracker_id => @tracker}, :class => 'selected' %></li>
8 <li><%= link_to l(:label_fields_permissions), {:action => 'permissions', :role_id => @role, :tracker_id => @tracker} %></li>
7 <li><%= link_to l(:label_status_transitions), workflows_edit_path(:role_id => @roles, :tracker_id => @trackers), :class => 'selected' %></li>
8 <li><%= link_to l(:label_fields_permissions), workflows_permissions_path(:role_id => @roles, :tracker_id => @trackers) %></li>
9 9 </ul>
10 10 </div>
11 11
@@ -14,10 +14,14
14 14 <%= form_tag({}, :method => 'get') do %>
15 15 <p>
16 16 <label><%=l(:label_role)%>:
17 <%= select_tag 'role_id', options_from_collection_for_select(@roles, "id", "name", @role && @role.id) %></label>
17 <%= options_for_workflow_select 'role_id[]', Role.sorted, @roles, :id => 'role_id', :class => 'expandable' %>
18 </label>
19 <a href="#" data-expands="#role_id"><%= image_tag 'bullet_toggle_plus.png' %></a>
18 20
19 21 <label><%=l(:label_tracker)%>:
20 <%= select_tag 'tracker_id', options_from_collection_for_select(@trackers, "id", "name", @tracker && @tracker.id) %></label>
22 <%= options_for_workflow_select 'tracker_id[]', Tracker.sorted, @trackers, :id => 'tracker_id', :class => 'expandable' %>
23 </label>
24 <a href="#" data-expands="#tracker_id"><%= image_tag 'bullet_toggle_plus.png' %></a>
21 25
22 26 <%= submit_tag l(:button_edit), :name => nil %>
23 27
@@ -27,10 +31,10
27 31 </p>
28 32 <% end %>
29 33
30 <% if @tracker && @role && @statuses.any? %>
34 <% if @trackers && @roles && @statuses.any? %>
31 35 <%= form_tag({}, :id => 'workflow_form' ) do %>
32 <%= hidden_field_tag 'tracker_id', @tracker.id %>
33 <%= hidden_field_tag 'role_id', @role.id %>
36 <%= @trackers.map {|tracker| hidden_field_tag 'tracker_id[]', tracker.id}.join.html_safe %>
37 <%= @roles.map {|role| hidden_field_tag 'role_id[]', role.id}.join.html_safe %>
34 38 <%= hidden_field_tag 'used_statuses_only', params[:used_statuses_only] %>
35 39 <div class="autoscroll">
36 40 <%= render :partial => 'form', :locals => {:name => 'always', :workflows => @workflows['always']} %>
@@ -54,3 +58,18
54 58 <%= submit_tag l(:button_save) %>
55 59 <% end %>
56 60 <% end %>
61
62 <%= javascript_tag do %>
63 $("a[data-expands]").click(function(e){
64 e.preventDefault();
65 var target = $($(this).attr("data-expands"));
66 if (target.attr("multiple")) {
67 target.attr("multiple", false);
68 target.find("option[value=all]").show();
69 } else {
70 target.attr("multiple", true);
71 target.find("option[value=all]").attr("selected", false).hide();
72 }
73 });
74
75 <% end %>
@@ -4,8 +4,8
4 4
5 5 <div class="tabs">
6 6 <ul>
7 <li><%= link_to l(:label_status_transitions), {:action => 'edit', :role_id => @role, :tracker_id => @tracker} %></li>
8 <li><%= link_to l(:label_fields_permissions), {:action => 'permissions', :role_id => @role, :tracker_id => @tracker}, :class => 'selected' %></li>
7 <li><%= link_to l(:label_status_transitions), workflows_edit_path(:role_id => @roles, :tracker_id => @trackers) %></li>
8 <li><%= link_to l(:label_fields_permissions), workflows_permissions_path(:role_id => @roles, :tracker_id => @trackers), :class => 'selected' %></li>
9 9 </ul>
10 10 </div>
11 11
@@ -14,10 +14,14
14 14 <%= form_tag({}, :method => 'get') do %>
15 15 <p>
16 16 <label><%=l(:label_role)%>:
17 <%= select_tag 'role_id', options_from_collection_for_select(@roles, "id", "name", @role && @role.id) %></label>
17 <%= options_for_workflow_select 'role_id[]', Role.sorted, @roles, :id => 'role_id', :class => 'expandable' %>
18 </label>
19 <a href="#" data-expands="#role_id"><%= image_tag 'bullet_toggle_plus.png' %></a>
18 20
19 21 <label><%=l(:label_tracker)%>:
20 <%= select_tag 'tracker_id', options_from_collection_for_select(@trackers, "id", "name", @tracker && @tracker.id) %></label>
22 <%= options_for_workflow_select 'tracker_id[]', Tracker.sorted, @trackers, :id => 'tracker_id', :class => 'expandable' %>
23 </label>
24 <a href="#" data-expands="#tracker_id"><%= image_tag 'bullet_toggle_plus.png' %></a>
21 25
22 26 <%= submit_tag l(:button_edit), :name => nil %>
23 27
@@ -26,13 +30,13
26 30 </p>
27 31 <% end %>
28 32
29 <% if @tracker && @role && @statuses.any? %>
33 <% if @trackers && @roles && @statuses.any? %>
30 34 <%= form_tag({}, :id => 'workflow_form' ) do %>
31 <%= hidden_field_tag 'tracker_id', @tracker.id %>
32 <%= hidden_field_tag 'role_id', @role.id %>
35 <%= @trackers.map {|tracker| hidden_field_tag 'tracker_id[]', tracker.id}.join.html_safe %>
36 <%= @roles.map {|role| hidden_field_tag 'role_id[]', role.id}.join.html_safe %>
33 37 <%= hidden_field_tag 'used_statuses_only', params[:used_statuses_only] %>
34 38 <div class="autoscroll">
35 <table class="list fields_permissions">
39 <table class="list workflows fields_permissions">
36 40 <thead>
37 41 <tr>
38 42 <th>
@@ -62,7 +66,7
62 66 </td>
63 67 <% for status in @statuses -%>
64 68 <td class="<%= @permissions[status.id][field] %>">
65 <%= field_permission_tag(@permissions, status, field, @role) %>
69 <%= field_permission_tag(@permissions, status, field, @roles) %>
66 70 <% unless status == @statuses.last %><a href="#" class="repeat-value">&#187;</a><% end %>
67 71 </td>
68 72 <% end -%>
@@ -82,7 +86,7
82 86 </td>
83 87 <% for status in @statuses -%>
84 88 <td class="<%= @permissions[status.id][field.id.to_s] %>">
85 <%= field_permission_tag(@permissions, status, field, @role) %>
89 <%= field_permission_tag(@permissions, status, field, @roles) %>
86 90 <% unless status == @statuses.last %><a href="#" class="repeat-value">&#187;</a><% end %>
87 91 </td>
88 92 <% end -%>
@@ -103,4 +107,17 $("a.repeat-value").click(function(e){
103 107 var selected = td.find("select").find(":selected").val();
104 108 td.nextAll('td').find("select").val(selected);
105 109 });
110
111 $("a[data-expands]").click(function(e){
112 e.preventDefault();
113 var target = $($(this).attr("data-expands"));
114 if (target.attr("multiple")) {
115 target.attr("multiple", false);
116 target.find("option[value=all]").show();
117 } else {
118 target.attr("multiple", true);
119 target.find("option[value=all]").attr("selected", false).hide();
120 }
121 });
122
106 123 <% end %>
@@ -254,6 +254,8 table.boards td.last-message {text-align:left;font-size:80%;}
254 254
255 255 table.messages td.last_message {text-align:left;}
256 256
257 #query_form_content {font-size:90%;}
258
257 259 table.query-columns {
258 260 border-collapse: collapse;
259 261 border: 0;
@@ -340,7 +342,7 div.issue table.attributes td {width:28%;}
340 342 #issue_tree td.checkbox, #relations td.checkbox {display:none;}
341 343 #relations td.buttons {padding:0;}
342 344
343 fieldset.collapsible { border-width: 1px 0 0 0; font-size: 0.9em; }
345 fieldset.collapsible {border-width: 1px 0 0 0;}
344 346 fieldset.collapsible>legend { padding-left: 16px; background: url(../images/arrow_expanded.png) no-repeat 0% 40%; cursor:pointer; }
345 347 fieldset.collapsible.collapsed>legend { background-image: url(../images/arrow_collapsed.png); }
346 348
@@ -453,10 +455,12 ul.properties li span {font-style:italic;}
453 455
454 456 #workflow_copy_form select { width: 200px; }
455 457 table.transitions td.enabled {background: #bfb;}
456 table.fields_permissions select {font-size:90%}
458 #workflow_form table select {font-size:90%; max-width:100px;}
457 459 table.fields_permissions td.readonly {background:#ddd;}
458 460 table.fields_permissions td.required {background:#d88;}
459 461
462 select.expandable {vertical-align:top;}
463
460 464 textarea#custom_field_possible_values {width: 95%; resize:vertical}
461 465 textarea#custom_field_default_value {width: 95%; resize:vertical}
462 466
@@ -39,8 +39,6 class WorkflowsControllerTest < ActionController::TestCase
39 39 get :edit
40 40 assert_response :success
41 41 assert_template 'edit'
42 assert_not_nil assigns(:roles)
43 assert_not_nil assigns(:trackers)
44 42 end
45 43
46 44 def test_get_edit_with_role_and_tracker
@@ -57,18 +55,11 class WorkflowsControllerTest < ActionController::TestCase
57 55 assert_equal [2, 3, 5], assigns(:statuses).collect(&:id)
58 56
59 57 # allowed transitions
60 assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
61 :name => 'issue_status[3][5][]',
62 :value => 'always',
63 :checked => 'checked' }
58 assert_select 'input[type=checkbox][name=?][value=1][checked=checked]', 'transitions[3][5][always]'
64 59 # not allowed
65 assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
66 :name => 'issue_status[3][2][]',
67 :value => 'always',
68 :checked => nil }
60 assert_select 'input[type=checkbox][name=?][value=1]:not([checked=checked])', 'transitions[3][2][always]'
69 61 # unused
70 assert_no_tag :tag => 'input', :attributes => { :type => 'checkbox',
71 :name => 'issue_status[1][1][]' }
62 assert_select 'input[type=checkbox][name=?]', 'transitions[1][1][always]', 0
72 63 end
73 64
74 65 def test_get_edit_with_role_and_tracker_and_all_statuses
@@ -81,19 +72,18 class WorkflowsControllerTest < ActionController::TestCase
81 72 assert_not_nil assigns(:statuses)
82 73 assert_equal IssueStatus.count, assigns(:statuses).size
83 74
84 assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
85 :name => 'issue_status[1][1][]',
86 :value => 'always',
87 :checked => nil }
75 assert_select 'input[type=checkbox][name=?]', 'transitions[1][1][always]'
88 76 end
89 77
90 78 def test_post_edit
79 WorkflowTransition.delete_all
80
91 81 post :edit, :role_id => 2, :tracker_id => 1,
92 :issue_status => {
93 '4' => {'5' => ['always']},
94 '3' => {'1' => ['always'], '2' => ['always']}
82 :transitions => {
83 '4' => {'5' => {'always' => '1'}},
84 '3' => {'1' => {'always' => '1'}, '2' => {'always' => '1'}}
95 85 }
96 assert_redirected_to '/workflows/edit?role_id=2&tracker_id=1'
86 assert_response 302
97 87
98 88 assert_equal 3, WorkflowTransition.where(:tracker_id => 1, :role_id => 2).count
99 89 assert_not_nil WorkflowTransition.where(:role_id => 2, :tracker_id => 1, :old_status_id => 3, :new_status_id => 2).first
@@ -101,12 +91,16 class WorkflowsControllerTest < ActionController::TestCase
101 91 end
102 92
103 93 def test_post_edit_with_additional_transitions
94 WorkflowTransition.delete_all
95
104 96 post :edit, :role_id => 2, :tracker_id => 1,
105 :issue_status => {
106 '4' => {'5' => ['always']},
107 '3' => {'1' => ['author'], '2' => ['assignee'], '4' => ['author', 'assignee']}
97 :transitions => {
98 '4' => {'5' => {'always' => '1', 'author' => '0', 'assignee' => '0'}},
99 '3' => {'1' => {'always' => '0', 'author' => '1', 'assignee' => '0'},
100 '2' => {'always' => '0', 'author' => '0', 'assignee' => '1'},
101 '4' => {'always' => '0', 'author' => '1', 'assignee' => '1'}}
108 102 }
109 assert_redirected_to '/workflows/edit?role_id=2&tracker_id=1'
103 assert_response 302
110 104
111 105 assert_equal 4, WorkflowTransition.where(:tracker_id => 1, :role_id => 2).count
112 106
@@ -124,20 +118,11 class WorkflowsControllerTest < ActionController::TestCase
124 118 assert w.assignee
125 119 end
126 120
127 def test_clear_workflow
128 assert WorkflowTransition.where(:role_id => 1, :tracker_id => 2).count > 0
129
130 post :edit, :role_id => 1, :tracker_id => 2
131 assert_equal 0, WorkflowTransition.where(:role_id => 1, :tracker_id => 2).count
132 end
133
134 121 def test_get_permissions
135 122 get :permissions
136 123
137 124 assert_response :success
138 125 assert_template 'permissions'
139 assert_not_nil assigns(:roles)
140 assert_not_nil assigns(:trackers)
141 126 end
142 127
143 128 def test_get_permissions_with_role_and_tracker
@@ -150,11 +135,11 class WorkflowsControllerTest < ActionController::TestCase
150 135 assert_response :success
151 136 assert_template 'permissions'
152 137
153 assert_select 'input[name=role_id][value=1]'
154 assert_select 'input[name=tracker_id][value=2]'
138 assert_select 'input[name=?][value=1]', 'role_id[]'
139 assert_select 'input[name=?][value=2]', 'tracker_id[]'
155 140
156 141 # Required field
157 assert_select 'select[name=?]', 'permissions[assigned_to_id][2]' do
142 assert_select 'select[name=?]', 'permissions[2][assigned_to_id]' do
158 143 assert_select 'option[value=]'
159 144 assert_select 'option[value=][selected=selected]', 0
160 145 assert_select 'option[value=readonly]', :text => 'Read-only'
@@ -164,7 +149,7 class WorkflowsControllerTest < ActionController::TestCase
164 149 end
165 150
166 151 # Read-only field
167 assert_select 'select[name=?]', 'permissions[fixed_version_id][3]' do
152 assert_select 'select[name=?]', 'permissions[3][fixed_version_id]' do
168 153 assert_select 'option[value=]'
169 154 assert_select 'option[value=][selected=selected]', 0
170 155 assert_select 'option[value=readonly]', :text => 'Read-only'
@@ -174,7 +159,7 class WorkflowsControllerTest < ActionController::TestCase
174 159 end
175 160
176 161 # Other field
177 assert_select 'select[name=?]', 'permissions[due_date][3]' do
162 assert_select 'select[name=?]', 'permissions[3][due_date]' do
178 163 assert_select 'option[value=]'
179 164 assert_select 'option[value=][selected=selected]', 0
180 165 assert_select 'option[value=readonly]', :text => 'Read-only'
@@ -193,7 +178,7 class WorkflowsControllerTest < ActionController::TestCase
193 178
194 179 # Custom field that is always required
195 180 # The default option is "(Required)"
196 assert_select 'select[name=?]', "permissions[#{cf.id}][3]" do
181 assert_select 'select[name=?]', "permissions[3][#{cf.id}]" do
197 182 assert_select 'option[value=]'
198 183 assert_select 'option[value=readonly]', :text => 'Read-only'
199 184 assert_select 'option[value=required]', 0
@@ -209,15 +194,56 class WorkflowsControllerTest < ActionController::TestCase
209 194 assert_response :success
210 195 assert_template 'permissions'
211 196
212 assert_select 'select[name=?]:not(.disabled)', "permissions[#{cf1.id}][1]"
213 assert_select 'select[name=?]:not(.disabled)', "permissions[#{cf3.id}][1]"
197 assert_select 'select[name=?]:not(.disabled)', "permissions[1][#{cf1.id}]"
198 assert_select 'select[name=?]:not(.disabled)', "permissions[1][#{cf3.id}]"
214 199
215 assert_select 'select[name=?][disabled=disabled]', "permissions[#{cf2.id}][1]" do
200 assert_select 'select[name=?][disabled=disabled]', "permissions[1][#{cf2.id}]" do
216 201 assert_select 'option[value=][selected=selected]', :text => 'Hidden'
217 202 end
218 203 end
219 204
220 def test_get_permissions_with_role_and_tracker_and_all_statuses
205 def test_get_permissions_with_missing_permissions_for_roles_should_default_to_no_change
206 WorkflowPermission.delete_all
207 WorkflowPermission.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 1, :field_name => 'assigned_to_id', :rule => 'required')
208
209 get :permissions, :role_id => [1, 2], :tracker_id => 2
210 assert_response :success
211
212 assert_select 'select[name=?]', 'permissions[1][assigned_to_id]' do
213 assert_select 'option[selected]', 1
214 assert_select 'option[selected][value=no_change]'
215 end
216 end
217
218 def test_get_permissions_with_different_permissions_for_roles_should_default_to_no_change
219 WorkflowPermission.delete_all
220 WorkflowPermission.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 1, :field_name => 'assigned_to_id', :rule => 'required')
221 WorkflowPermission.create!(:role_id => 2, :tracker_id => 2, :old_status_id => 1, :field_name => 'assigned_to_id', :rule => 'readonly')
222
223 get :permissions, :role_id => [1, 2], :tracker_id => 2
224 assert_response :success
225
226 assert_select 'select[name=?]', 'permissions[1][assigned_to_id]' do
227 assert_select 'option[selected]', 1
228 assert_select 'option[selected][value=no_change]'
229 end
230 end
231
232 def test_get_permissions_with_same_permissions_for_roles_should_default_to_permission
233 WorkflowPermission.delete_all
234 WorkflowPermission.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 1, :field_name => 'assigned_to_id', :rule => 'required')
235 WorkflowPermission.create!(:role_id => 2, :tracker_id => 2, :old_status_id => 1, :field_name => 'assigned_to_id', :rule => 'required')
236
237 get :permissions, :role_id => [1, 2], :tracker_id => 2
238 assert_response :success
239
240 assert_select 'select[name=?]', 'permissions[1][assigned_to_id]' do
241 assert_select 'option[selected]', 1
242 assert_select 'option[selected][value=required]'
243 end
244 end
245
246 def test_get_permissions_with_role_and_tracker_and_all_statuses_should_show_all_statuses
221 247 WorkflowTransition.delete_all
222 248
223 249 get :permissions, :role_id => 1, :tracker_id => 2, :used_statuses_only => '0'
@@ -229,11 +255,11 class WorkflowsControllerTest < ActionController::TestCase
229 255 WorkflowPermission.delete_all
230 256
231 257 post :permissions, :role_id => 1, :tracker_id => 2, :permissions => {
232 'assigned_to_id' => {'1' => '', '2' => 'readonly', '3' => ''},
233 'fixed_version_id' => {'1' => 'required', '2' => 'readonly', '3' => ''},
234 'due_date' => {'1' => '', '2' => '', '3' => ''},
258 '1' => {'assigned_to_id' => '', 'fixed_version_id' => 'required', 'due_date' => ''},
259 '2' => {'assigned_to_id' => 'readonly', 'fixed_version_id' => 'readonly', 'due_date' => ''},
260 '3' => {'assigned_to_id' => '', 'fixed_version_id' => '', 'due_date' => ''}
235 261 }
236 assert_redirected_to '/workflows/permissions?role_id=1&tracker_id=2'
262 assert_response 302
237 263
238 264 workflows = WorkflowPermission.all
239 265 assert_equal 3, workflows.size
@@ -246,22 +272,6 class WorkflowsControllerTest < ActionController::TestCase
246 272 assert workflows.detect {|wf| wf.old_status_id == 2 && wf.field_name == 'fixed_version_id' && wf.rule == 'readonly'}
247 273 end
248 274
249 def test_post_permissions_should_clear_permissions
250 WorkflowPermission.delete_all
251 WorkflowPermission.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :field_name => 'assigned_to_id', :rule => 'required')
252 WorkflowPermission.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :field_name => 'fixed_version_id', :rule => 'required')
253 wf1 = WorkflowPermission.create!(:role_id => 1, :tracker_id => 3, :old_status_id => 2, :field_name => 'fixed_version_id', :rule => 'required')
254 wf2 = WorkflowPermission.create!(:role_id => 2, :tracker_id => 2, :old_status_id => 3, :field_name => 'fixed_version_id', :rule => 'readonly')
255
256 post :permissions, :role_id => 1, :tracker_id => 2
257 assert_redirected_to '/workflows/permissions?role_id=1&tracker_id=2'
258
259 workflows = WorkflowPermission.all
260 assert_equal 2, workflows.size
261 assert wf1.reload
262 assert wf2.reload
263 end
264
265 275 def test_get_copy
266 276 get :copy
267 277 assert_response :success
General Comments 0
You need to be logged in to leave comments. Login now