openstreetmap-website/app/models/user.rb
Kai Krueger ca558c692e This is the initial implementation of login via OpenID
This is the patch presented in trac ticket #2500

With this commit, it is possible to assosciate an openID
to an existing OSM account. Once associated, it is possible
to either login via OpenID, or as always via username and password.

Other aspects, such as account creation and the need for a valid email
are unchanged.
2010-01-10 17:41:32 +00:00

170 lines
5.7 KiB
Ruby

class User < ActiveRecord::Base
require 'xml/libxml'
has_many :traces, :conditions => { :visible => true }
has_many :diary_entries, :order => 'created_at DESC'
has_many :messages, :foreign_key => :to_user_id, :conditions => { :to_user_visible => true }, :order => 'sent_on DESC'
has_many :new_messages, :class_name => "Message", :foreign_key => :to_user_id, :conditions => { :to_user_visible => true, :message_read => false }, :order => 'sent_on DESC'
has_many :sent_messages, :class_name => "Message", :foreign_key => :from_user_id, :conditions => { :from_user_visible => true }, :order => 'sent_on DESC'
has_many :friends, :include => :befriendee, :conditions => ["users.visible = ?", true]
has_many :tokens, :class_name => "UserToken"
has_many :preferences, :class_name => "UserPreference"
has_many :changesets
has_many :client_applications
has_many :oauth_tokens, :class_name => "OauthToken", :order => "authorized_at desc", :include => [:client_application]
has_many :active_blocks, :class_name => "UserBlock", :conditions => ['user_blocks.ends_at > \'#{Time.now.getutc.xmlschema(5)}\' or user_blocks.needs_view']
has_many :roles, :class_name => "UserRole"
validates_presence_of :email, :display_name
validates_confirmation_of :email#, :message => ' addresses must match'
validates_confirmation_of :pass_crypt#, :message => ' must match the confirmation password'
validates_uniqueness_of :display_name, :allow_nil => true
validates_uniqueness_of :email
validates_uniqueness_of :openid_url, :allow_nil => true
validates_length_of :pass_crypt, :within => 8..255
validates_length_of :display_name, :within => 3..255, :allow_nil => true
validates_length_of :email, :within => 6..255
validates_format_of :email, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
validates_format_of :display_name, :with => /^[^\/;.,?]*$/
validates_numericality_of :home_lat, :allow_nil => true
validates_numericality_of :home_lon, :allow_nil => true
validates_numericality_of :home_zoom, :only_integer => true, :allow_nil => true
before_save :encrypt_password
file_column :image, :magick => { :geometry => "100x100>" }
def after_initialize
self.creation_time = Time.now.getutc unless self.attribute_present?(:creation_time)
end
def encrypt_password
if pass_crypt_confirmation
self.pass_salt = OSM::make_token(8)
self.pass_crypt = OSM::encrypt_password(pass_crypt, pass_salt)
end
end
def self.authenticate(options)
if options[:username] and options[:password]
user = find(:first, :conditions => ["email = ? OR display_name = ?", options[:username], options[:username]])
user = nil if user and user.pass_crypt != OSM::encrypt_password(options[:password], user.pass_salt)
elsif options[:token]
token = UserToken.find(:first, :include => :user, :conditions => ["user_tokens.token = ?", options[:token]])
user = token.user if token
end
if user
user = nil unless user.visible? and (user.active? or options[:inactive])
end
token.update_attribute(:expiry, 1.week.from_now) if token and user
return user
end
def to_xml
doc = OSM::API.new.get_xml_doc
doc.root << to_xml_node()
return doc
end
def to_xml_node
el1 = XML::Node.new 'user'
el1['display_name'] = self.display_name.to_s
el1['account_created'] = self.creation_time.xmlschema
if self.home_lat and self.home_lon
home = XML::Node.new 'home'
home['lat'] = self.home_lat.to_s
home['lon'] = self.home_lon.to_s
home['zoom'] = self.home_zoom.to_s
el1 << home
end
return el1
end
def languages
attribute_present?(:languages) ? read_attribute(:languages).split(",") : []
end
def languages=(languages)
write_attribute(:languages, languages.join(","))
end
def preferred_language
languages.find { |l| Language.find(:first, :conditions => { :code => l }) }
end
def preferred_language_from(array)
(languages & array.collect { |i| i.to_s }).first
end
def nearby(radius = 50, num = 10)
if self.home_lon and self.home_lat
gc = OSM::GreatCircle.new(self.home_lat, self.home_lon)
bounds = gc.bounds(radius)
sql_for_distance = gc.sql_for_distance("home_lat", "home_lon")
nearby = User.find(:all,
:conditions => ["id != ? AND visible = ? AND data_public = ? AND #{sql_for_distance} <= ?", id, true, true, radius], :order => sql_for_distance, :limit => num)
else
nearby = []
end
return nearby
end
def distance(nearby_user)
return OSM::GreatCircle.new(self.home_lat, self.home_lon).distance(nearby_user.home_lat, nearby_user.home_lon)
end
def is_friends_with?(new_friend)
res = false
@new_friend = new_friend
self.friends.each do |friend|
if friend.friend_user_id == @new_friend.id
return true
end
end
return false
end
##
# returns true if the user has the moderator role, false otherwise
def moderator?
has_role? 'moderator'
end
##
# returns true if the user has the administrator role, false otherwise
def administrator?
has_role? 'administrator'
end
##
# returns true if the user has the requested role
def has_role?(role)
roles.any? { |r| r.role == role }
end
##
# returns the first active block which would require users to view
# a message, or nil if there are none.
def blocked_on_view
active_blocks.detect { |b| b.needs_view? }
end
def delete
self.active = false
self.display_name = "user_#{self.id}"
self.description = ""
self.home_lat = nil
self.home_lon = nil
self.image = nil
self.email_valid = false
self.new_email = nil
self.visible = false
self.save
end
end