Class: User

Inherits:
ApplicationRecord show all
Includes:
Connecting, Querying, SocialActions
Defined in:
app/models/user/connecting.rb,
app/models/user.rb

Overview

Copyright © 2010-2011, Diaspora Inc. This file is

licensed under the Affero General Public License version 3 or later.  See
the COPYRIGHT file.

Defined Under Namespace

Modules: Connecting, Querying, SocialActions

Class Method Summary collapse

Instance Method Summary collapse

Methods included from SocialActions

#build_conversation, #build_message, #comment!, #like!, #like_comment!, #participate!, #participate_in_poll!, #reshare!, #update_or_create_participation!

Methods included from Querying

#aspects_with_person, #aspects_with_shareable, #block_for, #contact_for, #contact_for_person_id, #find_visible_shareable_by_id, #has_contact_for?, #people_in_aspects, #photos_from, #posts_from, #visible_shareable_ids, #visible_shareables

Methods included from Connecting

#disconnect, #disconnected_by, #share_with

Class Method Details

.all_sharing_with_person(person) ⇒ Object



104
105
106
# File 'app/models/user.rb', line 104

def self.all_sharing_with_person(person)
  User.joins(:contacts).where(:contacts => {:person_id => person.id})
end

.build(opts = {}) ⇒ Object

Helpers ############



406
407
408
409
410
# File 'app/models/user.rb', line 406

def self.build(opts={})
  u = User.new(opts.except(:person, :id))
  u.setup(opts)
  u
end

.diaspora_id_hostObject



440
441
442
# File 'app/models/user.rb', line 440

def self.diaspora_id_host
  "@#{AppConfig.bare_pod_uri}"
end

.find_for_database_authentication(conditions = {}) ⇒ User?

This override allows a user to enter either their email address or their username into the username field.

Returns:

  • (User)

    The user that matches the username/email condition.

  • (nil)

    if no user matches that condition.



222
223
224
225
226
227
228
229
# File 'app/models/user.rb', line 222

def self.find_for_database_authentication(conditions={})
  conditions = conditions.dup
  conditions[:username] = conditions[:username].downcase
  if conditions[:username] =~ /^([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})$/i # email regex
    conditions[:email] = conditions.delete(:username)
  end
  where(conditions).first
end

.find_or_build(opts = {}) ⇒ Object



412
413
414
415
416
# File 'app/models/user.rb', line 412

def self.find_or_build(opts={})
  user = User.find_by(username: opts[:username])
  user ||= User.build(opts)
  user
end

Instance Method Details

#add_hidden_shareable(key, share_id, opts = {}) ⇒ Object



129
130
131
132
133
134
135
136
137
# File 'app/models/user.rb', line 129

def add_hidden_shareable(key, share_id, opts={})
  if self.hidden_shareables.has_key?(key)
    self.hidden_shareables[key] << share_id
  else
    self.hidden_shareables[key] = [share_id]
  end
  self.save unless opts[:batch]
  self.hidden_shareables
end

#add_to_streams(post, aspects_to_insert) ⇒ Object



257
258
259
260
261
# File 'app/models/user.rb', line 257

def add_to_streams(post, aspects_to_insert)
  aspects_to_insert.each do |aspect|
    aspect << post
  end
end

#admin?Boolean

Returns:

  • (Boolean)


480
481
482
# File 'app/models/user.rb', line 480

def admin?
  Role.is_admin?(self.person)
end

#after_database_authenticationObject



596
597
598
599
600
601
602
# File 'app/models/user.rb', line 596

def after_database_authentication
  # remove any possible remove_after timestamp flag set by maintenance.remove_old_users
  unless self.remove_after.nil?
    self.remove_after = nil
    self.save
  end
end

#aspects_from_ids(aspect_ids) ⇒ Object



263
264
265
266
267
268
269
# File 'app/models/user.rb', line 263

def aspects_from_ids(aspect_ids)
  if aspect_ids == "all" || aspect_ids == :all
    self.aspects
  else
    aspects.where(:id => aspect_ids).to_a
  end
end

#basic_profile_present?Boolean

Returns:

  • (Boolean)


401
402
403
# File 'app/models/user.rb', line 401

def basic_profile_present?
  tag_followings.any? || profile[:image_url]
end

#build_post(class_name, opts = {}) ⇒ Object

Posting ########



238
239
240
241
242
243
# File 'app/models/user.rb', line 238

