/
user.rb
227 lines (178 loc) · 7.42 KB
/
user.rb
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
227
# frozen_string_literal: true
require_dependency "devise/models/decidim_validatable"
require_dependency "devise/models/decidim_newsletterable"
require "valid_email2"
module Decidim
# A User is a citizen that wants to join the platform to participate.
class User < UserBaseEntity
include Decidim::DataPortability
include Decidim::Searchable
OMNIAUTH_PROVIDERS = [:facebook, :twitter, :google_oauth2, (:developer if Rails.env.development?)].compact
class Roles
def self.all
Decidim.config.user_roles
end
end
devise :invitable, :database_authenticatable, :registerable, :confirmable, :timeoutable,
:recoverable, :rememberable, :trackable, :decidim_validatable,
:decidim_newsletterable,
:omniauthable, omniauth_providers: OMNIAUTH_PROVIDERS,
request_keys: [:env], reset_password_keys: [:decidim_organization_id, :email],
confirmation_keys: [:decidim_organization_id, :email]
has_many :identities, foreign_key: "decidim_user_id", class_name: "Decidim::Identity", dependent: :destroy
has_many :memberships, class_name: "Decidim::UserGroupMembership", foreign_key: :decidim_user_id, dependent: :destroy
has_many :user_groups, through: :memberships, class_name: "Decidim::UserGroup", foreign_key: :decidim_user_group_id
has_many :access_grants, class_name: "Doorkeeper::AccessGrant", foreign_key: :resource_owner_id, dependent: :destroy
has_many :access_tokens, class_name: "Doorkeeper::AccessToken", foreign_key: :resource_owner_id, dependent: :destroy
validates :name, presence: true, unless: -> { deleted? }
validates :nickname, presence: true, unless: -> { deleted? || managed? }, length: { maximum: Decidim::User.nickname_max_length }
validates :locale, inclusion: { in: :available_locales }, allow_blank: true
validates :tos_agreement, acceptance: true, allow_nil: false, on: :create
validates :tos_agreement, acceptance: true, if: :user_invited?
validates :email, :nickname, uniqueness: { scope: :organization }, unless: -> { deleted? || managed? || nickname.blank? }
validate :all_roles_are_valid
mount_uploader :avatar, Decidim::AvatarUploader
scope :not_deleted, -> { where(deleted_at: nil) }
scope :managed, -> { where(managed: true) }
scope :not_managed, -> { where(managed: false) }
scope :officialized, -> { where.not(officialized_at: nil) }
scope :not_officialized, -> { where(officialized_at: nil) }
scope :confirmed, -> { where.not(confirmed_at: nil) }
scope :not_confirmed, -> { where(confirmed_at: nil) }
scope :interested_in_scopes, lambda { |scope_ids|
ids = scope_ids.map { |i| "%#{i}%" }.join(",")
where("extended_data->>'interested_scopes' ~~ ANY('{#{ids}}')")
}
attr_accessor :newsletter_notifications
searchable_fields({
# scope_id: :decidim_scope_id,
organization_id: :decidim_organization_id,
A: :name,
datetime: :created_at
},
index_on_create: ->(user) { !user.deleted? },
index_on_update: ->(user) { !user.deleted? })
before_save :ensure_encrypted_password
def user_invited?
invitation_token_changed? && invitation_accepted_at_changed?
end
# Public: Allows customizing the invitation instruction email content when
# inviting a user.
#
# Returns a String.
attr_accessor :invitation_instructions
def self.log_presenter_class_for(_log)
Decidim::AdminLog::UserPresenter
end
# Checks if the user has the given `role` or not.
#
# role - a String or a Symbol that represents the role that is being
# checked
#
# Returns a boolean.
def role?(role)
roles.include?(role.to_s)
end
# Public: Returns the active role of the user
def active_role
admin ? "admin" : roles.first
end
# Public: returns the user's name or the default one
def name
super || I18n.t("decidim.anonymous_user")
end
# Check if the user account has been deleted or not
def deleted?
deleted_at.present?
end
# Public: whether the user has been officialized or not
def officialized?
!officialized_at.nil?
end
def follows?(followable)
Decidim::Follow.where(user: self, followable: followable).any?
end
def unread_conversations
Decidim::Messaging::Conversation.unread_by(self)
end
# Check if the user exists with the given email and the current organization
#
# warden_conditions - A hash with the authentication conditions
# * email - a String that represents user's email.
# * env - A Hash containing environment variables.
# Returns a User.
def self.find_for_authentication(warden_conditions)
organization = warden_conditions.dig(:env, "decidim.current_organization")
find_by(
email: warden_conditions[:email].to_s.downcase,
decidim_organization_id: organization.id
)
end
def self.user_collection(user)
where(id: user.id)
end
def self.export_serializer
Decidim::DataPortabilitySerializers::DataPortabilityUserSerializer
end
def self.data_portability_images(user)
user_collection(user).map(&:avatar)
end
def tos_accepted?
return true if managed
return false if accepted_tos_version.nil?
# For some reason, if we don't use `#to_i` here we get some
# cases where the comparison returns false, but calling `#to_i` returns
# the same number :/
accepted_tos_version.to_i >= organization.tos_version.to_i
end
# Whether this user can be verified against some authorization or not.
def verifiable?
confirmed? || managed? || being_impersonated?
end
def being_impersonated?
ImpersonationLog.active.where(user: self).exists?
end
def interested_scopes_ids
extended_data["interested_scopes"] || []
end
def interested_scopes
@interested_scopes ||= organization.scopes.where(id: interested_scopes_ids)
end
protected
# Overrides devise email required validation.
# If the user has been deleted or it is managed the email field is not required anymore.
def email_required?
return false if deleted? || managed?
super
end
# Overrides devise password required validation.
# If the user is managed the password field is not required anymore.
def password_required?
return false if managed?
super
end
def after_confirmation
return unless organization.send_welcome_notification?
Decidim::EventsManager.publish(
event: "decidim.events.core.welcome_notification",
event_class: WelcomeNotificationEvent,
resource: self,
affected_users: [self]
)
end
private
# Changes default Devise behaviour to use ActiveJob to send async emails.
def send_devise_notification(notification, *args)
devise_mailer.send(notification, self, *args).deliver_later
end
def all_roles_are_valid
errors.add(:roles, :invalid) unless roles.compact.all? { |role| Roles.all.include?(role) }
end
def available_locales
Decidim.available_locales.map(&:to_s)
end
def ensure_encrypted_password
restore_encrypted_password! if will_save_change_to_encrypted_password? && encrypted_password.blank?
end
end
end