Patch by Vincent Robert and Marius BALTEANU. git-svn-id: http://svn.redmine.org/redmine/trunk@20941 e93f8b46-1217-0410-a6f0-8f06a7374b81tags/5.0.0
class WorkflowsController < ApplicationController | class WorkflowsController < ApplicationController | ||||
layout 'admin' | layout 'admin' | ||||
self.main_menu = false | self.main_menu = false | ||||
before_action :find_trackers_roles_and_statuses_for_edit, only: [:edit, :update, :permissions, :update_permissions] | |||||
before_action :require_admin | before_action :require_admin | ||||
end | end | ||||
def edit | def edit | ||||
find_trackers_roles_and_statuses_for_edit | |||||
if @trackers && @roles && @statuses.any? | |||||
workflows = WorkflowTransition. | |||||
where(:role_id => @roles.map(&:id), :tracker_id => @trackers.map(&:id)). | |||||
preload(:old_status, :new_status) | |||||
@workflows = {} | |||||
@workflows['always'] = workflows.select {|w| !w.author && !w.assignee} | |||||
@workflows['author'] = workflows.select {|w| w.author} | |||||
@workflows['assignee'] = workflows.select {|w| w.assignee} | |||||
end | |||||
end | |||||
if request.post? && @roles && @trackers && params[:transitions] | |||||
def update | |||||
if @roles && @trackers && params[:transitions] | |||||
transitions = params[:transitions].deep_dup | transitions = params[:transitions].deep_dup | ||||
transitions.each do |old_status_id, transitions_by_new_status| | transitions.each do |old_status_id, transitions_by_new_status| | ||||
transitions_by_new_status.each do |new_status_id, transition_by_rule| | transitions_by_new_status.each do |new_status_id, transition_by_rule| | ||||
end | end | ||||
WorkflowTransition.replace_transitions(@trackers, @roles, transitions) | WorkflowTransition.replace_transitions(@trackers, @roles, transitions) | ||||
flash[:notice] = l(:notice_successful_update) | flash[:notice] = l(:notice_successful_update) | ||||
redirect_to_referer_or workflows_edit_path | |||||
return | |||||
end | |||||
if @trackers && @roles && @statuses.any? | |||||
workflows = WorkflowTransition. | |||||
where(:role_id => @roles.map(&:id), :tracker_id => @trackers.map(&:id)). | |||||
preload(:old_status, :new_status) | |||||
@workflows = {} | |||||
@workflows['always'] = workflows.select {|w| !w.author && !w.assignee} | |||||
@workflows['author'] = workflows.select {|w| w.author} | |||||
@workflows['assignee'] = workflows.select {|w| w.assignee} | |||||
end | end | ||||
redirect_to_referer_or edit_workflows_path | |||||
end | end | ||||
def permissions | def permissions | ||||
find_trackers_roles_and_statuses_for_edit | |||||
if @roles && @trackers | |||||
@fields = (Tracker::CORE_FIELDS_ALL - @trackers.map(&:disabled_core_fields).reduce(:&)).map {|field| [field, l("field_"+field.sub(/_id$/, ''))]} | |||||
@custom_fields = @trackers.map(&:custom_fields).flatten.uniq.sort | |||||
@permissions = WorkflowPermission.rules_by_status_id(@trackers, @roles) | |||||
@statuses.each {|status| @permissions[status.id] ||= {}} | |||||
end | |||||
end | |||||
if request.post? && @roles && @trackers && params[:permissions] | |||||
def update_permissions | |||||
if @roles && @trackers && params[:permissions] | |||||
permissions = params[:permissions].deep_dup | permissions = params[:permissions].deep_dup | ||||
permissions.each do |field, rule_by_status_id| | permissions.each do |field, rule_by_status_id| | ||||
rule_by_status_id.reject! {|status_id, rule| rule == 'no_change'} | rule_by_status_id.reject! {|status_id, rule| rule == 'no_change'} | ||||
end | end | ||||
WorkflowPermission.replace_permissions(@trackers, @roles, permissions) | WorkflowPermission.replace_permissions(@trackers, @roles, permissions) | ||||
flash[:notice] = l(:notice_successful_update) | flash[:notice] = l(:notice_successful_update) | ||||
redirect_to_referer_or workflows_permissions_path | |||||
return | |||||
end | end | ||||
redirect_to_referer_or permissions_workflows_path | |||||
end | |||||
if @roles && @trackers | |||||
@fields = (Tracker::CORE_FIELDS_ALL - @trackers.map(&:disabled_core_fields).reduce(:&)).map {|field| [field, l("field_"+field.sub(/_id$/, ''))]} | |||||
@custom_fields = @trackers.map(&:custom_fields).flatten.uniq.sort | |||||
@permissions = WorkflowPermission.rules_by_status_id(@trackers, @roles) | |||||
@statuses.each {|status| @permissions[status.id] ||= {}} | |||||
def copy | |||||
find_sources_and_targets | |||||
end | |||||
def duplicate | |||||
find_sources_and_targets | |||||
if params[:source_tracker_id].blank? || params[:source_role_id].blank? || | |||||
(@source_tracker.nil? && @source_role.nil?) | |||||
flash.now[:error] = l(:error_workflow_copy_source) | |||||
render :copy | |||||
elsif @target_trackers.blank? || @target_roles.blank? | |||||
flash.now[:error] = l(:error_workflow_copy_target) | |||||
render :copy | |||||
else | |||||
WorkflowRule.copy(@source_tracker, @source_role, @target_trackers, @target_roles) | |||||
flash[:notice] = l(:notice_successful_update) | |||||
redirect_to copy_workflows_path( | |||||
:source_tracker_id => @source_tracker, | |||||
:source_role_id => @source_role | |||||
) | |||||
end | end | ||||
end | end | ||||
def copy | |||||
private | |||||
def find_sources_and_targets | |||||
@roles = Role.sorted.select(&:consider_workflow?) | @roles = Role.sorted.select(&:consider_workflow?) | ||||
@trackers = Tracker.sorted | @trackers = Tracker.sorted | ||||
if params[:source_tracker_id].blank? || params[:source_tracker_id] == 'any' | if params[:source_tracker_id].blank? || params[:source_tracker_id] == 'any' | ||||
@source_tracker = nil | @source_tracker = nil | ||||
else | else | ||||
else | else | ||||
Role.where(:id => params[:target_role_ids]).to_a | Role.where(:id => params[:target_role_ids]).to_a | ||||
end | end | ||||
if request.post? | |||||
if params[:source_tracker_id].blank? || params[:source_role_id].blank? || | |||||
(@source_tracker.nil? && @source_role.nil?) | |||||
flash.now[:error] = l(:error_workflow_copy_source) | |||||
elsif @target_trackers.blank? || @target_roles.blank? | |||||
flash.now[:error] = l(:error_workflow_copy_target) | |||||
else | |||||
WorkflowRule.copy(@source_tracker, @source_role, @target_trackers, @target_roles) | |||||
flash[:notice] = l(:notice_successful_update) | |||||
redirect_to( | |||||
workflows_copy_path(:source_tracker_id => @source_tracker, | |||||
:source_role_id => @source_role) | |||||
) | |||||
end | |||||
end | |||||
end | end | ||||
private | |||||
def find_trackers_roles_and_statuses_for_edit | def find_trackers_roles_and_statuses_for_edit | ||||
find_roles | find_roles | ||||
find_trackers | find_trackers |
<td> | <td> | ||||
<% unless WorkflowTransition.where('old_status_id = ? OR new_status_id = ?', status.id, status.id).exists? %> | <% unless WorkflowTransition.where('old_status_id = ? OR new_status_id = ?', status.id, status.id).exists? %> | ||||
<span class="icon icon-warning"> | <span class="icon icon-warning"> | ||||
<%= l(:text_status_no_workflow) %> (<%= link_to l(:button_edit), workflows_edit_path(:used_statuses_only => 0) %>) | |||||
<%= l(:text_status_no_workflow) %> (<%= link_to l(:button_edit), edit_workflows_path(:used_statuses_only => 0) %>) | |||||
</span> | </span> | ||||
<% end %> | <% end %> | ||||
</td> | </td> |
<td> | <td> | ||||
<% unless role.builtin? || role.workflow_rules.exists? %> | <% unless role.builtin? || role.workflow_rules.exists? %> | ||||
<span class="icon icon-warning"> | <span class="icon icon-warning"> | ||||
<%= l(:text_role_no_workflow) %> (<%= link_to l(:button_edit), workflows_edit_path(:role_id => role) %>) | |||||
<%= l(:text_role_no_workflow) %> (<%= link_to l(:button_edit), edit_workflows_path(:role_id => role) %>) | |||||
</span> | </span> | ||||
<% end %> | <% end %> | ||||
</td> | </td> | ||||
<%= javascript_tag do %> | <%= javascript_tag do %> | ||||
$(function() { $("table.roles tbody").positionedItems({items: ".givable"}); }); | $(function() { $("table.roles tbody").positionedItems({items: ".givable"}); }); | ||||
<% end %> | |||||
<% end %> |
<td> | <td> | ||||
<% unless tracker.workflow_rules.exists? %> | <% unless tracker.workflow_rules.exists? %> | ||||
<span class="icon icon-warning"> | <span class="icon icon-warning"> | ||||
<%= l(:text_tracker_no_workflow) %> (<%= link_to l(:button_edit), workflows_edit_path(:tracker_id => tracker) %>) | |||||
<%= l(:text_tracker_no_workflow) %> (<%= link_to l(:button_edit), edit_workflows_path(:tracker_id => tracker) %>) | |||||
</span> | </span> | ||||
<% end %> | <% end %> | ||||
</td> | </td> |
<%= title [l(:label_workflow), workflows_edit_path], l(:button_copy) %> | |||||
<%= title [l(:label_workflow), edit_workflows_path], l(:button_copy) %> | |||||
<%= form_tag({}, :id => 'workflow_copy_form') do %> | |||||
<%= form_tag duplicate_workflows_path, method: :post, id: 'workflow_copy_form' do %> | |||||
<fieldset class="tabular box"> | <fieldset class="tabular box"> | ||||
<legend><%= l(:label_copy_source) %></legend> | <legend><%= l(:label_copy_source) %></legend> | ||||
<p> | <p> |
<div class="tabs"> | <div class="tabs"> | ||||
<ul> | <ul> | ||||
<li><%= link_to l(:label_status_transitions), workflows_edit_path(:role_id => @roles, :tracker_id => @trackers), :class => 'selected' %></li> | |||||
<li><%= link_to l(:label_fields_permissions), workflows_permissions_path(:role_id => @roles, :tracker_id => @trackers) %></li> | |||||
<li><%= link_to l(:label_status_transitions), edit_workflows_path(:role_id => @roles, :tracker_id => @trackers), :class => 'selected' %></li> | |||||
<li><%= link_to l(:label_fields_permissions), permissions_workflows_path(:role_id => @roles, :tracker_id => @trackers) %></li> | |||||
</ul> | </ul> | ||||
</div> | </div> | ||||
<% end %> | <% end %> | ||||
<% if @trackers && @roles && @statuses.any? %> | <% if @trackers && @roles && @statuses.any? %> | ||||
<%= form_tag({}, :id => 'workflow_form' ) do %> | |||||
<%= form_tag workflows_path, method: :patch, id: 'workflow_form' do %> | |||||
<%= @trackers.map {|tracker| hidden_field_tag 'tracker_id[]', tracker.id, :id => nil}.join.html_safe %> | <%= @trackers.map {|tracker| hidden_field_tag 'tracker_id[]', tracker.id, :id => nil}.join.html_safe %> | ||||
<%= @roles.map {|role| hidden_field_tag 'role_id[]', role.id, :id => nil}.join.html_safe %> | <%= @roles.map {|role| hidden_field_tag 'role_id[]', role.id, :id => nil}.join.html_safe %> | ||||
<%= hidden_field_tag 'used_statuses_only', params[:used_statuses_only], :id => nil %> | <%= hidden_field_tag 'used_statuses_only', params[:used_statuses_only], :id => nil %> |
<%= title [l(:label_workflow), workflows_edit_path], l(:field_summary) %> | |||||
<%= title [l(:label_workflow), edit_workflows_path], l(:field_summary) %> | |||||
<% if @roles.empty? || @trackers.empty? %> | <% if @roles.empty? || @trackers.empty? %> | ||||
<p class="nodata"><%= l(:label_no_data) %></p> | <p class="nodata"><%= l(:label_no_data) %></p> |
<div class="tabs"> | <div class="tabs"> | ||||
<ul> | <ul> | ||||
<li><%= link_to l(:label_status_transitions), workflows_edit_path(:role_id => @roles, :tracker_id => @trackers) %></li> | |||||
<li><%= link_to l(:label_fields_permissions), workflows_permissions_path(:role_id => @roles, :tracker_id => @trackers), :class => 'selected' %></li> | |||||
<li><%= link_to l(:label_status_transitions), edit_workflows_path(:role_id => @roles, :tracker_id => @trackers) %></li> | |||||
<li><%= link_to l(:label_fields_permissions), permissions_workflows_path(:role_id => @roles, :tracker_id => @trackers), :class => 'selected' %></li> | |||||
</ul> | </ul> | ||||
</div> | </div> | ||||
<% end %> | <% end %> | ||||
<% if @trackers && @roles && @statuses.any? %> | <% if @trackers && @roles && @statuses.any? %> | ||||
<%= form_tag({}, :id => 'workflow_form' ) do %> | |||||
<%= form_tag update_permissions_workflows_path, method: :patch, id: 'workflow_form' do %> | |||||
<%= @trackers.map {|tracker| hidden_field_tag 'tracker_id[]', tracker.id, :id => nil}.join.html_safe %> | <%= @trackers.map {|tracker| hidden_field_tag 'tracker_id[]', tracker.id, :id => nil}.join.html_safe %> | ||||
<%= @roles.map {|role| hidden_field_tag 'role_id[]', role.id, :id => nil}.join.html_safe %> | <%= @roles.map {|role| hidden_field_tag 'role_id[]', role.id, :id => nil}.join.html_safe %> | ||||
<%= hidden_field_tag 'used_statuses_only', params[:used_statuses_only], :id => nil %> | <%= hidden_field_tag 'used_statuses_only', params[:used_statuses_only], :id => nil %> |
end | end | ||||
end | end | ||||
match 'workflows', :controller => 'workflows', :action => 'index', :via => :get | |||||
match 'workflows/edit', :controller => 'workflows', :action => 'edit', :via => [:get, :post] | |||||
match 'workflows/permissions', :controller => 'workflows', :action => 'permissions', :via => [:get, :post] | |||||
match 'workflows/copy', :controller => 'workflows', :action => 'copy', :via => [:get, :post] | |||||
resources :workflows, only: [:index] do | |||||
collection do | |||||
get 'edit' | |||||
patch 'update' | |||||
get 'permissions' | |||||
patch 'update_permissions' | |||||
get 'copy' | |||||
post 'duplicate' | |||||
end | |||||
end | |||||
match 'settings', :controller => 'settings', :action => 'index', :via => :get | match 'settings', :controller => 'settings', :action => 'index', :via => :get | ||||
match 'settings/edit', :controller => 'settings', :action => 'edit', :via => [:get, :post] | match 'settings/edit', :controller => 'settings', :action => 'edit', :via => [:get, :post] | ||||
match 'settings/plugin/:id', :controller => 'settings', :action => 'plugin', :via => [:get, :post], :as => 'plugin_settings' | match 'settings/plugin/:id', :controller => 'settings', :action => 'plugin', :via => [:get, :post], :as => 'plugin_settings' |
def test_post_edit | def test_post_edit | ||||
WorkflowTransition.delete_all | WorkflowTransition.delete_all | ||||
post :edit, :params => { | |||||
patch :update, :params => { | |||||
:role_id => 2, | :role_id => 2, | ||||
:tracker_id => 1, | :tracker_id => 1, | ||||
:transitions => { | :transitions => { | ||||
def test_post_edit_with_allowed_statuses_for_new_issues | def test_post_edit_with_allowed_statuses_for_new_issues | ||||
WorkflowTransition.delete_all | WorkflowTransition.delete_all | ||||
post :edit, :params => { | |||||
patch :update, :params => { | |||||
:role_id => 2, | :role_id => 2, | ||||
:tracker_id => 1, | :tracker_id => 1, | ||||
:transitions => { | :transitions => { | ||||
def test_post_edit_with_additional_transitions | def test_post_edit_with_additional_transitions | ||||
WorkflowTransition.delete_all | WorkflowTransition.delete_all | ||||
post :edit, :params => { | |||||
patch :update, :params => { | |||||
:role_id => 2, | :role_id => 2, | ||||
:tracker_id => 1, | :tracker_id => 1, | ||||
:transitions => { | :transitions => { | ||||
def test_post_permissions | def test_post_permissions | ||||
WorkflowPermission.delete_all | WorkflowPermission.delete_all | ||||
post :permissions, :params => { | |||||
patch :update_permissions, :params => { | |||||
:role_id => 1, | :role_id => 1, | ||||
:tracker_id => 2, | :tracker_id => 2, | ||||
:permissions => { | :permissions => { | ||||
def test_post_copy_one_to_one | def test_post_copy_one_to_one | ||||
source_transitions = status_transitions(:tracker_id => 1, :role_id => 2) | source_transitions = status_transitions(:tracker_id => 1, :role_id => 2) | ||||
post :copy, :params => { | |||||
post :duplicate, :params => { | |||||
:source_tracker_id => '1', :source_role_id => '2', | :source_tracker_id => '1', :source_role_id => '2', | ||||
:target_tracker_ids => ['3'], :target_role_ids => ['1'] | :target_tracker_ids => ['3'], :target_role_ids => ['1'] | ||||
} | } | ||||
def test_post_copy_one_to_many | def test_post_copy_one_to_many | ||||
source_transitions = status_transitions(:tracker_id => 1, :role_id => 2) | source_transitions = status_transitions(:tracker_id => 1, :role_id => 2) | ||||
post :copy, :params => { | |||||
post :duplicate, :params => { | |||||
:source_tracker_id => '1', :source_role_id => '2', | :source_tracker_id => '1', :source_role_id => '2', | ||||
:target_tracker_ids => ['2', '3'], :target_role_ids => ['1', '3'] | :target_tracker_ids => ['2', '3'], :target_role_ids => ['1', '3'] | ||||
} | } | ||||
source_t2 = status_transitions(:tracker_id => 2, :role_id => 2) | source_t2 = status_transitions(:tracker_id => 2, :role_id => 2) | ||||
source_t3 = status_transitions(:tracker_id => 3, :role_id => 2) | source_t3 = status_transitions(:tracker_id => 3, :role_id => 2) | ||||
post :copy, :params => { | |||||
post :duplicate, :params => { | |||||
:source_tracker_id => 'any', :source_role_id => '2', | :source_tracker_id => 'any', :source_role_id => '2', | ||||
:target_tracker_ids => ['2', '3'], :target_role_ids => ['1', '3'] | :target_tracker_ids => ['2', '3'], :target_role_ids => ['1', '3'] | ||||
} | } | ||||
def test_post_copy_with_incomplete_source_specification_should_fail | def test_post_copy_with_incomplete_source_specification_should_fail | ||||
assert_no_difference 'WorkflowRule.count' do | assert_no_difference 'WorkflowRule.count' do | ||||
post :copy, :params => { | |||||
post :duplicate, :params => { | |||||
:source_tracker_id => '', :source_role_id => '2', | :source_tracker_id => '', :source_role_id => '2', | ||||
:target_tracker_ids => ['2', '3'], :target_role_ids => ['1', '3'] | :target_tracker_ids => ['2', '3'], :target_role_ids => ['1', '3'] | ||||
} | } | ||||
def test_post_copy_with_incomplete_target_specification_should_fail | def test_post_copy_with_incomplete_target_specification_should_fail | ||||
assert_no_difference 'WorkflowRule.count' do | assert_no_difference 'WorkflowRule.count' do | ||||
post :copy, :params => { | |||||
post :duplicate, :params => { | |||||
:source_tracker_id => '1', :source_role_id => '2', | :source_tracker_id => '1', :source_role_id => '2', | ||||
:target_tracker_ids => ['2', '3'] | :target_tracker_ids => ['2', '3'] | ||||
} | } |
def test_workflows | def test_workflows | ||||
should_route 'GET /workflows' => 'workflows#index' | should_route 'GET /workflows' => 'workflows#index' | ||||
should_route 'GET /workflows/edit' => 'workflows#edit' | should_route 'GET /workflows/edit' => 'workflows#edit' | ||||
should_route 'POST /workflows/edit' => 'workflows#edit' | |||||
should_route 'PATCH /workflows/update' => 'workflows#update' | |||||
should_route 'GET /workflows/permissions' => 'workflows#permissions' | should_route 'GET /workflows/permissions' => 'workflows#permissions' | ||||
should_route 'POST /workflows/permissions' => 'workflows#permissions' | |||||
should_route 'PATCH /workflows/update_permissions' => 'workflows#update_permissions' | |||||
should_route 'GET /workflows/copy' => 'workflows#copy' | should_route 'GET /workflows/copy' => 'workflows#copy' | ||||
should_route 'POST /workflows/copy' => 'workflows#copy' | |||||
should_route 'POST /workflows/duplicate' => 'workflows#duplicate' | |||||
end | end | ||||
end | end |