Option is disabled by default. It can be turned on in application settings. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@6306 e93f8b46-1217-0410-a6f0-8f06a7374b81tags/1.3.0
@@ -1,5 +1,5 @@ | |||
# redMine - project management software | |||
# Copyright (C) 2006 Jean-Philippe Lang | |||
# Redmine - project management software | |||
# Copyright (C) 2006-2011 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 | |||
@@ -24,8 +24,8 @@ class ReportsController < ApplicationController | |||
@versions = @project.shared_versions.sort | |||
@priorities = IssuePriority.all | |||
@categories = @project.issue_categories | |||
@assignees = @project.members.collect { |m| m.user }.sort | |||
@authors = @project.members.collect { |m| m.user }.sort | |||
@assignees = (Setting.issue_group_assignment? ? @project.principals : @project.users).sort | |||
@authors = @project.users.sort | |||
@subprojects = @project.descendants.visible | |||
@issues_by_tracker = Issue.by_tracker(@project) | |||
@@ -63,12 +63,12 @@ class ReportsController < ApplicationController | |||
@report_title = l(:field_category) | |||
when "assigned_to" | |||
@field = "assigned_to_id" | |||
@rows = @project.members.collect { |m| m.user }.sort | |||
@rows = (Setting.issue_group_assignment? ? @project.principals : @project.users).sort | |||
@data = Issue.by_assigned_to(@project) | |||
@report_title = l(:field_assigned_to) | |||
when "author" | |||
@field = "author_id" | |||
@rows = @project.members.collect { |m| m.user }.sort | |||
@rows = @project.users.sort | |||
@data = Issue.by_author(@project) | |||
@report_title = l(:field_author) | |||
when "subproject" |
@@ -1,5 +1,5 @@ | |||
# Redmine - project management software | |||
# Copyright (C) 2006-2009 Jean-Philippe Lang | |||
# Copyright (C) 2006-2011 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 | |||
@@ -24,11 +24,15 @@ class Group < Principal | |||
validates_presence_of :lastname | |||
validates_uniqueness_of :lastname, :case_sensitive => false | |||
validates_length_of :lastname, :maximum => 30 | |||
before_destroy :remove_references_before_destroy | |||
def to_s | |||
lastname.to_s | |||
end | |||
alias :name :to_s | |||
def user_added(user) | |||
members.each do |member| | |||
next if member.project.nil? | |||
@@ -46,4 +50,13 @@ class Group < Principal | |||
:conditions => ["#{Member.table_name}.user_id = ? AND #{MemberRole.table_name}.inherited_from IN (?)", user.id, member.member_role_ids]).each(&:destroy) | |||
end | |||
end | |||
private | |||
# Removes references that are not handled by associations | |||
def remove_references_before_destroy | |||
return if self.id.nil? | |||
Issue.update_all 'assigned_to_id = NULL', ['assigned_to_id = ?', id] | |||
end | |||
end |
@@ -22,7 +22,7 @@ class Issue < ActiveRecord::Base | |||
belongs_to :tracker | |||
belongs_to :status, :class_name => 'IssueStatus', :foreign_key => 'status_id' | |||
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id' | |||
belongs_to :assigned_to, :class_name => 'User', :foreign_key => 'assigned_to_id' | |||
belongs_to :assigned_to, :class_name => 'Principal', :foreign_key => 'assigned_to_id' | |||
belongs_to :fixed_version, :class_name => 'Version', :foreign_key => 'fixed_version_id' | |||
belongs_to :priority, :class_name => 'IssuePriority', :foreign_key => 'priority_id' | |||
belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id' | |||
@@ -93,9 +93,11 @@ class Issue < ActiveRecord::Base | |||
when 'all' | |||
nil | |||
when 'default' | |||
"(#{table_name}.is_private = #{connection.quoted_false} OR #{table_name}.author_id = #{user.id} OR #{table_name}.assigned_to_id = #{user.id})" | |||
user_ids = [user.id] + user.groups.map(&:id) | |||
"(#{table_name}.is_private = #{connection.quoted_false} OR #{table_name}.author_id = #{user.id} OR #{table_name}.assigned_to_id IN (#{user_ids}))" | |||
when 'own' | |||
"(#{table_name}.author_id = #{user.id} OR #{table_name}.assigned_to_id = #{user.id})" | |||
user_ids = [user.id] + user.groups.map(&:id) | |||
"(#{table_name}.author_id = #{user.id} OR #{table_name}.assigned_to_id IN (#{user_ids}))" | |||
else | |||
'1=0' | |||
end | |||
@@ -109,9 +111,9 @@ class Issue < ActiveRecord::Base | |||
when 'all' | |||
true | |||
when 'default' | |||
!self.is_private? || self.author == user || self.assigned_to == user | |||
!self.is_private? || self.author == user || user.is_or_belongs_to?(assigned_to) | |||
when 'own' | |||
self.author == user || self.assigned_to == user | |||
self.author == user || user.is_or_belongs_to?(assigned_to) | |||
else | |||
false | |||
end | |||
@@ -482,7 +484,13 @@ class Issue < ActiveRecord::Base | |||
# Author and assignee are always notified unless they have been | |||
# locked or don't want to be notified | |||
notified << author if author && author.active? && author.notify_about?(self) | |||
notified << assigned_to if assigned_to && assigned_to.active? && assigned_to.notify_about?(self) | |||
if assigned_to | |||
if assigned_to.is_a?(Group) | |||
notified += assigned_to.users.select {|u| u.active? && u.notify_about?(self)} | |||
else | |||
notified << assigned_to if assigned_to.active? && assigned_to.notify_about?(self) | |||
end | |||
end | |||
notified.uniq! | |||
# Remove users that can not view the issue | |||
notified.reject! {|user| !visible?(user)} |
@@ -17,7 +17,7 @@ | |||
class IssueCategory < ActiveRecord::Base | |||
belongs_to :project | |||
belongs_to :assigned_to, :class_name => 'User', :foreign_key => 'assigned_to_id' | |||
belongs_to :assigned_to, :class_name => 'Principal', :foreign_key => 'assigned_to_id' | |||
has_many :issues, :foreign_key => 'category_id', :dependent => :nullify | |||
validates_presence_of :name |
@@ -261,8 +261,7 @@ class MailHandler < ActionMailer::Base | |||
# Returns a Hash of issue attributes extracted from keywords in the email body | |||
def issue_attributes_from_keywords(issue) | |||
assigned_to = (k = get_keyword(:assigned_to, :override => true)) && find_user_from_keyword(k) | |||
assigned_to = nil if assigned_to && !issue.assignable_users.include?(assigned_to) | |||
assigned_to = (k = get_keyword(:assigned_to, :override => true)) && find_assignee_from_keyword(k, issue) | |||
attrs = { | |||
'tracker_id' => (k = get_keyword(:tracker)) && issue.project.trackers.named(k).first.try(:id), | |||
@@ -353,14 +352,19 @@ class MailHandler < ActionMailer::Base | |||
end | |||
body.strip | |||
end | |||
def find_user_from_keyword(keyword) | |||
user ||= User.find_by_mail(keyword) | |||
user ||= User.find_by_login(keyword) | |||
if user.nil? && keyword.match(/ /) | |||
def find_assignee_from_keyword(keyword, issue) | |||
keyword = keyword.to_s.downcase | |||
assignable = issue.assignable_users | |||
assignee = nil | |||
assignee ||= assignable.detect {|a| a.mail.to_s.downcase == keyword || a.login.to_s.downcase == keyword} | |||
if assignee.nil? && keyword.match(/ /) | |||
firstname, lastname = *(keyword.split) # "First Last Throwaway" | |||
user ||= User.find_by_firstname_and_lastname(firstname, lastname) | |||
assignee ||= assignable.detect {|a| a.is_a?(User) && a.firstname.to_s.downcase == firstname && a.lastname.to_s.downcase == lastname} | |||
end | |||
if assignee.nil? | |||
assignee ||= assignable.detect {|a| a.is_a?(Group) && a.name.downcase == keyword} | |||
end | |||
user | |||
assignee | |||
end | |||
end |
@@ -1,5 +1,5 @@ | |||
# Redmine - project management software | |||
# Copyright (C) 2006-2009 Jean-Philippe Lang | |||
# Copyright (C) 2006-2011 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 | |||
@@ -21,6 +21,7 @@ class Principal < ActiveRecord::Base | |||
has_many :members, :foreign_key => 'user_id', :dependent => :destroy | |||
has_many :memberships, :class_name => 'Member', :foreign_key => 'user_id', :include => [ :project, :roles ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name" | |||
has_many :projects, :through => :memberships | |||
has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify | |||
# Groups and active users | |||
named_scope :active, :conditions => "#{Principal.table_name}.type='Group' OR (#{Principal.table_name}.type='User' AND #{Principal.table_name}.status = 1)" |
@@ -426,9 +426,10 @@ class Project < ActiveRecord::Base | |||
Member.delete_all(['project_id = ?', id]) | |||
end | |||
# Users issues can be assigned to | |||
# Users/groups issues can be assigned to | |||
def assignable_users | |||
members.select {|m| m.roles.detect {|role| role.assignable?}}.collect {|m| m.user}.sort | |||
assignable = Setting.issue_group_assignment? ? member_principals : members | |||
assignable.select {|m| m.roles.detect {|role| role.assignable?}}.collect {|m| m.principal}.sort | |||
end | |||
# Returns the mail adresses of users that should be always notified on project events |
@@ -223,15 +223,14 @@ class Query < ActiveRecord::Base | |||
"estimated_hours" => { :type => :float, :order => 13 }, | |||
"done_ratio" => { :type => :integer, :order => 14 }} | |||
user_values = [] | |||
user_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged? | |||
principals = [] | |||
if project | |||
user_values += project.users.sort.collect{|s| [s.name, s.id.to_s] } | |||
principals += project.principals.sort | |||
else | |||
all_projects = Project.visible.all | |||
if all_projects.any? | |||
# members of visible projects | |||
user_values += User.active.find(:all, :conditions => ["#{User.table_name}.id IN (SELECT DISTINCT user_id FROM members WHERE project_id IN (?))", all_projects.collect(&:id)]).sort.collect{|s| [s.name, s.id.to_s] } | |||
principals += Principal.active.find(:all, :conditions => ["#{User.table_name}.id IN (SELECT DISTINCT user_id FROM members WHERE project_id IN (?))", all_projects.collect(&:id)]).sort | |||
# project filter | |||
project_values = [] | |||
@@ -242,8 +241,17 @@ class Query < ActiveRecord::Base | |||
@available_filters["project_id"] = { :type => :list, :order => 1, :values => project_values} unless project_values.empty? | |||
end | |||
end | |||
@available_filters["assigned_to_id"] = { :type => :list_optional, :order => 4, :values => user_values } unless user_values.empty? | |||
@available_filters["author_id"] = { :type => :list, :order => 5, :values => user_values } unless user_values.empty? | |||
users = principals.select {|p| p.is_a?(User)} | |||
assigned_to_values = [] | |||
assigned_to_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged? | |||
assigned_to_values += (Setting.issue_group_assignment? ? principals : users).collect{|s| [s.name, s.id.to_s] } | |||
@available_filters["assigned_to_id"] = { :type => :list_optional, :order => 4, :values => assigned_to_values } unless assigned_to_values.empty? | |||
author_values = [] | |||
author_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged? | |||
author_values += users.collect{|s| [s.name, s.id.to_s] } | |||
@available_filters["author_id"] = { :type => :list, :order => 5, :values => author_values } unless author_values.empty? | |||
group_values = Group.all.collect {|g| [g.name, g.id.to_s] } | |||
@available_filters["member_of_group"] = { :type => :list_optional, :order => 6, :values => group_values } unless group_values.empty? |
@@ -45,7 +45,6 @@ class User < Principal | |||
has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)}, | |||
:after_remove => Proc.new {|user, group| group.user_removed(user)} | |||
has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify | |||
has_many :changesets, :dependent => :nullify | |||
has_one :preference, :dependent => :destroy, :class_name => 'UserPreference' | |||
has_one :rss_token, :class_name => 'Token', :conditions => "action='feeds'" | |||
@@ -388,6 +387,17 @@ class User < Principal | |||
@projects_by_role | |||
end | |||
# Returns true if user is arg or belongs to arg | |||
def is_or_belongs_to?(arg) | |||
if arg.is_a?(User) | |||
self == arg | |||
elsif arg.is_a?(Group) | |||
arg.users.include?(self) | |||
else | |||
false | |||
end | |||
end | |||
# Return true if the user is allowed to do the specified action on a specific context | |||
# Action can be: | |||
# * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit') | |||
@@ -469,7 +479,7 @@ class User < Principal | |||
true | |||
when 'selected' | |||
# user receives notifications for created/assigned issues on unselected projects | |||
if object.is_a?(Issue) && (object.author == self || object.assigned_to == self) | |||
if object.is_a?(Issue) && (object.author == self || is_or_belongs_to?(object.assigned_to)) | |||
true | |||
else | |||
false | |||
@@ -477,13 +487,13 @@ class User < Principal | |||
when 'none' | |||
false | |||
when 'only_my_events' | |||
if object.is_a?(Issue) && (object.author == self || object.assigned_to == self) | |||
if object.is_a?(Issue) && (object.author == self || is_or_belongs_to?(object.assigned_to)) | |||
true | |||
else | |||
false | |||
end | |||
when 'only_assigned' | |||
if object.is_a?(Issue) && object.assigned_to == self | |||
if object.is_a?(Issue) && is_or_belongs_to?(object.assigned_to) | |||
true | |||
else | |||
false |
@@ -2,5 +2,5 @@ | |||
<div class="box"> | |||
<p><%= f.text_field :name, :size => 30, :required => true %></p> | |||
<p><%= f.select :assigned_to_id, @project.users.sort.collect{|u| [u.name, u.id]}, :include_blank => true %></p> | |||
<p><%= f.select :assigned_to_id, @project.assignable_users.sort.collect{|u| [u.name, u.id]}, :include_blank => true %></p> | |||
</div> |
@@ -3,6 +3,8 @@ | |||
<div class="box tabular settings"> | |||
<p><%= setting_check_box :cross_project_issue_relations %></p> | |||
<p><%= setting_check_box :issue_group_assignment %></p> | |||
<p><%= setting_check_box :display_subprojects_issues %></p> | |||
<p><%= setting_select :issue_done_ratio, Issue::DONE_RATIO_OPTIONS.collect {|i| [l("setting_issue_done_ratio_#{i}"), i]} %></p> |
@@ -977,3 +977,4 @@ bg: | |||
enumeration_doc_categories: Категории документи | |||
enumeration_activities: Дейности (time tracking) | |||
enumeration_system_activity: Системна активност | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -992,3 +992,4 @@ bs: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -981,3 +981,4 @@ ca: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -982,3 +982,4 @@ cs: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -995,3 +995,4 @@ da: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -997,3 +997,4 @@ de: | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -978,3 +978,4 @@ el: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -981,3 +981,4 @@ en-GB: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -374,6 +374,7 @@ en: | |||
setting_commit_logtime_enabled: Enable time logging | |||
setting_commit_logtime_activity_id: Activity for logged time | |||
setting_gantt_items_limit: Maximum number of items displayed on the gantt chart | |||
setting_issue_group_assignment: Allow issue assignment to groups | |||
permission_add_project: Create project | |||
permission_add_subprojects: Create subprojects |
@@ -1015,3 +1015,4 @@ es: | |||
text_scm_command_not_available: La orden para el Scm no está disponible. Por favor, compruebe la configuración en el panel de administración. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -982,3 +982,4 @@ eu: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -981,3 +981,4 @@ fa: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -999,3 +999,4 @@ fi: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -371,6 +371,7 @@ fr: | |||
setting_commit_logtime_enabled: Permettre la saisie de temps | |||
setting_commit_logtime_activity_id: Activité pour le temps saisi | |||
setting_gantt_items_limit: Nombre maximum d'éléments affichés sur le gantt | |||
setting_issue_group_assignment: Permettre l'assignement des demandes aux groupes | |||
permission_add_project: Créer un projet | |||
permission_add_subprojects: Créer des sous-projets |
@@ -990,3 +990,4 @@ gl: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -983,3 +983,4 @@ he: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -985,3 +985,4 @@ hr: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -997,3 +997,4 @@ | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -986,3 +986,4 @@ id: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -979,3 +979,4 @@ it: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -1008,3 +1008,4 @@ ja: | |||
text_issues_destroy_descendants_confirmation: %{count}個の子チケットも削除されます。 | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -1030,3 +1030,4 @@ ko: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -1038,3 +1038,4 @@ lt: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -973,3 +973,4 @@ lv: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -978,3 +978,4 @@ mk: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -979,3 +979,4 @@ mn: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -960,3 +960,4 @@ nl: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -965,3 +965,4 @@ | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -995,3 +995,4 @@ pl: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -999,3 +999,4 @@ pt-BR: | |||
text_scm_command_not_available: Comando de versionamento não disponível. Por favor verifique as configurações no painel de administração. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -983,3 +983,4 @@ pt: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -971,3 +971,4 @@ ro: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -1091,3 +1091,4 @@ ru: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -973,3 +973,4 @@ sk: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -974,3 +974,4 @@ sl: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -978,3 +978,4 @@ sr-YU: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -979,3 +979,4 @@ sr: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -1019,3 +1019,4 @@ sv: | |||
enumeration_system_activity: Systemaktivitet | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -975,3 +975,4 @@ th: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -997,3 +997,4 @@ tr: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -974,3 +974,4 @@ uk: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -1029,3 +1029,4 @@ vi: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -1058,3 +1058,4 @@ | |||
enumeration_doc_categories: 文件分類 | |||
enumeration_activities: 活動 (時間追蹤) | |||
enumeration_system_activity: 系統活動 | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -981,3 +981,4 @@ zh: | |||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel. | |||
notice_issue_successful_create: Issue %{id} created. | |||
label_between: between | |||
setting_issue_group_assignment: Allow issue assignment to groups |
@@ -121,6 +121,8 @@ user_format: | |||
format: symbol | |||
cross_project_issue_relations: | |||
default: 0 | |||
issue_group_assignment: | |||
default: 0 | |||
notified_events: | |||
serialized: true | |||
default: |
@@ -535,6 +535,28 @@ class IssuesControllerTest < ActionController::TestCase | |||
assert_not_nil v | |||
assert_equal 'Value for field 2', v.value | |||
end | |||
def test_post_new_with_group_assignment | |||
group = Group.find(11) | |||
project = Project.find(1) | |||
project.members << Member.new(:principal => group, :roles => [Role.first]) | |||
with_settings :issue_group_assignment => '1' do | |||
@request.session[:user_id] = 2 | |||
assert_difference 'Issue.count' do | |||
post :create, :project_id => project.id, | |||
:issue => {:tracker_id => 3, | |||
:status_id => 1, | |||
:subject => 'This is the test_new_with_group_assignment issue', | |||
:assigned_to_id => group.id} | |||
end | |||
end | |||
assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id | |||
issue = Issue.find_by_subject('This is the test_new_with_group_assignment issue') | |||
assert_not_nil issue | |||
assert_equal group, issue.assigned_to | |||
end | |||
def test_post_create_without_start_date | |||
@request.session[:user_id] = 2 | |||
@@ -1309,6 +1331,22 @@ class IssuesControllerTest < ActionController::TestCase | |||
assert_equal 1, journal.details.size | |||
end | |||
def test_bulk_update_with_group_assignee | |||
group = Group.find(11) | |||
project = Project.find(1) | |||
project.members << Member.new(:principal => group, :roles => [Role.first]) | |||
@request.session[:user_id] = 2 | |||
# update issues assignee | |||
post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing', | |||
:issue => {:priority_id => '', | |||
:assigned_to_id => group.id, | |||
:custom_field_values => {'2' => ''}} | |||
assert_response 302 | |||
assert_equal [group, group], Issue.find_all_by_id([1, 2]).collect {|i| i.assigned_to} | |||
end | |||
def test_bulk_update_on_different_projects | |||
@request.session[:user_id] = 2 | |||
# update issues priority |
@@ -1,5 +1,5 @@ | |||
# Redmine - project management software | |||
# Copyright (C) 2006-2009 Jean-Philippe Lang | |||
# Copyright (C) 2006-2011 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 | |||
@@ -74,4 +74,14 @@ class GroupTest < ActiveSupport::TestCase | |||
User.find(8).groups.clear | |||
assert !User.find(8).member_of?(Project.find(5)) | |||
end | |||
def test_destroy_should_unassign_issues | |||
group = Group.first | |||
Issue.update_all(["assigned_to_id = ?", group.id], 'id = 1') | |||
assert group.destroy | |||
assert group.destroyed? | |||
assert_equal nil, Issue.find(1).assigned_to_id | |||
end | |||
end |
@@ -1,5 +1,5 @@ | |||
# redMine - project management software | |||
# Copyright (C) 2006-2007 Jean-Philippe Lang | |||
# Redmine - project management software | |||
# Copyright (C) 2006-2011 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 | |||
@@ -24,6 +24,19 @@ class IssueCategoryTest < ActiveSupport::TestCase | |||
@category = IssueCategory.find(1) | |||
end | |||
def test_create | |||
assert IssueCategory.new(:project_id => 2, :name => 'New category').save | |||
category = IssueCategory.first(:order => 'id DESC') | |||
assert_equal 'New category', category.name | |||
end | |||
def test_create_with_group_assignment | |||
assert IssueCategory.new(:project_id => 2, :name => 'Group assignment', :assigned_to_id => 11).save | |||
category = IssueCategory.first(:order => 'id DESC') | |||
assert_kind_of Group, category.assigned_to | |||
assert_equal Group.find(11), category.assigned_to | |||
end | |||
def test_destroy | |||
issue = @category.issues.first | |||
@category.destroy |
@@ -64,6 +64,15 @@ class IssueTest < ActiveSupport::TestCase | |||
issue.reload | |||
assert_equal 'PostgreSQL', issue.custom_value_for(field).value | |||
end | |||
def test_create_with_group_assignment | |||
with_settings :issue_group_assignment => '1' do | |||
assert Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1, :subject => 'Group assignment', :assigned_to_id => 11).save | |||
issue = Issue.first(:order => 'id DESC') | |||
assert_kind_of Group, issue.assigned_to | |||
assert_equal Group.find(11), issue.assigned_to | |||
end | |||
end | |||
def assert_visibility_match(user, issues) | |||
assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort | |||
@@ -579,6 +588,16 @@ class IssueTest < ActiveSupport::TestCase | |||
# author is not a member of project anymore | |||
assert !copy.recipients.include?(copy.author.mail) | |||
end | |||
def test_recipients_should_include_the_assigned_group_members | |||
group_member = User.generate_with_protected! | |||
group = Group.generate! | |||
group.users << group_member | |||
issue = Issue.find(12) | |||
issue.assigned_to = group | |||
assert issue.recipients.include?(group_member.mail) | |||
end | |||
def test_watcher_recipients_should_not_include_users_that_cannot_view_the_issue | |||
user = User.find(3) | |||
@@ -682,6 +701,28 @@ class IssueTest < ActiveSupport::TestCase | |||
assert_equal 1, assignable_user_ids.select {|i| i == user_id}.length, "User #{user_id} appears more or less than once" | |||
end | |||
end | |||
context "with issue_group_assignment" do | |||
should "include groups" do | |||
issue = Issue.new(:project => Project.find(2)) | |||
with_settings :issue_group_assignment => '1' do | |||
assert_equal %w(Group User), issue.assignable_users.map {|a| a.class.name}.uniq.sort | |||
assert issue.assignable_users.include?(Group.find(11)) | |||
end | |||
end | |||
end | |||
context "without issue_group_assignment" do | |||
should "not include groups" do | |||
issue = Issue.new(:project => Project.find(2)) | |||
with_settings :issue_group_assignment => '0' do | |||
assert_equal %w(User), issue.assignable_users.map {|a| a.class.name}.uniq.sort | |||
assert !issue.assignable_users.include?(Group.find(11)) | |||
end | |||
end | |||
end | |||
end | |||
def test_create_should_send_email_notification |
@@ -109,6 +109,18 @@ class MailHandlerTest < ActiveSupport::TestCase | |||
assert_equal 'Urgent', issue.priority.to_s | |||
assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') | |||
end | |||
def test_add_issue_with_group_assignment | |||
with_settings :issue_group_assignment => '1' do | |||
issue = submit_email('ticket_on_given_project.eml') do |email| | |||
email.gsub!('Assigned to: John Smith', 'Assigned to: B Team') | |||
end | |||
assert issue.is_a?(Issue) | |||
assert !issue.new_record? | |||
issue.reload | |||
assert_equal Group.find(11), issue.assigned_to | |||
end | |||
end | |||
def test_add_issue_with_partial_attributes_override | |||
issue = submit_email('ticket_with_attributes.eml', :issue => {:priority => 'High'}, :allow_override => ['tracker']) | |||
@@ -446,6 +458,7 @@ class MailHandlerTest < ActiveSupport::TestCase | |||
def submit_email(filename, options={}) | |||
raw = IO.read(File.join(FIXTURES_PATH, filename)) | |||
yield raw if block_given? | |||
MailHandler.receive(raw, options) | |||
end | |||