1 # == Schema Information
5 # id :integer not null, primary key
6 # email :string(255) default(""), not null
7 # encrypted_password :string(255) default(""), not null
8 # reset_password_token :string(255)
9 # reset_password_sent_at :datetime
10 # remember_created_at :datetime
11 # sign_in_count :integer default(0)
12 # current_sign_in_at :datetime
13 # last_sign_in_at :datetime
14 # current_sign_in_ip :string(255)
15 # last_sign_in_ip :string(255)
16 # created_at :datetime not null
17 # updated_at :datetime not null
19 # admin :boolean default(FALSE), not null
20 # projects_limit :integer default(10)
21 # skype :string(255) default(""), not null
22 # linkedin :string(255) default(""), not null
23 # twitter :string(255) default(""), not null
24 # authentication_token :string(255)
25 # theme_id :integer default(1), not null
27 # failed_attempts :integer default(0)
29 # extern_uid :string(255)
30 # provider :string(255)
31 # username :string(255)
32 # can_create_group :boolean default(TRUE), not null
33 # can_create_team :boolean default(TRUE), not null
35 # color_scheme_id :integer default(1), not null
36 # notification_level :integer default(1), not null
39 class User < ActiveRecord::Base
40 devise :database_authenticatable, :token_authenticatable, :lockable,
41 :recoverable, :rememberable, :trackable, :validatable, :omniauthable, :registerable
43 attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name, :username,
44 :skype, :linkedin, :twitter, :color_scheme_id, :theme_id, :force_random_password,
45 :extern_uid, :provider, as: [:default, :admin]
46 attr_accessible :projects_limit, :can_create_team, :can_create_group, as: :admin
48 attr_accessor :force_random_password
50 # Virtual attribute for authenticating by either username or email
53 # Add login to attr_accessible
54 attr_accessible :login
61 # Namespace for personal projects
62 has_one :namespace, dependent: :destroy, foreign_key: :owner_id, class_name: "Namespace", conditions: 'type IS NULL'
64 # Namespaces (owned groups and own namespace)
65 has_many :namespaces, foreign_key: :owner_id
68 has_many :keys, dependent: :destroy
71 has_many :groups, class_name: "Group", foreign_key: :owner_id
74 has_many :own_teams, dependent: :destroy, class_name: "UserTeam", foreign_key: :owner_id
75 has_many :user_team_user_relationships, dependent: :destroy
76 has_many :user_teams, through: :user_team_user_relationships
77 has_many :user_team_project_relationships, through: :user_teams
78 has_many :team_projects, through: :user_team_project_relationships
81 has_many :users_projects, dependent: :destroy
82 has_many :issues, dependent: :destroy, foreign_key: :author_id
83 has_many :notes, dependent: :destroy, foreign_key: :author_id
84 has_many :merge_requests, dependent: :destroy, foreign_key: :author_id
85 has_many :events, dependent: :destroy, foreign_key: :author_id, class_name: "Event"
86 has_many :recent_events, foreign_key: :author_id, class_name: "Event", order: "id DESC"
87 has_many :assigned_issues, dependent: :destroy, foreign_key: :assignee_id, class_name: "Issue"
88 has_many :assigned_merge_requests, dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest"
90 has_many :personal_projects, through: :namespace, source: :projects
91 has_many :projects, through: :users_projects
92 has_many :own_projects, foreign_key: :creator_id
93 has_many :owned_projects, through: :namespaces, source: :projects
98 validates :name, presence: true
99 validates :email, presence: true, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/ }
100 validates :bio, length: { within: 0..255 }
101 validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider}
102 validates :projects_limit, presence: true, numericality: {greater_than_or_equal_to: 0}
103 validates :username, presence: true, uniqueness: true,
104 format: { with: Gitlab::Regex.username_regex,
105 message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
107 validates :notification_level, inclusion: { in: Notification.notification_levels }, presence: true
109 validate :namespace_uniq, if: ->(user) { user.username_changed? }
111 before_validation :generate_password, on: :create
112 before_save :ensure_authentication_token
113 alias_attribute :private_token, :authentication_token
115 delegate :path, to: :namespace, allow_nil: true, prefix: true
117 state_machine :state, initial: :active do
118 after_transition any => :blocked do |user, transition|
119 # Remove user from all projects and
120 user.users_projects.find_each do |membership|
121 return false unless membership.destroy
126 transition active: :blocked
130 transition blocked: :active
135 scope :admins, -> { where(admin: true) }
136 scope :blocked, -> { with_state(:blocked) }
137 scope :active, -> { with_state(:active) }
138 scope :alphabetically, -> { order('name ASC') }
139 scope :in_team, ->(team){ where(id: team.member_ids) }
140 scope :not_in_team, ->(team){ where('users.id NOT IN (:ids)', ids: team.member_ids) }
141 scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : scoped }
142 scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)') }
144 scope :potential_team_members, ->(team) { team.members.any? ? active.not_in_team(team) : active }
150 # Devise method overriden to allow sing in with email or username
151 def find_for_database_authentication(warden_conditions)
152 conditions = warden_conditions.dup
153 if login = conditions.delete(:login)
154 where(conditions).where(["lower(username) = :value OR lower(email) = :value", { value: login.downcase }]).first
156 where(conditions).first
160 def filter filter_name
162 when "admins"; self.admins
163 when "blocked"; self.blocked
164 when "wop"; self.without_projects
170 def create_from_omniauth(auth, ldap = false)
171 gitlab_auth.create_from_omniauth(auth, ldap)
174 def find_or_new_for_omniauth(auth)
175 gitlab_auth.find_or_new_for_omniauth(auth)
178 def find_for_ldap_auth(auth, signed_in_resource = nil)
179 gitlab_auth.find_for_ldap_auth(auth, signed_in_resource)
187 where("name LIKE :query OR email LIKE :query OR username LIKE :query", query: "%#{query}%")
200 @notification ||= Notification.new(self)
203 def generate_password
204 if self.force_random_password
205 self.password = self.password_confirmation = Devise.friendly_token.first(8)
210 namespace_name = self.username
211 if Namespace.find_by_path(namespace_name)
212 self.errors.add :username, "already exist"
216 # Groups where user is an owner
225 # Groups user has access to
226 def authorized_groups
227 @group_ids ||= (groups.pluck(:id) + authorized_projects.pluck(:namespace_id))
228 Group.where(id: @group_ids)
232 # Projects user has access to
233 def authorized_projects
234 @project_ids ||= (owned_projects.pluck(:id) + projects.pluck(:id)).uniq
235 Project.where(id: @project_ids)
239 @team_ids ||= (user_teams.pluck(:id) + own_teams.pluck(:id)).uniq
240 UserTeam.where(id: @team_ids)
243 # Team membership in authorized projects
244 def tm_in_authorized_projects
245 UsersProject.where(project_id: authorized_projects.map(&:id), user_id: self.id)
256 def can_change_username?
257 Gitlab.config.gitlab.username_changing_enabled
260 def can_create_project?
261 projects_limit > owned_projects.count
264 def can_create_group?
265 can?(:create_group, nil)
276 def can_select_namespace?
277 several_namespaces? || admin
280 def can? action, subject
281 abilities.allowed?(self, action, subject)
285 name.split.first unless name.blank?
288 def cared_merge_requests
289 MergeRequest.cared(self)
292 def projects_limit_left
293 projects_limit - owned_projects.count
296 def projects_limit_percent
297 return 100 if projects_limit.zero?
298 (owned_projects.count.to_f / projects_limit) * 100
301 def recent_push project_id = nil
302 # Get push events not earlier than 2 hours ago
303 events = recent_events.code_push.where("created_at > ?", Time.now - 2.hours)
304 events = events.where(project_id: project_id) if project_id
306 # Take only latest one
307 events = events.recent.limit(1).first
310 def projects_sorted_by_activity
311 authorized_projects.sorted_by_activity
314 def several_namespaces?
322 def name_with_username
323 "#{name} (#{username})"
327 project.team_member_by_id(self.id)