principal.rb
185 lines
| 5.9 KiB
| text/x-ruby
|
RubyLexer
|
r2781 | # Redmine - project management software | ||
|
r13490 | # Copyright (C) 2006-2015 Jean-Philippe Lang | ||
|
r2781 | # | ||
# 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. | ||||
|
r6755 | # | ||
|
r2781 | # 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. | ||||
|
r6755 | # | ||
|
r2781 | # 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 Principal < ActiveRecord::Base | ||||
|
r9367 | self.table_name = "#{table_name_prefix}users#{table_name_suffix}" | ||
|
r2781 | |||
|
r10731 | # Account statuses | ||
STATUS_ANONYMOUS = 0 | ||||
STATUS_ACTIVE = 1 | ||||
STATUS_REGISTERED = 2 | ||||
STATUS_LOCKED = 3 | ||||
|
r2781 | has_many :members, :foreign_key => 'user_id', :dependent => :destroy | ||
|
r13100 | has_many :memberships, | ||
lambda {preload(:project, :roles). | ||||
joins(:project). | ||||
where("#{Project.table_name}.status<>#{Project::STATUS_ARCHIVED}"). | ||||
order("#{Project.table_name}.name")}, | ||||
:class_name => 'Member', | ||||
:foreign_key => 'user_id' | ||||
|
r2781 | has_many :projects, :through => :memberships | ||
|
r6186 | has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify | ||
|
r2781 | |||
# Groups and active users | ||||
|
r10765 | scope :active, lambda { where(:status => STATUS_ACTIVE) } | ||
|
r6755 | |||
|
r13202 | scope :visible, lambda {|*args| | ||
user = args.first || User.current | ||||
if user.admin? | ||||
all | ||||
else | ||||
view_all_active = false | ||||
if user.memberships.to_a.any? | ||||
view_all_active = user.memberships.any? {|m| m.roles.any? {|r| r.users_visibility == 'all'}} | ||||
else | ||||
view_all_active = user.builtin_role.users_visibility == 'all' | ||||
end | ||||
|
r13940 | |||
|
r13202 | if view_all_active | ||
active | ||||
else | ||||
# self and members of visible projects | ||||
active.where("#{table_name}.id = ? OR #{table_name}.id IN (SELECT user_id FROM #{Member.table_name} WHERE project_id IN (?))", | ||||
user.id, user.visible_project_ids | ||||
) | ||||
end | ||||
end | ||||
} | ||||
|
r9355 | scope :like, lambda {|q| | ||
|
r10578 | q = q.to_s | ||
|
r8755 | if q.blank? | ||
|
r10578 | where({}) | ||
|
r8755 | else | ||
pattern = "%#{q}%" | ||||
|
r13504 | sql = %w(login firstname lastname).map {|column| "LOWER(#{table_name}.#{column}) LIKE LOWER(:p)"}.join(" OR ") | ||
sql << " OR #{table_name}.id IN (SELECT user_id FROM #{EmailAddress.table_name} WHERE LOWER(address) LIKE LOWER(:p))" | ||||
|
r8755 | params = {:p => pattern} | ||
if q =~ /^(.+)\s+(.+)$/ | ||||
a, b = "#{$1}%", "#{$2}%" | ||||
|
r10579 | sql << " OR (LOWER(#{table_name}.firstname) LIKE LOWER(:a) AND LOWER(#{table_name}.lastname) LIKE LOWER(:b))" | ||
sql << " OR (LOWER(#{table_name}.firstname) LIKE LOWER(:b) AND LOWER(#{table_name}.lastname) LIKE LOWER(:a))" | ||||
|
r8755 | params.merge!(:a => a, :b => b) | ||
end | ||||
|
r10578 | where(sql, params) | ||
|
r8755 | end | ||
|
r2781 | } | ||
|
r6755 | |||
|
r8627 | # Principals that are members of a collection of projects | ||
|
r9355 | scope :member_of, lambda {|projects| | ||
|
r13100 | projects = [projects] if projects.is_a?(Project) | ||
if projects.blank? | ||||
|
r10580 | where("1=0") | ||
|
r8627 | else | ||
ids = projects.map(&:id) | ||||
|
r12864 | active.where("#{Principal.table_name}.id IN (SELECT DISTINCT user_id FROM #{Member.table_name} WHERE project_id IN (?))", ids) | ||
|
r8627 | end | ||
} | ||||
|
r9008 | # Principals that are not members of projects | ||
|
r9355 | scope :not_member_of, lambda {|projects| | ||
|
r9008 | projects = [projects] unless projects.is_a?(Array) | ||
if projects.empty? | ||||
|
r10580 | where("1=0") | ||
|
r9008 | else | ||
ids = projects.map(&:id) | ||||
|
r10580 | where("#{Principal.table_name}.id NOT IN (SELECT DISTINCT user_id FROM #{Member.table_name} WHERE project_id IN (?))", ids) | ||
|
r9008 | end | ||
} | ||||
|
r11029 | scope :sorted, lambda { order(*Principal.fields_for_order_statement)} | ||
|
r8627 | |||
|
r3229 | before_create :set_default_empty_values | ||
|
r3962 | |||
def name(formatter = nil) | ||||
to_s | ||||
end | ||||
|
r13504 | def mail=(*args) | ||
nil | ||||
end | ||||
def mail | ||||
nil | ||||
end | ||||
|
r13202 | def visible?(user=User.current) | ||
Principal.visible(user).where(:id => id).first == self | ||||
end | ||||
|
r13116 | # Return true if the principal is a member of project | ||
def member_of?(project) | ||||
projects.to_a.include?(project) | ||||
end | ||||
|
r2781 | def <=>(principal) | ||
|
r8743 | if principal.nil? | ||
-1 | ||||
elsif self.class.name == principal.class.name | ||||
|
r14102 | self.to_s.casecmp(principal.to_s) | ||
|
r3047 | else | ||
# groups after users | ||||
principal.class.name <=> self.class.name | ||||
end | ||||
|
r2781 | end | ||
|
r6755 | |||
|
r11029 | # Returns an array of fields names than can be used to make an order statement for principals. | ||
# Users are sorted before Groups. | ||||
# Examples: | ||||
def self.fields_for_order_statement(table=nil) | ||||
table ||= table_name | ||||
columns = ['type DESC'] + (User.name_formatter[:order] - ['id']) + ['lastname', 'id'] | ||||
columns.uniq.map {|field| "#{table}.#{field}"} | ||||
end | ||||
|
r14118 | # Returns the principal that matches the keyword among principals | ||
def self.detect_by_keyword(principals, keyword) | ||||
keyword = keyword.to_s | ||||
return nil if keyword.blank? | ||||
principal = nil | ||||
|
r14124 | principal ||= principals.detect {|a| keyword.casecmp(a.login.to_s) == 0} | ||
principal ||= principals.detect {|a| keyword.casecmp(a.mail.to_s) == 0} | ||||
|
r14118 | if principal.nil? && keyword.match(/ /) | ||
firstname, lastname = *(keyword.split) # "First Last Throwaway" | ||||
principal ||= principals.detect {|a| | ||||
a.is_a?(User) && | ||||
firstname.casecmp(a.firstname.to_s) == 0 && | ||||
lastname.casecmp(a.lastname.to_s) == 0 | ||||
} | ||||
end | ||||
if principal.nil? | ||||
principal ||= principals.detect {|a| keyword.casecmp(a.name) == 0} | ||||
end | ||||
principal | ||||
end | ||||
|
r3229 | protected | ||
|
r6755 | |||
|
r3229 | # Make sure we don't try to insert NULL values (see #4632) | ||
def set_default_empty_values | ||||
self.login ||= '' | ||||
self.hashed_password ||= '' | ||||
self.firstname ||= '' | ||||
self.lastname ||= '' | ||||
true | ||||
end | ||||
|
r2781 | end | ||
|
r13053 | |||
require_dependency "user" | ||||
require_dependency "group" | ||||