@@ -0,0 +1,23 | |||
|
1 | class InsertAllowedStatusesForNewIssues < ActiveRecord::Migration | |
|
2 | def self.up | |
|
3 | # Adds the default status for all trackers and roles | |
|
4 | sql = "INSERT INTO #{WorkflowTransition.table_name} (tracker_id, old_status_id, new_status_id, role_id, type)" + | |
|
5 | " SELECT t.id, 0, t.default_status_id, r.id, 'WorkflowTransition'" + | |
|
6 | " FROM #{Tracker.table_name} t, #{Role.table_name} r" | |
|
7 | WorkflowTransition.connection.execute(sql) | |
|
8 | ||
|
9 | # Adds other statuses that are reachable with one transition | |
|
10 | # to preserve previous behaviour as default | |
|
11 | sql = "INSERT INTO #{WorkflowTransition.table_name} (tracker_id, old_status_id, new_status_id, role_id, type)" + | |
|
12 | " SELECT t.id, 0, w.new_status_id, w.role_id, 'WorkflowTransition'" + | |
|
13 | " FROM #{Tracker.table_name} t" + | |
|
14 | " JOIN #{IssueStatus.table_name} s on s.id = t.default_status_id" + | |
|
15 | " JOIN #{WorkflowTransition.table_name} w on w.tracker_id = t.id and w.old_status_id = s.id and w.type = 'WorkflowTransition'" + | |
|
16 | " WHERE w.new_status_id <> t.default_status_id" | |
|
17 | WorkflowTransition.connection.execute(sql) | |
|
18 | end | |
|
19 | ||
|
20 | def self.down | |
|
21 | WorkflowTransition.where(:old_status_id => 0).delete_all | |
|
22 | end | |
|
23 | end |
@@ -427,12 +427,12 class IssuesController < ApplicationController | |||
|
427 | 427 | @issue.author ||= User.current |
|
428 | 428 | @issue.start_date ||= Date.today if Setting.default_issue_start_date_to_creation_date? |
|
429 | 429 | |
|
430 |
|
|
|
431 |
|
|
|
432 |
|
|
|
433 | end | |
|
434 | @issue.safe_attributes = attrs | |
|
430 | attrs = (params[:issue] || {}).deep_dup | |
|
431 | if action_name == 'new' && params[:was_default_status] == attrs[:status_id] | |
|
432 | attrs.delete(:status_id) | |
|
435 | 433 | end |
|
434 | @issue.safe_attributes = attrs | |
|
435 | ||
|
436 | 436 | if @issue.project |
|
437 | 437 | @issue.tracker ||= @issue.project.trackers.first |
|
438 | 438 | if @issue.tracker.nil? |
@@ -446,7 +446,7 class IssuesController < ApplicationController | |||
|
446 | 446 | end |
|
447 | 447 | |
|
448 | 448 | @priorities = IssuePriority.active |
|
449 |
@allowed_statuses = @issue.new_statuses_allowed_to(User.current |
|
|
449 | @allowed_statuses = @issue.new_statuses_allowed_to(User.current) | |
|
450 | 450 | end |
|
451 | 451 | |
|
452 | 452 | def parse_params_for_bulk_issue_attributes(params) |
@@ -43,7 +43,9 class WorkflowsController < ApplicationController | |||
|
43 | 43 | end |
|
44 | 44 | |
|
45 | 45 | if @trackers && @roles && @statuses.any? |
|
46 | workflows = WorkflowTransition.where(:role_id => @roles.map(&:id), :tracker_id => @trackers.map(&:id)) | |
|
46 | workflows = WorkflowTransition. | |
|
47 | where(:role_id => @roles.map(&:id), :tracker_id => @trackers.map(&:id)). | |
|
48 | preload(:old_status, :new_status) | |
|
47 | 49 | @workflows = {} |
|
48 | 50 | @workflows['always'] = workflows.select {|w| !w.author && !w.assignee} |
|
49 | 51 | @workflows['author'] = workflows.select {|w| w.author} |
@@ -75,14 +75,14 module WorkflowsHelper | |||
|
75 | 75 | end |
|
76 | 76 | |
|
77 | 77 | def transition_tag(workflows, old_status, new_status, name) |
|
78 |
w = workflows.select {|w| w.old_status |
|
|
78 | w = workflows.select {|w| w.old_status == old_status && w.new_status == new_status}.size | |
|
79 | 79 | |
|
80 | tag_name = "transitions[#{ old_status.id }][#{new_status.id}][#{name}]" | |
|
80 | tag_name = "transitions[#{ old_status.try(:id) || 0 }][#{new_status.id}][#{name}]" | |
|
81 | 81 | if w == 0 || w == @roles.size * @trackers.size |
|
82 | 82 | |
|
83 | 83 | hidden_field_tag(tag_name, "0", :id => nil) + |
|
84 | 84 | check_box_tag(tag_name, "1", w != 0, |
|
85 | :class => "old-status-#{old_status.id} new-status-#{new_status.id}") | |
|
85 | :class => "old-status-#{old_status.try(:id) || 0} new-status-#{new_status.id}") | |
|
86 | 86 | else |
|
87 | 87 | select_tag tag_name, |
|
88 | 88 | options_for_select([ |
@@ -465,11 +465,15 class Issue < ActiveRecord::Base | |||
|
465 | 465 | self.tracker ||= project.trackers.first |
|
466 | 466 | end |
|
467 | 467 | |
|
468 | statuses_allowed = new_statuses_allowed_to(user) | |
|
468 | 469 | if (s = attrs.delete('status_id')) && safe_attribute?('status_id') |
|
469 |
if |
|
|
470 | if statuses_allowed.collect(&:id).include?(s.to_i) | |
|
470 | 471 | self.status_id = s |
|
471 | 472 | end |
|
472 | 473 | end |
|
474 | if new_record? && !statuses_allowed.include?(status) | |
|
475 | self.status = statuses_allowed.first || default_status | |
|
476 | end | |
|
473 | 477 | |
|
474 | 478 | attrs = delete_unsafe_attributes(attrs, user) |
|
475 | 479 | return if attrs.empty? |
@@ -825,7 +829,7 class Issue < ActiveRecord::Base | |||
|
825 | 829 | else |
|
826 | 830 | initial_status = nil |
|
827 | 831 | if new_record? |
|
828 | initial_status = default_status | |
|
832 | # nop | |
|
829 | 833 | elsif tracker_id_changed? |
|
830 | 834 | if Tracker.where(:id => tracker_id_was, :default_status_id => status_id_was).any? |
|
831 | 835 | initial_status = default_status |
@@ -843,16 +847,15 class Issue < ActiveRecord::Base | |||
|
843 | 847 | (user.id == initial_assigned_to_id || user.group_ids.include?(initial_assigned_to_id)) |
|
844 | 848 | |
|
845 | 849 | statuses = [] |
|
846 | if initial_status | |
|
847 | statuses += initial_status.find_new_statuses_allowed_to( | |
|
848 |
|
|
|
849 |
|
|
|
850 |
|
|
|
851 |
|
|
|
852 |
|
|
|
853 | end | |
|
850 | statuses += IssueStatus.new_statuses_allowed( | |
|
851 | initial_status, | |
|
852 | user.admin ? Role.all.to_a : user.roles_for_project(project), | |
|
853 | tracker, | |
|
854 | author == user, | |
|
855 | assignee_transitions_allowed | |
|
856 | ) | |
|
854 | 857 | statuses << initial_status unless statuses.empty? |
|
855 | statuses << default_status if include_default | |
|
858 | statuses << default_status if include_default || (new_record? && statuses.empty?) | |
|
856 | 859 | statuses = statuses.compact.uniq.sort |
|
857 | 860 | if blocked? |
|
858 | 861 | statuses.reject!(&:is_closed?) |
@@ -45,28 +45,18 class IssueStatus < ActiveRecord::Base | |||
|
45 | 45 | end |
|
46 | 46 | |
|
47 | 47 | # Returns an array of all statuses the given role can switch to |
|
48 | # Uses association cache when called more than one time | |
|
49 | 48 | def new_statuses_allowed_to(roles, tracker, author=false, assignee=false) |
|
50 | if roles && tracker | |
|
51 | role_ids = roles.collect(&:id) | |
|
52 | transitions = workflows.select do |w| | |
|
53 | role_ids.include?(w.role_id) && | |
|
54 | w.tracker_id == tracker.id && | |
|
55 | ((!w.author && !w.assignee) || (author && w.author) || (assignee && w.assignee)) | |
|
56 | end | |
|
57 | transitions.map(&:new_status).compact.sort | |
|
58 | else | |
|
59 | [] | |
|
60 | end | |
|
49 | self.class.new_statuses_allowed(self, roles, tracker, author, assignee) | |
|
61 | 50 | end |
|
51 | alias :find_new_statuses_allowed_to :new_statuses_allowed_to | |
|
62 | 52 | |
|
63 | # Same thing as above but uses a database query | |
|
64 | # More efficient than the previous method if called just once | |
|
65 | def find_new_statuses_allowed_to(roles, tracker, author=false, assignee=false) | |
|
53 | def self.new_statuses_allowed(status, roles, tracker, author=false, assignee=false) | |
|
66 | 54 | if roles.present? && tracker |
|
55 | status_id = status.try(:id) || 0 | |
|
56 | ||
|
67 | 57 | scope = IssueStatus. |
|
68 | 58 | joins(:workflow_transitions_as_new_status). |
|
69 | where(:workflows => {:old_status_id => id, :role_id => roles.map(&:id), :tracker_id => tracker.id}) | |
|
59 | where(:workflows => {:old_status_id => status_id, :role_id => roles.map(&:id), :tracker_id => tracker.id}) | |
|
70 | 60 | |
|
71 | 61 | unless author && assignee |
|
72 | 62 | if author || assignee |
@@ -17,6 +17,7 | |||
|
17 | 17 | |
|
18 | 18 | class WorkflowPermission < WorkflowRule |
|
19 | 19 | validates_inclusion_of :rule, :in => %w(readonly required) |
|
20 | validates_presence_of :old_status | |
|
20 | 21 | validate :validate_field_name |
|
21 | 22 | |
|
22 | 23 | # Returns the workflow permissions for the given trackers and roles |
@@ -23,7 +23,7 class WorkflowRule < ActiveRecord::Base | |||
|
23 | 23 | belongs_to :old_status, :class_name => 'IssueStatus' |
|
24 | 24 | belongs_to :new_status, :class_name => 'IssueStatus' |
|
25 | 25 | |
|
26 |
validates_presence_of :role, :tracker |
|
|
26 | validates_presence_of :role, :tracker | |
|
27 | 27 | attr_protected :id |
|
28 | 28 | |
|
29 | 29 | # Copies workflows from source to targets |
@@ -20,16 +20,17 | |||
|
20 | 20 | </tr> |
|
21 | 21 | </thead> |
|
22 | 22 | <tbody> |
|
23 | <% for old_status in @statuses %> | |
|
23 | <% for old_status in [nil] + @statuses %> | |
|
24 | <% next if old_status.nil? && name != 'always' %> | |
|
24 | 25 | <tr class="<%= cycle("odd", "even") %>"> |
|
25 | 26 | <td class="name"> |
|
26 | <%= link_to_function(image_tag('toggle_check.png'), "toggleCheckboxesBySelector('table.transitions-#{name} input.old-status-#{old_status.id}')", | |
|
27 | <%= link_to_function(image_tag('toggle_check.png'), "toggleCheckboxesBySelector('table.transitions-#{name} input.old-status-#{old_status.try(:id) || 0}')", | |
|
27 | 28 | :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}") %> |
|
28 | 29 | |
|
29 | <%= old_status.name %> | |
|
30 | <%= old_status ? old_status.name : content_tag('em', l(:label_issue_new)) %> | |
|
30 | 31 | </td> |
|
31 | 32 | <% for new_status in @statuses -%> |
|
32 |
<% checked = workflows.detect {|w| w.old_status |
|
|
33 | <% checked = workflows.detect {|w| w.old_status == old_status && w.new_status == new_status} %> | |
|
33 | 34 | <td class="<%= checked ? 'enabled' : '' %>"> |
|
34 | 35 | <%= transition_tag workflows, old_status, new_status, name %> |
|
35 | 36 | </td> |
@@ -1882,3 +1882,45 WorkflowTransitions_188: | |||
|
1882 | 1882 | id: 188 |
|
1883 | 1883 | tracker_id: 3 |
|
1884 | 1884 | type: WorkflowTransition |
|
1885 | WorkflowTransitions_271: | |
|
1886 | new_status_id: 3 | |
|
1887 | role_id: 1 | |
|
1888 | old_status_id: 0 | |
|
1889 | id: 271 | |
|
1890 | tracker_id: 2 | |
|
1891 | type: WorkflowTransition | |
|
1892 | WorkflowTransitions_272: | |
|
1893 | new_status_id: 3 | |
|
1894 | role_id: 2 | |
|
1895 | old_status_id: 0 | |
|
1896 | id: 272 | |
|
1897 | tracker_id: 1 | |
|
1898 | type: WorkflowTransition | |
|
1899 | WorkflowTransitions_273: | |
|
1900 | new_status_id: 2 | |
|
1901 | role_id: 1 | |
|
1902 | old_status_id: 0 | |
|
1903 | id: 273 | |
|
1904 | tracker_id: 3 | |
|
1905 | type: WorkflowTransition | |
|
1906 | WorkflowTransitions_274: | |
|
1907 | new_status_id: 2 | |
|
1908 | role_id: 1 | |
|
1909 | old_status_id: 0 | |
|
1910 | id: 274 | |
|
1911 | tracker_id: 1 | |
|
1912 | type: WorkflowTransition | |
|
1913 | WorkflowTransitions_275: | |
|
1914 | new_status_id: 1 | |
|
1915 | role_id: 1 | |
|
1916 | old_status_id: 0 | |
|
1917 | id: 275 | |
|
1918 | tracker_id: 1 | |
|
1919 | type: WorkflowTransition | |
|
1920 | WorkflowTransitions_276: | |
|
1921 | new_status_id: 1 | |
|
1922 | role_id: 1 | |
|
1923 | old_status_id: 0 | |
|
1924 | id: 276 | |
|
1925 | tracker_id: 2 | |
|
1926 | type: WorkflowTransition |
@@ -1602,6 +1602,37 class IssuesControllerTest < ActionController::TestCase | |||
|
1602 | 1602 | assert_select 'input[name=was_default_status][value="1"]' |
|
1603 | 1603 | end |
|
1604 | 1604 | |
|
1605 | def test_new_should_propose_allowed_statuses | |
|
1606 | WorkflowTransition.delete_all | |
|
1607 | WorkflowTransition.create!(:tracker_id => 1, :role_id => 1, :old_status_id => 0, :new_status_id => 1) | |
|
1608 | WorkflowTransition.create!(:tracker_id => 1, :role_id => 1, :old_status_id => 0, :new_status_id => 3) | |
|
1609 | @request.session[:user_id] = 2 | |
|
1610 | ||
|
1611 | get :new, :project_id => 1 | |
|
1612 | assert_response :success | |
|
1613 | assert_select 'select[name=?]', 'issue[status_id]' do | |
|
1614 | assert_select 'option[value="1"]' | |
|
1615 | assert_select 'option[value="3"]' | |
|
1616 | assert_select 'option', 2 | |
|
1617 | assert_select 'option[value="1"][selected=selected]' | |
|
1618 | end | |
|
1619 | end | |
|
1620 | ||
|
1621 | def test_new_should_propose_allowed_statuses_without_default_status_allowed | |
|
1622 | WorkflowTransition.delete_all | |
|
1623 | WorkflowTransition.create!(:tracker_id => 1, :role_id => 1, :old_status_id => 0, :new_status_id => 2) | |
|
1624 | assert_equal 1, Tracker.find(1).default_status_id | |
|
1625 | @request.session[:user_id] = 2 | |
|
1626 | ||
|
1627 | get :new, :project_id => 1 | |
|
1628 | assert_response :success | |
|
1629 | assert_select 'select[name=?]', 'issue[status_id]' do | |
|
1630 | assert_select 'option[value="2"]' | |
|
1631 | assert_select 'option', 1 | |
|
1632 | assert_select 'option[value="2"][selected=selected]' | |
|
1633 | end | |
|
1634 | end | |
|
1635 | ||
|
1605 | 1636 | def test_get_new_with_list_custom_field |
|
1606 | 1637 | @request.session[:user_id] = 2 |
|
1607 | 1638 | get :new, :project_id => 1, :tracker_id => 1 |
@@ -1827,8 +1858,8 class IssuesControllerTest < ActionController::TestCase | |||
|
1827 | 1858 | def test_update_form_for_new_issue_should_propose_transitions_based_on_initial_status |
|
1828 | 1859 | @request.session[:user_id] = 2 |
|
1829 | 1860 | WorkflowTransition.delete_all |
|
1830 |
WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => |
|
|
1831 |
WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => |
|
|
1861 | WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 0, :new_status_id => 2) | |
|
1862 | WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 0, :new_status_id => 5) | |
|
1832 | 1863 | WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 5, :new_status_id => 4) |
|
1833 | 1864 | |
|
1834 | 1865 | xhr :post, :new, :project_id => 1, |
@@ -1837,7 +1868,7 class IssuesControllerTest < ActionController::TestCase | |||
|
1837 | 1868 | :subject => 'This is an issue'} |
|
1838 | 1869 | |
|
1839 | 1870 | assert_equal 5, assigns(:issue).status_id |
|
1840 |
assert_equal [ |
|
|
1871 | assert_equal [2,5], assigns(:allowed_statuses).map(&:id).sort | |
|
1841 | 1872 | end |
|
1842 | 1873 | |
|
1843 | 1874 | def test_update_form_with_default_status_should_ignore_submitted_status_id_if_equals |
@@ -61,6 +61,16 class WorkflowsControllerTest < ActionController::TestCase | |||
|
61 | 61 | assert_select 'input[type=checkbox][name=?]', 'transitions[1][1][always]', 0 |
|
62 | 62 | end |
|
63 | 63 | |
|
64 | def test_get_edit_should_include_allowed_statuses_for_new_issues | |
|
65 | WorkflowTransition.delete_all | |
|
66 | WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 0, :new_status_id => 1) | |
|
67 | ||
|
68 | get :edit, :role_id => 1, :tracker_id => 1 | |
|
69 | assert_response :success | |
|
70 | assert_select 'td', 'New issue' | |
|
71 | assert_select 'input[type=checkbox][name=?][value="1"][checked=checked]', 'transitions[0][1][always]' | |
|
72 | end | |
|
73 | ||
|
64 | 74 | def test_get_edit_with_all_roles_and_all_trackers |
|
65 | 75 | get :edit, :role_id => 'all', :tracker_id => 'all' |
|
66 | 76 | assert_response :success |
@@ -96,6 +106,20 class WorkflowsControllerTest < ActionController::TestCase | |||
|
96 | 106 | assert_nil WorkflowTransition.where(:role_id => 2, :tracker_id => 1, :old_status_id => 5, :new_status_id => 4).first |
|
97 | 107 | end |
|
98 | 108 | |
|
109 | def test_post_edit_with_allowed_statuses_for_new_issues | |
|
110 | WorkflowTransition.delete_all | |
|
111 | ||
|
112 | post :edit, :role_id => 2, :tracker_id => 1, | |
|
113 | :transitions => { | |
|
114 | '0' => {'1' => {'always' => '1'}, '2' => {'always' => '1'}} | |
|
115 | } | |
|
116 | assert_response 302 | |
|
117 | ||
|
118 | assert WorkflowTransition.where(:role_id => 2, :tracker_id => 1, :old_status_id => 0, :new_status_id => 1).any? | |
|
119 | assert WorkflowTransition.where(:role_id => 2, :tracker_id => 1, :old_status_id => 0, :new_status_id => 2).any? | |
|
120 | assert_equal 2, WorkflowTransition.where(:tracker_id => 1, :role_id => 2).count | |
|
121 | end | |
|
122 | ||
|
99 | 123 | def test_post_edit_with_additional_transitions |
|
100 | 124 | WorkflowTransition.delete_all |
|
101 | 125 |
@@ -47,13 +47,14 class RoleTest < ActiveSupport::TestCase | |||
|
47 | 47 | |
|
48 | 48 | def test_copy_workflows |
|
49 | 49 | source = Role.find(1) |
|
50 |
|
|
|
50 | rule_count = source.workflow_rules.count | |
|
51 | assert rule_count > 0 | |
|
51 | 52 | |
|
52 | 53 | target = Role.new(:name => 'Target') |
|
53 | 54 | assert target.save |
|
54 | 55 | target.workflow_rules.copy(source) |
|
55 | 56 | target.reload |
|
56 |
assert_equal |
|
|
57 | assert_equal rule_count, target.workflow_rules.size | |
|
57 | 58 | end |
|
58 | 59 | |
|
59 | 60 | def test_permissions_should_be_unserialized_with_its_coder |
@@ -30,13 +30,14 class TrackerTest < ActiveSupport::TestCase | |||
|
30 | 30 | |
|
31 | 31 | def test_copy_workflows |
|
32 | 32 | source = Tracker.find(1) |
|
33 |
|
|
|
33 | rules_count = source.workflow_rules.count | |
|
34 | assert rules_count > 0 | |
|
34 | 35 | |
|
35 | 36 | target = Tracker.new(:name => 'Target', :default_status_id => 1) |
|
36 | 37 | assert target.save |
|
37 | 38 | target.workflow_rules.copy(source) |
|
38 | 39 | target.reload |
|
39 |
assert_equal |
|
|
40 | assert_equal rules_count, target.workflow_rules.size | |
|
40 | 41 | end |
|
41 | 42 | |
|
42 | 43 | def test_issue_statuses |
General Comments 0
You need to be logged in to leave comments.
Login now