def build_post(class_name, opts={})
  opts[:author] = person

  model_class = class_name.to_s.camelize.constantize
  model_class.diaspora_initialize(opts)
end

#clear_account!Object



560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
# File 'app/models/user.rb', line 560

def clear_account!
  clearable_fields.each do |field|
    self[field] = nil
  end
  [:getting_started,
   :show_community_spotlight_in_stream,
   :post_default_public].each do |field|
    self[field] = false
  end
  self.remove_export = true
  self.remove_exported_photos_file = true
  self[:disable_mail] = true
  self[:email] = "deletedaccount_#{self[:id]}@example.org"

  random_password = SecureRandom.hex(20)
  self.password = random_password
  self.password_confirmation = random_password
  self.save(:validate => false)
end

#close_account!Object



550
551
552
553
554
# File 'app/models/user.rb', line 550

def close_account!
  self.person.lock_access!
  self.lock_access!
  AccountDeletion.create(person: person)
end

#closed_account?Boolean

Returns:

  • (Boolean)


556
557
558
# File 'app/models/user.rb', line 556

def closed_account?
  self.person.
end

#compressed_exportObject



333
334
335
# File 'app/models/user.rb', line 333

def compressed_export
  ActiveSupport::Gzip.compress Diaspora::Exporter.new(self).execute
end

#confirm_email(token) ⇒ Object



231
232
233
234
235
# File 'app/models/user.rb', line 231

def confirm_email(token)
  return false if token.blank? || token != confirm_email_token
  self.email = unconfirmed_email
  save
end

#deliver_profile_update(opts = {}) ⇒ Object



397
398
399
# File 'app/models/user.rb', line 397

def deliver_profile_update(opts={})
  Diaspora::Federation::Dispatcher.defer_dispatch(self, profile, opts)
end

#disable_getting_startedObject



207
208
209
# File 'app/models/user.rb', line 207

def disable_getting_started
  self.update_attribute(:getting_started, false) if self.getting_started?
end

#dispatch_post(post, opts = {}) ⇒ Object



245
246
247
248
# File 'app/models/user.rb', line 245

def dispatch_post(post, opts={})
  logger.info "user:#{id} dispatching #{post.class}:#{post.guid}"
  Diaspora::Federation::Dispatcher.defer_dispatch(self, post, opts)
end

#encryption_keyObject



476
477
478
# File 'app/models/user.rb', line 476

def encryption_key
  OpenSSL::PKey::RSA.new(serialized_private_key)
end

#flag_for_removal(remove_after) ⇒ Object



588
589
590
591
592
593
594
# File 'app/models/user.rb', line 588

def flag_for_removal(remove_after)
  # flag inactive user for future removal
  if AppConfig.settings.maintenance.remove_old_users.enable?
    self.remove_after = remove_after
    self.save
  end
end

#generate_keysObject

Generate public/private keys for User and associated Person



533
534
535
536
537
538
539
540
541
# File 'app/models/user.rb', line 533

def generate_keys
  key_size = (Rails.env == "test" ? 512 : 4096)

  self.serialized_private_key = OpenSSL::PKey::RSA.generate(key_size).to_s if serialized_private_key.blank?

  if self.person && self.person.serialized_public_key.blank?
    self.person.serialized_public_key = OpenSSL::PKey::RSA.new(self.serialized_private_key).public_key.to_s
  end
end

#guard_unconfirmed_emailObject



516
517
518
519
520
521
522
# File 'app/models/user.rb', line 516

def guard_unconfirmed_email
  self.unconfirmed_email = nil if unconfirmed_email.blank? || unconfirmed_email == email

  return unless will_save_change_to_unconfirmed_email?

  self.confirm_email_token = unconfirmed_email ? SecureRandom.hex(15) : nil
end

#has_hidden_shareables_of_type?(t = Post) ⇒ Boolean

Returns:

  • (Boolean)


168
169
170
171
# File 'app/models/user.rb', line 168

def has_hidden_shareables_of_type?(t = Post)
  share_type = t.base_class.to_s
  self.hidden_shareables[share_type].present?
end

#hidden_shareablesObject



125
126
127
# File 'app/models/user.rb', line 125

def hidden_shareables
  self[:hidden_shareables] ||= {}
end

#invitation_codeObject



121
122
123
# File 'app/models/user.rb', line 121

def invitation_code
  InvitationCode.find_or_create_by(user_id: self.id)
