diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index 9a841cc63c..fc2119e580 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -28,19 +28,10 @@ #++ module GroupsHelper - # Options for the new membership projects combo-box - def options_for_membership_project_select(user, projects) - options = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---") - options << project_tree_options_for_select(projects) do |p| - {:disabled => (user.projects.include?(p))} - end - options - end - def group_settings_tabs - tabs = [{:name => 'general', :partial => 'groups/general', :label => :label_general}, - {:name => 'users', :partial => 'groups/users', :label => :label_user_plural}, - {:name => 'memberships', :partial => 'groups/memberships', :label => :label_project_plural} - ] + [{:name => 'general', :partial => 'groups/general', :label => :label_general}, + {:name => 'users', :partial => 'groups/users', :label => :label_user_plural}, + {:name => 'memberships', :partial => 'groups/memberships', :label => :label_project_plural} + ] end end diff --git a/app/models/group.rb b/app/models/group.rb index e27323230b..df96a8b476 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -39,10 +39,14 @@ class Group < Principal 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? @@ -85,4 +89,19 @@ class Group < Principal def add_member!(users) self.users << users end + + private + + # Removes references that are not handled by associations + def remove_references_before_destroy + return if self.id.nil? + + deleted_user = DeletedUser.first + + WorkPackage.update_all({ :assigned_to_id => deleted_user.id }, + { :assigned_to_id => id }) + + Journal::WorkPackageJournal.update_all({ :assigned_to_id => deleted_user.id }, + { :assigned_to_id => id }) + end end diff --git a/app/models/issue_category.rb b/app/models/issue_category.rb index 2df37fc7ba..7ea6027990 100644 --- a/app/models/issue_category.rb +++ b/app/models/issue_category.rb @@ -30,7 +30,7 @@ class IssueCategory < ActiveRecord::Base include Redmine::SafeAttributes 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 :work_packages, :foreign_key => 'category_id', :dependent => :nullify attr_protected :project_id @@ -42,7 +42,7 @@ class IssueCategory < ActiveRecord::Base # validates that assignee is member of the issue category's project validates_each :assigned_to_id do |record, attr, value| if value # allow nil - record.errors.add(attr, l(:error_must_be_project_member)) unless record.project.user_ids.include?(value.to_i) + record.errors.add(attr, l(:error_must_be_project_member)) unless record.project.principals.map(&:id).include? value end end diff --git a/app/models/mail_handler.rb b/app/models/mail_handler.rb index 70b113565d..a6710ec3e9 100644 --- a/app/models/mail_handler.rb +++ b/app/models/mail_handler.rb @@ -426,8 +426,9 @@ class MailHandler < ActionMailer::Base } end if assignee.nil? - assignee ||= assignable.detect {|a| a.name.downcase == keyword} + assignee ||= assignable.detect {|a| a.is_a?(Group) && a.name.downcase == keyword} end + assignee end end diff --git a/app/models/principal.rb b/app/models/principal.rb index 6cb0ed6163..38e1616fc6 100644 --- a/app/models/principal.rb +++ b/app/models/principal.rb @@ -35,6 +35,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 scope :active, :conditions => "#{Principal.table_name}.type='Group' OR (#{Principal.table_name}.type='User' AND #{Principal.table_name}.status = 1)" diff --git a/app/models/project.rb b/app/models/project.rb index 49b9475d1e..9138bafb08 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -45,11 +45,8 @@ class Project < ActiveRecord::Base has_many :members, :include => [:user, :roles], :conditions => "#{User.table_name}.type='User' AND #{User.table_name}.status=#{User::STATUSES[:active]}" has_many :assignable_members, :class_name => 'Member', - :include => [:user, :roles], - :conditions => ["#{User.table_name}.type=? AND #{User.table_name}.status=? AND roles.assignable = ?", - 'User', - User::STATUSES[:active], - true] + :include => [:principal, :roles], + :conditions => Proc.new { self.class.assignable_members_condition } has_many :memberships, :class_name => 'Member' has_many :member_principals, :class_name => 'Member', :include => :principal, @@ -596,9 +593,9 @@ class Project < ActiveRecord::Base Member.delete_all(['project_id = ?', id]) end - # Users issues can be assigned to + # Users/groups a work_package can be assigned to def assignable_users - assignable_members.map(&:user).sort + assignable_members.map(&:principal).compact.sort end # Returns the mail adresses of users that should be always notified on project events @@ -1109,4 +1106,19 @@ class Project < ActiveRecord::Base end update_attribute :status, STATUS_ARCHIVED end + + protected + + def self.assignable_members_condition + + condition = Setting.work_package_group_assignment? ? + ["(#{Principal.table_name}.type=? OR #{Principal.table_name}.type=?)", 'User', 'Group'] : + ["(#{Principal.table_name}.type=?)", 'User'] + + condition[0] += " AND #{User.table_name}.status=? AND roles.assignable = ?" + condition << User::STATUSES[:active] + condition << true + + sanitize_sql_array condition + end end diff --git a/app/models/query.rb b/app/models/query.rb index 47243e99b6..1f6f8d9ddc 100644 --- a/app/models/query.rb +++ b/app/models/query.rb @@ -144,15 +144,14 @@ class Query < ActiveRecord::Base "estimated_hours" => { :type => :integer, :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 = [] @@ -163,8 +162,24 @@ 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? + principals_by_class = principals.group_by(&:class) + + user_values = principals_by_class[User].present? ? + principals_by_class[User].collect{ |s| [s.name, s.id.to_s] }.sort : + [] + + group_values = Setting.work_package_group_assignment? && principals_by_class[Group].present? ? + principals_by_class[Group].collect{ |s| [s.name, s.id.to_s] }.sort : + [] + + assigned_to_values = (user_values + group_values).sort + assigned_to_values = [["<< #{l(:label_me)} >>", "me"]] + assigned_to_values if User.current.logged? + @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 += user_values + @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, :name => I18n.t('query_fields.member_of_group') } unless group_values.empty? @@ -175,7 +190,8 @@ class Query < ActiveRecord::Base if User.current.logged? # populate the watcher list with the same user list as other user filters if the user has the :view_work_package_watchers permission in at least one project # TODO: this could be differentiated more, e.g. all users could watch issues in public projects, but won't necessarily be shown here - watcher_values = User.current.allowed_to_globally?(:view_work_package_watchers, {}) ? user_values : [["<< #{l(:label_me)} >>", "me"]] + watcher_values = [["<< #{l(:label_me)} >>", "me"]] + user_values.each { |v| watcher_values << v } if User.current.allowed_to_globally?(:view_work_packages_watchers, {}) @available_filters["watcher_id"] = { :type => :list, :order => 15, :values => watcher_values } end @@ -409,7 +425,14 @@ class Query < ActiveRecord::Base # "me" value subsitution if %w(assigned_to_id author_id watcher_id).include?(field) - v.push(User.current.logged? ? User.current.id.to_s : "0") if v.delete("me") + if v.delete("me") + if User.current.logged? + v.push(User.current.id.to_s) + v += User.current.group_ids.map(&:to_s) if field == 'assigned_to_id' + else + v.push("0") + end + end end sql = '' diff --git a/app/models/user.rb b/app/models/user.rb index 8daf690dd5..8990a8362b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -566,6 +566,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') @@ -657,7 +668,7 @@ class User < Principal true when 'selected' # user receives notifications for created/assigned issues on unselected projects - if object.is_a?(WorkPackage) && (object.author == self || object.assigned_to == self) + if object.is_a?(WorkPackage) && (object.author == self || is_or_belongs_to?(object.assigned_to)) true else false @@ -665,13 +676,13 @@ class User < Principal when 'none' false when 'only_my_events' - if object.is_a?(WorkPackage) && (object.author == self || object.assigned_to == self) + if object.is_a?(WorkPackage) && (object.author == self || is_or_belongs_to?(object.assigned_to)) true else false end when 'only_assigned' - if object.is_a?(WorkPackage) && object.assigned_to == self + if object.is_a?(WorkPackage) && is_or_belongs_to?(object.assigned_to) true else false diff --git a/app/models/work_package.rb b/app/models/work_package.rb index 57842df0ea..824786a89b 100644 --- a/app/models/work_package.rb +++ b/app/models/work_package.rb @@ -47,7 +47,7 @@ class WorkPackage < ActiveRecord::Base belongs_to :type 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 :responsible, :class_name => "User", :foreign_key => "responsible_id" belongs_to :fixed_version, :class_name => 'Version', :foreign_key => 'fixed_version_id' belongs_to :priority, :class_name => 'IssuePriority', :foreign_key => 'priority_id' @@ -467,7 +467,13 @@ class WorkPackage < 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)} diff --git a/app/views/issue_categories/_form.html.erb b/app/views/issue_categories/_form.html.erb index 3aeed68e84..5cd78b75dd 100644 --- a/app/views/issue_categories/_form.html.erb +++ b/app/views/issue_categories/_form.html.erb @@ -31,5 +31,7 @@ See doc/COPYRIGHT.rdoc for more details.

