if request.post?
Workflow.destroy_all( ["role_id=? and tracker_id=?", @role.id, @tracker.id])
- (params[:issue_status] || []).each { |old, news|
- news.each { |new|
- @role.workflows.build(:tracker_id => @tracker.id, :old_status_id => old, :new_status_id => new)
+ (params[:issue_status] || []).each { |status_id, transitions|
+ transitions.each { |new_status_id, options|
+ author = options.is_a?(Array) && options.include?('author') && !options.include?('always')
+ assignee = options.is_a?(Array) && options.include?('assignee') && !options.include?('always')
+ @role.workflows.build(:tracker_id => @tracker.id, :old_status_id => status_id, :new_status_id => new_status_id, :author => author, :assignee => assignee)
}
}
if @role.save
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'edit', :role_id => @role, :tracker_id => @tracker
+ return
end
end
@statuses = @tracker.issue_statuses
end
@statuses ||= IssueStatus.find(:all, :order => 'position')
+
+ if @tracker && @role && @statuses.any?
+ workflows = Workflow.all(:conditions => {:role_id => @role.id, :tracker_id => @tracker.id})
+ @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
def copy
# Returns an array of status that user is able to apply
def new_statuses_allowed_to(user, include_default=false)
- statuses = status.find_new_statuses_allowed_to(user.roles_for_project(project), tracker)
+ statuses = status.find_new_statuses_allowed_to(
+ user.roles_for_project(project),
+ tracker,
+ author == user,
+ assigned_to_id_changed? ? assigned_to_id_was == user.id : assigned_to_id == user.id
+ )
statuses << status unless statuses.empty?
statuses << IssueStatus.default if include_default
statuses = statuses.uniq.sort
# Returns an array of all statuses the given role can switch to
# Uses association cache when called more than one time
- def new_statuses_allowed_to(roles, tracker)
+ def new_statuses_allowed_to(roles, tracker, author=false, assignee=false)
if roles && tracker
role_ids = roles.collect(&:id)
- new_statuses = workflows.select {|w| role_ids.include?(w.role_id) && w.tracker_id == tracker.id}.collect{|w| w.new_status}.compact.sort
+ transitions = workflows.select do |w|
+ role_ids.include?(w.role_id) &&
+ w.tracker_id == tracker.id &&
+ (author || !w.author) &&
+ (assignee || !w.assignee)
+ end
+ transitions.collect{|w| w.new_status}.compact.sort
else
[]
end
# Same thing as above but uses a database query
# More efficient than the previous method if called just once
- def find_new_statuses_allowed_to(roles, tracker)
+ def find_new_statuses_allowed_to(roles, tracker, author=false, assignee=false)
if roles && tracker
+ conditions = {:role_id => roles.collect(&:id), :tracker_id => tracker.id}
+ conditions[:author] = false unless author
+ conditions[:assignee] = false unless assignee
+
workflows.find(:all,
:include => :new_status,
- :conditions => { :role_id => roles.collect(&:id),
- :tracker_id => tracker.id}).collect{ |w| w.new_status }.compact.sort
+ :conditions => conditions).collect{|w| w.new_status}.compact.sort
else
[]
end
end
-
- def new_status_allowed_to?(status, roles, tracker)
- if status && roles && tracker
- !workflows.find(:first, :conditions => {:new_status_id => status.id, :role_id => roles.collect(&:id), :tracker_id => tracker.id}).nil?
- else
- false
- end
- end
def <=>(status)
position <=> status.position
--- /dev/null
+<table class="list transitions-<%= name %>">
+<thead>
+ <tr>
+ <th align="left">
+ <%= link_to_function(image_tag('toggle_check.png'), "toggleCheckboxesBySelector('table.transitions-#{name} input')",
+ :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}") %>
+ <%=l(:label_current_status)%>
+ </th>
+ <th align="center" colspan="<%= @statuses.length %>"><%=l(:label_new_statuses_allowed)%></th>
+ </tr>
+ <tr>
+ <td></td>
+ <% for new_status in @statuses %>
+ <td width="<%= 75 / @statuses.size %>%" align="center">
+ <%= link_to_function(image_tag('toggle_check.png'), "toggleCheckboxesBySelector('table.transitions-#{name} input.new-status-#{new_status.id}')",
+ :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}") %>
+ <%=h new_status.name %>
+ </td>
+ <% end %>
+ </tr>
+</thead>
+<tbody>
+ <% for old_status in @statuses %>
+ <tr class="<%= cycle("odd", "even") %>">
+ <td>
+ <%= link_to_function(image_tag('toggle_check.png'), "toggleCheckboxesBySelector('table.transitions-#{name} input.old-status-#{old_status.id}')",
+ :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}") %>
+
+ <%=h old_status.name %>
+ </td>
+ <% for new_status in @statuses -%>
+ <td align="center">
+ <%= check_box_tag "issue_status[#{ old_status.id }][#{new_status.id}][]", name, workflows.detect {|w| w.old_status_id == old_status.id && w.new_status_id == new_status.id},
+ :class => "old-status-#{old_status.id} new-status-#{new_status.id}" %>
+ </td>
+ <% end -%>
+ </tr>
+ <% end %>
+</tbody>
+</table>
\ No newline at end of file
</p>
<% end %>
-
<% if @tracker && @role && @statuses.any? %>
-<% form_tag({}, :id => 'workflow_form' ) do %>
-<%= hidden_field_tag 'tracker_id', @tracker.id %>
-<%= hidden_field_tag 'role_id', @role.id %>
-<div class="autoscroll">
-<table class="list">
-<thead>
- <tr>
- <th align="left"><%=l(:label_current_status)%></th>
- <th align="center" colspan="<%= @statuses.length %>"><%=l(:label_new_statuses_allowed)%></th>
- </tr>
- <tr>
- <td></td>
- <% for new_status in @statuses %>
- <td width="<%= 75 / @statuses.size %>%" align="center">
- <%= link_to_function(image_tag('toggle_check.png'), "toggleCheckboxesBySelector('input.new-status-#{new_status.id}')",
- :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}") %>
- <%= new_status.name %>
- </td>
- <% end %>
- </tr>
-</thead>
-<tbody>
- <% for old_status in @statuses %>
- <tr class="<%= cycle("odd", "even") %>">
- <td>
- <%= link_to_function(image_tag('toggle_check.png'), "toggleCheckboxesBySelector('input.old-status-#{old_status.id}')",
- :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}") %>
-
- <%= old_status.name %>
- </td>
- <% new_status_ids_allowed = old_status.find_new_statuses_allowed_to([@role], @tracker).collect(&:id) -%>
- <% for new_status in @statuses -%>
- <td align="center">
- <%= check_box_tag "issue_status[#{ old_status.id }][]", new_status.id, new_status_ids_allowed.include?(new_status.id),
- :class => "old-status-#{old_status.id} new-status-#{new_status.id}" %>
- </td>
- <% end -%>
- </tr>
- <% end %>
-</tbody>
-</table>
-</div>
-<p><%= check_all_links 'workflow_form' %></p>
-
-<%= submit_tag l(:button_save) %>
-<% end %>
+ <% form_tag({}, :id => 'workflow_form' ) do %>
+ <%= hidden_field_tag 'tracker_id', @tracker.id %>
+ <%= hidden_field_tag 'role_id', @role.id %>
+ <div class="autoscroll">
+ <%= render :partial => 'form', :locals => {:name => 'always', :workflows => @workflows['always']} %>
+
+ <fieldset class="collapsible" style="padding: 0; margin-top: 0.5em;">
+ <legend onclick="toggleFieldset(this);">Autorisations supplémentaires lorsque l'utilisateur a créé la demande</legend>
+ <div id="author_workflows" style="margin: 0.5em 0 0.5em 0;">
+ <%= render :partial => 'form', :locals => {:name => 'author', :workflows => @workflows['author']} %>
+ </div>
+ </fieldset>
+ <%= javascript_tag "hideFieldset($('author_workflows'))" unless @workflows['author'].present? %>
+
+ <fieldset class="collapsible" style="padding: 0;">
+ <legend onclick="toggleFieldset(this);">Autorisations supplémentaires lorsque la demande est assignée à l'utilisateur</legend>
+ <div id="assignee_workflows" style="margin: 0.5em 0 0.5em 0;">
+ <%= render :partial => 'form', :locals => {:name => 'assignee', :workflows => @workflows['assignee']} %>
+ </div>
+ </fieldset>
+ <%= javascript_tag "hideFieldset($('assignee_workflows'))" unless @workflows['assignee'].present? %>
+ </div>
+ <%= submit_tag l(:button_save) %>
+ <% end %>
<% end %>
<% html_title(l(:label_workflow)) -%>
--- /dev/null
+class AddWorkflowsAssigneeAndAuthor < ActiveRecord::Migration
+ def self.up
+ add_column :workflows, :assignee, :boolean, :null => false, :default => false
+ add_column :workflows, :author, :boolean, :null => false, :default => false
+ Workflow.update_all("assignee = #{Workflow.connection.quoted_false}")
+ Workflow.update_all("author = #{Workflow.connection.quoted_false}")
+ end
+
+ def self.down
+ remove_column :workflows, :assignee
+ remove_column :workflows, :author
+ end
+end
Effect.toggle(fieldset.down('div'), 'slide', {duration:0.2});
}
+function hideFieldset(el) {
+ var fieldset = Element.up(el, 'fieldset');
+ fieldset.toggleClassName('collapsed');
+ fieldset.down('div').hide();
+}
+
var fileFieldCount = 1;
function addFileField() {
---
-issue_statuses_006:
- name: Rejected
- is_default: false
- is_closed: true
- id: 6
issue_statuses_001:
+ id: 1
name: New
is_default: true
is_closed: false
- id: 1
+ position: 1
issue_statuses_002:
+ id: 2
name: Assigned
is_default: false
is_closed: false
- id: 2
+ position: 2
issue_statuses_003:
+ id: 3
name: Resolved
is_default: false
is_closed: false
- id: 3
+ position: 3
issue_statuses_004:
name: Feedback
+ id: 4
is_default: false
is_closed: false
- id: 4
+ position: 4
issue_statuses_005:
+ id: 5
name: Closed
is_default: false
is_closed: true
- id: 5
+ position: 5
+issue_statuses_006:
+ id: 6
+ name: Rejected
+ is_default: false
+ is_closed: true
+ position: 6
# allowed transitions
assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
- :name => 'issue_status[3][]',
- :value => '5',
+ :name => 'issue_status[3][5][]',
+ :value => 'always',
:checked => 'checked' }
# not allowed
assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
- :name => 'issue_status[3][]',
- :value => '2',
+ :name => 'issue_status[3][2][]',
+ :value => 'always',
:checked => nil }
# unused
assert_no_tag :tag => 'input', :attributes => { :type => 'checkbox',
- :name => 'issue_status[4][]' }
+ :name => 'issue_status[1][1][]' }
end
def test_get_edit_with_role_and_tracker_and_all_statuses
assert_equal IssueStatus.count, assigns(:statuses).size
assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
- :name => 'issue_status[1][]',
- :value => '1',
+ :name => 'issue_status[1][1][]',
+ :value => 'always',
:checked => nil }
end
def test_post_edit
- post :edit, :role_id => 2, :tracker_id => 1, :issue_status => {'4' => ['5'], '3' => ['1', '2']}
+ post :edit, :role_id => 2, :tracker_id => 1,
+ :issue_status => {
+ '4' => {'5' => ['always']},
+ '3' => {'1' => ['always'], '2' => ['always']}
+ }
assert_redirected_to '/workflows/edit?role_id=2&tracker_id=1'
assert_equal 3, Workflow.count(:conditions => {:tracker_id => 1, :role_id => 2})
assert_nil Workflow.find(:first, :conditions => {:role_id => 2, :tracker_id => 1, :old_status_id => 5, :new_status_id => 4})
end
+ def test_post_edit_with_additional_transitions
+ post :edit, :role_id => 2, :tracker_id => 1,
+ :issue_status => {
+ '4' => {'5' => ['always']},
+ '3' => {'1' => ['author'], '2' => ['assignee'], '4' => ['author', 'assignee']}
+ }
+ assert_redirected_to '/workflows/edit?role_id=2&tracker_id=1'
+
+ assert_equal 4, Workflow.count(:conditions => {:tracker_id => 1, :role_id => 2})
+
+ w = Workflow.find(:first, :conditions => {:role_id => 2, :tracker_id => 1, :old_status_id => 4, :new_status_id => 5})
+ assert ! w.author
+ assert ! w.assignee
+ w = Workflow.find(:first, :conditions => {:role_id => 2, :tracker_id => 1, :old_status_id => 3, :new_status_id => 1})
+ assert w.author
+ assert ! w.assignee
+ w = Workflow.find(:first, :conditions => {:role_id => 2, :tracker_id => 1, :old_status_id => 3, :new_status_id => 2})
+ assert ! w.author
+ assert w.assignee
+ w = Workflow.find(:first, :conditions => {:role_id => 2, :tracker_id => 1, :old_status_id => 3, :new_status_id => 4})
+ assert w.author
+ assert w.assignee
+ end
+
def test_clear_workflow
assert Workflow.count(:conditions => {:tracker_id => 1, :role_id => 2}) > 0
require File.expand_path('../../test_helper', __FILE__)
class IssueStatusTest < ActiveSupport::TestCase
- fixtures :issue_statuses, :issues
+ fixtures :issue_statuses, :issues, :roles, :trackers
def test_create
status = IssueStatus.new :name => "Assigned"
status.reload
assert status.is_default?
end
+
+ def test_new_statuses_allowed_to
+ Workflow.delete_all
+
+ Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 2, :author => false, :assignee => false)
+ Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3, :author => true, :assignee => false)
+ Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4, :author => false, :assignee => true)
+ Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 5, :author => true, :assignee => true)
+ status = IssueStatus.find(1)
+ role = Role.find(1)
+ tracker = Tracker.find(1)
+
+ assert_equal [2], status.new_statuses_allowed_to([role], tracker, false, false).map(&:id)
+ assert_equal [2], status.find_new_statuses_allowed_to([role], tracker, false, false).map(&:id)
+
+ assert_equal [2, 3], status.new_statuses_allowed_to([role], tracker, true, false).map(&:id)
+ assert_equal [2, 3], status.find_new_statuses_allowed_to([role], tracker, true, false).map(&:id)
+
+ assert_equal [2, 4], status.new_statuses_allowed_to([role], tracker, false, true).map(&:id)
+ assert_equal [2, 4], status.find_new_statuses_allowed_to([role], tracker, false, true).map(&:id)
+
+ assert_equal [2, 3, 4, 5], status.new_statuses_allowed_to([role], tracker, true, true).map(&:id)
+ assert_equal [2, 3, 4, 5], status.find_new_statuses_allowed_to([role], tracker, true, true).map(&:id)
+ end
context "#update_done_ratios" do
setup do
assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to
end
+
+
+ def test_new_statuses_allowed_to
+ Workflow.delete_all
+
+ Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 2, :author => false, :assignee => false)
+ Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3, :author => true, :assignee => false)
+ Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4, :author => false, :assignee => true)
+ Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 5, :author => true, :assignee => true)
+ status = IssueStatus.find(1)
+ role = Role.find(1)
+ tracker = Tracker.find(1)
+ user = User.find(2)
+
+ issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1)
+ assert_equal [1, 2], issue.new_statuses_allowed_to(user).map(&:id)
+
+ issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1, :author => user)
+ assert_equal [1, 2, 3], issue.new_statuses_allowed_to(user).map(&:id)
+
+ issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1, :assigned_to => user)
+ assert_equal [1, 2, 4], issue.new_statuses_allowed_to(user).map(&:id)
+
+ issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1, :author => user, :assigned_to => user)
+ assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
+ end
+
def test_copy
issue = Issue.new.copy_from(1)
assert issue.save