end

#is_shareable_hidden?(shareable) ⇒ Boolean

Returns:

  • (Boolean)


145
146
147
148
149
150
151
152
# File 'app/models/user.rb', line 145

def is_shareable_hidden?(shareable)
  shareable_type = shareable.class.base_class.name
  if self.hidden_shareables.has_key?(shareable_type)
    self.hidden_shareables[shareable_type].include?(shareable.id.to_s)
  else
    false
  end
end

#like_for(target) ⇒ Like

Get the user’s like of a post, if there is one.

Parameters:

Returns:



303
304
305
306
307
308
309
# File 'app/models/user.rb', line 303

def like_for(target)
  if target.likes.loaded?
    target.likes.find {|like| like.author_id == person.id }
  else
    Like.find_by(author_id: person.id, target_type: target.class.base_class.to_s, target_id: target.id)
  end
end

#liked?(target) ⇒ Boolean

Check whether the user has liked a post.

Parameters:

Returns:

  • (Boolean)


288
289
290
291
292
293
294
295
296
297
298
# File 'app/models/user.rb', line 288

def liked?(target)
  if target.likes.loaded?
    if self.like_for(target)
      return true
    else
      return false
    end
  else
    Like.exists?(:author_id => self.person.id, :target_type => target.class.base_class.to_s, :target_id => target.id)
  end
end

#mail(job, *args) ⇒ Object

Mailer #######################



354
355
356
357
358
359
360
# File 'app/models/user.rb', line 354

def mail(job, *args)
  return unless job.present?
  pref = job.to_s.gsub('Workers::Mail::', '').underscore
  if(self.disable_mail == false && !self.user_preferences.exists?(:email_type => pref))
    job.perform_async(*args)
  end
end

#mine?(target) ⇒ Boolean

Returns:

  • (Boolean)


500
501
502
503
504
505
506
# File 'app/models/user.rb', line 500

def mine?(target)
  if target.present? && target.respond_to?(:user_id)
    return self.id == target.user_id
  end

  false
end

#moderator?Boolean

Returns:

  • (Boolean)


484
485
486
# File 'app/models/user.rb', line 484

def moderator?
  Role.moderator?(person)
end

#moderator_only?Boolean

Returns:

  • (Boolean)


488
489
490
# File 'app/models/user.rb', line 488

def moderator_only?
  Role.moderator_only?(person)
end

#no_person_with_same_usernameObject



543
544
545
546
547
548
# File 'app/models/user.rb', line 543

def no_person_with_same_username
  diaspora_id = "#{username}#{User.diaspora_id_host}"
  return unless username_changed? && Person.exists?(diaspora_handle: diaspora_id)

  errors.add(:base, "That username has already been taken")
end

#perform_export!Object



319
320
321
322
323
324
325
326
327
328
329
330
331
# File 'app/models/user.rb', line 319

def perform_export!
  export = Tempfile.new([username, ".json.gz"], encoding: "ascii-8bit")
  export.write(compressed_export) && export.close
  if export.present?
    update exporting: false, export: export, exported_at: Time.zone.now
  else
    update exporting: false
  end
rescue StandardError => e
  logger.error "Unexpected error while exporting data for '#{username}': #{e.class}: #{e.message}\n" \
               "#{e.backtrace.first(15).join("\n")}"
  update exporting: false
end

#perform_export_photos!Object



345
346
347
348
349
350
351
# File 'app/models/user.rb', line 345

def perform_export_photos!
  PhotoExporter.new(self).perform
rescue StandardError => e
  logger.error "Unexpected error while exporting photos for '#{username}': #{e.class}: #{e.message}\n" \
               "#{e.backtrace.first(15).join("\n")}"
  update exporting_photos: false
end

#podmin_account?Boolean

Returns:

  • (Boolean)


496
497
498
# File 'app/models/user.rb', line 496

def podmin_account?
  username == AppConfig.admins.
end

#post_default_aspectsObject



271
272
273
274
275
276
277
# File 'app/models/user.rb', line 271

def post_default_aspects
  if post_default_public
    ["public"]
  else
    aspects.where(post_default: true).to_a
  end
end

#process_invite_acceptence(invite) ⇒ Object



116
117
118
119
# File 'app/models/user.rb', line 116

def process_invite_acceptence(invite)
  self.invited_by = invite.user
  invite.use! unless AppConfig.settings.enable_registrations?
