##// END OF EJS Templates
Adds workflow copy functionality (#1727)....
Jean-Philippe Lang -
r3040:5c6ce51ec9f1
parent child
Show More
@@ -0,0 +1,5
1 <div class="contextual">
2 <%= link_to l(:button_edit), {:action => 'edit'}, :class => 'icon icon-edit' %>
3 <%= link_to l(:button_copy), {:action => 'copy'}, :class => 'icon icon-copy' %>
4 <%= link_to l(:field_summary), {:action => 'index'}, :class => 'icon icon-summary' %>
5 </div>
@@ -0,0 +1,33
1 <%= render :partial => 'action_menu' %>
2
3 <h2><%=l(:label_workflow)%></h2>
4
5 <% form_tag({}, :id => 'workflow_copy_form') do %>
6 <div class="tabular box">
7 <p>
8 <label><%= l(:label_copy_source) %></label>
9 <%= l(:label_tracker) %><br />
10 <%= select_tag('source_tracker_id',
11 "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>" +
12 "<option value=\"any\">--- #{ l(:label_copy_same_as_target) } ---</option>" +
13 options_from_collection_for_select(@trackers, 'id', 'name', @source_tracker && @source_tracker.id)) %><br />
14 <%= l(:label_role) %><br />
15 <%= select_tag('source_role_id',
16 "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>" +
17 "<option value=\"any\">--- #{ l(:label_copy_same_as_target) } ---</option>" +
18 options_from_collection_for_select(@roles, 'id', 'name', @source_role && @source_role.id)) %>
19 </p>
20 <p>
21 <label><%= l(:label_copy_target) %></label>
22 <%= l(:label_tracker) %><br />
23 <%= select_tag 'target_tracker_ids',
24 "<option value=\"\" disabled=\"disabled\">--- #{l(:actionview_instancetag_blank_option)} ---</option>" +
25 options_from_collection_for_select(@trackers, 'id', 'name', @target_trackers && @target_trackers.map(&:id)), :multiple => true %><br />
26 <%= l(:label_role) %><br />
27 <%= select_tag 'target_role_ids',
28 "<option value=\"\" disabled=\"disabled\">--- #{l(:actionview_instancetag_blank_option)} ---</option>" +
29 options_from_collection_for_select(@roles, 'id', 'name', @target_roles && @target_roles.map(&:id)), :multiple => true %>
30 </p>
31 </div>
32 <%= submit_tag l(:button_copy) %>
33 <% end %>
1 NO CONTENT: new file 100644, binary diff hidden
@@ -42,4 +42,27 class WorkflowsController < ApplicationController
42 42 @trackers = Tracker.find(:all, :order => 'position')
43 43 @statuses = IssueStatus.find(:all, :order => 'position')
44 44 end
45
46 def copy
47 @trackers = Tracker.find(:all, :order => 'position')
48 @roles = Role.find(:all, :order => 'builtin, position')
49
50 @source_tracker = params[:source_tracker_id].blank? ? nil : Tracker.find_by_id(params[:source_tracker_id])
51 @source_role = params[:source_role_id].blank? ? nil : Role.find_by_id(params[:source_role_id])
52
53 @target_trackers = params[:target_tracker_ids].blank? ? nil : Tracker.find_all_by_id(params[:target_tracker_ids])
54 @target_roles = params[:target_role_ids].blank? ? nil : Role.find_all_by_id(params[:target_role_ids])
55
56 if request.post?
57 if params[:source_tracker_id].blank? || params[:source_role_id].blank? || (@source_tracker.nil? && @source_role.nil?)
58 flash.now[:error] = l(:error_workflow_copy_source)
59 elsif @target_trackers.nil? || @target_roles.nil?
60 flash.now[:error] = l(:error_workflow_copy_target)
61 else
62 Workflow.copy(@source_tracker, @source_role, @target_trackers, @target_roles)
63 flash[:notice] = l(:notice_successful_update)
64 redirect_to :action => 'copy', :source_tracker_id => @source_tracker, :source_role_id => @source_role
65 end
66 end
67 end
45 68 end
@@ -28,14 +28,8 class Role < ActiveRecord::Base
28 28
29 29 before_destroy :check_deletable
30 30 has_many :workflows, :dependent => :delete_all do
31 def copy(role)
32 raise "Can not copy workflow from a #{role.class}" unless role.is_a?(Role)
33 raise "Can not copy workflow from/to an unsaved role" if proxy_owner.new_record? || role.new_record?
34 clear
35 connection.insert "INSERT INTO #{Workflow.table_name} (tracker_id, old_status_id, new_status_id, role_id)" +
36 " SELECT tracker_id, old_status_id, new_status_id, #{proxy_owner.id}" +
37 " FROM #{Workflow.table_name}" +
38 " WHERE role_id = #{role.id}"
31 def copy(source_role)
32 Workflow.copy(nil, source_role, nil, proxy_owner)
39 33 end
40 34 end
41 35
@@ -19,14 +19,8 class Tracker < ActiveRecord::Base
19 19 before_destroy :check_integrity
20 20 has_many :issues
21 21 has_many :workflows, :dependent => :delete_all do
22 def copy(tracker)
23 raise "Can not copy workflow from a #{tracker.class}" unless tracker.is_a?(Tracker)
24 raise "Can not copy workflow from/to an unsaved tracker" if proxy_owner.new_record? || tracker.new_record?
25 clear
26 connection.insert "INSERT INTO #{Workflow.table_name} (tracker_id, old_status_id, new_status_id, role_id)" +
27 " SELECT #{proxy_owner.id}, old_status_id, new_status_id, role_id" +
28 " FROM #{Workflow.table_name}" +
29 " WHERE tracker_id = #{tracker.id}"
22 def copy(source_tracker)
23 Workflow.copy(source_tracker, nil, proxy_owner, nil)
30 24 end
31 25 end
32 26
@@ -51,4 +51,50 class Workflow < ActiveRecord::Base
51 51 uniq.
52 52 sort
53 53 end
54
55 # Copies workflows from source to targets
56 def self.copy(source_tracker, source_role, target_trackers, target_roles)
57 unless source_tracker.is_a?(Tracker) || source_role.is_a?(Role)
58 raise ArgumentError.new "source_tracker or source_role must be specified"
59 end
60
61 target_trackers = [target_trackers].flatten.compact
62 target_roles = [target_roles].flatten.compact
63
64 target_trackers = Tracker.all if target_trackers.empty?
65 target_roles = Role.all if target_roles.empty?
66
67 target_trackers.each do |target_tracker|
68 target_roles.each do |target_role|
69 copy_one(source_tracker || target_tracker,
70 source_role || target_role,
71 target_tracker,
72 target_role)
73 end
74 end
75 end
76
77 # Copies a single set of workflows from source to target
78 def self.copy_one(source_tracker, source_role, target_tracker, target_role)
79 unless source_tracker.is_a?(Tracker) && !source_tracker.new_record? &&
80 source_role.is_a?(Role) && !source_role.new_record? &&
81 target_tracker.is_a?(Tracker) && !target_tracker.new_record? &&
82 target_role.is_a?(Role) && !target_role.new_record?
83
84 raise ArgumentError.new("arguments can not be nil or unsaved objects")
85 end
86
87 if source_tracker == target_tracker && source_role == target_role
88 false
89 else
90 transaction do
91 delete_all :tracker_id => target_tracker.id, :role_id => target_role.id
92 connection.insert "INSERT INTO #{Workflow.table_name} (tracker_id, role_id, old_status_id, new_status_id)" +
93 " SELECT #{target_tracker.id}, #{target_role.id}, old_status_id, new_status_id" +
94 " FROM #{Workflow.table_name}" +
95 " WHERE tracker_id = #{source_tracker.id} AND role_id = #{source_role.id}"
96 end
97 true
98 end
99 end
54 100 end
@@ -1,6 +1,4
1 <div class="contextual">
2 <%= link_to l(:field_summary), :action => 'index' %>
3 </div>
1 <%= render :partial => 'action_menu' %>
4 2
5 3 <h2><%=l(:label_workflow)%></h2>
6 4
@@ -1,3 +1,5
1 <%= render :partial => 'action_menu' %>
2
1 3 <h2><%=l(:label_workflow)%></h2>
2 4
3 5 <% if @workflow_counts.empty? %>
@@ -159,6 +159,8 en:
159 159 error_can_not_reopen_issue_on_closed_version: 'An issue assigned to a closed version can not be reopened'
160 160 error_can_not_archive_project: This project can not be archived
161 161 error_issue_done_ratios_not_updated: "Issue done ratios not updated."
162 error_workflow_copy_source: 'Please select a source tracker or role'
163 error_workflow_copy_target: 'Please select target tracker(s) and role(s)'
162 164
163 165 warning_attachments_not_saved: "{{count}} file(s) could not be saved."
164 166
@@ -720,6 +722,9 en:
720 722 label_version_sharing_tree: With project tree
721 723 label_version_sharing_system: With all projects
722 724 label_update_issue_done_ratios: Update issue done ratios
725 label_copy_source: Source
726 label_copy_target: Target
727 label_copy_same_as_target: Same as target
723 728
724 729 button_login: Login
725 730 button_submit: Submit
@@ -179,6 +179,8 fr:
179 179 error_issue_not_found_in_project: "La demande n'existe pas ou n'appartient pas Γ  ce projet"
180 180 error_can_not_reopen_issue_on_closed_version: 'Une demande assignΓ©e Γ  une version fermΓ©e ne peut pas Γͺtre rΓ©ouverte'
181 181 error_can_not_archive_project: "Ce projet ne peut pas Γͺtre archivΓ©"
182 error_workflow_copy_source: 'Veuillez sΓ©lectionner un tracker et/ou un rΓ΄le source'
183 error_workflow_copy_target: 'Veuillez sΓ©lectionner les trackers et rΓ΄les cibles'
182 184
183 185 warning_attachments_not_saved: "{{count}} fichier(s) n'ont pas pu Γͺtre sauvegardΓ©s."
184 186
@@ -732,6 +734,9 fr:
732 734 label_version_sharing_hierarchy: Avec toute la hiΓ©rarchie
733 735 label_version_sharing_tree: Avec tout l'arbre
734 736 label_version_sharing_system: Avec tous les projets
737 label_copy_source: Source
738 label_copy_target: Cible
739 label_copy_same_as_target: Comme la cible
735 740
736 741 button_login: Connexion
737 742 button_submit: Soumettre
@@ -297,6 +297,8 ul.properties li span {font-style:italic;}
297 297 .autoscroll {overflow-x: auto; padding:1px; margin-bottom: 1.2em;}
298 298 #user_firstname, #user_lastname, #user_mail, #my_account_form select { width: 90%; }
299 299
300 #workflow_copy_form select { width: 200px; }
301
300 302 .pagination {font-size: 90%}
301 303 p.pagination {margin-top:8px;}
302 304
@@ -728,6 +730,7 vertical-align: middle;
728 730 .icon-details { background-image: url(../images/zoom_in.png); }
729 731 .icon-report { background-image: url(../images/report.png); }
730 732 .icon-comment { background-image: url(../images/comment.png); }
733 .icon-summary { background-image: url(../images/lightning.png); }
731 734
732 735 .icon-file { background-image: url(../images/files/default.png); }
733 736 .icon-file.text-plain { background-image: url(../images/files/text.png); }
@@ -22,7 +22,7 require 'workflows_controller'
22 22 class WorkflowsController; def rescue_action(e) raise e end; end
23 23
24 24 class WorkflowsControllerTest < ActionController::TestCase
25 fixtures :roles, :trackers, :workflows
25 fixtures :roles, :trackers, :workflows, :users
26 26
27 27 def setup
28 28 @controller = WorkflowsController.new
@@ -81,4 +81,50 class WorkflowsControllerTest < ActionController::TestCase
81 81 post :edit, :role_id => 2, :tracker_id => 1
82 82 assert_equal 0, Workflow.count(:conditions => {:tracker_id => 1, :role_id => 2})
83 83 end
84
85 def test_get_copy
86 get :copy
87 assert_response :success
88 assert_template 'copy'
89 end
90
91 def test_post_copy_one_to_one
92 source_transitions = status_transitions(:tracker_id => 1, :role_id => 2)
93
94 post :copy, :source_tracker_id => '1', :source_role_id => '2',
95 :target_tracker_ids => ['3'], :target_role_ids => ['1']
96 assert_response 302
97 assert_equal source_transitions, status_transitions(:tracker_id => 3, :role_id => 1)
98 end
99
100 def test_post_copy_one_to_many
101 source_transitions = status_transitions(:tracker_id => 1, :role_id => 2)
102
103 post :copy, :source_tracker_id => '1', :source_role_id => '2',
104 :target_tracker_ids => ['2', '3'], :target_role_ids => ['1', '3']
105 assert_response 302
106 assert_equal source_transitions, status_transitions(:tracker_id => 2, :role_id => 1)
107 assert_equal source_transitions, status_transitions(:tracker_id => 3, :role_id => 1)
108 assert_equal source_transitions, status_transitions(:tracker_id => 2, :role_id => 3)
109 assert_equal source_transitions, status_transitions(:tracker_id => 3, :role_id => 3)
110 end
111
112 def test_post_copy_many_to_many
113 source_t2 = status_transitions(:tracker_id => 2, :role_id => 2)
114 source_t3 = status_transitions(:tracker_id => 3, :role_id => 2)
115
116 post :copy, :source_tracker_id => 'any', :source_role_id => '2',
117 :target_tracker_ids => ['2', '3'], :target_role_ids => ['1', '3']
118 assert_response 302
119 assert_equal source_t2, status_transitions(:tracker_id => 2, :role_id => 1)
120 assert_equal source_t3, status_transitions(:tracker_id => 3, :role_id => 1)
121 assert_equal source_t2, status_transitions(:tracker_id => 2, :role_id => 3)
122 assert_equal source_t3, status_transitions(:tracker_id => 3, :role_id => 3)
123 end
124
125 # Returns an array of status transitions that can be compared
126 def status_transitions(conditions)
127 Workflow.find(:all, :conditions => conditions,
128 :order => 'tracker_id, role_id, old_status_id, new_status_id').collect {|w| [w.old_status, w.new_status_id]}
129 end
84 130 end
General Comments 0
You need to be logged in to leave comments. Login now