<%= f.text_field :name, :size => 30, :required => true %>

-

<%= f.select :assigned_to_id, @project.users.sort.collect{|u| [u.name, u.id]}, :include_blank => true %>

+

+ <%= f.select :assigned_to_id, @project.assignable_users.sort.collect{|u| [u.name, u.id]}, :include_blank => true %> +

diff --git a/app/views/my/blocks/_issuesassignedtome.html.erb b/app/views/my/blocks/_issuesassignedtome.html.erb index 91bb55cce8..b6b24be992 100644 --- a/app/views/my/blocks/_issuesassignedtome.html.erb +++ b/app/views/my/blocks/_issuesassignedtome.html.erb @@ -33,7 +33,9 @@ See doc/COPYRIGHT.rdoc for more details. :include => [ :status, :project, :type, :priority ], :order => "#{IssuePriority.table_name}.position DESC, #{WorkPackage.table_name}.updated_at DESC") %> -<% assigned_count = WorkPackage.visible.open.count(:conditions => {:assigned_to_id => User.current.id}) %> +<% assigned_count = WorkPackage.visible.open.count(:conditions => { + :assigned_to_id => User.current.id + }) %>

<%=l(:label_assigned_to_me_work_packages)%> (<%= assigned_count %>)

@@ -41,10 +43,10 @@ See doc/COPYRIGHT.rdoc for more details. <% if assigned.length > 0 %>

