<?php
/**
* This is the model class for table "{{users}}".
*
* The followings are the available columns in table '{{users}}':
* @property string $id
* @property string $password_hash
* @property string $email
*/
class User extends CActiveRecord {
const MALE = 'male';
const FEMALE = 'female';
public $password;
public $rememberMe = true;
public $model;
public $birth_year;
public $birth_month;
public $birth_day;
private $_identity;
/**
* @return string the associated database table name
*/
public function tableName() {
return '{{users}}';
}
/**
* @return array validation rules for model attributes.
*/
public function rules() {
return array(
// common rules
array('email, first_name, last_name, patronymic', 'length', 'max' => 254),
array('password_hash', 'length', 'max' => 64),
array('birth_year', 'numerical', 'integerOnly' => true, 'min' => 1900, 'max' => date('Y') - 5),
array('birth_month', 'numerical', 'integerOnly' => true, 'min' => 1, 'max' => 12),
array('birth_day', 'numerical', 'integerOnly' => true, 'min' => 1, 'max' => 31),
array('gender', 'in', 'range' => array('male', 'female')),
// both
array('email', 'required', 'on' => 'login, registration'),
// login
array('password', 'authenticate', 'on' => 'login'),
array('password', 'required', 'on' => 'login'),
array('rememberMe', 'boolean', 'on' => 'login'),
// registration
array('password', 'required', 'on' => 'registration'),
array('email', 'emailAvailable', 'on' => 'registration'),
// fill name
array('first_name, last_name', 'required', 'on' => 'fillName'),
// edit personal info
array('first_name, last_name, gender', 'required', 'on' => 'edit'),
array('birth_date', 'checkDate', 'dayAttribute' => 'birth_day', 'monthAttribute' => 'birth_month', 'yearAttribute' => 'birth_year', 'on' => 'edit'),
// edit site settings
array('language', 'in', 'range' => array_keys($this->possibleLanguages)),
array('timezone', 'in', 'range' => array_keys($this->possibleTimezones)),
array('language, timezone', 'default', 'value' => null),
array('id, email, password_hash, registrated_at, visited_at', 'safe', 'on' => 'search'),
);
}
/**
* @return array relational rules.
*/
public function relations() {
return array(
'profile' => array(self::BELONGS_TO, 'Profile', 'id'),
'totalPhotos' => array(self::STAT, 'GalleryPhoto', 'user_id'),
'totalForumTopics' => array(self::STAT, 'Topic', 'initiator_id'),
'totalPublics' => array(self::STAT, 'PublicReader', 'user_id'),
'totalGroups' => array(self::STAT, 'GroupMember', 'user_id'),
// 'totalFriends' => array(self::STAT, 'UserFriend', 'friend_id', 'condition' => 'totalFriends.user_id = :user OR totalFriends.friend_id = :user', 'params' => array(':user' => $this->id)),
);
}
/**
* @return array customized attribute labels (name=>label)
*/
public function attributeLabels() {
return array(
'id' => Yii::t('Users', 'User id'),
'email' => Yii::t('Users', 'Email'),
'password_hash' => Yii::t('Users', 'Password hash'),
'password' => Yii::t('Users', 'Password'),
'rememberMe' => Yii::t('Users', 'Remember me'),
'first_name' => Yii::t('Users', 'First name'),
'last_name' => Yii::t('Users', 'Last name'),
'patronymic' => Yii::t('Users', 'Patronymic'),
'gender' => Yii::t('Users', 'Gender'),
'birth_date' => Yii::t('Users', 'Date of birth'),
'registrated_at' => Yii::t('Users', 'DateTime of registration'),
'visited_at' => Yii::t('Users', 'DateTime of last activity'),
'timezone' => Yii::t('Users', 'Timezone'),
'language' => Yii::t('Users', 'Language'),
);
}
/**
* Authenticates the password.
* This is the 'authenticate' validator as declared in rules().
*/
public function authenticate($attribute, $params) {
if (!$this->hasErrors()) {
$this->_identity = new DatabaseUserIdentity($this->email, $this->password);
if (!$this->_identity->authenticate())
$this->addError('password', Yii::t('Users', 'Username or password is wrong.'));
}
}
/**
* Checks email availability.
*/
public function emailAvailable($attribute, $params) {
if (!$this->hasErrors()) {
if (self::model()->findByAttributes(array('email' => $this->$attribute)) !== null)
$this->addError($attribute, Yii::t('Users', 'Email already used.'));
}
}
/**
* Logs in the user using the given username and password in the model.
* @return boolean whether login is successful
*/
public function login() {
if ($this->_identity->errorCode === DatabaseUserIdentity::ERROR_NONE) {
$duration = $this->rememberMe ? 3600*24*30 : 0; // 30 days
Yii::app()->user->login($this->_identity, $duration);
return true;
}
return false;
}
/**
* Logs in the user using the given username and password in the model.
* @return boolean whether login is successful
*/
public function registrate() {
$transaction = $this->dbConnection->beginTransaction();
$profile = new Profile;
$profile->type = 'user';
$profile->save();
$user = new self;
$user->id = $profile->id;
$user->email = $this->email;
$user->password_hash = crypt($this->password, self::blowfishSalt());
$user->save();
$transaction->commit();
return $user;
}
/**
* Retrieves a list of models based on the current search/filter conditions.
*
* Typical usecase:
* - Initialize the model fields with values from filter form.
* - Execute this method to get CActiveDataProvider instance which will filter
* models according to data in model fields.
* - Pass data provider to CGridView, CListView or any similar widget.
*
* @return CActiveDataProvider the data provider that can return the models
* based on the search/filter conditions.
*/
public function search($pageSize) {
$criteria = new CDbCriteria;
$criteria->compare('id', $this->id, true);
$criteria->compare('email', $this->email, true);
$criteria->compare('password_hash', $this->password_hash, true);
$criteria->compare('registrated_at', $this->registrated_at, true);
$criteria->compare('visited_at', $this->visited_at, true);
$criteria->compare('first_name', $this->first_name, true);
$criteria->compare('last_name', $this->last_name, true);
$criteria->compare('patronymic', $this->patronymic, true);
return new CActiveDataProvider($this, array(
'criteria' => $criteria,
'pagination' => array(
'pageSize' => $pageSize,
),
'sort' => array(
'defaultOrder' => 'visited_at DESC',
),
));
}
/**
* Returns the static model of the specified AR class.
* Please note that you should have this exact method in all your CActiveRecord descendants!
* @param string $className active record class name.
* @return User the static model class
*/
public static function model($className=__CLASS__) {
return parent::model($className);
}
/**
* Generate a random salt in the crypt(3) standard Blowfish format.
* @param int $cost Cost parameter from 4 to 31.
* @throws Exception on invalid cost parameter.
* @return string A Blowfish hash salt for use in PHP's crypt()
*/
static public function blowfishSalt($cost = 13) {
if (!is_numeric($cost) || $cost < 4 || $cost > 31) {
throw new Exception("cost parameter must be between 4 and 31");
}
$rand = array();
for ($i = 0; $i < 8; $i += 1) {
$rand[] = pack('S', mt_rand(0, 0xffff));
}
$rand[] = substr(microtime(), 2, 6);
$rand = sha1(implode('', $rand), true);
$salt = '$2a$' . sprintf('%02d', $cost) . '$';
$salt .= strtr(substr(base64_encode($rand), 0, 22), array('+' => '.'));
return $salt;
}
/**
* Updates time of last acitivty
*/
public function updateVisitTime() {
$this->visited_at = new CDbExpression('NOW()');
return $this->save(true, array('visited_at'));
}
/**
* Checks if user online
*/
public function isOnline() {
return ((time() - Yii::app()->params['onlineStatusLimit']) < strtotime($this->visited_at));
}
/**
* Named scopes
*/
public function scopes() {
return array(
'online' => array(
'order' => 'visited_at DESC',
'condition' => 'visited_at >= :min_last_acitivty_datetime',
'params' => array(':min_last_acitivty_datetime' => date('Y-m-d G:i:s', time() - Yii::app()->params['onlineStatusLimit'])),
),
'offline' => array(
'order' => 'visited_at DESC',
'condition' => 'visited_at < :min_last_acitivty_datetime',
'params' => array(':min_last_acitivty_datetime' => date('Y-m-d G:i:s', time() - Yii::app()->params['onlineStatusLimit'])),
),
);
}
/**
* Date validator
*/
public function checkDate($attribute, $params) {
if (!$this->hasErrors() && !checkdate($this->{$params['monthAttribute']}, $this->{$params['dayAttribute']}, $this->{$params['yearAttribute']}))
$this->addError($attribute, Yii::t('Users', 'Date is wrong.'));
else
$this->$attribute = $this->{$params['yearAttribute']}.'-'.$this->{$params['monthAttribute']}.'-'.$this->{$params['dayAttribute']};
}
/**
* Returns localized month names to use it in dropDownList()
*/
public function getPossibleMonths() {
return CLocale::getInstance(Yii::app()->language)->getMonthNames();
}
/**
* Returns localized language names to use it in dropDownList()
*/
public function getPossibleLanguages() {
$locale = CLocale::getInstance(Yii::app()->language);
return array(
null => Yii::t('Users', 'Default site language ({language})', array('{language}' => $locale->getLocaleDisplayName(Settings::instance()->language))),
'ru' => $locale->getLocaleDisplayName('ru'),
'en' => $locale->getLocaleDisplayName('en'),
);
}
/**
* Returns all known timezones to use it in dropDownList()
*/
public function getPossibleTimezones() {
$default_timezone = Yii::app()->timeZone;
$lands = require Yii::getPathOfAlias('application.data').'/timezones.php';
$timezones = array(
Yii::t('Users', 'Default site timezone ({timezone})', array('{timezone}' => Yii::t('Users', Settings::instance()->timezone))) => '',
);
foreach ($lands as $land => $land_timezones)
$timezones = array_merge($timezones, $land_timezones);
$timezones = array_flip($timezones);
foreach ($timezones as $i => $timezone) {
if (!empty($i))
date_default_timezone_set($i);
$timezones[$i] = is_numeric($timezone) ? Yii::t('Users', $i).' '.date('P (G:i)') : $timezone;
}
date_default_timezone_set($default_timezone);
return $timezones;
}
/**
* Returns compiled name
*/
public function getName() {
return $this->first_name.' '.$this->last_name;
}
/**
* Add notification to user
*/
public function addNotification($text, $type) {
$notification = new UserNotification;
$notification->user_id = $this->id;
$notification->type = $type;
$notification->text = $text;
$notification->new = 1;
return $notification->save();
}
/**
* Turn off delete() operation.
*/
public function beforeDelete() {
parent::beforeDelete();
return false;
}
public function getTotalFriends() {
return UserFriend::model()->allUserFriends($this->id)->count('approved = "1"');
}
}