end

#queue_exportObject



314
315
316
317
# File 'app/models/user.rb', line 314

def queue_export
  update exporting: true, export: nil, exported_at: nil
  Workers::ExportUser.perform_async(id)
end

#queue_export_photosObject



340
341
342
343
# File 'app/models/user.rb', line 340

def queue_export_photos
  update exporting_photos: true, exported_photos_file: nil, exported_photos_at: nil
  Workers::ExportPhotos.perform_async(id)
end

#remember_meObject



604
605
606
# File 'app/models/user.rb', line 604

def remember_me
  true
end

#remove_hidden_shareable(key, share_id) ⇒ Object



139
140
141
142
143
# File 'app/models/user.rb', line 139

def remove_hidden_shareable(key, share_id)
  if self.hidden_shareables.has_key?(key)
    self.hidden_shareables[key].delete(share_id)
  end
end

#remove_invalid_unconfirmed_emailsObject

Whenever email is set, clear all unconfirmed emails which match



525
526
527
528
529
530
# File 'app/models/user.rb', line 525

def remove_invalid_unconfirmed_emails
  return unless saved_change_to_email?
  # rubocop:disable Rails/SkipsModelValidations
  User.where(unconfirmed_email: email).update_all(unconfirmed_email: nil, confirm_email_token: nil)
  # rubocop:enable Rails/SkipsModelValidations
end

#retract(target) ⇒ Object

Posts and Such ###############



368
369
370
371
372
# File 'app/models/user.rb', line 368

def retract(target)
  retraction = Retraction.for(target)
  retraction.defer_dispatch(self)
  retraction.perform
end

#seed_aspectsObject



444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
# File 'app/models/user.rb', line 444

def seed_aspects
  self.aspects.create(:name => I18n.t('aspects.seed.family'))
  self.aspects.create(:name => I18n.t('aspects.seed.friends'))
  self.aspects.create(:name => I18n.t('aspects.seed.work'))
  aq = self.aspects.create(:name => I18n.t('aspects.seed.acquaintances'))

  if AppConfig.settings.autofollow_on_join?
    begin
       = Person.find_or_fetch_by_identifier(AppConfig.settings.autofollow_on_join_user)
      share_with(, aq)
    rescue DiasporaFederation::Discovery::DiscoveryError
      logger.warn "Error auto-sharing with #{AppConfig.settings.autofollow_on_join_user}
                   fix autofollow_on_join_user in configuration."
    end
  end
  aq
end

#send_confirm_emailObject



362
363
364
365
# File 'app/models/user.rb', line 362

def send_confirm_email
  return if unconfirmed_email.blank?
  Workers::Mail::ConfirmEmail.perform_async(id)
end

#send_reset_password_instructionsObject



177
178
179
# File 'app/models/user.rb', line 177

def send_reset_password_instructions
  Workers::ResetPassword.perform_async(self.id)
end

#send_reset_password_instructions!Object

Copy the method provided by Devise to be able to call it later from a Sidekiq job



175
# File 'app/models/user.rb', line 175

alias_method :send_reset_password_instructions!, :send_reset_password_instructions

#send_welcome_messageObject



462
463
464
465
466
467
468
469
470
471
472
473
474
# File 'app/models/user.rb', line 462

def send_welcome_message
  return unless AppConfig.settings.welcome_message.enabled? && AppConfig.admins.account?
  sender_username = AppConfig.admins..get
  sender = User.find_by(username: sender_username)
  return if sender.nil?
  conversation = sender.build_conversation(
    participant_ids: [sender.person.id, person.id],
    subject:         AppConfig.settings.welcome_message.subject.get,
    message:         {text: AppConfig.settings.welcome_message.text.get % {username: username}}
  )

  Diaspora::Federation::Dispatcher.build(sender, conversation).dispatch if conversation.save
end

#set_current_languageObject



211
212
213
# File 'app/models/user.rb', line 211

def set_current_language
  self.language = I18n.locale.to_s if self.language.blank?
end

#set_default_color_themeObject



215
216
217
# File 'app/models/user.rb', line 215

def set_default_color_theme
  self.color_theme ||= AppConfig.settings.default_color_theme
end

#set_person(person) ⇒ Object



435
436
437
438
# File 'app/models/user.rb', line 435

def set_person(person)
  person.diaspora_handle = "#{self.username}#{User.diaspora_id_host}"
  self.person = person
end