<%= link_to l(:label_work_package_view_all_assigned_to_me), controller: :work_packages, - action: :index, - set_filter: 1, - assigned_to_id: 'me', - sort: 'priority:desc,updated_at:desc' %>

+ action: :index, + set_filter: 1, + assigned_to_id: 'me', + sort: 'priority:desc,updated_at:desc' %>

<% end %> <% content_for :header_tags do %> diff --git a/app/views/settings/_issues.html.erb b/app/views/settings/_issues.html.erb index 776d3d241b..981ca9f20d 100644 --- a/app/views/settings/_issues.html.erb +++ b/app/views/settings/_issues.html.erb @@ -32,6 +32,8 @@ See doc/COPYRIGHT.rdoc for more details.

<%= setting_check_box :cross_project_work_package_relations %>

+

<%= setting_check_box :work_package_group_assignment %>

+

<%= setting_check_box :display_subprojects_work_packages %>

<%= setting_check_box :work_package_startdate_is_adddate %>

diff --git a/config/locales/de.yml b/config/locales/de.yml index 6dad2949f1..deee4f167e 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -1219,6 +1219,7 @@ de: setting_users_deletable_by_self: "Nutzer können ihren Account löschen" setting_welcome_text: "Willkommenstext" setting_wiki_compression: "Wiki-Historie komprimieren" + setting_work_package_group_assignment: "Zuweisung zu Gruppen erlauben" settings: general: "Allgemein" @@ -1393,7 +1394,6 @@ de: parent: "Zeige Unterprojekte von" planning_element_filters: "Planungselemente filtern" planning_element_responsible: "Planungselemente von diesem Planungsverantwortlichen anzeigen" - planning_element_types: "Typ anzeigen" project_time_filter: "Projekte mit Planungselementen eines Typs in einem Zeitfenster" project_time_filter_historical: "von %{startdate} bis %{enddate}" @@ -1521,7 +1521,6 @@ de: version_status_closed: "abgeschlossen" version_status_locked: "gesperrt" version_status_open: "offen" - warning_attachments_not_saved: "%{count} Datei(en) konnten nicht gespeichert werden." wiki_menu_item: "Menüpunkt" diff --git a/config/locales/en.yml b/config/locales/en.yml index 07dd9aefe7..a9ae8fa48d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1195,6 +1195,7 @@ en: setting_users_deletable_by_self: "Users allowed to delete their accounts" setting_welcome_text: "Welcome text" setting_wiki_compression: "Wiki history compression" + setting_work_package_group_assignment: "Allow assignment to groups" settings: general: "General" diff --git a/config/settings.yml b/config/settings.yml index a154ac8e40..d14615cc41 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -162,6 +162,8 @@ user_format: format: symbol cross_project_work_package_relations: default: 0 +work_package_group_assignment: + default: 0 notified_events: serialized: true default: diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index b063ab8764..1d1ddd1b27 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -29,6 +29,7 @@ See doc/COPYRIGHT.rdoc for more details. # Changelog +* `#1715` Group assigned work packages * `#1770` New Comment Section layout errors * `#1790` Fix activity view bug coming up during the meeting adaptions to acts_as_journalized diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb new file mode 100644 index 0000000000..dd6d530a2b --- /dev/null +++ b/spec/models/group_spec.rb @@ -0,0 +1,70 @@ +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2013 the OpenProject Foundation (OPF) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# 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. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +require 'spec_helper' +require_relative '../support/shared/become_member' + +describe Group do + include BecomeMember + + let(:group) { FactoryGirl.build(:group) } + let(:user) { FactoryGirl.build(:user) } + let(:project) { FactoryGirl.create(:project_with_types) } + let(:issue_status) { FactoryGirl.create(:issue_status) } + let(:package) { FactoryGirl.build(:work_package, :type => project.types.first, + :author => user, + :project => project, + :status => issue_status) } + + describe :destroy do + describe 'work packages assigned to the group' do + before do + become_member_with_permissions project, group, [:view_work_packages] + package.assigned_to = group + + package.save! + end + + it 'should reassign the work package to nobody' do + group.destroy + + package.reload + + package.assigned_to.should == DeletedUser.first + end + + it 'should update all journals to have the deleted user as assigned' do + group.destroy + + package.reload + + package.journals.all?{ |j| j.data.assigned_to_id == DeletedUser.first.id }.should be_true + end + end + end +end diff --git a/spec/models/work_package/work_package_action_mailer_spec.rb b/spec/models/work_package/work_package_action_mailer_spec.rb index 9dba184e77..cdf3daf151 100644 --- a/spec/models/work_package/work_package_action_mailer_spec.rb +++ b/spec/models/work_package/work_package_action_mailer_spec.rb @@ -76,5 +76,18 @@ describe WorkPackage do it { should eq(0) } end + + context :group_assigned_work_package do + let(:group) { FactoryGirl.create(:group) } + + before do + group.users << user_1 + work_package.assigned_to = group + end + + subject { work_package.recipients } + + it { should include(user_1.mail) } + end end end diff --git a/spec/models/work_package_spec.rb b/spec/models/work_package_spec.rb index ac22a3ca91..267061ebb0 100644 --- a/spec/models/work_package_spec.rb +++ b/spec/models/work_package_spec.rb @@ -95,6 +95,21 @@ describe WorkPackage do it { should be_nil } end end + + describe :assigned_to do + context :group_assignment do + let(:group) { FactoryGirl.create(:group) } + + before do + Setting.stub(:work_package_group_assignment).and_return(true) + end + + subject { FactoryGirl.create(:work_package, + assigned_to: group).assigned_to } + + it { should eq(group) } + end + end end describe :type do @@ -165,6 +180,32 @@ describe WorkPackage do end end + context "with work_package_group_assignment" do + let(:group) { FactoryGirl.create(:group) } + let(:work_package) { FactoryGirl.create(:work_package) } + + before do + Setting.stub(:work_package_group_assignment?).and_return(true) + work_package.project.add_member! group, FactoryGirl.create(:role) + end + + subject { work_package.assignable_users } + it { should include(group) } + end + + context "without work_package_group_assignment" do + let(:group) { FactoryGirl.create(:group) } + let(:work_package) { FactoryGirl.create(:work_package) } + + before do + Setting.stub(:work_package_group_assignment?).and_return(false) + work_package.project.add_member! group, FactoryGirl.create(:role) + end + + subject { work_package.assignable_users } + it { should_not include(group) } + end + context "multiple users" do let(:user_2) { FactoryGirl.build_stubbed(:user) } @@ -302,7 +343,7 @@ describe WorkPackage do end shared_examples_for "save with open version" do - before do + before do work_package.status = status_open work_package.fixed_version = version_open end @@ -315,7 +356,7 @@ describe WorkPackage do context "in closed version" do include_context "in closed version" - before do + before do work_package.status = status_open work_package.save end @@ -373,13 +414,13 @@ describe WorkPackage do end context "time entry 1" do - subject { work_package.time_entries } + subject { work_package.time_entries } it { should include(time_entry_1) } end context "time entry 2" do - subject { work_package.time_entries } + subject { work_package.time_entries } it { should include(time_entry_2) } end @@ -414,7 +455,7 @@ describe WorkPackage do it { should eq(target_category.id) } end - + it_behaves_like "moved work package" end diff --git a/spec/support/shared/become_member.rb b/spec/support/shared/become_member.rb index 4cf3ff8a0d..cc667cc2ab 100644 --- a/spec/support/shared/become_member.rb +++ b/spec/support/shared/become_member.rb @@ -38,7 +38,7 @@ module BecomeMember role = FactoryGirl.create(:role, :permissions => permissions) - member = FactoryGirl.build(:member, :user => user, :project => project) + member = FactoryGirl.build(:member, :principal => user, :project => project) member.roles = [role] member.save! end diff --git a/test/functional/issues_controller_test.rb b/test/functional/issues_controller_test.rb index 45cd755bee..bc7233e497 100644 --- a/test/functional/issues_controller_test.rb +++ b/test/functional/issues_controller_test.rb @@ -119,6 +119,22 @@ class IssuesControllerTest < ActionController::TestCase assert_equal 0, ActionMailer::Base.deliveries.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], WorkPackage.find_all_by_id([1, 2]).collect {|i| i.assigned_to} + end + def test_bulk_update_on_different_projects issue = WorkPackage.find(1) issue.recreate_initial_journal! diff --git a/test/unit/group_test.rb b/test/unit/group_test.rb index 1fb59ac984..5ef4d6f9e2 100644 --- a/test/unit/group_test.rb +++ b/test/unit/group_test.rb @@ -34,6 +34,7 @@ class GroupTest < ActiveSupport::TestCase super @group = FactoryGirl.create :group @member = FactoryGirl.build :member + @work_package = FactoryGirl.create :work_package @roles = FactoryGirl.create_list :role, 2 @member.force_attributes = { :principal => @group, :role_ids => @roles.map(&:id) } @member.save! diff --git a/test/unit/issue_category_test.rb b/test/unit/issue_category_test.rb index 1e734abafe..eb16d4cf54 100644 --- a/test/unit/issue_category_test.rb +++ b/test/unit/issue_category_test.rb @@ -38,6 +38,26 @@ class IssueCategoryTest < ActiveSupport::TestCase assert_equal @category.work_packages, [@issue] end + def test_create + (new_cat = IssueCategory.new).force_attributes = {:project_id => @project.id, :name => 'New category'} + assert new_cat.valid? + assert new_cat.save + assert_equal 'New category', new_cat.name + end + + def test_create_with_group_assignment + group = FactoryGirl.create :group + role = FactoryGirl.create :role + (Member.new.tap do |m| + m.force_attributes = { :principal => group, :project => @project, :role_ids => [role.id] } + end).save! + (new_cat = IssueCategory.new).force_attributes = {:project_id => @project.id, :name => 'Group assignment', :assigned_to_id => group.id} + assert new_cat.valid? + assert new_cat.save + assert_kind_of Group, new_cat.assigned_to + assert_equal group, new_cat.assigned_to + end + # Make sure the category was nullified on the issue def test_destroy @category.destroy diff --git a/test/unit/mail_handler_test.rb b/test/unit/mail_handler_test.rb index 346352dcc8..ee908a4ac0 100644 --- a/test/unit/mail_handler_test.rb +++ b/test/unit/mail_handler_test.rb @@ -103,6 +103,18 @@ class MailHandlerTest < ActiveSupport::TestCase assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') end + def test_add_work_package_with_group_assignment + with_settings :work_package_group_assignment => '1' do + work_package = submit_email('ticket_on_given_project.eml') do |email| + email.gsub!('Assigned to: John Smith', 'Assigned to: B Team') + end + assert work_package.is_a?(WorkPackage) + assert !work_package.new_record? + work_package.reload + assert_equal Group.find(11), work_package.assigned_to + end + end + def test_add_issue_with_partial_attributes_override issue = submit_email('ticket_with_attributes.eml', :issue => {:priority => 'High'}, :allow_override => ['type']) assert issue.is_a?(WorkPackage) @@ -159,10 +171,10 @@ class MailHandlerTest < ActiveSupport::TestCase end def test_add_issue_should_match_assignee_on_display_name # added from redmine - not sure if it is ok here - user = User.generate!(:firstname => 'Foo Bar', :lastname => 'Foo Baz') + user = User.generate!(:firstname => 'Foo', :lastname => 'Bar') User.add_to_project(user, Project.find(2), Role.generate!(:name => 'Superhero')) issue = submit_email('ticket_on_given_project.eml') do |email| - email.sub!(/^Assigned to.*$/, 'Assigned to: Foo Bar Foo baz') + email.sub!(/^Assigned to.*$/, 'Assigned to: Foo Bar') end assert issue.is_a?(WorkPackage) assert_equal user, issue.assigned_to diff --git a/test/unit/query_test.rb b/test/unit/query_test.rb index d2d683cd85..565a9afa1d 100644 --- a/test/unit/query_test.rb +++ b/test/unit/query_test.rb @@ -209,6 +209,27 @@ class QueryTest < ActiveSupport::TestCase find_issues_with_query(query) end + def test_filter_assigned_to_me + user = User.find(2) + group = Group.find(10) + project = Project.find(1) + User.current = user + i1 = FactoryGirl.create(:work_package, :project => project, :type => project.types.first, :assigned_to => user) + i2 = FactoryGirl.create(:work_package, :project => project, :type => project.types.first, :assigned_to => group) + i3 = FactoryGirl.create(:work_package, :project => project, :type => project.types.first, :assigned_to => Group.find(11)) + group.users << user + + query = Query.new(:name => '_', :filters => { 'assigned_to_id' => {:operator => '=', :values => ['me']}}) + result = query.results.work_packages + assert_equal WorkPackage.visible.all(:conditions => {:assigned_to_id => ([2] + user.reload.group_ids)}).sort_by(&:id), result.sort_by(&:id) + + assert result.include?(i1) + assert result.include?(i2) + assert !result.include?(i3) + + User.current = nil + end + def test_filter_watched_issues User.current = User.find(1) query = Query.new(:name => '_', :filters => { 'watcher_id' => {:operator => '=', :values => ['me']}})