581 lines
14 KiB
PHP
581 lines
14 KiB
PHP
<?php namespace RainLab\User\Models;
|
|
|
|
use Str;
|
|
use Auth;
|
|
use Mail;
|
|
use Event;
|
|
use Config;
|
|
use Carbon\Carbon;
|
|
use October\Rain\Auth\Models\User as UserBase;
|
|
use RainLab\User\Models\Settings as UserSettings;
|
|
use October\Rain\Auth\AuthException;
|
|
use TPS\Birzha\Models\Message;
|
|
|
|
class User extends UserBase
|
|
{
|
|
use \October\Rain\Database\Traits\SoftDelete;
|
|
|
|
/**
|
|
* @var string The database table used by the model.
|
|
*/
|
|
protected $table = 'users';
|
|
|
|
/**
|
|
* Validation rules
|
|
*/
|
|
public $rules = [
|
|
'email' => 'between:6,255|unique:users',
|
|
'avatar' => 'nullable|image|max:4000',
|
|
'username' => 'required|between:2,255|unique:users',
|
|
'password' => 'required:create|between:8,255|confirmed',
|
|
'password_confirmation' => 'required_with:password|between:8,255',
|
|
];
|
|
|
|
/**
|
|
* @var array Relations
|
|
*/
|
|
public $belongsToMany = [
|
|
'groups' => [UserGroup::class, 'table' => 'users_groups'],
|
|
'chatrooms' => [
|
|
'TPS\Birzha\Models\Chatroom',
|
|
'table'=>'tps_birzha_chatrooms_users',
|
|
'delete' => true,
|
|
'softDelete' => true
|
|
],
|
|
'categories' => [
|
|
'TPS\Birzha\Models\Category',
|
|
'table'=>'tps_birzha_users_categories',
|
|
'delete' => true,
|
|
'softDelete' => true
|
|
],
|
|
|
|
];
|
|
|
|
|
|
|
|
public $attachOne = [
|
|
'avatar' => \System\Models\File::class
|
|
];
|
|
|
|
public $hasMany = [
|
|
'products' => ['TPS\Birzha\Models\Product', 'key' => 'vendor_id', 'softDelete' => true],
|
|
'transactions' => ['TPS\Birzha\Models\Transaction', 'softDelete' => true],
|
|
'exchangerequests' => ['TPS\Birzha\Models\Exchangerequest', 'softDelete' => true],
|
|
'sliders' => ['TPS\Birzha\Models\UserSliders', 'softDelete' => true],
|
|
'favourites' => ['TPS\Birzha\Models\Favourites', 'softDelete' => true, 'table' => 'tps_birzha_favourites'],
|
|
'comments' => ['TPS\Birzha\Models\Comment','table' => 'tps_birzha_comments'],
|
|
'orders' => ['TPS\Birzha\Models\Orders','table' => 'tps_birzha_orders'],
|
|
'vendor_sales' => ['TPS\Birzha\Models\VendorSales', 'key' => 'vendor_id', 'softDelete' => true],
|
|
'messages' => ['TPS\Birzha\Models\Message', 'key' => 'reciver_id', 'softDelete' => true],
|
|
];
|
|
|
|
/**
|
|
* @var array The attributes that are mass assignable.
|
|
*/
|
|
protected $fillable = [
|
|
'name',
|
|
'surname',
|
|
'login',
|
|
'username',
|
|
'email',
|
|
'password',
|
|
'password_confirmation',
|
|
'created_ip_address',
|
|
'last_ip_address'
|
|
];
|
|
|
|
/**
|
|
* Reset guarded fields, because we use $fillable instead.
|
|
* @var array The attributes that aren't mass assignable.
|
|
*/
|
|
protected $guarded = ['*'];
|
|
|
|
/**
|
|
* Purge attributes from data set.
|
|
*/
|
|
protected $purgeable = ['password_confirmation', 'send_invite'];
|
|
|
|
protected $dates = [
|
|
'last_seen',
|
|
'deleted_at',
|
|
'created_at',
|
|
'updated_at',
|
|
'activated_at',
|
|
'last_login'
|
|
];
|
|
|
|
public static $loginAttribute = null;
|
|
|
|
/**
|
|
* Sends the confirmation email to a user, after activating.
|
|
* @param string $code
|
|
* @return bool
|
|
*/
|
|
public function attemptActivation($code)
|
|
{
|
|
if ($this->trashed()) {
|
|
if ($code === $this->activation_code) {
|
|
$this->restore();
|
|
} else {
|
|
return false;
|
|
}
|
|
} else {
|
|
$result = parent::attemptActivation($code);
|
|
|
|
if ($result === false) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Event::fire('rainlab.user.activate', [$this]);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Converts a guest user to a registered one and sends an invitation notification.
|
|
* @return void
|
|
*/
|
|
public function convertToRegistered($sendNotification = true)
|
|
{
|
|
// Already a registered user
|
|
if (!$this->is_guest) {
|
|
return;
|
|
}
|
|
|
|
if ($sendNotification) {
|
|
$this->generatePassword();
|
|
}
|
|
|
|
$this->is_guest = false;
|
|
$this->save();
|
|
|
|
if ($sendNotification) {
|
|
$this->sendInvitation();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Constructors
|
|
//
|
|
|
|
/**
|
|
* findByEmail looks up a user by their email address.
|
|
* @return self
|
|
*/
|
|
public static function findByEmail($email)
|
|
{
|
|
if (!$email) {
|
|
return;
|
|
}
|
|
|
|
return self::where('email', $email)->first();
|
|
}
|
|
|
|
//
|
|
// Getters
|
|
//
|
|
|
|
/**
|
|
* clearPersistCode will forcibly sign the user out
|
|
*/
|
|
public function clearPersistCode()
|
|
{
|
|
$this->persist_code = null;
|
|
$this->timestamps = false;
|
|
$this->save();
|
|
}
|
|
|
|
/**
|
|
* Gets a code for when the user is persisted to a cookie or session which identifies the user.
|
|
* @return string
|
|
*/
|
|
public function getPersistCode()
|
|
{
|
|
$block = UserSettings::get('block_persistence', false);
|
|
|
|
if ($block || !$this->persist_code) {
|
|
return parent::getPersistCode();
|
|
}
|
|
|
|
return $this->persist_code;
|
|
}
|
|
|
|
/**
|
|
* Returns the public image file path to this user's avatar.
|
|
*/
|
|
public function getAvatarThumb($size = 25, $options = null)
|
|
{
|
|
if (is_string($options)) {
|
|
$options = ['default' => $options];
|
|
}
|
|
elseif (!is_array($options)) {
|
|
$options = [];
|
|
}
|
|
|
|
// Default is "mm" (Mystery man)
|
|
$default = array_get($options, 'default', 'mm');
|
|
|
|
if ($this->avatar) {
|
|
return $this->avatar->getThumb($size, $size, $options);
|
|
}
|
|
else {
|
|
return '//www.gravatar.com/avatar/'.
|
|
md5(strtolower(trim($this->email))).
|
|
'?s='.$size.
|
|
'&d='.urlencode($default);
|
|
}
|
|
}
|
|
|
|
public function getUnreadMessagesCount(){
|
|
$count = Message::where('reciver_id', $this->id)->whereNull('read_at')->whereNull('deleted_at')->count();
|
|
return $count;
|
|
}
|
|
|
|
/**
|
|
* Returns the name for the user's login.
|
|
* @return string
|
|
*/
|
|
public function getLoginName()
|
|
{
|
|
if (static::$loginAttribute !== null) {
|
|
return static::$loginAttribute;
|
|
}
|
|
|
|
return static::$loginAttribute = UserSettings::get('login_attribute', UserSettings::LOGIN_EMAIL);
|
|
}
|
|
|
|
/**
|
|
* Returns the minimum length for a new password from settings.
|
|
* @return int
|
|
*/
|
|
public static function getMinPasswordLength()
|
|
{
|
|
return Config::get('rainlab.user::minPasswordLength', 8);
|
|
}
|
|
|
|
//
|
|
// Scopes
|
|
//
|
|
|
|
/**
|
|
* scopeIsActivated
|
|
*/
|
|
public function scopeIsActivated($query)
|
|
{
|
|
return $query->where('is_activated', 1);
|
|
}
|
|
|
|
public function scopeIsFeatured($query)
|
|
{
|
|
return $query->where('is_featured', 1)->where('type', '!=', 'simple');
|
|
}
|
|
|
|
/**
|
|
* scopeFilterByGroup
|
|
*/
|
|
public function scopeFilterByGroup($query, $filter)
|
|
{
|
|
return $query->whereHas('groups', function($group) use ($filter) {
|
|
$group->whereIn('id', $filter);
|
|
});
|
|
}
|
|
|
|
//
|
|
// Events
|
|
//
|
|
|
|
/**
|
|
* beforeValidate event
|
|
* @return void
|
|
*/
|
|
public function beforeValidate()
|
|
{
|
|
/*
|
|
* Guests are special
|
|
*/
|
|
if ($this->is_guest && !$this->password) {
|
|
$this->generatePassword();
|
|
}
|
|
|
|
/*
|
|
* When the username is not used, the email is substituted.
|
|
*/
|
|
if (
|
|
(!$this->username) ||
|
|
($this->isDirty('email') && $this->getOriginal('email') == $this->username)
|
|
) {
|
|
$this->username = $this->email;
|
|
}
|
|
|
|
/*
|
|
* Apply Password Length Settings
|
|
*/
|
|
$minPasswordLength = static::getMinPasswordLength();
|
|
$this->rules['password'] = "required:create|between:$minPasswordLength,255|confirmed";
|
|
$this->rules['password_confirmation'] = "required_with:password|between:$minPasswordLength,255";
|
|
}
|
|
|
|
/**
|
|
* afterCreate event
|
|
* @return void
|
|
*/
|
|
public function afterCreate()
|
|
{
|
|
$this->restorePurgedValues();
|
|
|
|
if ($this->send_invite) {
|
|
$this->sendInvitation();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* beforeLogin event
|
|
* @return void
|
|
*/
|
|
public function beforeLogin()
|
|
{
|
|
if ($this->is_guest) {
|
|
$login = $this->getLogin();
|
|
throw new AuthException(sprintf(
|
|
'Cannot login user "%s" as they are not registered.', $login
|
|
));
|
|
}
|
|
|
|
parent::beforeLogin();
|
|
}
|
|
|
|
/**
|
|
* afterLogin event
|
|
* @return void
|
|
*/
|
|
public function afterLogin()
|
|
{
|
|
$this->last_login = $this->freshTimestamp();
|
|
|
|
if ($this->trashed()) {
|
|
$this->restore();
|
|
|
|
Mail::sendTo($this, 'rainlab.user::mail.reactivate', [
|
|
'name' => $this->name
|
|
]);
|
|
|
|
Event::fire('rainlab.user.reactivate', [$this]);
|
|
}
|
|
else {
|
|
parent::afterLogin();
|
|
}
|
|
|
|
Event::fire('rainlab.user.login', [$this]);
|
|
}
|
|
|
|
/**
|
|
* afterDelete event
|
|
* @return void
|
|
*/
|
|
public function afterDelete()
|
|
{
|
|
if ($this->isSoftDelete()) {
|
|
Event::fire('rainlab.user.deactivate', [$this]);
|
|
return;
|
|
}
|
|
|
|
$this->avatar && $this->avatar->delete();
|
|
|
|
parent::afterDelete();
|
|
}
|
|
|
|
//
|
|
// Banning
|
|
//
|
|
|
|
/**
|
|
* Ban this user, preventing them from signing in.
|
|
* @return void
|
|
*/
|
|
public function ban()
|
|
{
|
|
Auth::findThrottleByUserId($this->id)->ban();
|
|
}
|
|
|
|
/**
|
|
* Remove the ban on this user.
|
|
* @return void
|
|
*/
|
|
public function unban()
|
|
{
|
|
Auth::findThrottleByUserId($this->id)->unban();
|
|
}
|
|
|
|
/**
|
|
* Check if the user is banned.
|
|
* @return bool
|
|
*/
|
|
public function isBanned()
|
|
{
|
|
$throttle = Auth::createThrottleModel()->where('user_id', $this->id)->first();
|
|
return $throttle ? $throttle->is_banned : false;
|
|
}
|
|
|
|
//
|
|
// Suspending
|
|
//
|
|
|
|
/**
|
|
* Check if the user is suspended.
|
|
* @return bool
|
|
*/
|
|
public function isSuspended()
|
|
{
|
|
return Auth::findThrottleByUserId($this->id)->checkSuspended();
|
|
}
|
|
|
|
/**
|
|
* Remove the suspension on this user.
|
|
* @return void
|
|
*/
|
|
public function unsuspend()
|
|
{
|
|
Auth::findThrottleByUserId($this->id)->unsuspend();
|
|
}
|
|
|
|
//
|
|
// IP Recording and Throttle
|
|
//
|
|
|
|
/**
|
|
* Records the last_ip_address to reflect the last known IP for this user.
|
|
* @param string|null $ipAddress
|
|
* @return void
|
|
*/
|
|
public function touchIpAddress($ipAddress)
|
|
{
|
|
$this
|
|
->newQuery()
|
|
->where('id', $this->id)
|
|
->update(['last_ip_address' => $ipAddress])
|
|
;
|
|
}
|
|
|
|
/**
|
|
* Returns true if IP address is throttled and cannot register
|
|
* again. Maximum 3 registrations every 60 minutes.
|
|
* @param string|null $ipAddress
|
|
* @return bool
|
|
*/
|
|
public static function isRegisterThrottled($ipAddress)
|
|
{
|
|
if (!$ipAddress) {
|
|
return false;
|
|
}
|
|
|
|
$timeLimit = Carbon::now()->subMinutes(60);
|
|
$count = static::make()
|
|
->where('created_ip_address', $ipAddress)
|
|
->where('created_at', '>', $timeLimit)
|
|
->count()
|
|
;
|
|
|
|
return $count > 2;
|
|
}
|
|
|
|
//
|
|
// Last Seen
|
|
//
|
|
|
|
/**
|
|
* Checks if the user has been seen in the last 5 minutes, and if not,
|
|
* updates the last_seen timestamp to reflect their online status.
|
|
* @return void
|
|
*/
|
|
public function touchLastSeen()
|
|
{
|
|
if ($this->isOnline()) {
|
|
return;
|
|
}
|
|
|
|
$oldTimestamps = $this->timestamps;
|
|
$this->timestamps = false;
|
|
|
|
$this
|
|
->newQuery()
|
|
->where('id', $this->id)
|
|
->update(['last_seen' => $this->freshTimestamp()])
|
|
;
|
|
|
|
$this->last_seen = $this->freshTimestamp();
|
|
$this->timestamps = $oldTimestamps;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the user has been active within the last 5 minutes.
|
|
* @return bool
|
|
*/
|
|
public function isOnline()
|
|
{
|
|
if (!$this->last_seen) {
|
|
return false;
|
|
}
|
|
|
|
return $this->last_seen > $this->freshTimestamp()->subMinutes(5);
|
|
}
|
|
|
|
/**
|
|
* Returns the date this user was last seen.
|
|
* @deprecated use last_seen attribute
|
|
* @return Carbon\Carbon
|
|
*/
|
|
public function getLastSeen()
|
|
{
|
|
return $this->last_seen ?: $this->created_at;
|
|
}
|
|
|
|
//
|
|
// Utils
|
|
//
|
|
|
|
/**
|
|
* Returns the variables available when sending a user notification.
|
|
* @return array
|
|
*/
|
|
public function getNotificationVars()
|
|
{
|
|
$vars = [
|
|
'name' => $this->name,
|
|
'email' => $this->email,
|
|
'username' => $this->username,
|
|
'login' => $this->getLogin(),
|
|
'password' => $this->getOriginalHashValue('password')
|
|
];
|
|
|
|
/*
|
|
* Extensibility
|
|
*/
|
|
$result = Event::fire('rainlab.user.getNotificationVars', [$this]);
|
|
if ($result && is_array($result)) {
|
|
$vars = call_user_func_array('array_merge', $result) + $vars;
|
|
}
|
|
|
|
return $vars;
|
|
}
|
|
|
|
/**
|
|
* sendInvitation sends an invitation to the user using template
|
|
* "rainlab.user::mail.invite"
|
|
* @return void
|
|
*/
|
|
protected function sendInvitation()
|
|
{
|
|
Mail::sendTo($this, 'rainlab.user::mail.invite', $this->getNotificationVars());
|
|
}
|
|
|
|
/**
|
|
* generatePassword assigns this user with a random password.
|
|
* @return void
|
|
*/
|
|
protected function generatePassword()
|
|
{
|
|
$this->password = $this->password_confirmation = Str::random(static::getMinPasswordLength());
|
|
}
|
|
}
|