tracks/app/models/user.rb
Luke Melia 901a58f8a3 Upgraded to Rails 2.1. This can have wide ranging consequences, so please help track down any issues introduced by the upgrade. Requires environment.rb modifications.
Changes you will need to make:

 * In your environment.rb, you will need to update references to a few files per environment.rb.tmpl
 * In your environment.rb, you will need to specify the local time zone of the computer that is running your Tracks install.

Other notes on my changes:

 * Modified our code to take advantage of Rails 2.1's slick time zone support.
 * Upgraded will_paginate for compatibility
 * Hacked the Selenium on Rails plugin, which has not been updated in some time and does not support Rails 2.1
 * Verified that all tests pass on my machine, including Selenium tests -- I'd like confirmation from others, too.
2008-06-17 01:13:25 -04:00

223 lines
7.1 KiB
Ruby

require 'digest/sha1'
class User < ActiveRecord::Base
# Virtual attribute for the unencrypted password
attr_accessor :password
has_many :contexts,
:order => 'position ASC',
:dependent => :delete_all do
def find_by_params(params)
find(params['id'] || params['context_id']) || nil
end
end
has_many :projects,
:order => 'projects.position ASC',
:dependent => :delete_all do
def find_by_params(params)
find(params['id'] || params['project_id'])
end
def update_positions(project_ids)
project_ids.each_with_index do |id, position|
project = self.detect { |p| p.id == id.to_i }
raise "Project id #{id} not associated with user id #{@user.id}." if project.nil?
project.update_attribute(:position, position + 1)
end
end
def projects_in_state_by_position(state)
self.sort{ |a,b| a.position <=> b.position }.select{ |p| p.state == state }
end
def next_from(project)
self.offset_from(project, 1)
end
def previous_from(project)
self.offset_from(project, -1)
end
def offset_from(project, offset)
projects = self.projects_in_state_by_position(project.state)
position = projects.index(project)
return nil if position == 0 && offset < 0
projects.at( position + offset)
end
def cache_note_counts
project_note_counts = Note.count(:group => 'project_id')
self.each do |project|
project.cached_note_count = project_note_counts[project.id] || 0
end
end
def alphabetize(scope_conditions = {})
projects = find(:all, :conditions => scope_conditions)
projects.sort!{ |x,y| x.name.downcase <=> y.name.downcase }
self.update_positions(projects.map{ |p| p.id })
return projects
end
end
has_many :active_projects,
:class_name => 'Project',
:order => 'projects.position ASC',
:conditions => [ 'state = ?', 'active' ]
has_many :active_contexts,
:class_name => 'Context',
:order => 'position ASC',
:conditions => [ 'hide = ?', 'true' ]
has_many :todos,
:order => 'todos.completed_at DESC, todos.created_at DESC',
:dependent => :delete_all
has_many :deferred_todos,
:class_name => 'Todo',
:conditions => [ 'state = ?', 'deferred' ],
:order => 'show_from ASC, todos.created_at DESC' do
def find_and_activate_ready
find(:all, :conditions => ['show_from <= ?', proxy_owner.time ]).collect { |t| t.activate! }
end
end
has_many :completed_todos,
:class_name => 'Todo',
:conditions => ['todos.state = ? and todos.completed_at is not null', 'completed'],
:order => 'todos.completed_at DESC',
:include => [ :project, :context ] do
def completed_within( date )
reject { |x| x.completed_at < date }
end
def completed_more_than( date )
reject { |x| x.completed_at > date }
end
end
has_many :notes, :order => "created_at DESC", :dependent => :delete_all
has_one :preference, :dependent => :destroy
has_many :taggings
has_many :tags, :through => :taggings, :select => "DISTINCT tags.*"
attr_protected :is_admin
validates_presence_of :login
validates_presence_of :password, :if => :password_required?
validates_length_of :password, :within => 5..40, :if => :password_required?
validates_presence_of :password_confirmation, :if => :password_required?
validates_confirmation_of :password
validates_length_of :login, :within => 3..80
validates_uniqueness_of :login, :on => :create
validates_presence_of :open_id_url, :if => :using_openid?
before_create :crypt_password, :generate_token
before_update :crypt_password
before_save :normalize_open_id_url
def validate
unless Tracks::Config.auth_schemes.include?(auth_type)
errors.add("auth_type", "not a valid authentication type (#{auth_type})")
end
end
alias_method :prefs, :preference
def self.authenticate(login, pass)
return nil if login.blank?
candidate = find(:first, :conditions => ["login = ?", login])
return nil if candidate.nil?
return candidate if candidate.auth_type == 'database' && candidate.crypted_password == sha1(pass)
if Tracks::Config.auth_schemes.include?('ldap')
return candidate if candidate.auth_type == 'ldap' && SimpleLdapAuthenticator.valid?(login, pass)
end
return nil
end
def self.find_by_open_id_url(raw_open_id_url)
normalized_open_id_url = normalize_open_id_url(raw_open_id_url)
find(:first, :conditions => ['open_id_url = ?', normalized_open_id_url])
end
def self.no_users_yet?
count == 0
end
def self.find_admin
find(:first, :conditions => [ "is_admin = ?", true ])
end
def to_param
login
end
def display_name
if first_name.blank? && last_name.blank?
return login
elsif first_name.blank?
return last_name
elsif last_name.blank?
return first_name
end
"#{first_name} #{last_name}"
end
def change_password(pass,pass_confirm)
self.password = pass
self.password_confirmation = pass_confirm
save!
end
def time
Time.now.in_time_zone(prefs.time_zone)
end
def date
time.to_date
end
def generate_token
self.token = Digest::SHA1.hexdigest "#{Time.now.to_i}#{rand}"
end
def remember_token?
remember_token_expires_at && Time.now.utc < remember_token_expires_at
end
# These create and unset the fields required for remembering users between browser closes
def remember_me
self.remember_token_expires_at = 2.weeks.from_now.utc
self.remember_token = self.class.sha1("#{login}--#{remember_token_expires_at}")
save(false)
end
def forget_me
self.remember_token_expires_at = nil
self.remember_token = nil
save(false)
end
protected
def self.sha1(s)
Digest::SHA1.hexdigest("#{Tracks::Config.salt}--#{s}--")
end
def crypt_password
return if password.blank?
write_attribute("crypted_password", self.class.sha1(password)) if password == password_confirmation
end
def password_required?
auth_type == 'database' && crypted_password.blank? || !password.blank?
end
def using_openid?
auth_type == 'open_id'
end
def password_matches?(pass)
crypted_password == sha1(pass)
end
def normalize_open_id_url
return if open_id_url.nil?
self.open_id_url = self.class.normalize_open_id_url(open_id_url)
end
def self.normalize_open_id_url(raw_open_id_url)
normalized = raw_open_id_url
normalized = "http://#{raw_open_id_url}" unless raw_open_id_url =~ /\:\/\//
normalized.downcase.chomp('/')
end
end