#setup(opts) ⇒ Object



418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
# File 'app/models/user.rb', line 418

def setup(opts)
  self.username = opts[:username]
  self.email = opts[:email]
  self.language = opts[:language]
  self.language ||= I18n.locale.to_s
  self.color_theme = opts[:color_theme]
  self.color_theme ||= AppConfig.settings.default_color_theme
  valid?
  errors = self.errors
  errors.delete :person
  return if errors.size > 0

  self.set_person(Person.new((opts[:person] || {}).except(:id)))
  self.generate_keys
  self
end

#sign_upObject



580
581
582
583
584
585
586
# File 'app/models/user.rb', line 580

def 
  if AppConfig.settings.captcha.enable?
    save_with_captcha
  else
    save
  end
end

#spotlight?Boolean

Returns:

  • (Boolean)


492
493
494
# File 'app/models/user.rb', line 492

def spotlight?
  Role.spotlight?(person)
end

#strip_and_downcase_usernameObject



200
201
202
203
204
205
# File 'app/models/user.rb', line 200

def strip_and_downcase_username
  if username.present?
    username.strip!
    username.downcase!
  end
end

#toggle_hidden_shareable(share) ⇒ Object



154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'app/models/user.rb', line 154

def toggle_hidden_shareable(share)
  share_id = share.id.to_s
  key = share.class.base_class.to_s
  if self.hidden_shareables.has_key?(key) && self.hidden_shareables[key].include?(share_id)
    self.remove_hidden_shareable(key, share_id)
    self.save
    false
  else
    self.add_hidden_shareable(key, share_id)
    self.save
    true
  end
end

#unconfirmed_email_quasiuniquenessObject

Ensure that the unconfirmed email isn’t already someone’s email



510
511
512
513
514
# File 'app/models/user.rb', line 510

def unconfirmed_email_quasiuniqueness
  if User.exists?(["id != ? AND email = ?", id, unconfirmed_email])
    errors.add(:unconfirmed_email, I18n.t("errors.messages.taken"))
  end
end

#unread_message_countObject



112
113
114
# File 'app/models/user.rb', line 112

def unread_message_count
  ConversationVisibility.where(person_id: self.person_id).sum(:unread)
end

#unread_notificationsObject



108
109
110
# File 'app/models/user.rb', line 108

def unread_notifications
  notifications.where(:unread => true)
end

#update_post(post, post_hash = {}) ⇒ Object



250
251
252
253
254
255
# File 'app/models/user.rb', line 250

def update_post(post, post_hash={})
  if self.owns? post
    post.update(post_hash)
    self.dispatch_post(post)
  end
end

#update_post_default_aspects(post_default_aspect_ids) ⇒ Object



279
280
281
282
283
284
# File 'app/models/user.rb', line 279

def update_post_default_aspects(post_default_aspect_ids)
  aspects.each do |aspect|
    enable = post_default_aspect_ids.include?(aspect.id.to_s)
    aspect.update_attribute(:post_default, enable)
  end
end

#update_profile(params) ⇒ Object

Profile ######################



375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
# File 'app/models/user.rb', line 375

def update_profile(params)
  if photo = params.delete(:photo)
    photo.update(pending: false) if photo.pending
    params[:image_url] = photo.url(:thumb_large)
    params[:image_url_medium] = photo.url(:thumb_medium)
    params[:image_url_small] = photo.url(:thumb_small)
  end

  params.stringify_keys!
  params.slice!(*(Profile.column_names+['tag_string', 'date']))
  if profile.update(params)
    deliver_profile_update
    true
  else
    false
  end
end

#update_profile_with_omniauth(user_info) ⇒ Object



393
394
395
# File 'app/models/user.rb', line 393

def update_profile_with_omniauth(  )
  update_profile( self.profile.from_omniauth_hash(  ) )
end

#update_user_preferences(pref_hash) ⇒ Object



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'app/models/user.rb', line 181

def update_user_preferences(pref_hash)
  if self.disable_mail
    UserPreference::VALID_EMAIL_TYPES.each{|x| self.user_preferences.find_or_create_by(email_type: x)}
    self.disable_mail = false
    self.save
  end

  pref_hash.keys.each do |key|
    if pref_hash[key] == 'true'
      self.user_preferences.find_or_create_by(email_type: key)
    else
      block = user_preferences.find_by(email_type: key)
      if block
        block.destroy
      end
    end
  end
end