Class: User
Overview
Represents a user. Most of the User’s logic is controlled by Devise and its overrides. A user, as far as the application code (i.e. excluding Devise) is concerned, has many questions, answers, and votes.
Class Method Summary
collapse
Instance Method Summary
collapse
Methods included from SamlInit
#saml_identifier, #saml_identifier=, #saml_init_email, #saml_init_email=, #saml_init_email_and_identifier, #saml_init_email_and_identifier=, #saml_init_identifier, #saml_init_identifier=, #saml_init_username_no_update, #saml_init_username_no_update=
#attributes_print, fuzzy_search, match_search, #match_search, sanitize_for_search, sanitize_name, sanitize_sql_in, useful_err_msg, with_lax_group_rules
Class Method Details
.list_includes ⇒ Object
50
51
52
|
# File 'app/models/user.rb', line 50
def self.list_includes
includes(:posts, :avatar_attachment)
end
|
.search(term) ⇒ Object
54
55
56
|
# File 'app/models/user.rb', line 54
def self.search(term)
where('username LIKE ?', "#{sanitize_sql_like(term)}%")
end
|
Instance Method Details
#active_flags(post) ⇒ Object
350
351
352
|
# File 'app/models/user.rb', line 350
def active_flags(post)
post.flags.where(user: self, status: nil)
end
|
#answers ⇒ Object
134
135
136
|
# File 'app/models/user.rb', line 134
def answers
posts.where(post_type_id: Answer.post_type_id)
end
|
#block(reason, length: 180.days) ⇒ Object
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
|
# File 'app/models/user.rb', line 293
def block(reason, length: 180.days)
user_email = email
user_ip = [last_sign_in_ip]
if current_sign_in_ip
user_ip << current_sign_in_ip
end
BlockedItem.create(item_type: 'email', value: user_email, expires: length.from_now,
automatic: true, reason: "#{reason}: #" + id.to_s)
user_ip.compact.uniq.each do |ip|
BlockedItem.create(item_type: 'ip', value: ip, expires: length.from_now,
automatic: true, reason: "#{reason}: #" + id.to_s)
end
end
|
#can_push_to_network(post_type) ⇒ Boolean
Checks if the user can push a given post type to network
87
88
89
|
# File 'app/models/user.rb', line 87
def can_push_to_network(post_type)
post_type.system? && (is_global_moderator || is_global_admin)
end
|
#can_update(post, post_type) ⇒ Boolean
Checks if the user can directly update a given post
95
96
97
98
|
# File 'app/models/user.rb', line 95
def can_update(post, post_type)
privilege?('edit_posts') || is_moderator || self == post.user || \
(post_type.is_freely_editable && privilege?('unrestricted'))
end
|
#category_preference(category_id) ⇒ Object
320
321
322
323
324
|
# File 'app/models/user.rb', line 320
def category_preference(category_id)
category_key = "prefs.#{id}.category.#{RequestContext.}.category.#{category_id}"
AppConfig.preferences.select { |_, v| v['category'] }.transform_values { |v| v['default'] }
.merge(RequestContext.redis.hgetall(category_key))
end
|
#create_notification(content, link) ⇒ Object
122
123
124
|
# File 'app/models/user.rb', line 122
def create_notification(content, link)
notifications.create!(content: content, link: link)
end
|
#do_soft_delete(attribute_to) ⇒ Object
354
355
356
357
358
359
360
361
362
|
# File 'app/models/user.rb', line 354
def do_soft_delete(attribute_to)
AuditLog.moderator_audit(event_type: 'user_delete', related: self, user: attribute_to,
comment: attributes_print(join: "\n"))
assign_attributes(deleted: true, deleted_by_id: attribute_to.id, deleted_at: DateTime.now,
username: "user#{id}", email: "#{id}@deleted.localhost",
password: SecureRandom.hex(32))
skip_reconfirmation!
save
end
|
#email_domain_not_blocklisted ⇒ Object
220
221
222
223
224
225
226
227
228
229
230
231
232
233
|
# File 'app/models/user.rb', line 220
def email_domain_not_blocklisted
return unless File.exist?(Rails.root.join('../.qpixel-domain-blocklist.txt'))
return unless saved_changes.include? 'email'
blocklist = File.read(Rails.root.join('../.qpixel-domain-blocklist.txt')).split("\n")
email_domain = email.split('@')[-1]
matched = blocklist.select { |x| email_domain == x }
if matched.any?
errors.add(:base, ApplicationRecord.useful_err_msg.sample)
matched_domains = matched.map { |d| "equals: #{d}" }
AuditLog.block_log(event_type: 'user_email_domain_blocked',
comment: "email: #{email}\n#{matched_domains.join("\n")}\nsource: file")
end
end
|
#email_not_bad_pattern ⇒ Object
254
255
256
257
258
259
260
261
262
263
264
265
266
|
# File 'app/models/user.rb', line 254
def email_not_bad_pattern
return unless File.exist?(Rails.root.join('../.qpixel-email-patterns.txt'))
return unless changes.include? 'email'
patterns = File.read(Rails.root.join('../.qpixel-email-patterns.txt')).split("\n")
matched = patterns.select { |p| email.match? Regexp.new(p) }
if matched.any?
errors.add(:base, ApplicationRecord.useful_err_msg.sample)
matched_patterns = matched.map { |p| "matched: #{p}" }
AuditLog.block_log(event_type: 'user_email_pattern_match',
comment: "email: #{email}\n#{matched_patterns.join("\n")}")
end
end
|
268
269
270
|
# File 'app/models/user.rb', line 268
def
|| (reputation: SiteSetting['NewUserInitialRep'])
end
|
#ensure_websites ⇒ Object
146
147
148
149
150
151
152
|
# File 'app/models/user.rb', line 146
def ensure_websites
pos = user_websites.size
while pos < UserWebsite::MAX_ROWS
pos += 1
UserWebsite.create(user_id: id, position: pos)
end
end
|
280
281
282
283
284
|
# File 'app/models/user.rb', line 280
def (request)
request.['CF-Connecting-IP'] || request.ip
end
|
#has_ability_on(community_id, ability_internal_id) ⇒ Object
184
185
186
187
188
189
190
191
192
193
194
195
196
|
# File 'app/models/user.rb', line 184
def has_ability_on(, ability_internal_id)
cu = .where(community_id: ).first
if cu&.is_moderator || cu&.is_admin || is_global_moderator || is_global_admin || cu&.privilege?('mod')
true
elsif cu.nil?
false
else
Ability.unscoped do
UserAbility.joins(:ability).where(community_user_id: cu&.id, is_suspended: false,
ability: { internal_id: ability_internal_id }).exists?
end
end
end
|
#has_active_flags?(post) ⇒ Boolean
346
347
348
|
# File 'app/models/user.rb', line 346
def has_active_flags?(post)
!post.flags.where(user: self, status: nil).empty?
end
|
#has_post_privilege?(name, post) ⇒ Boolean
This class makes heavy use of predicate names, and their use is prevalent throughout the codebase because of the importance of these methods. rubocop:disable Naming/PredicateName
76
77
78
79
80
81
82
|
# File 'app/models/user.rb', line 76
def has_post_privilege?(name, post)
if post.user == self
true
else
privilege?(name)
end
end
|
#has_profile_on(community_id) ⇒ Object
Used by network profile: does this user have a profile on that other comm?
163
164
165
166
|
# File 'app/models/user.rb', line 163
def has_profile_on()
cu = .where(community_id: ).first
!cu&.user_id.nil? || false
end
|
#inspect ⇒ Object
58
59
60
|
# File 'app/models/user.rb', line 58
def inspect
"#<User #{attributes.compact.map { |k, v| "#{k}: #{v}" }.join(', ')}>"
end
|
#is_admin ⇒ Object
158
159
160
|
# File 'app/models/user.rb', line 158
def is_admin
is_global_admin || &.is_admin || false
end
|
#is_moderator ⇒ Object
154
155
156
|
# File 'app/models/user.rb', line 154
def is_moderator
is_global_moderator || &.is_moderator || is_admin || &.privilege?('mod') || false
end
|
#is_moderator_on(community_id) ⇒ Object
178
179
180
181
182
|
# File 'app/models/user.rb', line 178
def is_moderator_on()
cu = .where(community_id: ).first
is_global_moderator || is_admin || cu&.is_moderator || cu&.privilege?('mod') || false
end
|
#is_not_blocklisted ⇒ Object
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
|
# File 'app/models/user.rb', line 235
def is_not_blocklisted
return unless saved_changes.include? 'email'
email_domain = email.split('@')[-1]
is_mail_blocked = BlockedItem.emails.where(value: email)
is_mail_host_blocked = BlockedItem.email_hosts.where(value: email_domain)
if is_mail_blocked.any? || is_mail_host_blocked.any?
errors.add(:base, ApplicationRecord.useful_err_msg.sample)
if is_mail_blocked.any?
AuditLog.block_log(event_type: 'user_email_blocked', related: is_mail_blocked.first,
comment: "email: #{email}\nfull match to: #{is_mail_blocked.first.value}")
end
if is_mail_host_blocked.any?
AuditLog.block_log(event_type: 'user_email_domain_blocked', related: is_mail_host_blocked.first,
comment: "email: #{email}\ndomain match to: #{is_mail_host_blocked.first.value}")
end
end
end
|
#metric(key) ⇒ Object
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
# File 'app/models/user.rb', line 100
def metric(key)
Rails.cache.fetch("community_user/#{.id}/metric/#{key}", expires_in: 24.hours) do
case key
when 'p'
Post.qa_only.undeleted.where(user: self).count
when '1'
Post.undeleted.where(post_type: PostType.top_level, user: self).count
when '2'
Post.undeleted.where(post_type: PostType.second_level, user: self).count
when 's'
Vote.where(recv_user_id: id, vote_type: 1).count - \
Vote.where(recv_user_id: id, vote_type: -1).count
when 'v'
Vote.where(recv_user_id: id).count
when 'V'
votes.count
when 'E'
PostHistory.where(user: self, post_history_type: PostHistoryType.find_by(name: 'post_edited')).count
end
end
end
|
#no_blank_unicode_in_username ⇒ Object
213
214
215
216
217
218
|
# File 'app/models/user.rb', line 213
def no_blank_unicode_in_username
not_valid = !username.scan(/[\u200B-\u200D\uFEFF]/).empty?
if not_valid
errors.add(:username, 'may not contain blank unicode characters')
end
end
|
#no_links_in_username ⇒ Object
272
273
274
275
276
277
278
|
# File 'app/models/user.rb', line 272
def no_links_in_username
if %r{(?:http|ftp)s?://(?:\w+\.)+[a-zA-Z]{2,10}}.match?(username)
errors.add(:username, 'cannot contain links')
AuditLog.block_log(event_type: 'user_username_link_blocked',
comment: "username: #{username}")
end
end
|
#post_count_on(community_id) ⇒ Object
173
174
175
176
|
# File 'app/models/user.rb', line 173
def post_count_on()
cu = .where(community_id: ).first
cu&.post_count || 0
end
|
#preference(name, community: false) ⇒ Object
342
343
344
|
# File 'app/models/user.rb', line 342
def preference(name, community: false)
preferences[ ? :community : :global][name]
end
|
#preferences ⇒ Object
309
310
311
312
313
314
315
316
317
318
|
# File 'app/models/user.rb', line 309
def preferences
global_key = "prefs.#{id}"
= "prefs.#{id}.community.#{RequestContext.}"
{
global: AppConfig.preferences.select { |_, v| v['global'] }.transform_values { |v| v['default'] }
.merge(RequestContext.redis.hgetall(global_key)),
community: AppConfig.preferences.select { |_, v| v['community'] }.transform_values { |v| v['default'] }
.merge(RequestContext.redis.hgetall())
}
end
|
#questions ⇒ Object
130
131
132
|
# File 'app/models/user.rb', line 130
def questions
posts.where(post_type_id: Question.post_type_id)
end
|
#reputation_on(community_id) ⇒ Object
168
169
170
171
|
# File 'app/models/user.rb', line 168
def reputation_on()
cu = .where(community_id: ).first
cu&.reputation || 1
end
|
#rtl_safe_username ⇒ Object
198
199
200
|
# File 'app/models/user.rb', line 198
def rtl_safe_username
"#{username}\u202D"
end
|
#same_as?(user) ⇒ Boolean
Checks whether this user is the same as a given user
68
69
70
|
# File 'app/models/user.rb', line 68
def same_as?(user)
id == user.id
end
|
#send_welcome_tour_message ⇒ Object
286
287
288
289
290
291
|
# File 'app/models/user.rb', line 286
def send_welcome_tour_message
return if id == -1 || RequestContext..nil?
create_notification("👋 Welcome to #{SiteSetting['SiteName'] || 'Codidact'}! Take our tour to find out " \
'how this site works.', '/tour')
end
|
#trust_level ⇒ Object
62
63
64
|
# File 'app/models/user.rb', line 62
def trust_level
.trust_level
end
|
#unread_count ⇒ Object
126
127
128
|
# File 'app/models/user.rb', line 126
def unread_count
notifications.unscoped.where(user: self, is_read: false).count
end
|
#username_not_fake_admin ⇒ Object
202
203
204
205
206
207
208
209
210
211
|
# File 'app/models/user.rb', line 202
def username_not_fake_admin
admin_badge = SiteSetting['AdminBadgeCharacter']
mod_badge = SiteSetting['ModBadgeCharacter']
[admin_badge, mod_badge].each do |badge|
if badge.present? && username.include?(badge)
errors.add(:username, "may not include the #{badge} character")
end
end
end
|
#valid_websites_for ⇒ Object
142
143
144
|
# File 'app/models/user.rb', line 142
def valid_websites_for
user_websites.where.not(url: [nil, '']).order(position: :asc)
end
|
#validate_prefs! ⇒ Object
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
|
# File 'app/models/user.rb', line 326
def validate_prefs!
global_key = "prefs.#{id}"
= "prefs.#{id}.community.#{RequestContext.}"
{
global_key => AppConfig.preferences.reject { |_, v| v['community'] },
=> AppConfig.preferences.select { |_, v| v['community'] }
}.each do |key, prefs|
saved = RequestContext.redis.hgetall(key)
valid_prefs = prefs.keys
deprecated = saved.reject { |k, _v| valid_prefs.include? k }.map { |k, _v| k }
unless deprecated.empty?
RequestContext.redis.hdel key, *deprecated
end
end
end
|
#website_domain ⇒ Object
138
139
140
|
# File 'app/models/user.rb', line 138
def website_domain
website.nil? ? website : URI.parse(website).hostname
end
|