mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
185 lines
6.1 KiB
Ruby
185 lines
6.1 KiB
Ruby
module Spree
|
|
class User < ActiveRecord::Base
|
|
include Core::UserBanners
|
|
|
|
devise :database_authenticatable, :token_authenticatable, :registerable, :recoverable,
|
|
:rememberable, :trackable, :validatable, :encryptable, encryptor: 'authlogic_sha512'
|
|
|
|
has_many :orders
|
|
belongs_to :ship_address, foreign_key: 'ship_address_id', class_name: 'Spree::Address'
|
|
belongs_to :bill_address, foreign_key: 'bill_address_id', class_name: 'Spree::Address'
|
|
|
|
before_validation :set_login
|
|
before_destroy :check_completed_orders
|
|
|
|
# Setup accessible (or protected) attributes for your model
|
|
attr_accessible :email, :password, :password_confirmation,
|
|
:remember_me, :persistence_token, :login
|
|
|
|
users_table_name = User.table_name
|
|
roles_table_name = Role.table_name
|
|
|
|
scope :admin, lambda { includes(:spree_roles).where("#{roles_table_name}.name" => "admin") }
|
|
scope :registered, -> { where("#{users_table_name}.email NOT LIKE ?", "%@example.net") }
|
|
|
|
has_many :enterprise_roles, dependent: :destroy
|
|
has_many :enterprises, through: :enterprise_roles
|
|
has_many :owned_enterprises, class_name: 'Enterprise',
|
|
foreign_key: :owner_id, inverse_of: :owner
|
|
has_many :owned_groups, class_name: 'EnterpriseGroup',
|
|
foreign_key: :owner_id, inverse_of: :owner
|
|
has_many :customers
|
|
has_many :credit_cards
|
|
|
|
accepts_nested_attributes_for :enterprise_roles, allow_destroy: true
|
|
|
|
accepts_nested_attributes_for :bill_address
|
|
accepts_nested_attributes_for :ship_address
|
|
|
|
attr_accessible :enterprise_ids, :enterprise_roles_attributes, :enterprise_limit,
|
|
:locale, :bill_address_attributes, :ship_address_attributes
|
|
after_create :associate_customers
|
|
|
|
validate :limit_owned_enterprises
|
|
|
|
# We use the same options as Spree and add :confirmable
|
|
devise :confirmable, reconfirmable: true
|
|
# TODO: Later versions of devise have a dedicated after_confirmation callback, so use that
|
|
after_update :welcome_after_confirm, if: lambda {
|
|
confirmation_token_changed? && confirmation_token.nil?
|
|
}
|
|
|
|
class DestroyWithOrdersError < StandardError; end
|
|
|
|
# Creates an anonymous user. An anonymous user is basically an auto-generated +User+ account
|
|
# that is created for the customer behind the scenes and it's transparent to the customer.
|
|
# All +Orders+ must have a +User+ so this is necessary when adding to the "cart" (an order)
|
|
# and before the customer has a chance to provide an email or to register.
|
|
def self.anonymous!
|
|
token = User.generate_token(:persistence_token)
|
|
User.create(email: "#{token}@example.net",
|
|
password: token, password_confirmation: token, persistence_token: token)
|
|
end
|
|
|
|
def self.admin_created?
|
|
User.admin.count > 0
|
|
end
|
|
|
|
def admin?
|
|
has_spree_role?('admin')
|
|
end
|
|
|
|
def anonymous?
|
|
email =~ /@example.net$/ ? true : false
|
|
end
|
|
|
|
def send_reset_password_instructions
|
|
generate_reset_password_token!
|
|
UserMailer.reset_password_instructions(id).deliver
|
|
end
|
|
# handle_asynchronously will define send_reset_password_instructions_with_delay.
|
|
# If handle_asynchronously is called twice, we get an infinite job loop.
|
|
handle_asynchronously :send_reset_password_instructions unless method_defined? :send_reset_password_instructions_with_delay
|
|
|
|
def known_users
|
|
if admin?
|
|
Spree::User.scoped
|
|
else
|
|
Spree::User
|
|
.includes(:enterprises)
|
|
.where("enterprises.id IN (SELECT enterprise_id FROM enterprise_roles WHERE user_id = ?)",
|
|
id)
|
|
end
|
|
end
|
|
|
|
def build_enterprise_roles
|
|
Enterprise.all.find_each do |enterprise|
|
|
unless enterprise_roles.find_by_enterprise_id enterprise.id
|
|
enterprise_roles.build(enterprise: enterprise)
|
|
end
|
|
end
|
|
end
|
|
|
|
def customer_of(enterprise)
|
|
return nil unless enterprise
|
|
customers.find_by_enterprise_id(enterprise)
|
|
end
|
|
|
|
def welcome_after_confirm
|
|
# Send welcome email if we are confirming an user's email
|
|
# Note: this callback only runs on email confirmation
|
|
return unless confirmed? && unconfirmed_email.nil? && !unconfirmed_email_changed?
|
|
send_signup_confirmation
|
|
end
|
|
|
|
def send_signup_confirmation
|
|
Delayed::Job.enqueue ConfirmSignupJob.new(id)
|
|
end
|
|
|
|
def associate_customers
|
|
self.customers = Customer.where(email: email)
|
|
end
|
|
|
|
def can_own_more_enterprises?
|
|
owned_enterprises(:reload).size < enterprise_limit
|
|
end
|
|
|
|
def default_card
|
|
credit_cards.where(is_default: true).first
|
|
end
|
|
|
|
# Checks whether the specified user is a superadmin, with full control of the
|
|
# instance
|
|
#
|
|
# @return [Boolean]
|
|
def superadmin?
|
|
has_spree_role?('admin')
|
|
end
|
|
|
|
protected
|
|
|
|
def password_required?
|
|
!persisted? || password.present? || password_confirmation.present?
|
|
end
|
|
|
|
private
|
|
|
|
def check_completed_orders
|
|
raise DestroyWithOrdersError if orders.complete.present?
|
|
end
|
|
|
|
def set_login
|
|
# for now force login to be same as email, eventually we will make this configurable, etc.
|
|
self.login ||= email if email
|
|
end
|
|
|
|
# Generate a friendly string randomically to be used as token.
|
|
def self.friendly_token
|
|
SecureRandom.base64(15).tr('+/=', '-_ ').strip.delete("\n")
|
|
end
|
|
|
|
# Generate a token by looping and ensuring does not already exist.
|
|
def self.generate_token(column)
|
|
loop do
|
|
token = friendly_token
|
|
break token unless find(:first, conditions: { column => token })
|
|
end
|
|
end
|
|
|
|
def limit_owned_enterprises
|
|
return unless owned_enterprises.size > enterprise_limit
|
|
errors.add(:owned_enterprises, I18n.t(:spree_user_enterprise_limit_error,
|
|
email: email,
|
|
enterprise_limit: enterprise_limit))
|
|
end
|
|
|
|
def remove_payments_in_checkout(enterprises)
|
|
enterprises.each do |enterprise|
|
|
enterprise.distributed_orders.each do |order|
|
|
order.payments.keep_if { |payment| payment.state != "checkout" }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|