mirror of
https://github.com/redmine/redmine.git
synced 2026-03-05 12:01:23 +01:00
User groups feature initial commit.
git-svn-id: http://redmine.rubyforge.org/svn/branches/work@1373 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
# Copyright (C) 2006-2008 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
|
||||
@@ -32,16 +32,18 @@ class CustomFieldsController < ApplicationController
|
||||
|
||||
def new
|
||||
case params[:type]
|
||||
when "IssueCustomField"
|
||||
@custom_field = IssueCustomField.new(params[:custom_field])
|
||||
@custom_field.trackers = Tracker.find(params[:tracker_ids]) if params[:tracker_ids]
|
||||
when "UserCustomField"
|
||||
@custom_field = UserCustomField.new(params[:custom_field])
|
||||
when "ProjectCustomField"
|
||||
@custom_field = ProjectCustomField.new(params[:custom_field])
|
||||
else
|
||||
redirect_to :action => 'list'
|
||||
return
|
||||
when "IssueCustomField"
|
||||
@custom_field = IssueCustomField.new(params[:custom_field])
|
||||
@custom_field.trackers = Tracker.find(params[:tracker_ids]) if params[:tracker_ids]
|
||||
when "UserCustomField"
|
||||
@custom_field = UserCustomField.new(params[:custom_field])
|
||||
when "ProjectCustomField"
|
||||
@custom_field = ProjectCustomField.new(params[:custom_field])
|
||||
when "GroupCustomField"
|
||||
@custom_field = GroupCustomField.new(params[:custom_field])
|
||||
else
|
||||
render_404
|
||||
return
|
||||
end
|
||||
if request.post? and @custom_field.save
|
||||
flash[:notice] = l(:notice_successful_create)
|
||||
|
||||
114
groups/app/controllers/groups_controller.rb
Normal file
114
groups/app/controllers/groups_controller.rb
Normal file
@@ -0,0 +1,114 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2008 FreeCode
|
||||
#
|
||||
# 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 GroupsController < ApplicationController
|
||||
layout 'base'
|
||||
before_filter :require_admin
|
||||
|
||||
helper :custom_fields
|
||||
|
||||
# GET /groups
|
||||
# GET /groups.xml
|
||||
def index
|
||||
@groups = Group.find(:all, :order => 'name')
|
||||
@group = Group.new
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.erb
|
||||
format.xml { render :xml => @groups }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /groups/1
|
||||
# GET /groups/1.xml
|
||||
def show
|
||||
@group = Group.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
format.html # show.html.erb
|
||||
format.xml { render :xml => @group }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /groups/new
|
||||
# GET /groups/new.xml
|
||||
def new
|
||||
@group = Group.new
|
||||
@custom_values = GroupCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @group) }
|
||||
|
||||
respond_to do |format|
|
||||
format.html # new.html.erb
|
||||
format.xml { render :xml => @group }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /groups/1/edit
|
||||
def edit
|
||||
@group = Group.find(params[:id])
|
||||
@custom_values = GroupCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| @group.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x) }
|
||||
end
|
||||
|
||||
# POST /groups
|
||||
# POST /groups.xml
|
||||
def create
|
||||
@group = Group.new(params[:group])
|
||||
@custom_values = GroupCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @group, :value => (params[:custom_fields] ? params["custom_fields"][x.id.to_s] : nil)) }
|
||||
@group.custom_values = @custom_values
|
||||
|
||||
respond_to do |format|
|
||||
if @group.save
|
||||
flash[:notice] = l(:notice_successful_create)
|
||||
format.html { redirect_to(groups_path) }
|
||||
format.xml { render :xml => @group, :status => :created, :location => @group }
|
||||
else
|
||||
format.html { render :action => "new" }
|
||||
format.xml { render :xml => @group.errors, :status => :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# PUT /groups/1
|
||||
# PUT /groups/1.xml
|
||||
def update
|
||||
@group = Group.find(params[:id])
|
||||
@custom_values = GroupCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @group, :value => (params[:custom_fields] ? params["custom_fields"][x.id.to_s] : nil)) }
|
||||
@group.custom_values = @custom_values
|
||||
|
||||
respond_to do |format|
|
||||
if @group.update_attributes(params[:group])
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
format.html { redirect_to(groups_path) }
|
||||
format.xml { head :ok }
|
||||
else
|
||||
format.html { render :action => "edit" }
|
||||
format.xml { render :xml => @group.errors, :status => :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /groups/1
|
||||
# DELETE /groups/1.xml
|
||||
def destroy
|
||||
@group = Group.find(params[:id])
|
||||
@group.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to(groups_url) }
|
||||
format.xml { head :ok }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -20,11 +20,17 @@ class MembersController < ApplicationController
|
||||
before_filter :find_member, :except => :new
|
||||
before_filter :find_project, :only => :new
|
||||
before_filter :authorize
|
||||
|
||||
helper :projects
|
||||
|
||||
def new
|
||||
@project.members << Member.new(params[:member]) if request.post?
|
||||
member = Member.new(params[:member])
|
||||
if params[:principal].to_s =~ %r{^(group|user)_(\d+)$}
|
||||
member.principal_type, member.principal_id = $1.camelize, $2.to_i
|
||||
end
|
||||
@project.members << member if request.post?
|
||||
respond_to do |format|
|
||||
format.html { redirect_to :action => 'settings', :tab => 'members', :id => @project }
|
||||
format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
|
||||
format.js { render(:update) {|page| page.replace_html "tab-content-members", :partial => 'projects/settings/members'} }
|
||||
end
|
||||
end
|
||||
@@ -54,7 +60,7 @@ private
|
||||
end
|
||||
|
||||
def find_member
|
||||
@member = Member.find(params[:id])
|
||||
@member = Member.find(params[:id], :conditions => 'inherited_from IS NULL')
|
||||
@project = @member.project
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
render_404
|
||||
|
||||
@@ -42,6 +42,7 @@ class UsersController < ApplicationController
|
||||
per_page_option,
|
||||
params['page']
|
||||
@users = User.find :all,:order => sort_clause,
|
||||
:include => :group,
|
||||
:conditions => conditions,
|
||||
:limit => @user_pages.items_per_page,
|
||||
:offset => @user_pages.current.offset
|
||||
@@ -58,6 +59,7 @@ class UsersController < ApplicationController
|
||||
@user.admin = params[:user][:admin] || false
|
||||
@user.login = params[:user][:login]
|
||||
@user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless @user.auth_source_id
|
||||
@user.group_id = params[:user][:group_id]
|
||||
@custom_values = UserCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @user, :value => (params[:custom_fields] ? params["custom_fields"][x.id.to_s] : nil)) }
|
||||
@user.custom_values = @custom_values
|
||||
if @user.save
|
||||
@@ -67,6 +69,7 @@ class UsersController < ApplicationController
|
||||
end
|
||||
end
|
||||
@auth_sources = AuthSource.find(:all)
|
||||
@groups = Group.find(:all, :order => 'name')
|
||||
end
|
||||
|
||||
def edit
|
||||
@@ -77,6 +80,7 @@ class UsersController < ApplicationController
|
||||
@user.admin = params[:user][:admin] if params[:user][:admin]
|
||||
@user.login = params[:user][:login] if params[:user][:login]
|
||||
@user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless params[:password].nil? or params[:password].empty? or @user.auth_source_id
|
||||
@user.group_id = params[:user][:group_id]
|
||||
if params[:custom_fields]
|
||||
@custom_values = UserCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @user, :value => params["custom_fields"][x.id.to_s]) }
|
||||
@user.custom_values = @custom_values
|
||||
@@ -88,6 +92,7 @@ class UsersController < ApplicationController
|
||||
end
|
||||
end
|
||||
@auth_sources = AuthSource.find(:all)
|
||||
@groups = Group.find(:all, :order => 'name')
|
||||
@roles = Role.find_all_givable
|
||||
@projects = Project.find(:all, :order => 'name', :conditions => "status=#{Project::STATUS_ACTIVE}") - @user.projects
|
||||
@membership ||= Member.new
|
||||
@@ -95,7 +100,7 @@ class UsersController < ApplicationController
|
||||
|
||||
def edit_membership
|
||||
@user = User.find(params[:id])
|
||||
@membership = params[:membership_id] ? Member.find(params[:membership_id]) : Member.new(:user => @user)
|
||||
@membership = params[:membership_id] ? Member.find(params[:membership_id], :conditions => 'inherited_from IS NULL') : Member.new(:principal => @user)
|
||||
@membership.attributes = params[:membership]
|
||||
if request.post? and @membership.save
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
@@ -105,7 +110,7 @@ class UsersController < ApplicationController
|
||||
|
||||
def destroy_membership
|
||||
@user = User.find(params[:id])
|
||||
if request.post? and Member.find(params[:membership_id]).destroy
|
||||
if request.post? and Member.find(params[:membership_id], :conditions => 'inherited_from IS NULL').destroy
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
end
|
||||
redirect_to :action => 'edit', :id => @user and return
|
||||
|
||||
@@ -20,7 +20,8 @@ module CustomFieldsHelper
|
||||
def custom_fields_tabs
|
||||
tabs = [{:name => 'IssueCustomField', :label => :label_issue_plural},
|
||||
{:name => 'ProjectCustomField', :label => :label_project_plural},
|
||||
{:name => 'UserCustomField', :label => :label_user_plural}
|
||||
{:name => 'UserCustomField', :label => :label_user_plural},
|
||||
{:name => 'GroupCustomField', :label => :label_group_plural}
|
||||
]
|
||||
end
|
||||
|
||||
|
||||
19
groups/app/helpers/groups_helper.rb
Normal file
19
groups/app/helpers/groups_helper.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2008 FreeCode
|
||||
#
|
||||
# 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.
|
||||
|
||||
module GroupsHelper
|
||||
end
|
||||
@@ -42,6 +42,19 @@ module ProjectsHelper
|
||||
tabs.select {|tab| User.current.allowed_to?(tab[:action], @project)}
|
||||
end
|
||||
|
||||
def principal_select_tag(groups, users)
|
||||
options = ''
|
||||
options << "<optgroup label=\"#{l(:label_group_plural)}\" class=\"groups\">" +
|
||||
options_for_select(groups.collect {|g| [g.name, "group_#{g.id}"]}) +
|
||||
"</optgroup>" unless groups.empty?
|
||||
|
||||
options << "<optgroup label=\"#{l(:label_user_plural)}\" class=\"users\">" +
|
||||
options_for_select(users.collect {|u| [u.name, "user_#{u.id}"]}) +
|
||||
"</optgroup>" unless users.empty?
|
||||
|
||||
select_tag 'principal', options
|
||||
end
|
||||
|
||||
# Generates a gantt image
|
||||
# Only defined if RMagick is avalaible
|
||||
def gantt_image(events, date_from, months, zoom)
|
||||
|
||||
39
groups/app/models/group.rb
Normal file
39
groups/app/models/group.rb
Normal file
@@ -0,0 +1,39 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2008 FreeCode
|
||||
#
|
||||
# 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 Group < ActiveRecord::Base
|
||||
|
||||
has_many :users, :dependent => :nullify
|
||||
has_many :memberships, :class_name => 'Member', :as => :principal, :dependent => :destroy
|
||||
has_many :members, :as => :principal,
|
||||
:include => [ :project, :role ],
|
||||
:conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}",
|
||||
:order => "#{Project.table_name}.name"
|
||||
has_many :custom_values, :dependent => :delete_all, :as => :customized
|
||||
|
||||
validates_presence_of :name
|
||||
validates_uniqueness_of :name
|
||||
validates_length_of :name, :maximum => 30
|
||||
|
||||
def <=>(group)
|
||||
name <=> group.name
|
||||
end
|
||||
|
||||
def to_s
|
||||
name
|
||||
end
|
||||
end
|
||||
22
groups/app/models/group_custom_field.rb
Normal file
22
groups/app/models/group_custom_field.rb
Normal file
@@ -0,0 +1,22 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2008 FreeCode
|
||||
#
|
||||
# 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 GroupCustomField < CustomField
|
||||
def type_name
|
||||
:label_group_plural
|
||||
end
|
||||
end
|
||||
@@ -16,27 +16,52 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
class Member < ActiveRecord::Base
|
||||
belongs_to :user
|
||||
belongs_to :role
|
||||
belongs_to :project
|
||||
belongs_to :role
|
||||
belongs_to :principal, :polymorphic => true
|
||||
belongs_to :user, :foreign_key => 'principal_id'
|
||||
|
||||
validates_presence_of :role, :user, :project
|
||||
validates_uniqueness_of :user_id, :scope => :project_id
|
||||
|
||||
attr_protected :inherited_from
|
||||
|
||||
validates_presence_of :project, :role, :principal
|
||||
validates_uniqueness_of :principal_id, :scope => [:project_id, :principal_type, :inherited_from]
|
||||
|
||||
def validate
|
||||
errors.add :role_id, :activerecord_error_invalid if role && !role.member?
|
||||
end
|
||||
|
||||
def name
|
||||
self.user.name
|
||||
principal.name
|
||||
end
|
||||
|
||||
# Groups sorted by role then users sorted by role
|
||||
def <=>(member)
|
||||
role == member.role ? (user <=> member.user) : (role <=> member.role)
|
||||
principal_type == member.principal_type ?
|
||||
(role == member.role ? principal <=> member.principal : role <=> member.role) :
|
||||
(principal_type <=> member.principal_type)
|
||||
end
|
||||
|
||||
def to_s
|
||||
principal.to_s
|
||||
end
|
||||
|
||||
def after_save
|
||||
# Update memberships based on group inheritance
|
||||
if principal.is_a? Group
|
||||
Member.delete_all "inherited_from = #{id}"
|
||||
principal.users.each do |user|
|
||||
Member.create! :project => project, :role => role, :principal => user, :inherited_from => id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def before_destroy
|
||||
# remove category based auto assignments for this member
|
||||
IssueCategory.update_all "assigned_to_id = NULL", ["project_id = ? AND assigned_to_id = ?", project.id, user.id]
|
||||
# Remove inherited memberships
|
||||
Member.delete_all "inherited_from = #{id}"
|
||||
|
||||
# Remove category based auto assignments for this member
|
||||
if principal.is_a? User
|
||||
IssueCategory.update_all "assigned_to_id = NULL", ["project_id = ? AND assigned_to_id = ?", project_id, principal_id]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -20,8 +20,9 @@ class Project < ActiveRecord::Base
|
||||
STATUS_ACTIVE = 1
|
||||
STATUS_ARCHIVED = 9
|
||||
|
||||
has_many :members, :include => :user, :conditions => "#{User.table_name}.status=#{User::STATUS_ACTIVE}"
|
||||
has_many :users, :through => :members
|
||||
has_many :members, :include => :user, :conditions => "#{Member.table_name}.principal_type='User' AND #{User.table_name}.status=#{User::STATUS_ACTIVE}"
|
||||
has_many :memberships, :class_name => 'Member'
|
||||
has_many :users, :through => :members, :uniq => true
|
||||
has_many :custom_values, :dependent => :delete_all, :as => :customized
|
||||
has_many :enabled_modules, :dependent => :delete_all
|
||||
has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
|
||||
|
||||
@@ -35,18 +35,25 @@ class User < ActiveRecord::Base
|
||||
:username => '#{login}'
|
||||
}
|
||||
|
||||
has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name", :dependent => :delete_all
|
||||
has_many :memberships, :class_name => 'Member',
|
||||
:as => :principal,
|
||||
:include => [ :project, :role ],
|
||||
:conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}",
|
||||
:order => "#{Project.table_name}.name, inherited_from ASC",
|
||||
:dependent => :delete_all
|
||||
|
||||
has_many :projects, :through => :memberships
|
||||
has_many :custom_values, :dependent => :delete_all, :as => :customized
|
||||
has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
|
||||
has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
|
||||
has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
|
||||
belongs_to :auth_source
|
||||
belongs_to :group
|
||||
|
||||
attr_accessor :password, :password_confirmation
|
||||
attr_accessor :last_before_login_on
|
||||
# Prevents unauthorized assignments
|
||||
attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
|
||||
attr_protected :login, :admin, :password, :password_confirmation, :hashed_password, :group_id
|
||||
|
||||
validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
|
||||
validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }
|
||||
@@ -71,6 +78,23 @@ class User < ActiveRecord::Base
|
||||
# update hashed_password if password was set
|
||||
self.hashed_password = User.hash_password(self.password) if self.password
|
||||
end
|
||||
|
||||
def after_save
|
||||
if @group_changed
|
||||
# Update inherited memberships
|
||||
Member.delete_all "principal_type = 'User' AND principal_id = #{id} AND inherited_from IS NOT NULL"
|
||||
unless group.nil?
|
||||
group.memberships.each do |m|
|
||||
Member.create! :project => m.project, :role => m.role, :principal => self, :inherited_from => m.id
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def group_id=(gid)
|
||||
@group_changed = true unless gid == group_id
|
||||
write_attribute(:group_id, gid)
|
||||
end
|
||||
|
||||
def self.active
|
||||
with_scope :find => { :conditions => [ "status = ?", STATUS_ACTIVE ] } do
|
||||
@@ -167,8 +191,8 @@ class User < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def notified_project_ids=(ids)
|
||||
Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
|
||||
Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
|
||||
Member.update_all("mail_notification = #{connection.quoted_false}", ["principal_type = 'User' AND principal_id = ?", id])
|
||||
Member.update_all("mail_notification = #{connection.quoted_true}", ["principal_type = 'User' AND principal_id = ? AND project_id IN (?)", id, ids]) if ids && !ids.empty?
|
||||
@notified_projects_ids = nil
|
||||
notified_projects_ids
|
||||
end
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
</p>
|
||||
|
||||
<p class="icon22 icon22-users">
|
||||
<%= link_to l(:label_group_plural), :controller => 'groups' %> |
|
||||
<%= link_to l(:label_new), :controller => 'groups', :action => 'new' %>
|
||||
</p>
|
||||
|
||||
<p class="icon22 icon22-user">
|
||||
<%= link_to l(:label_user_plural), :controller => 'users' %> |
|
||||
<%= link_to l(:label_new), :controller => 'users', :action => 'add' %>
|
||||
</p>
|
||||
|
||||
@@ -99,12 +99,9 @@ when "IssueCustomField" %>
|
||||
<p><%= f.check_box :is_filter %></p>
|
||||
<p><%= f.check_box :searchable %></p>
|
||||
|
||||
<% when "UserCustomField" %>
|
||||
<% else %>
|
||||
<p><%= f.check_box :is_required %></p>
|
||||
|
||||
<% when "ProjectCustomField" %>
|
||||
<p><%= f.check_box :is_required %></p>
|
||||
|
||||
|
||||
<% end %>
|
||||
</div>
|
||||
<%= javascript_tag "toggle_custom_field_format();" %>
|
||||
|
||||
10
groups/app/views/groups/_form.html.erb
Normal file
10
groups/app/views/groups/_form.html.erb
Normal file
@@ -0,0 +1,10 @@
|
||||
<div class="box tabular">
|
||||
<p><%= f.text_field :name %></p>
|
||||
<% for @custom_value in @custom_values %>
|
||||
<p><%= custom_field_tag_with_label @custom_value %></p>
|
||||
<% end %>
|
||||
|
||||
<% unless @group.users.empty? %>
|
||||
<p><label><%= l(:label_user_plural) %> (<%= @group.users.size %>)</label><%= @group.users.collect {|u| link_to_user u }.join(', ') %></p>
|
||||
<% end %>
|
||||
</div>
|
||||
8
groups/app/views/groups/edit.html.erb
Normal file
8
groups/app/views/groups/edit.html.erb
Normal file
@@ -0,0 +1,8 @@
|
||||
<h2><%= l(:label_group) %></h2>
|
||||
|
||||
<%= error_messages_for :group %>
|
||||
|
||||
<% form_for(@group, :builder => TabularFormBuilder, :lang => current_language) do |f| %>
|
||||
<%= render :partial => 'form', :locals => { :f => f } %>
|
||||
<p><%= f.submit l(:button_save) %></p>
|
||||
<% end %>
|
||||
25
groups/app/views/groups/index.html.erb
Normal file
25
groups/app/views/groups/index.html.erb
Normal file
@@ -0,0 +1,25 @@
|
||||
<h2><%= l(:label_group_plural) %></h2>
|
||||
|
||||
<% if @groups.any? %>
|
||||
<table class="list groups">
|
||||
<thead><tr>
|
||||
<th><%=l(:label_group)%></th>
|
||||
<th><%=l(:label_user_plural)%></th>
|
||||
<th></th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<% @groups.each do |group| %>
|
||||
<tr class="group <%= cycle 'odd', 'even' %>">
|
||||
<td><%= link_to h(group.name), :action => 'edit', :id => group %></td>
|
||||
<td align="center"><%= group.users.size %></td>
|
||||
<td align="right"><%= link_to l(:button_delete), group, :confirm => l(:text_are_you_sure), :method => :delete, :class => 'icon icon-del' %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
<% else %>
|
||||
<p class="nodata"><%= l(:label_no_data) %></p>
|
||||
<% end %>
|
||||
|
||||
<% form_for(@group) do |f| %>
|
||||
<p><label><%= l(:label_group_new) %>: <%= f.text_field :name %> <%= f.submit l(:button_create) %></p>
|
||||
<% end %>
|
||||
8
groups/app/views/groups/new.html.erb
Normal file
8
groups/app/views/groups/new.html.erb
Normal file
@@ -0,0 +1,8 @@
|
||||
<h2><%= l(:label_group_new) %></h2>
|
||||
|
||||
<%= error_messages_for :group %>
|
||||
|
||||
<% form_for(@group, :builder => TabularFormBuilder, :lang => current_language) do |f| %>
|
||||
<%= render :partial => 'form', :locals => { :f => f } %>
|
||||
<p><%= f.submit l(:button_create) %></p>
|
||||
<% end %>
|
||||
7
groups/app/views/groups/show.html.erb
Normal file
7
groups/app/views/groups/show.html.erb
Normal file
@@ -0,0 +1,7 @@
|
||||
<h2><%=h @group %></h2>
|
||||
|
||||
<ul>
|
||||
<% @group.users.each do |user| %>
|
||||
<li><%=h user %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
@@ -1,22 +1,23 @@
|
||||
<%= error_messages_for 'member' %>
|
||||
<% roles = Role.find_all_givable %>
|
||||
<% users = User.find_active(:all).sort - @project.users %>
|
||||
<% users = User.find_active(:all).sort - @project.users.find(:all, :conditions => 'inherited_from IS NULL') %>
|
||||
<% groups = Group.find(:all, :order => 'name') - @project.memberships.collect(&:principal) %>
|
||||
<% # members sorted by role position
|
||||
members = @project.members.find(:all, :include => [:role, :user]).sort %>
|
||||
members = @project.memberships.select {|m| m.inherited_from.nil? }.sort #members.find(:all, :include => [:role, :user]).sort %>
|
||||
|
||||
<% if members.any? %>
|
||||
<table class="list">
|
||||
<table class="list members">
|
||||
<thead>
|
||||
<th><%= l(:label_user) %></th>
|
||||
<th><%= l(:label_member) %></th>
|
||||
<th><%= l(:label_role) %></th>
|
||||
<th style="width:15%"></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% members.each do |member| %>
|
||||
<% next if member.new_record? %>
|
||||
<tr class="<%= cycle 'odd', 'even' %>">
|
||||
<td><%= member.name %></td>
|
||||
<td align="center">
|
||||
<tr class="<%= cycle 'odd', 'even' %> <%= member.principal.class.name.downcase %>">
|
||||
<td class="name"><%=h member %></td>
|
||||
<td class="role">
|
||||
<% if authorize_for('members', 'edit') %>
|
||||
<% remote_form_for(:member, member, :url => {:controller => 'members', :action => 'edit', :id => member}, :method => :post) do |f| %>
|
||||
<%= f.select :role_id, roles.collect{|role| [role.name, role.id]}, {}, :class => "small" %>
|
||||
@@ -38,10 +39,10 @@
|
||||
<p class="nodata"><%= l(:label_no_data) %></p>
|
||||
<% end %>
|
||||
|
||||
<% if authorize_for('members', 'new') && !users.empty? %>
|
||||
<% if authorize_for('members', 'new') && !(users.empty? && groups.empty?) %>
|
||||
<p><%=l(:label_member_new)%></p>
|
||||
<% remote_form_for(:member, @member, :url => {:controller => 'members', :action => 'new', :id => @project}, :method => :post) do |f| %>
|
||||
<p><label for="member_user_id"><%=l(:label_member_new)%></label><br />
|
||||
<%= f.select :user_id, users.collect{|user| [user.name, user.id]} %>
|
||||
<p><%= principal_select_tag(groups, users) %>
|
||||
<%= l(:label_role) %>: <%= f.select :role_id, roles.collect{|role| [role.name, role.id]}, :selected => nil %>
|
||||
<%= submit_tag l(:button_add) %></p>
|
||||
<% end %>
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
<p><%= f.text_field :firstname, :required => true %></p>
|
||||
<p><%= f.text_field :lastname, :required => true %></p>
|
||||
<p><%= f.text_field :mail, :required => true %></p>
|
||||
<% unless @groups.empty? -%>
|
||||
<p><%= f.select :group_id, @groups.collect {|g| [g.name, g.id]}, { :include_blank => true } %></p>
|
||||
<% end -%>
|
||||
<p><%= f.select :language, lang_options_for_select %></p>
|
||||
|
||||
<% for @custom_value in @custom_values %>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<div class="box" style="margin-top: 16px;">
|
||||
<h3><%= l(:label_project_plural) %></h3>
|
||||
|
||||
<% @user.memberships.each do |membership| %>
|
||||
<% @user.memberships.select {|m| m.inherited_from.nil? }.each do |membership| %>
|
||||
<% form_tag({ :action => 'edit_membership', :id => @user, :membership_id => membership }, :class => "tabular") do %>
|
||||
<p style="margin:0;padding-top:0;">
|
||||
<label><%= membership.project.name %></label>
|
||||
@@ -13,6 +13,8 @@
|
||||
</p>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% unless @projects.empty? || @roles.empty? %>
|
||||
<hr />
|
||||
<p>
|
||||
<label><%=l(:label_project_new)%></label><br/>
|
||||
@@ -20,10 +22,12 @@
|
||||
<select name="membership[project_id]">
|
||||
<%= options_from_collection_for_select @projects, "id", "name", @membership.project_id %>
|
||||
</select>
|
||||
<%= l(:label_role) %>:
|
||||
<select name="membership[role_id]">
|
||||
<%= options_from_collection_for_select @roles, "id", "name", @membership.role_id %>
|
||||
</select>
|
||||
<%= submit_tag l(:button_add) %>
|
||||
<% end %>
|
||||
</p>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -18,6 +18,7 @@
|
||||
<%= sort_header_tag('firstname', :caption => l(:field_firstname)) %>
|
||||
<%= sort_header_tag('lastname', :caption => l(:field_lastname)) %>
|
||||
<%= sort_header_tag('mail', :caption => l(:field_mail)) %>
|
||||
<%= sort_header_tag("#{Group.table_name}.name", :caption => l(:field_group)) %>
|
||||
<%= sort_header_tag('admin', :caption => l(:field_admin), :default_order => 'desc') %>
|
||||
<%= sort_header_tag('created_on', :caption => l(:field_created_on), :default_order => 'desc') %>
|
||||
<%= sort_header_tag('last_login_on', :caption => l(:field_last_login_on), :default_order => 'desc') %>
|
||||
@@ -27,9 +28,10 @@
|
||||
<% for user in @users -%>
|
||||
<tr class="user <%= cycle("odd", "even") %> <%= %w(anon active registered locked)[user.status] %>">
|
||||
<td class="username"><%= link_to user.login, :action => 'edit', :id => user %></td>
|
||||
<td class="firstname"><%= user.firstname %></td>
|
||||
<td class="lastname"><%= user.lastname %></td>
|
||||
<td class="email"><%= user.mail %></td>
|
||||
<td class="firstname"><%=h user.firstname %></td>
|
||||
<td class="lastname"><%=h user.lastname %></td>
|
||||
<td class="email"><%=h user.mail %></td>
|
||||
<td class="group"><%=h user.group %></td>
|
||||
<td align="center"><%= image_tag('true.png') if user.admin? %></td>
|
||||
<td class="created_on" align="center"><%= format_time(user.created_on) %></td>
|
||||
<td class="last_login_on" align="center"><%= format_time(user.last_login_on) unless user.last_login_on.nil? %></td>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
ActionController::Routing::Routes.draw do |map|
|
||||
map.resources :groups
|
||||
|
||||
# Add your own custom routes here.
|
||||
# The priority is based upon order of creation: first created -> highest priority.
|
||||
|
||||
|
||||
11
groups/db/migrate/093_create_groups.rb
Normal file
11
groups/db/migrate/093_create_groups.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
class CreateGroups < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table :groups do |t|
|
||||
t.column :name, :string, :null => false
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :groups
|
||||
end
|
||||
end
|
||||
9
groups/db/migrate/094_add_users_group_id.rb
Normal file
9
groups/db/migrate/094_add_users_group_id.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
class AddUsersGroupId < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :users, :group_id, :integer
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :users, :group_id
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
class ChangeMembersUsersAssociationToPolymorphic < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :members, :principal_type, :string
|
||||
add_column :members, :principal_id, :integer
|
||||
add_column :members, :inherited_from, :integer
|
||||
Member.update_all "principal_type = 'User', principal_id = user_id"
|
||||
remove_column :members, :user_id
|
||||
add_index :members, [:principal_type, :principal_id], :name => :members_principal
|
||||
end
|
||||
|
||||
def self.down
|
||||
# Remove inherited memberships
|
||||
Member.delete_all "inherited_from IS NOT NULL"
|
||||
add_column :members, :user_id, :integer, :default => 0, :null => false
|
||||
Member.update_all "user_id = principal_id"
|
||||
remove_column :members, :principal_type, :string
|
||||
remove_column :members, :principal_id, :integer
|
||||
remove_column :members, :inherited_from, :integer
|
||||
end
|
||||
end
|
||||
@@ -179,6 +179,7 @@ field_time_zone: Time zone
|
||||
field_searchable: Searchable
|
||||
field_default_value: Default value
|
||||
field_comments_sorting: Display comments
|
||||
field_group: Group
|
||||
|
||||
setting_app_title: Application title
|
||||
setting_app_subtitle: Application subtitle
|
||||
@@ -510,6 +511,9 @@ label_preferences: Preferences
|
||||
label_chronological_order: In chronological order
|
||||
label_reverse_chronological_order: In reverse chronological order
|
||||
label_planning: Planning
|
||||
label_group: Group
|
||||
label_group_plural: Groups
|
||||
label_group_new: New group
|
||||
|
||||
button_login: Login
|
||||
button_submit: Submit
|
||||
|
||||
BIN
groups/public/images/22x22/user.png
Normal file
BIN
groups/public/images/22x22/user.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 781 B |
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
@@ -105,12 +105,19 @@ tr.message td.last_message { font-size: 80%; }
|
||||
tr.message.locked td.subject a { background-image: url(../images/locked.png); }
|
||||
tr.message.sticky td.subject a { background-image: url(../images/sticky.png); font-weight: bold; }
|
||||
|
||||
table.members td { vertical-align: middle; }
|
||||
table.members td.name { padding-left: 20px; }
|
||||
table.members tr.group td.name { background: url(../images/users.png) 0 50% no-repeat; }
|
||||
table.members tr.user td.name { background: url(../images/user.png) 0 50% no-repeat; }
|
||||
|
||||
tr.user td { width:13%; }
|
||||
tr.user td.email { width:18%; }
|
||||
tr.user td { white-space: nowrap; }
|
||||
tr.user.locked, tr.user.registered { color: #aaa; }
|
||||
tr.user.locked a, tr.user.registered a { color: #aaa; }
|
||||
|
||||
optgroup { font-style: normal; }
|
||||
|
||||
tr.time-entry { text-align: center; white-space: nowrap; }
|
||||
tr.time-entry td.subject, tr.time-entry td.comments { text-align: left; white-space: normal; }
|
||||
td.hours { text-align: right; font-weight: bold; padding-right: 0.5em; }
|
||||
@@ -574,6 +581,7 @@ vertical-align: middle;
|
||||
|
||||
.icon22-projects { background-image: url(../images/22x22/projects.png); }
|
||||
.icon22-users { background-image: url(../images/22x22/users.png); }
|
||||
.icon22-user { background-image: url(../images/22x22/user.png); }
|
||||
.icon22-tracker { background-image: url(../images/22x22/tracker.png); }
|
||||
.icon22-role { background-image: url(../images/22x22/role.png); }
|
||||
.icon22-workflow { background-image: url(../images/22x22/workflow.png); }
|
||||
|
||||
7
groups/test/fixtures/groups.yml
vendored
Normal file
7
groups/test/fixtures/groups.yml
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
managers:
|
||||
id: 1
|
||||
name: Managers
|
||||
clients:
|
||||
id: 2
|
||||
name: Clients
|
||||
|
||||
27
groups/test/fixtures/members.yml
vendored
27
groups/test/fixtures/members.yml
vendored
@@ -4,24 +4,43 @@ members_001:
|
||||
project_id: 1
|
||||
role_id: 1
|
||||
id: 1
|
||||
user_id: 2
|
||||
principal_type: User
|
||||
principal_id: 2
|
||||
members_002:
|
||||
created_on: 2006-07-19 19:35:36 +02:00
|
||||
project_id: 1
|
||||
role_id: 2
|
||||
id: 2
|
||||
user_id: 3
|
||||
principal_type: User
|
||||
principal_id: 3
|
||||
members_003:
|
||||
created_on: 2006-07-19 19:35:36 +02:00
|
||||
project_id: 2
|
||||
role_id: 2
|
||||
id: 3
|
||||
user_id: 2
|
||||
principal_type: User
|
||||
principal_id: 2
|
||||
members_004:
|
||||
id: 4
|
||||
created_on: 2006-07-19 19:35:36 +02:00
|
||||
project_id: 1
|
||||
role_id: 2
|
||||
# Locked user
|
||||
user_id: 5
|
||||
principal_type: User
|
||||
principal_id: 5
|
||||
members_005:
|
||||
id: 5
|
||||
created_on: 2008-01-19 19:35:36 +02:00
|
||||
project_id: 1
|
||||
role_id: 3
|
||||
principal_type: Group
|
||||
principal_id: 2 # Clients
|
||||
members_006:
|
||||
id: 6
|
||||
created_on: 2008-01-19 19:35:36 +02:00
|
||||
project_id: 1
|
||||
role_id: 3
|
||||
principal_type: User
|
||||
principal_id: 7
|
||||
inherited_from: 5
|
||||
|
||||
10
groups/test/fixtures/roles.yml
vendored
10
groups/test/fixtures/roles.yml
vendored
@@ -1,5 +1,5 @@
|
||||
---
|
||||
roles_001:
|
||||
manager:
|
||||
name: Manager
|
||||
id: 1
|
||||
builtin: 0
|
||||
@@ -41,7 +41,7 @@ roles_001:
|
||||
- :view_changesets
|
||||
|
||||
position: 1
|
||||
roles_002:
|
||||
developer:
|
||||
name: Developer
|
||||
id: 2
|
||||
builtin: 0
|
||||
@@ -78,7 +78,7 @@ roles_002:
|
||||
- :view_changesets
|
||||
|
||||
position: 2
|
||||
roles_003:
|
||||
reporter:
|
||||
name: Reporter
|
||||
id: 3
|
||||
builtin: 0
|
||||
@@ -113,7 +113,7 @@ roles_003:
|
||||
- :view_changesets
|
||||
|
||||
position: 3
|
||||
roles_004:
|
||||
non_member:
|
||||
name: Non member
|
||||
id: 4
|
||||
builtin: 1
|
||||
@@ -141,7 +141,7 @@ roles_004:
|
||||
- :view_changesets
|
||||
|
||||
position: 4
|
||||
roles_005:
|
||||
anonymous:
|
||||
name: Anonymous
|
||||
id: 5
|
||||
builtin: 2
|
||||
|
||||
33
groups/test/fixtures/users.yml
vendored
33
groups/test/fixtures/users.yml
vendored
@@ -96,5 +96,38 @@ users_006:
|
||||
mail_notification: false
|
||||
login: ''
|
||||
type: AnonymousUser
|
||||
client:
|
||||
id: 7
|
||||
created_on: 2006-07-19 19:33:19 +02:00
|
||||
status: 1
|
||||
last_login_on:
|
||||
language: ''
|
||||
hashed_password: 7feb7657aa7a7bf5aef3414a5084875f27192415
|
||||
updated_on: 2006-07-19 19:33:19 +02:00
|
||||
admin: false
|
||||
mail: 'client@foo.bar'
|
||||
lastname: User
|
||||
firstname: Client
|
||||
auth_source_id:
|
||||
mail_notification: false
|
||||
login: client
|
||||
type: User
|
||||
group_id: 2 # Clients
|
||||
new_client:
|
||||
id: 8
|
||||
created_on: 2006-07-19 19:33:19 +02:00
|
||||
status: 1
|
||||
last_login_on:
|
||||
language: ''
|
||||
hashed_password: 7feb7657aa7a7bf5aef3414a5084875f27192415
|
||||
updated_on: 2006-07-19 19:33:19 +02:00
|
||||
admin: false
|
||||
mail: 'newclient@foo.bar'
|
||||
lastname: New
|
||||
firstname: Client
|
||||
auth_source_id:
|
||||
mail_notification: false
|
||||
login: newclient
|
||||
type: User
|
||||
|
||||
|
||||
76
groups/test/functional/groups_controller_test.rb
Normal file
76
groups/test/functional/groups_controller_test.rb
Normal file
@@ -0,0 +1,76 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2008 FreeCode
|
||||
#
|
||||
# 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.
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'groups_controller'
|
||||
|
||||
# Re-raise errors caught by the controller.
|
||||
class GroupsController; def rescue_action(e) raise e end; end
|
||||
|
||||
class GroupsControllerTest < Test::Unit::TestCase
|
||||
fixtures :groups, :users
|
||||
|
||||
def setup
|
||||
@controller = GroupsController.new
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
User.current = nil
|
||||
@request.session[:user_id] = 1 # admin
|
||||
end
|
||||
|
||||
def test_should_get_index
|
||||
get :index
|
||||
assert_response :success
|
||||
assert_not_nil assigns(:groups)
|
||||
end
|
||||
|
||||
def test_should_get_new
|
||||
get :new
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_should_create_group
|
||||
assert_difference('Group.count') do
|
||||
post :create, :group => { :name => 'New group' }
|
||||
end
|
||||
assert_redirected_to groups_path
|
||||
assert_not_nil Group.find_by_name('New group')
|
||||
end
|
||||
|
||||
def test_should_show_group
|
||||
get :show, :id => 1
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_should_get_edit
|
||||
get :edit, :id => 1
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_should_update_group
|
||||
put :update, :id => 1, :group => { :name => 'Renamed' }
|
||||
assert_redirected_to groups_path
|
||||
assert_equal 'Renamed', Group.find(1).name
|
||||
end
|
||||
|
||||
def test_should_destroy_group
|
||||
assert_difference('Group.count', -1) do
|
||||
delete :destroy, :id => 1
|
||||
end
|
||||
assert_redirected_to groups_path
|
||||
end
|
||||
end
|
||||
80
groups/test/functional/members_controller_test.rb
Normal file
80
groups/test/functional/members_controller_test.rb
Normal file
@@ -0,0 +1,80 @@
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2008 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.
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'members_controller'
|
||||
|
||||
# Re-raise errors caught by the controller.
|
||||
class MembersController; def rescue_action(e) raise e end; end
|
||||
|
||||
class MembersControllerTest < Test::Unit::TestCase
|
||||
fixtures :projects, :roles, :users, :groups, :members
|
||||
|
||||
def setup
|
||||
@controller = MembersController.new
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
User.current = nil
|
||||
@request.session[:user_id] = 1 # admin
|
||||
end
|
||||
|
||||
def test_should_create_user_member
|
||||
p = Project.find(1)
|
||||
u = users(:new_client)
|
||||
assert_difference('Member.count') do
|
||||
post :new, :id => p.id, :member => { :role_id => roles(:reporter).id }, :principal => "user_#{u.id}"
|
||||
end
|
||||
assert_redirected_to :controller => 'projects', :action => 'settings', :id => p, :tab => 'members'
|
||||
assert u.reload.member_of?(p)
|
||||
assert_equal roles(:reporter), u.role_for_project(p)
|
||||
end
|
||||
|
||||
def test_should_create_group_member
|
||||
p = Project.find(2)
|
||||
assert_difference('Member.count') do
|
||||
post :new, :id => p.id, :member => { :role_id => roles(:reporter) }, :principal => 'group_1'
|
||||
end
|
||||
assert_redirected_to :controller => 'projects', :action => 'settings', :id => p, :tab => 'members'
|
||||
end
|
||||
|
||||
def test_should_update_user_member
|
||||
u = User.find(3)
|
||||
p = Project.find(1)
|
||||
assert_equal roles(:developer), u.role_for_project(p)
|
||||
assert_difference('Member.count', 0) do
|
||||
post :edit, :id => 2, :member => { :role_id => roles(:manager).id }
|
||||
end
|
||||
assert_redirected_to :controller => 'projects', :action => 'settings', :id => p, :tab => 'members'
|
||||
assert_equal roles(:manager), u.reload.role_for_project(p)
|
||||
end
|
||||
|
||||
def test_should_destroy
|
||||
p = Project.find(1)
|
||||
assert_difference('Member.count', -1) do
|
||||
post :destroy, :id => 2
|
||||
end
|
||||
assert_redirected_to :controller => 'projects', :action => 'settings', :id => p, :tab => 'members'
|
||||
end
|
||||
|
||||
def test_should_not_destroy_inherited_membership
|
||||
p = Project.find(1)
|
||||
assert_difference('Member.count', 0) do
|
||||
post :destroy, :id => 6
|
||||
end
|
||||
assert_response 404
|
||||
end
|
||||
end
|
||||
@@ -47,6 +47,14 @@ class UsersControllerTest < Test::Unit::TestCase
|
||||
assert_nil assigns(:users).detect {|u| !u.active?}
|
||||
end
|
||||
|
||||
def test_should_add_membership
|
||||
assert_difference('User.find(2).memberships.count') do
|
||||
post :edit_membership, :id => 2, :membership => { :role_id => 1, :project_id => 3 }
|
||||
assert_redirected_to 'users/edit/2'
|
||||
assert User.find(2).member_of?(Project.find(3))
|
||||
end
|
||||
end
|
||||
|
||||
def test_edit_membership
|
||||
post :edit_membership, :id => 2, :membership_id => 1,
|
||||
:membership => { :role_id => 2}
|
||||
|
||||
129
groups/test/unit/group_test.rb
Normal file
129
groups/test/unit/group_test.rb
Normal file
@@ -0,0 +1,129 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2008 FreeCode
|
||||
#
|
||||
# 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.
|
||||
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
|
||||
class GroupTest < ActiveSupport::TestCase
|
||||
fixtures :groups, :users, :projects, :roles, :members
|
||||
|
||||
def test_should_validate_presence_of_name
|
||||
g = Group.new(:name => '')
|
||||
assert !g.save
|
||||
assert_equal 1, g.errors.size
|
||||
end
|
||||
|
||||
def test_should_validate_uniqueness_of_name
|
||||
g = Group.new(:name => groups(:clients).name)
|
||||
assert !g.save
|
||||
assert_equal 1, g.errors.size
|
||||
end
|
||||
|
||||
def test_should_create
|
||||
g = Group.new(:name => 'New group')
|
||||
assert g.save
|
||||
assert g.users.empty?
|
||||
end
|
||||
|
||||
def test_should_destroy
|
||||
g = groups(:clients)
|
||||
p = Project.find(1)
|
||||
u = users(:client)
|
||||
assert u.member_of?(p)
|
||||
|
||||
assert_difference('Member.count', -2) do
|
||||
g.destroy
|
||||
end
|
||||
u.reload
|
||||
assert_nil u.group
|
||||
assert !u.member_of?(p)
|
||||
end
|
||||
|
||||
def test_should_add_user_to_group
|
||||
g = groups(:clients)
|
||||
p = Project.find(1)
|
||||
u = users(:new_client)
|
||||
r = roles(:reporter)
|
||||
assert !u.member_of?(p)
|
||||
|
||||
assert_difference('Member.count') do
|
||||
assert_difference('g.reload.users.size') do
|
||||
u.group_id = g.id
|
||||
assert u.save
|
||||
end
|
||||
end
|
||||
u.reload
|
||||
assert u.group = g
|
||||
assert u.member_of?(p)
|
||||
assert_equal r, u.role_for_project(p)
|
||||
end
|
||||
|
||||
def test_should_add_group_to_project
|
||||
g = groups(:clients)
|
||||
p = Project.find(2)
|
||||
u = users(:client)
|
||||
r = roles(:reporter)
|
||||
assert !u.member_of?(p)
|
||||
|
||||
assert_difference('Member.count', 2) do
|
||||
assert_difference('p.reload.users.size') do
|
||||
m = Member.new(:project => p, :principal => g, :role => r)
|
||||
assert m.save
|
||||
end
|
||||
end
|
||||
u.reload
|
||||
assert u.member_of?(p)
|
||||
assert_equal r, u.role_for_project(p)
|
||||
end
|
||||
|
||||
def test_should_remove_user_from_group
|
||||
g = groups(:clients)
|
||||
p = Project.find(1)
|
||||
u = users(:client)
|
||||
assert u.member_of?(p)
|
||||
|
||||
assert_difference('Member.count', -1) do
|
||||
assert_difference('g.reload.users.size', -1) do
|
||||
u.group_id = nil
|
||||
assert u.save
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_override_group_role
|
||||
g = groups(:clients)
|
||||
p = Project.find(1)
|
||||
u = users(:client)
|
||||
assert u.member_of?(p)
|
||||
assert_equal roles(:reporter), u.role_for_project(p)
|
||||
|
||||
assert_difference('Member.count', 1) do
|
||||
assert_difference('p.reload.users.size', 0) do
|
||||
m = Member.new(:project => p, :principal => u, :role => roles(:manager))
|
||||
assert m.save
|
||||
end
|
||||
end
|
||||
assert_equal roles(:manager), u.reload.role_for_project(p)
|
||||
|
||||
# Remove the group, user should still be a member
|
||||
assert_difference('Member.count', -2) do
|
||||
assert_difference('p.reload.users.size', 0) do
|
||||
assert g.destroy
|
||||
end
|
||||
end
|
||||
assert_equal roles(:manager), u.reload.role_for_project(p)
|
||||
end
|
||||
end
|
||||
@@ -25,7 +25,7 @@ class MemberTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_create
|
||||
member = Member.new(:project_id => 1, :user_id => 4, :role_id => 1)
|
||||
member = Member.new(:project_id => 1, :principal_type => 'User', :principal_id => 4, :role_id => 1)
|
||||
assert member.save
|
||||
end
|
||||
|
||||
@@ -39,7 +39,7 @@ class MemberTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_validate
|
||||
member = Member.new(:project_id => 1, :user_id => 2, :role_id =>2)
|
||||
member = Member.new(:project_id => 1, :principal_type => 'User', :principal_id => 2, :role_id =>2)
|
||||
# same use can't have more than one role for a project
|
||||
assert !member.save
|
||||
end
|
||||
|
||||
@@ -80,10 +80,10 @@ class ProjectTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_destroy
|
||||
# 2 active members
|
||||
assert_equal 2, @ecookbook.members.size
|
||||
# 3 active members
|
||||
assert_equal 3, @ecookbook.members.size
|
||||
# and 1 is locked
|
||||
assert_equal 3, Member.find(:all, :conditions => ['project_id = ?', @ecookbook.id]).size
|
||||
assert_equal 5, Member.find(:all, :conditions => ['project_id = ?', @ecookbook.id]).size
|
||||
# some boards
|
||||
assert @ecookbook.boards.any?
|
||||
|
||||
|
||||
Reference in New Issue
Block a user