EdinburghUniversityTheatreCompany / black_lightning

MIT License
3 stars 4 forks source link

Merge tool for users #144

Open mickzijdel opened 1 year ago

mickzijdel commented 1 year ago

Duplicate users occurr often enough that it is worthwhile. Can just be a task, but having a button on a user to merge them into another user (transferring all data from that user to the other user) would be nice.

Approximate code:

# Merges the source into the target user by changing all references to the source to the target, and then deletes the target.
# Fails if there is something umergeable.
def merge_user_into(source_user, target_user)
  ActiveRecord::Base.transaction do
    has_manies = [:team_membership, :staffing_jobs, :admin_staffing_debts, :admin_maintenance_debts, :membership_activation_tokens, :roles]
    has_manies.each do |has_many_relation|
      source_user.send(has_many_relation).each do |relation|
        relation.update!(user: target_user)
      end
    end

    # Has One's
    if source_user.membership_card.present?
      raise(ActiveRecord::AttributeAssignmentError, "The source and target user both have a Membership Card attached. Please delete one and try merging again.") if target_user.membership_card.present?
      target_user.membership_card = source_user.membership_card
    end
    if source_user.marketing_creatives_profile.present?
      raise(ActiveRecord::AttributeAssignmentError, "The source and target user both have a Marketing Creatives Profile attached. Please delete one and try merging again.") if target_user.marketing_creatives_profile.present?
      target_user.marketing_creatives_profile = source_user.marketing_creatives_profile
    end

    if source_user.avatar.attached? && !target_user.avatar.attached?
      source_user.avatar.blob.open do |tempfile|
        target_user.avatar.attach(
          io: tempfile,
          filename: source_user.avatar.filename,
          content_type: source_user.avatar.content_type
          )
      end
    end

    single_fields = [:phone_number, :first_name, :last_name, :bio]
    single_fields.each do |single_field|
      target_user[single_field] ||= source_user[single_field]
    end

    target_user.public_profile = false unless source_user.public_profile
    target_user.sign_in_count += source_user.sign_in_count

    target_user.save!

    # TODO: This raise is broken, which means the others likely are too.
    #raise(ActiveRecord::AttributeAssignmentError, "test")

    # Reload changes to the has_many relations and such ..
    source_user.reload
    # and try to destroy the old user.
    begin
      source_user.destroy!
    rescue ActiveRecord::RecordNotDestroyed => e
      # TODO: This is untested. Can potentially be tested by commenting out the source_user.reload bit a few lines above.
      raise(e, e.message + "; Messages: #{source_user.errors}")
    end
  end
end