Workflow enhancement: editable and required fields configurable by role, tracker and status (#703, #3521).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9977 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
parent
54d55a360a
commit
d7b669e50b
|
@ -129,11 +129,7 @@ class IssuesController < ApplicationController
|
|||
format.html { render :action => 'new', :layout => !request.xhr? }
|
||||
format.js {
|
||||
render(:update) { |page|
|
||||
if params[:project_change]
|
||||
page.replace_html 'all_attributes', :partial => 'form'
|
||||
else
|
||||
page.replace_html 'attributes', :partial => 'attributes'
|
||||
end
|
||||
m = User.current.allowed_to?(:log_time, @issue.project) ? 'show' : 'hide'
|
||||
page << "if ($('log_time')) {Element.#{m}('log_time');}"
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ class RolesController < ApplicationController
|
|||
if request.post? && @role.save
|
||||
# workflow copy
|
||||
if !params[:copy_workflow_from].blank? && (copy_from = Role.find_by_id(params[:copy_workflow_from]))
|
||||
@role.workflows.copy(copy_from)
|
||||
@role.workflow_rules.copy(copy_from)
|
||||
end
|
||||
flash[:notice] = l(:notice_successful_create)
|
||||
redirect_to :action => 'index'
|
||||
|
|
|
@ -45,7 +45,7 @@ class TrackersController < ApplicationController
|
|||
if request.post? and @tracker.save
|
||||
# workflow copy
|
||||
if !params[:copy_workflow_from].blank? && (copy_from = Tracker.find_by_id(params[:copy_workflow_from]))
|
||||
@tracker.workflows.copy(copy_from)
|
||||
@tracker.workflow_rules.copy(copy_from)
|
||||
end
|
||||
flash[:notice] = l(:notice_successful_create)
|
||||
redirect_to :action => 'index'
|
||||
|
|
|
@ -23,7 +23,7 @@ class WorkflowsController < ApplicationController
|
|||
before_filter :find_trackers
|
||||
|
||||
def index
|
||||
@workflow_counts = Workflow.count_by_tracker_and_role
|
||||
@workflow_counts = WorkflowTransition.count_by_tracker_and_role
|
||||
end
|
||||
|
||||
def edit
|
||||
|
@ -31,16 +31,15 @@ class WorkflowsController < ApplicationController
|
|||
@tracker = Tracker.find_by_id(params[:tracker_id])
|
||||
|
||||
if request.post?
|
||||
Workflow.destroy_all( ["role_id=? and tracker_id=?", @role.id, @tracker.id])
|
||||
WorkflowTransition.destroy_all( ["role_id=? and tracker_id=?", @role.id, @tracker.id])
|
||||
(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)
|
||||
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)
|
||||
}
|
||||
}
|
||||
if @role.save
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
redirect_to :action => 'edit', :role_id => @role, :tracker_id => @tracker
|
||||
return
|
||||
end
|
||||
|
@ -53,7 +52,7 @@ class WorkflowsController < ApplicationController
|
|||
@statuses ||= IssueStatus.find(:all, :order => 'position')
|
||||
|
||||
if @tracker && @role && @statuses.any?
|
||||
workflows = Workflow.all(:conditions => {:role_id => @role.id, :tracker_id => @tracker.id})
|
||||
workflows = WorkflowTransition.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}
|
||||
|
@ -61,6 +60,37 @@ class WorkflowsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def permissions
|
||||
@role = Role.find_by_id(params[:role_id])
|
||||
@tracker = Tracker.find_by_id(params[:tracker_id])
|
||||
|
||||
if @role && @tracker
|
||||
if request.post?
|
||||
WorkflowPermission.destroy_all({:role_id => @role.id, :tracker_id => @tracker.id})
|
||||
(params[:permissions] || {}).each { |field, rule_by_status_id|
|
||||
rule_by_status_id.each { |status_id, rule|
|
||||
if rule.present?
|
||||
WorkflowPermission.create(:role_id => @role.id, :tracker_id => @tracker.id, :old_status_id => status_id, :field_name => field, :rule => rule)
|
||||
end
|
||||
}
|
||||
}
|
||||
redirect_to :action => 'permissions', :role_id => @role, :tracker_id => @tracker
|
||||
return
|
||||
end
|
||||
|
||||
@statuses = @tracker.issue_statuses
|
||||
@fields = (Tracker::CORE_FIELDS_ALL - @tracker.disabled_core_fields).map {|field| [field, l("field_"+field.sub(/_id$/, ''))]}
|
||||
@custom_fields = @tracker.custom_fields
|
||||
|
||||
@permissions = WorkflowPermission.where(:tracker_id => @tracker.id, :role_id => @role.id).all.inject({}) do |h, w|
|
||||
h[w.old_status_id] ||= {}
|
||||
h[w.old_status_id][w.field_name] = w.rule
|
||||
h
|
||||
end
|
||||
@statuses.each {|status| @permissions[status.id] ||= {}}
|
||||
end
|
||||
end
|
||||
|
||||
def copy
|
||||
|
||||
if params[:source_tracker_id].blank? || params[:source_tracker_id] == 'any'
|
||||
|
@ -83,7 +113,7 @@ class WorkflowsController < ApplicationController
|
|||
elsif @target_trackers.nil? || @target_roles.nil?
|
||||
flash.now[:error] = l(:error_workflow_copy_target)
|
||||
else
|
||||
Workflow.copy(@source_tracker, @source_role, @target_trackers, @target_roles)
|
||||
WorkflowRule.copy(@source_tracker, @source_role, @target_trackers, @target_roles)
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
redirect_to :action => 'copy', :source_tracker_id => @source_tracker, :source_role_id => @source_role
|
||||
end
|
||||
|
|
|
@ -73,15 +73,17 @@ module CustomFieldsHelper
|
|||
end
|
||||
|
||||
# Return custom field label tag
|
||||
def custom_field_label_tag(name, custom_value)
|
||||
def custom_field_label_tag(name, custom_value, options={})
|
||||
required = options[:required] || custom_value.custom_field.is_required?
|
||||
|
||||
content_tag "label", h(custom_value.custom_field.name) +
|
||||
(custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>".html_safe : ""),
|
||||
(required ? " <span class=\"required\">*</span>".html_safe : ""),
|
||||
:for => "#{name}_custom_field_values_#{custom_value.custom_field.id}"
|
||||
end
|
||||
|
||||
# Return custom field tag with its label tag
|
||||
def custom_field_tag_with_label(name, custom_value)
|
||||
custom_field_label_tag(name, custom_value) + custom_field_tag(name, custom_value)
|
||||
def custom_field_tag_with_label(name, custom_value, options={})
|
||||
custom_field_label_tag(name, custom_value, options) + custom_field_tag(name, custom_value)
|
||||
end
|
||||
|
||||
def custom_field_tag_for_bulk_edit(name, custom_field, projects=nil)
|
||||
|
|
|
@ -18,4 +18,10 @@
|
|||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module WorkflowsHelper
|
||||
def field_permission_tag(permissions, status, field)
|
||||
name = field.is_a?(CustomField) ? field.id.to_s : field
|
||||
select_tag("permissions[#{name}][#{status.id}]",
|
||||
options_for_select([["", ""], ["Read-only", "readonly"], ["Required", "required"]], permissions[status.id][name])
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -58,7 +58,7 @@ class Issue < ActiveRecord::Base
|
|||
validates_length_of :subject, :maximum => 255
|
||||
validates_inclusion_of :done_ratio, :in => 0..100
|
||||
validates_numericality_of :estimated_hours, :allow_nil => true
|
||||
validate :validate_issue
|
||||
validate :validate_issue, :validate_required_fields
|
||||
|
||||
scope :visible,
|
||||
lambda {|*args| { :include => :project,
|
||||
|
@ -146,6 +146,11 @@ class Issue < ActiveRecord::Base
|
|||
super
|
||||
end
|
||||
|
||||
def reload(*args)
|
||||
@workflow_rule_by_attribute = nil
|
||||
super
|
||||
end
|
||||
|
||||
# Overrides Redmine::Acts::Customizable::InstanceMethods#available_custom_fields
|
||||
def available_custom_fields
|
||||
(project && tracker) ? (project.all_issue_custom_fields & tracker.custom_fields.all) : []
|
||||
|
@ -208,7 +213,9 @@ class Issue < ActiveRecord::Base
|
|||
|
||||
def status_id=(sid)
|
||||
self.status = nil
|
||||
write_attribute(:status_id, sid)
|
||||
result = write_attribute(:status_id, sid)
|
||||
@workflow_rule_by_attribute = nil
|
||||
result
|
||||
end
|
||||
|
||||
def priority_id=(pid)
|
||||
|
@ -230,6 +237,7 @@ class Issue < ActiveRecord::Base
|
|||
self.tracker = nil
|
||||
result = write_attribute(:tracker_id, tid)
|
||||
@custom_field_values = nil
|
||||
@workflow_rule_by_attribute = nil
|
||||
result
|
||||
end
|
||||
|
||||
|
@ -336,9 +344,10 @@ class Issue < ActiveRecord::Base
|
|||
:if => lambda {|issue, user| (issue.new_record? || user.allowed_to?(:edit_issues, issue.project)) &&
|
||||
user.allowed_to?(:manage_subtasks, issue.project)}
|
||||
|
||||
def safe_attribute_names(*args)
|
||||
names = super(*args)
|
||||
def safe_attribute_names(user=nil)
|
||||
names = super
|
||||
names -= disabled_core_fields
|
||||
names -= read_only_attribute_names(user)
|
||||
names
|
||||
end
|
||||
|
||||
|
@ -362,15 +371,15 @@ class Issue < ActiveRecord::Base
|
|||
self.tracker_id = t
|
||||
end
|
||||
|
||||
if (s = attrs.delete('status_id')) && safe_attribute?('status_id')
|
||||
if new_statuses_allowed_to(user).collect(&:id).include?(s.to_i)
|
||||
self.status_id = s
|
||||
end
|
||||
end
|
||||
|
||||
attrs = delete_unsafe_attributes(attrs, user)
|
||||
return if attrs.empty?
|
||||
|
||||
if attrs['status_id']
|
||||
unless new_statuses_allowed_to(user).collect(&:id).include?(attrs['status_id'].to_i)
|
||||
attrs.delete('status_id')
|
||||
end
|
||||
end
|
||||
|
||||
unless leaf?
|
||||
attrs.reject! {|k,v| %w(priority_id done_ratio start_date due_date estimated_hours).include?(k)}
|
||||
end
|
||||
|
@ -379,6 +388,14 @@ class Issue < ActiveRecord::Base
|
|||
attrs.delete('parent_issue_id') unless Issue.visible(user).exists?(attrs['parent_issue_id'].to_i)
|
||||
end
|
||||
|
||||
if attrs['custom_field_values'].present?
|
||||
attrs['custom_field_values'] = attrs['custom_field_values'].reject {|k, v| read_only_attribute_names(user).include? k.to_s}
|
||||
end
|
||||
|
||||
if attrs['custom_fields'].present?
|
||||
attrs['custom_fields'] = attrs['custom_fields'].reject {|c| read_only_attribute_names(user).include? c['id'].to_s}
|
||||
end
|
||||
|
||||
# mass-assignment security bypass
|
||||
assign_attributes attrs, :without_protection => true
|
||||
end
|
||||
|
@ -387,6 +404,76 @@ class Issue < ActiveRecord::Base
|
|||
tracker ? tracker.disabled_core_fields : []
|
||||
end
|
||||
|
||||
# Returns the custom_field_values that can be edited by the given user
|
||||
def editable_custom_field_values(user=nil)
|
||||
custom_field_values.reject do |value|
|
||||
read_only_attribute_names(user).include?(value.custom_field_id.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the names of attributes that are read-only for user or the current user
|
||||
# For users with multiple roles, the read-only fields are the intersection of
|
||||
# read-only fields of each role
|
||||
# The result is an array of strings where sustom fields are represented with their ids
|
||||
#
|
||||
# Examples:
|
||||
# issue.read_only_attribute_names # => ['due_date', '2']
|
||||
# issue.read_only_attribute_names(user) # => []
|
||||
def read_only_attribute_names(user=nil)
|
||||
workflow_rule_by_attribute(user).select {|attr, rule| rule == 'readonly'}.keys
|
||||
end
|
||||
|
||||
# Returns the names of required attributes for user or the current user
|
||||
# For users with multiple roles, the required fields are the intersection of
|
||||
# required fields of each role
|
||||
# The result is an array of strings where sustom fields are represented with their ids
|
||||
#
|
||||
# Examples:
|
||||
# issue.required_attribute_names # => ['due_date', '2']
|
||||
# issue.required_attribute_names(user) # => []
|
||||
def required_attribute_names(user=nil)
|
||||
workflow_rule_by_attribute(user).select {|attr, rule| rule == 'required'}.keys
|
||||
end
|
||||
|
||||
# Returns true if the attribute is required for user
|
||||
def required_attribute?(name, user=nil)
|
||||
required_attribute_names(user).include?(name.to_s)
|
||||
end
|
||||
|
||||
# Returns a hash of the workflow rule by attribute for the given user
|
||||
#
|
||||
# Examples:
|
||||
# issue.workflow_rule_by_attribute # => {'due_date' => 'required', 'start_date' => 'readonly'}
|
||||
def workflow_rule_by_attribute(user=nil)
|
||||
return @workflow_rule_by_attribute if @workflow_rule_by_attribute && user.nil?
|
||||
|
||||
user_real = user || User.current
|
||||
roles = user_real.admin ? Role.all : user_real.roles_for_project(project)
|
||||
return {} if roles.empty?
|
||||
|
||||
result = {}
|
||||
workflow_permissions = WorkflowPermission.where(:tracker_id => tracker_id, :old_status_id => status_id, :role_id => roles.map(&:id)).all
|
||||
if workflow_permissions.any?
|
||||
workflow_rules = workflow_permissions.inject({}) do |h, wp|
|
||||
h[wp.field_name] ||= []
|
||||
h[wp.field_name] << wp.rule
|
||||
h
|
||||
end
|
||||
workflow_rules.each do |attr, rules|
|
||||
next if rules.size < roles.size
|
||||
uniq_rules = rules.uniq
|
||||
if uniq_rules.size == 1
|
||||
result[attr] = uniq_rules.first
|
||||
else
|
||||
result[attr] = 'required'
|
||||
end
|
||||
end
|
||||
end
|
||||
@workflow_rule_by_attribute = result if user.nil?
|
||||
result
|
||||
end
|
||||
private :workflow_rule_by_attribute
|
||||
|
||||
def done_ratio
|
||||
if Issue.use_status_for_done_ratio? && status && status.default_done_ratio
|
||||
status.default_done_ratio
|
||||
|
@ -448,6 +535,25 @@ class Issue < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
# Validates the issue against additional workflow requirements
|
||||
def validate_required_fields
|
||||
user = new_record? ? author : current_journal.try(:user)
|
||||
|
||||
required_attribute_names(user).each do |attribute|
|
||||
if attribute =~ /^\d+$/
|
||||
attribute = attribute.to_i
|
||||
v = custom_field_values.detect {|v| v.custom_field_id == attribute }
|
||||
if v && v.value.blank?
|
||||
errors.add :base, v.custom_field.name + ' ' + l('activerecord.errors.messages.blank')
|
||||
end
|
||||
else
|
||||
if respond_to?(attribute) && send(attribute).blank?
|
||||
errors.add attribute, :blank
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Set the done_ratio using the status if that setting is set. This will keep the done_ratios
|
||||
# even if the user turns off the setting later
|
||||
def update_done_ratio_from_issue_status
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
|
||||
class IssueStatus < ActiveRecord::Base
|
||||
before_destroy :check_integrity
|
||||
has_many :workflows, :foreign_key => "old_status_id"
|
||||
has_many :workflows, :class_name => 'WorkflowTransition', :foreign_key => "old_status_id"
|
||||
acts_as_list
|
||||
|
||||
before_destroy :delete_workflows
|
||||
before_destroy :delete_workflow_rules
|
||||
after_save :update_default
|
||||
|
||||
validates_presence_of :name
|
||||
|
@ -98,7 +98,7 @@ private
|
|||
end
|
||||
|
||||
# Deletes associated workflows
|
||||
def delete_workflows
|
||||
Workflow.delete_all(["old_status_id = :id OR new_status_id = :id", {:id => id}])
|
||||
def delete_workflow_rules
|
||||
WorkflowRule.delete_all(["old_status_id = :id OR new_status_id = :id", {:id => id}])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -47,9 +47,9 @@ class Role < ActiveRecord::Base
|
|||
}
|
||||
|
||||
before_destroy :check_deletable
|
||||
has_many :workflows, :dependent => :delete_all do
|
||||
has_many :workflow_rules, :dependent => :delete_all do
|
||||
def copy(source_role)
|
||||
Workflow.copy(nil, source_role, nil, proxy_association.owner)
|
||||
WorkflowRule.copy(nil, source_role, nil, proxy_association.owner)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -17,14 +17,17 @@
|
|||
|
||||
class Tracker < ActiveRecord::Base
|
||||
|
||||
# Other fields should be appended, not inserted!
|
||||
CORE_FIELDS = %w(assigned_to_id category_id fixed_version_id parent_issue_id start_date due_date estimated_hours done_ratio)
|
||||
CORE_FIELDS_UNDISABLABLE = %w(project_id tracker_id subject description priority_id is_private).freeze
|
||||
# Fields that can be disabled
|
||||
# Other (future) fields should be appended, not inserted!
|
||||
CORE_FIELDS = %w(assigned_to_id category_id fixed_version_id parent_issue_id start_date due_date estimated_hours done_ratio).freeze
|
||||
CORE_FIELDS_ALL = (CORE_FIELDS_UNDISABLABLE + CORE_FIELDS).freeze
|
||||
|
||||
before_destroy :check_integrity
|
||||
has_many :issues
|
||||
has_many :workflows, :dependent => :delete_all do
|
||||
has_many :workflow_rules, :dependent => :delete_all do
|
||||
def copy(source_tracker)
|
||||
Workflow.copy(source_tracker, nil, proxy_association.owner, nil)
|
||||
WorkflowRule.copy(source_tracker, nil, proxy_association.owner, nil)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -56,8 +59,8 @@ class Tracker < ActiveRecord::Base
|
|||
return []
|
||||
end
|
||||
|
||||
ids = Workflow.
|
||||
connection.select_rows("SELECT DISTINCT old_status_id, new_status_id FROM #{Workflow.table_name} WHERE tracker_id = #{id}").
|
||||
ids = WorkflowTransition.
|
||||
connection.select_rows("SELECT DISTINCT old_status_id, new_status_id FROM #{WorkflowTransition.table_name} WHERE tracker_id = #{id} AND type = 'WorkflowTransition'").
|
||||
flatten.
|
||||
uniq
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
class WorkflowPermission < WorkflowRule
|
||||
validates_inclusion_of :rule, :in => %w(readonly required)
|
||||
validate :validate_field_name
|
||||
|
||||
protected
|
||||
|
||||
def validate_field_name
|
||||
unless Tracker::CORE_FIELDS_ALL.include?(field_name) || field_name.to_s.match(/^\d+$/)
|
||||
errors.add :field_name, :invalid
|
||||
end
|
||||
end
|
||||
end
|
|
@ -15,31 +15,15 @@
|
|||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
class Workflow < ActiveRecord::Base
|
||||
class WorkflowRule < ActiveRecord::Base
|
||||
self.table_name = "#{table_name_prefix}workflows#{table_name_suffix}"
|
||||
|
||||
belongs_to :role
|
||||
belongs_to :tracker
|
||||
belongs_to :old_status, :class_name => 'IssueStatus', :foreign_key => 'old_status_id'
|
||||
belongs_to :new_status, :class_name => 'IssueStatus', :foreign_key => 'new_status_id'
|
||||
|
||||
validates_presence_of :role, :old_status, :new_status
|
||||
|
||||
# Returns workflow transitions count by tracker and role
|
||||
def self.count_by_tracker_and_role
|
||||
counts = connection.select_all("SELECT role_id, tracker_id, count(id) AS c FROM #{Workflow.table_name} GROUP BY role_id, tracker_id")
|
||||
roles = Role.sorted.all
|
||||
trackers = Tracker.sorted.all
|
||||
|
||||
result = []
|
||||
trackers.each do |tracker|
|
||||
t = []
|
||||
roles.each do |role|
|
||||
row = counts.detect {|c| c['role_id'].to_s == role.id.to_s && c['tracker_id'].to_s == tracker.id.to_s}
|
||||
t << [role, (row.nil? ? 0 : row['c'].to_i)]
|
||||
end
|
||||
result << [tracker, t]
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
validates_presence_of :role, :tracker, :old_status
|
||||
|
||||
# Copies workflows from source to targets
|
||||
def self.copy(source_tracker, source_role, target_trackers, target_roles)
|
||||
|
@ -78,9 +62,9 @@ class Workflow < ActiveRecord::Base
|
|||
else
|
||||
transaction do
|
||||
delete_all :tracker_id => target_tracker.id, :role_id => target_role.id
|
||||
connection.insert "INSERT INTO #{Workflow.table_name} (tracker_id, role_id, old_status_id, new_status_id, author, assignee)" +
|
||||
" SELECT #{target_tracker.id}, #{target_role.id}, old_status_id, new_status_id, author, assignee" +
|
||||
" FROM #{Workflow.table_name}" +
|
||||
connection.insert "INSERT INTO #{WorkflowRule.table_name} (tracker_id, role_id, old_status_id, new_status_id, author, assignee, field_name, rule, type)" +
|
||||
" SELECT #{target_tracker.id}, #{target_role.id}, old_status_id, new_status_id, author, assignee, field_name, rule, type" +
|
||||
" FROM #{WorkflowRule.table_name}" +
|
||||
" WHERE tracker_id = #{source_tracker.id} AND role_id = #{source_role.id}"
|
||||
end
|
||||
true
|
|
@ -0,0 +1,39 @@
|
|||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2012 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
class WorkflowTransition < WorkflowRule
|
||||
validates_presence_of :new_status
|
||||
|
||||
# Returns workflow transitions count by tracker and role
|
||||
def self.count_by_tracker_and_role
|
||||
counts = connection.select_all("SELECT role_id, tracker_id, count(id) AS c FROM #{table_name} WHERE type = 'WorkflowTransition' GROUP BY role_id, tracker_id")
|
||||
roles = Role.sorted.all
|
||||
trackers = Tracker.sorted.all
|
||||
|
||||
result = []
|
||||
trackers.each do |tracker|
|
||||
t = []
|
||||
roles.each do |role|
|
||||
row = counts.detect {|c| c['role_id'].to_s == role.id.to_s && c['tracker_id'].to_s == tracker.id.to_s}
|
||||
t << [role, (row.nil? ? 0 : row['c'].to_i)]
|
||||
end
|
||||
result << [tracker, t]
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
end
|
|
@ -4,6 +4,9 @@
|
|||
<div class="splitcontentleft">
|
||||
<% if @issue.safe_attribute? 'status_id' %>
|
||||
<p><%= f.select :status_id, (@allowed_statuses.collect {|p| [p.name, p.id]}), :required => true %></p>
|
||||
<%= observe_field :issue_status_id, :url => project_issue_form_path(@project, :id => @issue),
|
||||
:with => "Form.serialize('issue-form')" %>
|
||||
|
||||
<% else %>
|
||||
<p><label><%= l(:field_status) %></label> <%= h(@issue.status.name) %></p>
|
||||
<% end %>
|
||||
|
@ -13,11 +16,11 @@
|
|||
<% end %>
|
||||
|
||||
<% if @issue.safe_attribute? 'assigned_to_id' %>
|
||||
<p><%= f.select :assigned_to_id, principals_options_for_select(@issue.assignable_users, @issue.assigned_to), :include_blank => true %></p>
|
||||
<p><%= f.select :assigned_to_id, principals_options_for_select(@issue.assignable_users, @issue.assigned_to), :include_blank => true, :required => @issue.required_attribute?('assigned_to_id') %></p>
|
||||
<% end %>
|
||||
|
||||
<% if @issue.safe_attribute?('category_id') && @issue.project.issue_categories.any? %>
|
||||
<p><%= f.select :category_id, (@issue.project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %>
|
||||
<p><%= f.select :category_id, (@issue.project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true, :required => @issue.required_attribute?('category_id') %>
|
||||
<%= link_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'),
|
||||
{:url => new_project_issue_category_path(@issue.project), :method => 'get'},
|
||||
:title => l(:label_issue_category_new),
|
||||
|
@ -25,7 +28,7 @@
|
|||
<% end %>
|
||||
|
||||
<% if @issue.safe_attribute?('fixed_version_id') && @issue.assignable_versions.any? %>
|
||||
<p><%= f.select :fixed_version_id, version_options_for_select(@issue.assignable_versions, @issue.fixed_version), :include_blank => true %>
|
||||
<p><%= f.select :fixed_version_id, version_options_for_select(@issue.assignable_versions, @issue.fixed_version), :include_blank => true, :required => @issue.required_attribute?('fixed_version_id') %>
|
||||
<%= link_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'),
|
||||
{:url => new_project_version_path(@issue.project), :method => 'get'},
|
||||
:title => l(:label_version_new),
|
||||
|
@ -36,25 +39,25 @@
|
|||
|
||||
<div class="splitcontentright">
|
||||
<% if @issue.safe_attribute? 'parent_issue_id' %>
|
||||
<p id="parent_issue"><%= f.text_field :parent_issue_id, :size => 10 %></p>
|
||||
<p id="parent_issue"><%= f.text_field :parent_issue_id, :size => 10, :required => @issue.required_attribute?('parent_issue_id') %></p>
|
||||
<div id="parent_issue_candidates" class="autocomplete"></div>
|
||||
<%= javascript_tag "observeParentIssueField('#{auto_complete_issues_path(:id => @issue, :project_id => @issue.project) }')" %>
|
||||
<% end %>
|
||||
|
||||
<% if @issue.safe_attribute? 'start_date' %>
|
||||
<p><%= f.text_field :start_date, :size => 10, :disabled => !@issue.leaf? %><%= calendar_for('issue_start_date') if @issue.leaf? %></p>
|
||||
<p><%= f.text_field :start_date, :size => 10, :disabled => !@issue.leaf?, :required => @issue.required_attribute?('start_date') %><%= calendar_for('issue_start_date') if @issue.leaf? %></p>
|
||||
<% end %>
|
||||
|
||||
<% if @issue.safe_attribute? 'due_date' %>
|
||||
<p><%= f.text_field :due_date, :size => 10, :disabled => !@issue.leaf? %><%= calendar_for('issue_due_date') if @issue.leaf? %></p>
|
||||
<p><%= f.text_field :due_date, :size => 10, :disabled => !@issue.leaf?, :required => @issue.required_attribute?('due_date') %><%= calendar_for('issue_due_date') if @issue.leaf? %></p>
|
||||
<% end %>
|
||||
|
||||
<% if @issue.safe_attribute? 'estimated_hours' %>
|
||||
<p><%= f.text_field :estimated_hours, :size => 3, :disabled => !@issue.leaf? %> <%= l(:field_hours) %></p>
|
||||
<p><%= f.text_field :estimated_hours, :size => 3, :disabled => !@issue.leaf?, :required => @issue.required_attribute?('estimated_hours') %> <%= l(:field_hours) %></p>
|
||||
<% end %>
|
||||
|
||||
<% if @issue.safe_attribute?('done_ratio') && @issue.leaf? && Issue.use_field_for_done_ratio? %>
|
||||
<p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p>
|
||||
<p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }), :required => @issue.required_attribute?('done_ratio') %></p>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
<% if @issue.safe_attribute? 'project_id' %>
|
||||
<p><%= f.select :project_id, project_tree_options_for_select(@issue.allowed_target_projects, :selected => @issue.project), :required => true %></p>
|
||||
<%= observe_field :issue_project_id, :url => project_issue_form_path(@project, :id => @issue, :project_change => '1'),
|
||||
<%= observe_field :issue_project_id, :url => project_issue_form_path(@project, :id => @issue),
|
||||
:with => "Form.serialize('issue-form')" %>
|
||||
<% end %>
|
||||
|
||||
|
@ -25,7 +25,7 @@
|
|||
|
||||
<% if @issue.safe_attribute? 'description' %>
|
||||
<p>
|
||||
<label><%= l(:field_description) %></label>
|
||||
<%= f.label_for_field :description, :required => @issue.required_attribute?('description') %>
|
||||
<%= link_to_function image_tag('edit.png'),
|
||||
'Element.hide(this); Effect.toggle("issue_description_and_toolbar", "appear", {duration:0.3})' unless @issue.new_record? %>
|
||||
<%= content_tag 'span', :id => "issue_description_and_toolbar", :style => (@issue.new_record? ? nil : 'display:none') do %>
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
<div class="splitcontentleft">
|
||||
<% i = 0 %>
|
||||
<% split_on = (@issue.custom_field_values.size / 2.0).ceil - 1 %>
|
||||
<% @issue.custom_field_values.each do |value| %>
|
||||
<p><%= custom_field_tag_with_label :issue, value %></p>
|
||||
<% @issue.editable_custom_field_values.each do |value| %>
|
||||
<p><%= custom_field_tag_with_label :issue, value, :required => @issue.required_attribute?(value.custom_field_id) %></p>
|
||||
<% if i == split_on -%>
|
||||
</div><div class="splitcontentright">
|
||||
<% end -%>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<% for tracker in @trackers %>
|
||||
<tr class="<%= cycle("odd", "even") %>">
|
||||
<td><%= link_to h(tracker.name), edit_tracker_path(tracker) %></td>
|
||||
<td align="center"><% unless tracker.workflows.count > 0 %><span class="icon icon-warning"><%= l(:text_tracker_no_workflow) %> (<%= link_to l(:button_edit), {:controller => 'workflows', :action => 'edit', :tracker_id => tracker} %>)</span><% end %></td>
|
||||
<td align="center"><% unless tracker.workflow_rules.count > 0 %><span class="icon icon-warning"><%= l(:text_tracker_no_workflow) %> (<%= link_to l(:button_edit), {:controller => 'workflows', :action => 'edit', :tracker_id => tracker} %>)</span><% end %></td>
|
||||
<td align="center" style="width:15%;"><%= reorder_links('tracker', {:action => 'update', :id => tracker}, :put) %></td>
|
||||
<td class="buttons">
|
||||
<%= delete_link tracker_path(tracker) %>
|
||||
|
|
|
@ -2,6 +2,13 @@
|
|||
|
||||
<h2><%=l(:label_workflow)%></h2>
|
||||
|
||||
<div class="tabs">
|
||||
<ul>
|
||||
<li><%= link_to 'Status transitions', {:action => 'edit', :role_id => @role, :tracker_id => @tracker}, :class => 'selected' %></li>
|
||||
<li><%= link_to 'Fields permissions', {:action => 'permissions', :role_id => @role, :tracker_id => @tracker} %></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<p><%=l(:text_workflow_edit)%>:</p>
|
||||
|
||||
<%= form_tag({}, :method => 'get') do %>
|
||||
|
@ -12,11 +19,11 @@
|
|||
<label><%=l(:label_tracker)%>:
|
||||
<%= select_tag 'tracker_id', options_from_collection_for_select(@trackers, "id", "name", @tracker && @tracker.id) %></label>
|
||||
|
||||
<%= submit_tag l(:button_edit), :name => nil %>
|
||||
|
||||
<%= hidden_field_tag 'used_statuses_only', '0' %>
|
||||
<label><%= check_box_tag 'used_statuses_only', '1', @used_statuses_only %> <%= l(:label_display_used_statuses_only) %></label>
|
||||
</p>
|
||||
<p>
|
||||
<%= submit_tag l(:button_edit), :name => nil %>
|
||||
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
<%= render :partial => 'action_menu' %>
|
||||
|
||||
<h2><%=l(:label_workflow)%></h2>
|
||||
|
||||
<div class="tabs">
|
||||
<ul>
|
||||
<li><%= link_to 'Status transitions', {:action => 'edit', :role_id => @role, :tracker_id => @tracker} %></li>
|
||||
<li><%= link_to 'Fields permissions', {:action => 'permissions', :role_id => @role, :tracker_id => @tracker}, :class => 'selected' %></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<p><%=l(:text_workflow_edit)%>:</p>
|
||||
|
||||
<%= form_tag({}, :method => 'get') do %>
|
||||
<p>
|
||||
<label><%=l(:label_role)%>:
|
||||
<%= select_tag 'role_id', options_from_collection_for_select(@roles, "id", "name", @role && @role.id) %></label>
|
||||
|
||||
<label><%=l(:label_tracker)%>:
|
||||
<%= select_tag 'tracker_id', options_from_collection_for_select(@trackers, "id", "name", @tracker && @tracker.id) %></label>
|
||||
|
||||
<%= submit_tag l(:button_edit), :name => nil %>
|
||||
</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 fields_permissions">
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="left">
|
||||
</th>
|
||||
<th align="center" colspan="<%= @statuses.length %>"><%=l(:label_issue_status)%></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<% for status in @statuses %>
|
||||
<td width="<%= 75 / @statuses.size %>%" align="center">
|
||||
<%=h status.name %>
|
||||
</td>
|
||||
<% end %>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="group open">
|
||||
<td colspan="<%= @statuses.size + 1 %>">
|
||||
<span class="expander" onclick="toggleRowGroup(this);"> </span>
|
||||
<%= l(:field_core_fields) %>
|
||||
</td>
|
||||
</tr>
|
||||
<% @fields.each do |field, name| %>
|
||||
<tr class="<%= cycle("odd", "even") %>">
|
||||
<td>
|
||||
<%=h name %>
|
||||
</td>
|
||||
<% for status in @statuses -%>
|
||||
<td align="center" class="<%= @permissions[status.id][field] %>">
|
||||
<%= field_permission_tag(@permissions, status, field) %>
|
||||
</td>
|
||||
<% end -%>
|
||||
</tr>
|
||||
<% end %>
|
||||
<% if @custom_fields.any? %>
|
||||
<tr class="group open">
|
||||
<td colspan="<%= @statuses.size + 1 %>">
|
||||
<span class="expander" onclick="toggleRowGroup(this);"> </span>
|
||||
<%= l(:label_custom_field_plural) %>
|
||||
</td>
|
||||
</tr>
|
||||
<% @custom_fields.each do |field| %>
|
||||
<tr class="<%= cycle("odd", "even") %>">
|
||||
<td>
|
||||
<%=h field.name %>
|
||||
</td>
|
||||
<% for status in @statuses -%>
|
||||
<td align="center" class="<%= @permissions[status.id][field.id.to_s] %>">
|
||||
<%= field_permission_tag(@permissions, status, field) %>
|
||||
</td>
|
||||
<% end -%>
|
||||
</tr>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<%= submit_tag l(:button_save) %>
|
||||
<% end %>
|
||||
<% end %>
|
|
@ -312,6 +312,7 @@ RedmineApp::Application.routes.draw do
|
|||
|
||||
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]
|
||||
match 'settings', :controller => 'settings', :action => 'index', :via => :get
|
||||
match 'settings/edit', :controller => 'settings', :action => 'edit', :via => [:get, :post]
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
class AddWorkflowsType < ActiveRecord::Migration
|
||||
def up
|
||||
add_column :workflows, :type, :string, :limit => 30
|
||||
end
|
||||
|
||||
def down
|
||||
remove_column :workflows, :type
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
class UpdateWorkflowsToSti < ActiveRecord::Migration
|
||||
def up
|
||||
WorkflowRule.update_all "type = 'WorkflowTransition'"
|
||||
end
|
||||
|
||||
def down
|
||||
WorkflowRule.update_all "type = NULL"
|
||||
end
|
||||
end
|
|
@ -0,0 +1,11 @@
|
|||
class AddWorkflowsRuleFields < ActiveRecord::Migration
|
||||
def up
|
||||
add_column :workflows, :field_name, :string, :limit => 30
|
||||
add_column :workflows, :rule, :string, :limit => 30
|
||||
end
|
||||
|
||||
def down
|
||||
remove_column :workflows, :field_name
|
||||
remove_column :workflows, :rule
|
||||
end
|
||||
end
|
|
@ -140,7 +140,7 @@ module Redmine
|
|||
Tracker.find(:all).each { |t|
|
||||
IssueStatus.find(:all).each { |os|
|
||||
IssueStatus.find(:all).each { |ns|
|
||||
Workflow.create!(:tracker_id => t.id, :role_id => manager.id, :old_status_id => os.id, :new_status_id => ns.id) unless os == ns
|
||||
WorkflowTransition.create!(:tracker_id => t.id, :role_id => manager.id, :old_status_id => os.id, :new_status_id => ns.id) unless os == ns
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ module Redmine
|
|||
Tracker.find(:all).each { |t|
|
||||
[new, in_progress, resolved, feedback].each { |os|
|
||||
[in_progress, resolved, feedback, closed].each { |ns|
|
||||
Workflow.create!(:tracker_id => t.id, :role_id => developer.id, :old_status_id => os.id, :new_status_id => ns.id) unless os == ns
|
||||
WorkflowTransition.create!(:tracker_id => t.id, :role_id => developer.id, :old_status_id => os.id, :new_status_id => ns.id) unless os == ns
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,10 +156,10 @@ module Redmine
|
|||
Tracker.find(:all).each { |t|
|
||||
[new, in_progress, resolved, feedback].each { |os|
|
||||
[closed].each { |ns|
|
||||
Workflow.create!(:tracker_id => t.id, :role_id => reporter.id, :old_status_id => os.id, :new_status_id => ns.id) unless os == ns
|
||||
WorkflowTransition.create!(:tracker_id => t.id, :role_id => reporter.id, :old_status_id => os.id, :new_status_id => ns.id) unless os == ns
|
||||
}
|
||||
}
|
||||
Workflow.create!(:tracker_id => t.id, :role_id => reporter.id, :old_status_id => resolved.id, :new_status_id => feedback.id)
|
||||
WorkflowTransition.create!(:tracker_id => t.id, :role_id => reporter.id, :old_status_id => resolved.id, :new_status_id => feedback.id)
|
||||
}
|
||||
|
||||
# Enumerations
|
||||
|
|
|
@ -433,6 +433,9 @@ ul.properties li span {font-style:italic;}
|
|||
#user_login, #user_firstname, #user_lastname, #user_mail, #my_account_form select, #user_form select, #user_identity_url { width: 90%; }
|
||||
|
||||
#workflow_copy_form select { width: 200px; }
|
||||
table.fields_permissions select {font-size:90%}
|
||||
table.fields_permissions td.readonly {background:#ddd;}
|
||||
table.fields_permissions td.required {background:#d88;}
|
||||
|
||||
textarea#custom_field_possible_values {width: 99%}
|
||||
input#content_comments {width: 99%}
|
||||
|
@ -504,7 +507,7 @@ input#time_entry_comments { width: 90%;}
|
|||
fieldset.settings label { display: block; }
|
||||
fieldset#notified_events .parent { padding-left: 20px; }
|
||||
|
||||
.required {color: #bb0000;}
|
||||
span.required {color: #bb0000;}
|
||||
.summary {font-style: italic;}
|
||||
|
||||
#attachments_fields input.description {margin-left: 8px; width:340px;}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -823,7 +823,7 @@ class IssuesControllerTest < ActionController::TestCase
|
|||
|
||||
def test_show_should_display_update_form_with_minimal_permissions
|
||||
Role.find(1).update_attribute :permissions, [:view_issues, :add_issue_notes]
|
||||
Workflow.delete_all :role_id => 1
|
||||
WorkflowTransition.delete_all :role_id => 1
|
||||
|
||||
@request.session[:user_id] = 2
|
||||
get :show, :id => 1
|
||||
|
@ -1300,7 +1300,7 @@ class IssuesControllerTest < ActionController::TestCase
|
|||
|
||||
def test_get_new_with_minimal_permissions
|
||||
Role.find(1).update_attribute :permissions, [:add_issues]
|
||||
Workflow.delete_all :role_id => 1
|
||||
WorkflowTransition.delete_all :role_id => 1
|
||||
|
||||
@request.session[:user_id] = 2
|
||||
get :new, :project_id => 1, :tracker_id => 1
|
||||
|
@ -1426,6 +1426,50 @@ class IssuesControllerTest < ActionController::TestCase
|
|||
:attributes => {:name => 'issue[custom_field_values][2]', :value => 'Custom field value'}
|
||||
end
|
||||
|
||||
def test_get_new_should_mark_required_fields
|
||||
cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
|
||||
cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
|
||||
WorkflowPermission.delete_all
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'required')
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'required')
|
||||
@request.session[:user_id] = 2
|
||||
|
||||
get :new, :project_id => 1
|
||||
assert_response :success
|
||||
assert_template 'new'
|
||||
|
||||
assert_select 'label[for=issue_start_date]' do
|
||||
assert_select 'span[class=required]', 0
|
||||
end
|
||||
assert_select 'label[for=issue_due_date]' do
|
||||
assert_select 'span[class=required]'
|
||||
end
|
||||
assert_select 'label[for=?]', "issue_custom_field_values_#{cf1.id}" do
|
||||
assert_select 'span[class=required]', 0
|
||||
end
|
||||
assert_select 'label[for=?]', "issue_custom_field_values_#{cf2.id}" do
|
||||
assert_select 'span[class=required]'
|
||||
end
|
||||
end
|
||||
|
||||
def test_get_new_should_not_display_readonly_fields
|
||||
cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
|
||||
cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
|
||||
WorkflowPermission.delete_all
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
|
||||
@request.session[:user_id] = 2
|
||||
|
||||
get :new, :project_id => 1
|
||||
assert_response :success
|
||||
assert_template 'new'
|
||||
|
||||
assert_select 'input[name=?]', 'issue[start_date]'
|
||||
assert_select 'input[name=?]', 'issue[due_date]', 0
|
||||
assert_select 'input[name=?]', "issue[custom_field_values][#{cf1.id}]"
|
||||
assert_select 'input[name=?]', "issue[custom_field_values][#{cf2.id}]", 0
|
||||
end
|
||||
|
||||
def test_get_new_without_tracker_id
|
||||
@request.session[:user_id] = 2
|
||||
get :new, :project_id => 1
|
||||
|
@ -1463,7 +1507,7 @@ class IssuesControllerTest < ActionController::TestCase
|
|||
:description => 'This is the description',
|
||||
:priority_id => 5}
|
||||
assert_response :success
|
||||
assert_template 'attributes'
|
||||
assert_template 'form'
|
||||
|
||||
issue = assigns(:issue)
|
||||
assert_kind_of Issue, issue
|
||||
|
@ -1474,10 +1518,10 @@ class IssuesControllerTest < ActionController::TestCase
|
|||
|
||||
def test_update_new_form_should_propose_transitions_based_on_initial_status
|
||||
@request.session[:user_id] = 2
|
||||
Workflow.delete_all
|
||||
Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 2)
|
||||
Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 5)
|
||||
Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 5, :new_status_id => 4)
|
||||
WorkflowTransition.delete_all
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 2)
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 5)
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 5, :new_status_id => 4)
|
||||
|
||||
xhr :post, :new, :project_id => 1,
|
||||
:issue => {:tracker_id => 1,
|
||||
|
@ -1678,6 +1722,58 @@ class IssuesControllerTest < ActionController::TestCase
|
|||
assert_error_tag :content => /Database can't be blank/
|
||||
end
|
||||
|
||||
def test_create_should_validate_required_fields
|
||||
cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
|
||||
cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
|
||||
WorkflowPermission.delete_all
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => 'due_date', :rule => 'required')
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'required')
|
||||
@request.session[:user_id] = 2
|
||||
|
||||
assert_no_difference 'Issue.count' do
|
||||
post :create, :project_id => 1, :issue => {
|
||||
:tracker_id => 2,
|
||||
:status_id => 1,
|
||||
:subject => 'Test',
|
||||
:start_date => '',
|
||||
:due_date => '',
|
||||
:custom_field_values => {cf1.id.to_s => '', cf2.id.to_s => ''}
|
||||
}
|
||||
assert_response :success
|
||||
assert_template 'new'
|
||||
end
|
||||
|
||||
assert_error_tag :content => /Due date can't be blank/i
|
||||
assert_error_tag :content => /Bar can't be blank/i
|
||||
end
|
||||
|
||||
def test_create_should_ignore_readonly_fields
|
||||
cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
|
||||
cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
|
||||
WorkflowPermission.delete_all
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
|
||||
@request.session[:user_id] = 2
|
||||
|
||||
assert_difference 'Issue.count' do
|
||||
post :create, :project_id => 1, :issue => {
|
||||
:tracker_id => 2,
|
||||
:status_id => 1,
|
||||
:subject => 'Test',
|
||||
:start_date => '2012-07-14',
|
||||
:due_date => '2012-07-16',
|
||||
:custom_field_values => {cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'}
|
||||
}
|
||||
assert_response 302
|
||||
end
|
||||
|
||||
issue = Issue.first(:order => 'id DESC')
|
||||
assert_equal Date.parse('2012-07-14'), issue.start_date
|
||||
assert_nil issue.due_date
|
||||
assert_equal 'value1', issue.custom_field_value(cf1)
|
||||
assert_nil issue.custom_field_value(cf2)
|
||||
end
|
||||
|
||||
def test_post_create_with_watchers
|
||||
@request.session[:user_id] = 2
|
||||
ActionMailer::Base.deliveries.clear
|
||||
|
@ -1917,7 +2013,7 @@ class IssuesControllerTest < ActionController::TestCase
|
|||
|
||||
context "without workflow privilege" do
|
||||
setup do
|
||||
Workflow.delete_all(["role_id = ?", Role.anonymous.id])
|
||||
WorkflowTransition.delete_all(["role_id = ?", Role.anonymous.id])
|
||||
Role.anonymous.add_permission! :add_issues, :add_issue_notes
|
||||
end
|
||||
|
||||
|
@ -1976,9 +2072,9 @@ class IssuesControllerTest < ActionController::TestCase
|
|||
|
||||
context "with workflow privilege" do
|
||||
setup do
|
||||
Workflow.delete_all(["role_id = ?", Role.anonymous.id])
|
||||
Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3)
|
||||
Workflow.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4)
|
||||
WorkflowTransition.delete_all(["role_id = ?", Role.anonymous.id])
|
||||
WorkflowTransition.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3)
|
||||
WorkflowTransition.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4)
|
||||
Role.anonymous.add_permission! :add_issues, :add_issue_notes
|
||||
end
|
||||
|
||||
|
@ -2286,7 +2382,7 @@ class IssuesControllerTest < ActionController::TestCase
|
|||
:description => 'This is the description',
|
||||
:priority_id => 5}
|
||||
assert_response :success
|
||||
assert_template 'attributes'
|
||||
assert_template 'form'
|
||||
|
||||
issue = assigns(:issue)
|
||||
assert_kind_of Issue, issue
|
||||
|
@ -2298,10 +2394,10 @@ class IssuesControllerTest < ActionController::TestCase
|
|||
|
||||
def test_update_edit_form_should_propose_transitions_based_on_initial_status
|
||||
@request.session[:user_id] = 2
|
||||
Workflow.delete_all
|
||||
Workflow.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 1)
|
||||
Workflow.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 5)
|
||||
Workflow.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 5, :new_status_id => 4)
|
||||
WorkflowTransition.delete_all
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 1)
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 5)
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 5, :new_status_id => 4)
|
||||
|
||||
xhr :put, :new, :project_id => 1,
|
||||
:id => 2,
|
||||
|
@ -2317,7 +2413,6 @@ class IssuesControllerTest < ActionController::TestCase
|
|||
@request.session[:user_id] = 2
|
||||
xhr :put, :new, :project_id => 1,
|
||||
:id => 1,
|
||||
:project_change => '1',
|
||||
:issue => {:project_id => 2,
|
||||
:tracker_id => 2,
|
||||
:subject => 'This is the test_new issue',
|
||||
|
@ -2845,13 +2940,13 @@ class IssuesControllerTest < ActionController::TestCase
|
|||
end
|
||||
|
||||
def test_bulk_edit_should_only_propose_statuses_allowed_for_all_issues
|
||||
Workflow.delete_all
|
||||
Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 1)
|
||||
Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3)
|
||||
Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4)
|
||||
Workflow.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 1)
|
||||
Workflow.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 3)
|
||||
Workflow.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 5)
|
||||
WorkflowTransition.delete_all
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 1)
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3)
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4)
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 1)
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 3)
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 5)
|
||||
@request.session[:user_id] = 2
|
||||
get :bulk_edit, :ids => [1, 2]
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ class RolesControllerTest < ActionController::TestCase
|
|||
assert_redirected_to '/roles'
|
||||
role = Role.find_by_name('RoleWithWorkflowCopy')
|
||||
assert_not_nil role
|
||||
assert_equal Role.find(1).workflows.size, role.workflows.size
|
||||
assert_equal Role.find(1).workflow_rules.size, role.workflow_rules.size
|
||||
end
|
||||
|
||||
def test_edit
|
||||
|
|
|
@ -66,7 +66,7 @@ class TrackersControllerTest < ActionController::TestCase
|
|||
assert_equal [1], tracker.project_ids.sort
|
||||
assert_equal Tracker::CORE_FIELDS, tracker.core_fields
|
||||
assert_equal [1, 6], tracker.custom_field_ids.sort
|
||||
assert_equal 0, tracker.workflows.count
|
||||
assert_equal 0, tracker.workflow_rules.count
|
||||
end
|
||||
|
||||
def create_with_disabled_core_fields
|
||||
|
@ -86,7 +86,7 @@ class TrackersControllerTest < ActionController::TestCase
|
|||
assert_redirected_to :action => 'index'
|
||||
tracker = Tracker.find_by_name('New tracker')
|
||||
assert_equal 0, tracker.projects.count
|
||||
assert_equal Tracker.find(1).workflows.count, tracker.workflows.count
|
||||
assert_equal Tracker.find(1).workflow_rules.count, tracker.workflow_rules.count
|
||||
end
|
||||
|
||||
def test_create_with_failure
|
||||
|
|
|
@ -37,7 +37,7 @@ class WorkflowsControllerTest < ActionController::TestCase
|
|||
assert_response :success
|
||||
assert_template 'index'
|
||||
|
||||
count = Workflow.count(:all, :conditions => 'role_id = 1 AND tracker_id = 2')
|
||||
count = WorkflowTransition.count(:all, :conditions => 'role_id = 1 AND tracker_id = 2')
|
||||
assert_tag :tag => 'a', :content => count.to_s,
|
||||
:attributes => { :href => '/workflows/edit?role_id=1&tracker_id=2' }
|
||||
end
|
||||
|
@ -51,9 +51,9 @@ class WorkflowsControllerTest < ActionController::TestCase
|
|||
end
|
||||
|
||||
def test_get_edit_with_role_and_tracker
|
||||
Workflow.delete_all
|
||||
Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 2, :new_status_id => 3)
|
||||
Workflow.create!(:role_id => 2, :tracker_id => 1, :old_status_id => 3, :new_status_id => 5)
|
||||
WorkflowTransition.delete_all
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 2, :new_status_id => 3)
|
||||
WorkflowTransition.create!(:role_id => 2, :tracker_id => 1, :old_status_id => 3, :new_status_id => 5)
|
||||
|
||||
get :edit, :role_id => 2, :tracker_id => 1
|
||||
assert_response :success
|
||||
|
@ -79,7 +79,7 @@ class WorkflowsControllerTest < ActionController::TestCase
|
|||
end
|
||||
|
||||
def test_get_edit_with_role_and_tracker_and_all_statuses
|
||||
Workflow.delete_all
|
||||
WorkflowTransition.delete_all
|
||||
|
||||
get :edit, :role_id => 2, :tracker_id => 1, :used_statuses_only => '0'
|
||||
assert_response :success
|
||||
|
@ -102,9 +102,9 @@ class WorkflowsControllerTest < ActionController::TestCase
|
|||
}
|
||||
assert_redirected_to '/workflows/edit?role_id=2&tracker_id=1'
|
||||
|
||||
assert_equal 3, Workflow.count(:conditions => {:tracker_id => 1, :role_id => 2})
|
||||
assert_not_nil Workflow.find(:first, :conditions => {:role_id => 2, :tracker_id => 1, :old_status_id => 3, :new_status_id => 2})
|
||||
assert_nil Workflow.find(:first, :conditions => {:role_id => 2, :tracker_id => 1, :old_status_id => 5, :new_status_id => 4})
|
||||
assert_equal 3, WorkflowTransition.count(:conditions => {:tracker_id => 1, :role_id => 2})
|
||||
assert_not_nil WorkflowTransition.find(:first, :conditions => {:role_id => 2, :tracker_id => 1, :old_status_id => 3, :new_status_id => 2})
|
||||
assert_nil WorkflowTransition.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
|
||||
|
@ -115,27 +115,117 @@ class WorkflowsControllerTest < ActionController::TestCase
|
|||
}
|
||||
assert_redirected_to '/workflows/edit?role_id=2&tracker_id=1'
|
||||
|
||||
assert_equal 4, Workflow.count(:conditions => {:tracker_id => 1, :role_id => 2})
|
||||
assert_equal 4, WorkflowTransition.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})
|
||||
w = WorkflowTransition.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})
|
||||
w = WorkflowTransition.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})
|
||||
w = WorkflowTransition.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})
|
||||
w = WorkflowTransition.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
|
||||
assert WorkflowTransition.count(:conditions => {:tracker_id => 1, :role_id => 2}) > 0
|
||||
|
||||
post :edit, :role_id => 2, :tracker_id => 1
|
||||
assert_equal 0, Workflow.count(:conditions => {:tracker_id => 1, :role_id => 2})
|
||||
assert_equal 0, WorkflowTransition.count(:conditions => {:tracker_id => 1, :role_id => 2})
|
||||
end
|
||||
|
||||
def test_get_permissions
|
||||
get :permissions
|
||||
|
||||
assert_response :success
|
||||
assert_template 'permissions'
|
||||
assert_not_nil assigns(:roles)
|
||||
assert_not_nil assigns(:trackers)
|
||||
end
|
||||
|
||||
def test_get_permissions_with_role_and_tracker
|
||||
WorkflowPermission.delete_all
|
||||
WorkflowPermission.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :field_name => 'assigned_to_id', :rule => 'required')
|
||||
WorkflowPermission.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :field_name => 'fixed_version_id', :rule => 'required')
|
||||
WorkflowPermission.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 3, :field_name => 'fixed_version_id', :rule => 'readonly')
|
||||
|
||||
get :permissions, :role_id => 1, :tracker_id => 2
|
||||
assert_response :success
|
||||
assert_template 'permissions'
|
||||
|
||||
assert_select 'input[name=role_id][value=1]'
|
||||
assert_select 'input[name=tracker_id][value=2]'
|
||||
|
||||
# Required field
|
||||
assert_select 'select[name=?]', 'permissions[assigned_to_id][2]' do
|
||||
assert_select 'option[value=]'
|
||||
assert_select 'option[value=][selected=selected]', 0
|
||||
assert_select 'option[value=readonly]', :text => 'Read-only'
|
||||
assert_select 'option[value=readonly][selected=selected]', 0
|
||||
assert_select 'option[value=required]', :text => 'Required'
|
||||
assert_select 'option[value=required][selected=selected]'
|
||||
end
|
||||
|
||||
# Read-only field
|
||||
assert_select 'select[name=?]', 'permissions[fixed_version_id][3]' do
|
||||
assert_select 'option[value=]'
|
||||
assert_select 'option[value=][selected=selected]', 0
|
||||
assert_select 'option[value=readonly]', :text => 'Read-only'
|
||||
assert_select 'option[value=readonly][selected=selected]'
|
||||
assert_select 'option[value=required]', :text => 'Required'
|
||||
assert_select 'option[value=required][selected=selected]', 0
|
||||
end
|
||||
|
||||
# Other field
|
||||
assert_select 'select[name=?]', 'permissions[due_date][3]' do
|
||||
assert_select 'option[value=]'
|
||||
assert_select 'option[value=][selected=selected]', 0
|
||||
assert_select 'option[value=readonly]', :text => 'Read-only'
|
||||
assert_select 'option[value=readonly][selected=selected]', 0
|
||||
assert_select 'option[value=required]', :text => 'Required'
|
||||
assert_select 'option[value=required][selected=selected]', 0
|
||||
end
|
||||
end
|
||||
|
||||
def test_post_permissions
|
||||
WorkflowPermission.delete_all
|
||||
|
||||
post :permissions, :role_id => 1, :tracker_id => 2, :permissions => {
|
||||
'assigned_to_id' => {'1' => '', '2' => 'readonly', '3' => ''},
|
||||
'fixed_version_id' => {'1' => 'required', '2' => 'readonly', '3' => ''},
|
||||
'due_date' => {'1' => '', '2' => '', '3' => ''},
|
||||
}
|
||||
assert_redirected_to '/workflows/permissions?role_id=1&tracker_id=2'
|
||||
|
||||
workflows = WorkflowPermission.all
|
||||
assert_equal 3, workflows.size
|
||||
workflows.each do |workflow|
|
||||
assert_equal 1, workflow.role_id
|
||||
assert_equal 2, workflow.tracker_id
|
||||
end
|
||||
assert workflows.detect {|wf| wf.old_status_id == 2 && wf.field_name == 'assigned_to_id' && wf.rule == 'readonly'}
|
||||
assert workflows.detect {|wf| wf.old_status_id == 1 && wf.field_name == 'fixed_version_id' && wf.rule == 'required'}
|
||||
assert workflows.detect {|wf| wf.old_status_id == 2 && wf.field_name == 'fixed_version_id' && wf.rule == 'readonly'}
|
||||
end
|
||||
|
||||
def test_post_permissions_should_clear_permissions
|
||||
WorkflowPermission.delete_all
|
||||
WorkflowPermission.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :field_name => 'assigned_to_id', :rule => 'required')
|
||||
WorkflowPermission.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :field_name => 'fixed_version_id', :rule => 'required')
|
||||
wf1 = WorkflowPermission.create!(:role_id => 1, :tracker_id => 3, :old_status_id => 2, :field_name => 'fixed_version_id', :rule => 'required')
|
||||
wf2 = WorkflowPermission.create!(:role_id => 2, :tracker_id => 2, :old_status_id => 3, :field_name => 'fixed_version_id', :rule => 'readonly')
|
||||
|
||||
post :permissions, :role_id => 1, :tracker_id => 2
|
||||
assert_redirected_to '/workflows/permissions?role_id=1&tracker_id=2'
|
||||
|
||||
workflows = WorkflowPermission.all
|
||||
assert_equal 2, workflows.size
|
||||
assert wf1.reload
|
||||
assert wf2.reload
|
||||
end
|
||||
|
||||
def test_get_copy
|
||||
|
@ -192,7 +282,7 @@ class WorkflowsControllerTest < ActionController::TestCase
|
|||
|
||||
# Returns an array of status transitions that can be compared
|
||||
def status_transitions(conditions)
|
||||
Workflow.find(:all, :conditions => conditions,
|
||||
WorkflowTransition.find(:all, :conditions => conditions,
|
||||
:order => 'tracker_id, role_id, old_status_id, new_status_id').collect {|w| [w.old_status, w.new_status_id]}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -36,8 +36,8 @@ class IssueStatusTest < ActiveSupport::TestCase
|
|||
assert_difference 'IssueStatus.count', -1 do
|
||||
assert status.destroy
|
||||
end
|
||||
assert_nil Workflow.first(:conditions => {:old_status_id => status.id})
|
||||
assert_nil Workflow.first(:conditions => {:new_status_id => status.id})
|
||||
assert_nil WorkflowTransition.first(:conditions => {:old_status_id => status.id})
|
||||
assert_nil WorkflowTransition.first(:conditions => {:new_status_id => status.id})
|
||||
end
|
||||
|
||||
def test_destroy_status_in_use
|
||||
|
@ -70,12 +70,12 @@ class IssueStatusTest < ActiveSupport::TestCase
|
|||
end
|
||||
|
||||
def test_new_statuses_allowed_to
|
||||
Workflow.delete_all
|
||||
WorkflowTransition.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)
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 2, :author => false, :assignee => false)
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3, :author => true, :assignee => false)
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4, :author => false, :assignee => true)
|
||||
WorkflowTransition.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)
|
||||
|
|
|
@ -31,6 +31,10 @@ class IssueTest < ActiveSupport::TestCase
|
|||
|
||||
include Redmine::I18n
|
||||
|
||||
def teardown
|
||||
User.current = nil
|
||||
end
|
||||
|
||||
def test_create
|
||||
issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
|
||||
:status_id => 1, :priority => IssuePriority.all.first,
|
||||
|
@ -362,12 +366,12 @@ class IssueTest < ActiveSupport::TestCase
|
|||
end
|
||||
|
||||
def test_new_statuses_allowed_to
|
||||
Workflow.delete_all
|
||||
WorkflowTransition.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)
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 2, :author => false, :assignee => false)
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3, :author => true, :assignee => false)
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4, :author => false, :assignee => true)
|
||||
WorkflowTransition.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)
|
||||
|
@ -390,7 +394,7 @@ class IssueTest < ActiveSupport::TestCase
|
|||
admin = User.find(1)
|
||||
issue = Issue.find(1)
|
||||
assert !admin.member_of?(issue.project)
|
||||
expected_statuses = [issue.status] + Workflow.find_all_by_old_status_id(issue.status_id).map(&:new_status).uniq.sort
|
||||
expected_statuses = [issue.status] + WorkflowTransition.find_all_by_old_status_id(issue.status_id).map(&:new_status).uniq.sort
|
||||
|
||||
assert_equal expected_statuses, issue.new_statuses_allowed_to(admin)
|
||||
end
|
||||
|
@ -403,7 +407,7 @@ class IssueTest < ActiveSupport::TestCase
|
|||
assert_equal [1, 2], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
|
||||
end
|
||||
|
||||
def test_safe_attributes_should_not_include_disabled_field
|
||||
def test_safe_attributes_names_should_not_include_disabled_field
|
||||
tracker = Tracker.new(:core_fields => %w(assigned_to_id fixed_version_id))
|
||||
|
||||
issue = Issue.new(:tracker => tracker)
|
||||
|
@ -435,7 +439,7 @@ class IssueTest < ActiveSupport::TestCase
|
|||
assert_equal Date.parse('2012-07-14'), issue.due_date
|
||||
end
|
||||
|
||||
def test_safe_attributes_should_accept_target_tracker_fields
|
||||
def test_safe_attributes_should_accept_target_tracker_enabled_fields
|
||||
source = Tracker.find(1)
|
||||
source.core_fields = []
|
||||
source.save!
|
||||
|
@ -449,6 +453,165 @@ class IssueTest < ActiveSupport::TestCase
|
|||
assert_equal Date.parse('2012-07-14'), issue.due_date
|
||||
end
|
||||
|
||||
def test_safe_attributes_should_not_include_readonly_fields
|
||||
WorkflowPermission.delete_all
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
|
||||
user = User.find(2)
|
||||
|
||||
issue = Issue.new(:project_id => 1, :tracker_id => 1)
|
||||
assert_equal %w(due_date), issue.read_only_attribute_names(user)
|
||||
assert_not_include 'due_date', issue.safe_attribute_names(user)
|
||||
|
||||
issue.send :safe_attributes=, {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}, user
|
||||
assert_equal Date.parse('2012-07-14'), issue.start_date
|
||||
assert_nil issue.due_date
|
||||
end
|
||||
|
||||
def test_safe_attributes_should_not_include_readonly_custom_fields
|
||||
cf1 = IssueCustomField.create!(:name => 'Writable field', :field_format => 'string', :is_for_all => true, :tracker_ids => [1])
|
||||
cf2 = IssueCustomField.create!(:name => 'Readonly field', :field_format => 'string', :is_for_all => true, :tracker_ids => [1])
|
||||
|
||||
WorkflowPermission.delete_all
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
|
||||
user = User.find(2)
|
||||
|
||||
issue = Issue.new(:project_id => 1, :tracker_id => 1)
|
||||
assert_equal [cf2.id.to_s], issue.read_only_attribute_names(user)
|
||||
assert_not_include cf2.id.to_s, issue.safe_attribute_names(user)
|
||||
|
||||
issue.send :safe_attributes=, {'custom_field_values' => {cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'}}, user
|
||||
assert_equal 'value1', issue.custom_field_value(cf1)
|
||||
assert_nil issue.custom_field_value(cf2)
|
||||
|
||||
issue.send :safe_attributes=, {'custom_fields' => [{'id' => cf1.id.to_s, 'value' => 'valuea'}, {'id' => cf2.id.to_s, 'value' => 'valueb'}]}, user
|
||||
assert_equal 'valuea', issue.custom_field_value(cf1)
|
||||
assert_nil issue.custom_field_value(cf2)
|
||||
end
|
||||
|
||||
def test_editable_custom_field_values_should_return_non_readonly_custom_values
|
||||
cf1 = IssueCustomField.create!(:name => 'Writable field', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
|
||||
cf2 = IssueCustomField.create!(:name => 'Readonly field', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
|
||||
|
||||
WorkflowPermission.delete_all
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
|
||||
user = User.find(2)
|
||||
|
||||
issue = Issue.new(:project_id => 1, :tracker_id => 1)
|
||||
values = issue.editable_custom_field_values(user)
|
||||
assert values.detect {|value| value.custom_field == cf1}
|
||||
assert_nil values.detect {|value| value.custom_field == cf2}
|
||||
|
||||
issue.tracker_id = 2
|
||||
values = issue.editable_custom_field_values(user)
|
||||
assert values.detect {|value| value.custom_field == cf1}
|
||||
assert values.detect {|value| value.custom_field == cf2}
|
||||
end
|
||||
|
||||
def test_safe_attributes_should_accept_target_tracker_writable_fields
|
||||
WorkflowPermission.delete_all
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => 'start_date', :rule => 'readonly')
|
||||
user = User.find(2)
|
||||
|
||||
issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
|
||||
|
||||
issue.send :safe_attributes=, {'start_date' => '2012-07-12', 'due_date' => '2012-07-14'}, user
|
||||
assert_equal Date.parse('2012-07-12'), issue.start_date
|
||||
assert_nil issue.due_date
|
||||
|
||||
issue.send :safe_attributes=, {'start_date' => '2012-07-15', 'due_date' => '2012-07-16', 'tracker_id' => 2}, user
|
||||
assert_equal Date.parse('2012-07-12'), issue.start_date
|
||||
assert_equal Date.parse('2012-07-16'), issue.due_date
|
||||
end
|
||||
|
||||
def test_safe_attributes_should_accept_target_status_writable_fields
|
||||
WorkflowPermission.delete_all
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
|
||||
WorkflowPermission.create!(:old_status_id => 2, :tracker_id => 1, :role_id => 1, :field_name => 'start_date', :rule => 'readonly')
|
||||
user = User.find(2)
|
||||
|
||||
issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
|
||||
|
||||
issue.send :safe_attributes=, {'start_date' => '2012-07-12', 'due_date' => '2012-07-14'}, user
|
||||
assert_equal Date.parse('2012-07-12'), issue.start_date
|
||||
assert_nil issue.due_date
|
||||
|
||||
issue.send :safe_attributes=, {'start_date' => '2012-07-15', 'due_date' => '2012-07-16', 'status_id' => 2}, user
|
||||
assert_equal Date.parse('2012-07-12'), issue.start_date
|
||||
assert_equal Date.parse('2012-07-16'), issue.due_date
|
||||
end
|
||||
|
||||
def test_required_attributes_should_be_validated
|
||||
cf = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
|
||||
|
||||
WorkflowPermission.delete_all
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'required')
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'category_id', :rule => 'required')
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf.id.to_s, :rule => 'required')
|
||||
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => 'start_date', :rule => 'required')
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf.id.to_s, :rule => 'required')
|
||||
user = User.find(2)
|
||||
|
||||
issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1, :subject => 'Required fields', :author => user)
|
||||
assert_equal [cf.id.to_s, "category_id", "due_date"], issue.required_attribute_names(user).sort
|
||||
assert !issue.save, "Issue was saved"
|
||||
assert_equal ["Category can't be blank", "Due date can't be blank", "Foo can't be blank"], issue.errors.full_messages.sort
|
||||
|
||||
issue.tracker_id = 2
|
||||
assert_equal [cf.id.to_s, "start_date"], issue.required_attribute_names(user).sort
|
||||
assert !issue.save, "Issue was saved"
|
||||
assert_equal ["Foo can't be blank", "Start date can't be blank"], issue.errors.full_messages.sort
|
||||
|
||||
issue.start_date = Date.today
|
||||
issue.custom_field_values = {cf.id.to_s => 'bar'}
|
||||
assert issue.save
|
||||
end
|
||||
|
||||
def test_required_attribute_names_for_multiple_roles_should_intersect_rules
|
||||
WorkflowPermission.delete_all
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'required')
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'start_date', :rule => 'required')
|
||||
user = User.find(2)
|
||||
member = Member.find(1)
|
||||
issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
|
||||
|
||||
assert_equal %w(due_date start_date), issue.required_attribute_names(user).sort
|
||||
|
||||
member.role_ids = [1, 2]
|
||||
member.save!
|
||||
assert_equal [], issue.required_attribute_names(user.reload)
|
||||
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 2, :field_name => 'due_date', :rule => 'required')
|
||||
assert_equal %w(due_date), issue.required_attribute_names(user)
|
||||
|
||||
member.role_ids = [1, 2, 3]
|
||||
member.save!
|
||||
assert_equal [], issue.required_attribute_names(user.reload)
|
||||
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 2, :field_name => 'due_date', :rule => 'readonly')
|
||||
# required + readonly => required
|
||||
assert_equal %w(due_date), issue.required_attribute_names(user)
|
||||
end
|
||||
|
||||
def test_read_only_attribute_names_for_multiple_roles_should_intersect_rules
|
||||
WorkflowPermission.delete_all
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'start_date', :rule => 'readonly')
|
||||
user = User.find(2)
|
||||
member = Member.find(1)
|
||||
issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
|
||||
|
||||
assert_equal %w(due_date start_date), issue.read_only_attribute_names(user).sort
|
||||
|
||||
member.role_ids = [1, 2]
|
||||
member.save!
|
||||
assert_equal [], issue.read_only_attribute_names(user.reload)
|
||||
|
||||
WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 2, :field_name => 'due_date', :rule => 'readonly')
|
||||
assert_equal %w(due_date), issue.read_only_attribute_names(user)
|
||||
end
|
||||
|
||||
def test_copy
|
||||
issue = Issue.new.copy_from(1)
|
||||
assert issue.copy?
|
||||
|
|
|
@ -35,13 +35,13 @@ class RoleTest < ActiveSupport::TestCase
|
|||
|
||||
def test_copy_workflows
|
||||
source = Role.find(1)
|
||||
assert_equal 90, source.workflows.size
|
||||
assert_equal 90, source.workflow_rules.size
|
||||
|
||||
target = Role.new(:name => 'Target')
|
||||
assert target.save
|
||||
target.workflows.copy(source)
|
||||
target.workflow_rules.copy(source)
|
||||
target.reload
|
||||
assert_equal 90, target.workflows.size
|
||||
assert_equal 90, target.workflow_rules.size
|
||||
end
|
||||
|
||||
def test_permissions_should_be_unserialized_with_its_coder
|
||||
|
|
|
@ -30,20 +30,20 @@ class TrackerTest < ActiveSupport::TestCase
|
|||
|
||||
def test_copy_workflows
|
||||
source = Tracker.find(1)
|
||||
assert_equal 89, source.workflows.size
|
||||
assert_equal 89, source.workflow_rules.size
|
||||
|
||||
target = Tracker.new(:name => 'Target')
|
||||
assert target.save
|
||||
target.workflows.copy(source)
|
||||
target.workflow_rules.copy(source)
|
||||
target.reload
|
||||
assert_equal 89, target.workflows.size
|
||||
assert_equal 89, target.workflow_rules.size
|
||||
end
|
||||
|
||||
def test_issue_statuses
|
||||
tracker = Tracker.find(1)
|
||||
Workflow.delete_all
|
||||
Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 2, :new_status_id => 3)
|
||||
Workflow.create!(:role_id => 2, :tracker_id => 1, :old_status_id => 3, :new_status_id => 5)
|
||||
WorkflowTransition.delete_all
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 2, :new_status_id => 3)
|
||||
WorkflowTransition.create!(:role_id => 2, :tracker_id => 1, :old_status_id => 3, :new_status_id => 5)
|
||||
|
||||
assert_kind_of Array, tracker.issue_statuses
|
||||
assert_kind_of IssueStatus, tracker.issue_statuses.first
|
||||
|
@ -51,7 +51,7 @@ class TrackerTest < ActiveSupport::TestCase
|
|||
end
|
||||
|
||||
def test_issue_statuses_empty
|
||||
Workflow.delete_all("tracker_id = 1")
|
||||
WorkflowTransition.delete_all("tracker_id = 1")
|
||||
assert_equal [], Tracker.find(1).issue_statuses
|
||||
end
|
||||
|
||||
|
|
|
@ -21,17 +21,45 @@ class WorkflowTest < ActiveSupport::TestCase
|
|||
fixtures :roles, :trackers, :issue_statuses
|
||||
|
||||
def test_copy
|
||||
Workflow.delete_all
|
||||
Workflow.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 1, :new_status_id => 2)
|
||||
Workflow.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 1, :new_status_id => 3, :assignee => true)
|
||||
Workflow.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 1, :new_status_id => 4, :author => true)
|
||||
WorkflowTransition.delete_all
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 1, :new_status_id => 2)
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 1, :new_status_id => 3, :assignee => true)
|
||||
WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 1, :new_status_id => 4, :author => true)
|
||||
|
||||
assert_difference 'Workflow.count', 3 do
|
||||
Workflow.copy(Tracker.find(2), Role.find(1), Tracker.find(3), Role.find(2))
|
||||
assert_difference 'WorkflowTransition.count', 3 do
|
||||
WorkflowTransition.copy(Tracker.find(2), Role.find(1), Tracker.find(3), Role.find(2))
|
||||
end
|
||||
|
||||
assert Workflow.first(:conditions => {:role_id => 2, :tracker_id => 3, :old_status_id => 1, :new_status_id => 2, :author => false, :assignee => false})
|
||||
assert Workflow.first(:conditions => {:role_id => 2, :tracker_id => 3, :old_status_id => 1, :new_status_id => 3, :author => false, :assignee => true})
|
||||
assert Workflow.first(:conditions => {:role_id => 2, :tracker_id => 3, :old_status_id => 1, :new_status_id => 4, :author => true, :assignee => false})
|
||||
assert WorkflowTransition.first(:conditions => {:role_id => 2, :tracker_id => 3, :old_status_id => 1, :new_status_id => 2, :author => false, :assignee => false})
|
||||
assert WorkflowTransition.first(:conditions => {:role_id => 2, :tracker_id => 3, :old_status_id => 1, :new_status_id => 3, :author => false, :assignee => true})
|
||||
assert WorkflowTransition.first(:conditions => {:role_id => 2, :tracker_id => 3, :old_status_id => 1, :new_status_id => 4, :author => true, :assignee => false})
|
||||
end
|
||||
|
||||
def test_workflow_permission_should_validate_rule
|
||||
wp = WorkflowPermission.new(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :field_name => 'due_date')
|
||||
assert !wp.save
|
||||
|
||||
wp.rule = 'foo'
|
||||
assert !wp.save
|
||||
|
||||
wp.rule = 'required'
|
||||
assert wp.save
|
||||
|
||||
wp.rule = 'readonly'
|
||||
assert wp.save
|
||||
end
|
||||
|
||||
def test_workflow_permission_should_validate_field_name
|
||||
wp = WorkflowPermission.new(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :rule => 'required')
|
||||
assert !wp.save
|
||||
|
||||
wp.field_name = 'foo'
|
||||
assert !wp.save
|
||||
|
||||
wp.field_name = 'due_date'
|
||||
assert wp.save
|
||||
|
||||
wp.field_name = '1'
|
||||
assert wp.save
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue