Files
growstuff/app/models/member.rb

267 lines
7.5 KiB
Ruby

class Member < ActiveRecord::Base
extend FriendlyId
friendly_id :login_name, use: :slugged
has_many :posts, :foreign_key => 'author_id'
has_many :comments, :foreign_key => 'author_id'
has_many :forums, :foreign_key => 'owner_id'
has_many :gardens, :foreign_key => 'owner_id'
has_many :plantings, :foreign_key => 'owner_id'
has_many :seeds, :foreign_key => 'owner_id'
has_and_belongs_to_many :roles
has_many :notifications, :foreign_key => 'recipient_id'
has_many :sent_notifications, :foreign_key => 'sender_id'
has_many :authentications
has_many :orders
has_one :account
has_one :account_type, :through => :account
has_many :photos
default_scope order("lower(login_name) asc")
scope :confirmed, where('confirmed_at IS NOT NULL')
scope :located, where("location <> '' and latitude IS NOT NULL and longitude IS NOT NULL")
scope :recently_signed_in, reorder('updated_at DESC')
scope :wants_newsletter, where(:newsletter => true)
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable, :lockable, :timeoutable
# Setup accessible (or protected) attributes for your model
attr_accessible :login_name, :email, :password, :password_confirmation,
:remember_me, :login, :tos_agreement, :show_email, :newsletter,
:location, :latitude, :longitude, :send_notification_email, :bio
# set up geocoding
geocoded_by :location
after_validation :geocode
after_validation :empty_unwanted_geocodes
# Virtual attribute for authenticating by either username or email
# This is in addition to a real persisted field like 'username'
attr_accessor :login
# Requires acceptance of the Terms of Service
validates_acceptance_of :tos_agreement, :allow_nil => false,
:accept => true
validates :login_name,
:length => {
:minimum => 2,
:maximum => 25,
:message => "should be between 2 and 25 characters long"
},
:exclusion => {
:in => %w(growstuff admin moderator staff nearby),
:message => "name is reserved"
},
:format => {
:with => /^\w+$/,
:message => "may only include letters, numbers, or underscores"
},
:uniqueness => {
:case_sensitive => false
}
# Give each new member a default garden
after_create {|member| Garden.create(:name => "Garden", :owner_id => member.id) }
# and an account record (for paid accounts etc)
# we use find_or_create to avoid accidentally creating a second one,
# which can happen sometimes especially with FactoryGirl associations
after_create {|member| Account.find_or_create_by_member_id(:member_id => member.id) }
after_save :update_newsletter_subscription
# allow login via either login_name or email address
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions).where(["lower(login_name) = :value OR lower(email) = :value", { :value => login.downcase }]).first
else
where(conditions).first
end
end
def to_s
return login_name
end
def has_role?(role_sym)
roles.any? { |r| r.name.gsub(/\s+/, "_").underscore.to_sym == role_sym }
end
def current_order
orders.where(:completed_at => nil).first
end
# when purchasing a product that gives you a paid account, this method
# does all the messing around to actually make sure the account is
# updated correctly -- account type, paid until, etc. Usually this is
# called by order.update_account, which loops through all order items
# and does this for each one.
def update_account_after_purchase(product)
if product.account_type
account.account_type = product.account_type
end
if product.paid_months
start_date = account.paid_until || Time.zone.now
account.paid_until = start_date + product.paid_months.months
end
account.save
end
def is_paid?
if account.account_type.is_permanent_paid
return true
elsif account.account_type.is_paid and account.paid_until >= Time.zone.now
return true
else
return false
end
end
def auth(provider)
return authentications.find_by_provider(provider)
end
# Authenticates against Flickr and returns an object we can use for subsequent api calls
def flickr
if @flickr.nil?
flickr_auth = auth('flickr')
if flickr_auth
FlickRaw.api_key = ENV['FLICKR_KEY']
FlickRaw.shared_secret = ENV['FLICKR_SECRET']
@flickr = FlickRaw::Flickr.new
@flickr.access_token = flickr_auth.token
@flickr.access_secret = flickr_auth.secret
end
end
return @flickr
end
# Fetches a collection of photos from Flickr
# Returns a [[page of photos], total] pair.
# Total is needed for pagination.
def flickr_photos(page_num=1, set=nil)
result = false
if set
result = flickr.photosets.getPhotos(
:photoset_id => set,
:page => page_num,
:per_page => 30
)
else
result = flickr.people.getPhotos(
:user_id => 'me',
:page => page_num,
:per_page => 30
)
end
if result
return [result.photo, result.total]
else
return [[], 0]
end
end
# Returns a hash of Flickr photosets' ids and titles
def flickr_sets
sets = Hash.new
flickr.photosets.getList.each do |p|
sets[p.title] = p.id
end
return sets
end
def interesting?
# we assume we're being passed something from
# Member.confirmed.located as those are required for
# interestingness, as well.
return true if plantings.present?
return false
end
def Member.interesting
howmany = 12 # max number to find
interesting_members = Array.new
Member.confirmed.located.recently_signed_in.each do |m|
break if interesting_members.length == howmany
if m.interesting?
interesting_members.push(m)
end
end
return interesting_members
end
def Member.nearest_to(place)
nearby_members = []
if place
latitude, longitude = Geocoder.coordinates(place, params: {limit: 1})
if latitude && longitude
nearby_members = Member.located.sort_by { |x| x.distance_from([latitude, longitude]) }
end
end
return nearby_members
end
private
def geocode
unless self.location.blank?
self.latitude, self.longitude =
Geocoder.coordinates(location, params: {limit: 1})
end
end
def empty_unwanted_geocodes
if self.location.blank?
self.latitude = nil
self.longitude = nil
end
end
def update_newsletter_subscription
if confirmed_at_changed? and newsletter # just signed up
newsletter_subscribe
elsif confirmed_at # i.e. after member's confirmed their account
if newsletter_changed? # edited member settings
if newsletter
newsletter_subscribe
else
newsletter_unsubscribe
end
end
end
end
def newsletter_subscribe
gb = Gibbon::API.new
res = gb.lists.subscribe({
:id => ENV['MAILCHIMP_NEWSLETTER_ID'],
:email => { :email => email },
:merge_vars => { :login_name => login_name },
:double_optin => false # they alredy confirmed their email with us
})
end
def newsletter_unsubscribe
gb = Gibbon::API.new
res = gb.lists.unsubscribe({
:id => ENV['MAILCHIMP_NEWSLETTER_ID'],
:email => { :email => email }
})
end
end