1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
|
# frozen_string_literal: true
# Redmine - project management software
# Copyright (C) 2006- Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class Member < ApplicationRecord
belongs_to :user
belongs_to :principal, :foreign_key => 'user_id'
has_many :member_roles, :dependent => :destroy
has_many :roles, lambda {distinct}, :through => :member_roles
belongs_to :project
validates_presence_of :principal, :project
validates_uniqueness_of :user_id, :scope => :project_id, :case_sensitive => true
validate :validate_role
before_destroy :set_issue_category_nil, :remove_from_project_default_assigned_to
scope :active, (lambda do
joins(:principal).where(:users => {:status => Principal::STATUS_ACTIVE})
end)
# Sort by first role and principal
scope :sorted, (lambda do
includes(:member_roles, :roles, :principal).
reorder("#{Role.table_name}.position").
order(Principal.fields_for_order_statement)
end)
scope :sorted_by_project, (lambda do
includes(:project).
reorder("#{Project.table_name}.lft")
end)
alias :base_reload :reload
def reload(*args)
@managed_roles = nil
base_reload(*args)
end
def role
end
def role=
end
def name
self.user.name
end
alias :base_role_ids= :role_ids=
def role_ids=(arg)
ids = (arg || []).collect(&:to_i) - [0]
# Keep inherited roles
ids |= member_roles.select {|mr| !mr.inherited_from.nil?}.collect(&:role_id)
new_role_ids = ids - role_ids
# Add new roles
new_role_ids.each do |id|
member_roles << MemberRole.new(:role_id => id, :member => self)
end
# Remove roles (Rails' #role_ids= will not trigger MemberRole#on_destroy)
member_roles_to_destroy = member_roles.select {|mr| !ids.include?(mr.role_id)}
if member_roles_to_destroy.any?
member_roles_to_destroy.each(&:destroy)
end
member_roles.reload
super(ids)
end
def <=>(member)
return nil unless member.is_a?(Member)
a, b = roles.sort, member.roles.sort
if a == b
if principal
principal <=> member.principal
else
1
end
elsif a.any?
b.any? ? a <=> b : -1
else
1
end
end
# Set member role ids ignoring any change to roles that
# user is not allowed to manage
def set_editable_role_ids(ids, user=User.current)
ids = (ids || []).collect(&:to_i) - [0]
editable_role_ids = user.managed_roles(project).map(&:id)
untouched_role_ids = self.role_ids - editable_role_ids
touched_role_ids = ids & editable_role_ids
self.role_ids = untouched_role_ids + touched_role_ids
end
# Returns true if one of the member roles is inherited
def any_inherited_role?
member_roles.any? {|mr| mr.inherited_from}
end
# Returns true if the member has the role and if it's inherited
def has_inherited_role?(role)
member_roles.any? {|mr| mr.role_id == role.id && mr.inherited_from.present?}
end
# Returns an Array of Project and/or Group from which the given role
# was inherited, or an empty Array if the role was not inherited
def role_inheritance(role)
member_roles.
select {|mr| mr.role_id == role.id && mr.inherited_from.present?}.
filter_map {|mr| mr.inherited_from_member_role.try(:member)}.
map {|m| m.project == project ? m.principal : m.project}
end
# Returns true if the member's role is editable by user
def role_editable?(role, user=User.current)
if has_inherited_role?(role)
false
else
user.managed_roles(project).include?(role)
end
end
# Returns true if the member is deletable by user
def deletable?(user=User.current)
if any_inherited_role?
false
else
roles & user.managed_roles(project) == roles
end
end
# Destroys the member
def destroy
member_roles.reload.each(&:destroy_without_member_removal)
super
end
# Returns true if the member is user or is a group
# that includes user
def include?(user)
if principal.is_a?(Group)
!user.nil? && user.groups.include?(principal)
else
self.principal == user
end
end
def set_issue_category_nil
if user_id && project_id
# remove category based auto assignments for this member
IssueCategory.where(["project_id = ? AND assigned_to_id = ?", project_id, user_id]).
update_all("assigned_to_id = NULL")
end
end
def remove_from_project_default_assigned_to
if user_id && project && project.default_assigned_to_id == user_id
# remove project based auto assignments for this member
project.update_column(:default_assigned_to_id, nil)
end
end
# Returns the roles that the member is allowed to manage
# in the project the member belongs to
def managed_roles
@managed_roles ||= begin
if principal.try(:admin?)
Role.givable.to_a
else
members_management_roles = roles.select do |role|
role.has_permission?(:manage_members)
end
if members_management_roles.empty?
[]
elsif members_management_roles.any?(&:all_roles_managed?)
Role.givable.to_a
else
members_management_roles.map(&:managed_roles).reduce(&:|)
end
end
end
end
# Creates memberships for principal with the attributes, or add the roles
# if the membership already exists.
# * project_ids : one or more project ids
# * role_ids : ids of the roles to give to each membership
#
# Example:
# Member.create_principal_memberships(user, :project_ids => [2, 5], :role_ids => [1, 3]
def self.create_principal_memberships(principal, attributes)
members = []
if attributes
project_ids = Array.wrap(attributes[:project_ids] || attributes[:project_id])
role_ids = Array.wrap(attributes[:role_ids])
project_ids.each do |project_id|
member = Member.find_or_initialize_by(:project_id => project_id, :user_id => principal.id)
member.role_ids |= role_ids
member.save
members << member
end
end
members
end
protected
def validate_role
errors.add(:role, :empty) if member_roles.empty? && roles.empty?
end
end
|