|
|
|
<?php
|
|
|
|
|
|
|
|
namespace App\Models;
|
|
|
|
|
|
|
|
use AdamWathan\EloquentOAuth\OAuthIdentity;
|
|
|
|
use App\Models\Concerns\InstanceCache;
|
|
|
|
use App\Models\Concerns\Reportable;
|
|
|
|
use App\Notifications\ConfirmEmail;
|
|
|
|
use Hash;
|
|
|
|
use Illuminate\Database\Eloquent\Collection;
|
|
|
|
use Illuminate\Notifications\Notifiable;
|
|
|
|
use Illuminate\Notifications\Notification;
|
|
|
|
use MightyPork\Exceptions\ArgumentException;
|
|
|
|
use MightyPork\Exceptions\NotExistException;
|
|
|
|
use Illuminate\Auth\Authenticatable;
|
|
|
|
use Illuminate\Auth\Passwords\CanResetPassword;
|
|
|
|
use Illuminate\Foundation\Auth\Access\Authorizable;
|
|
|
|
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
|
|
|
|
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
|
|
|
|
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
|
|
|
|
use MightyPork\Utils\Str;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A user in the application
|
|
|
|
*
|
|
|
|
* @property int $id
|
|
|
|
* @property \Carbon\Carbon $created_at
|
|
|
|
* @property \Carbon\Carbon $updated_at
|
|
|
|
* @property string $name - unique, for vanity URL
|
|
|
|
* @property string $title - for display
|
|
|
|
* @property string $bio - user bio
|
|
|
|
* @property string $website - custom url
|
|
|
|
* @property string $email - unique, for login and social auth chaining
|
|
|
|
* @property string $password - hashed pw
|
|
|
|
* @property bool $confirmed - user e-mail is confirmed
|
|
|
|
* @property-read Proposal[]|Collection $proposals
|
|
|
|
* @property-read Table[]|Collection $tables
|
|
|
|
* @property-read OAuthIdentity[]|Collection $socialIdentities
|
|
|
|
* @property-read TableComment[]|Collection $tableComments
|
|
|
|
* @property-read Table[]|Collection $favouriteTables
|
|
|
|
* @property-read Table[]|Collection $followedDiscussions
|
|
|
|
* @property-read EmailConfirmation[]|Collection $emailConfirmations
|
|
|
|
* @property-read ContentReport[]|Collection $authoredReports
|
|
|
|
* @property-read Notification[]|Collection $notifications
|
|
|
|
* @property-read Notification[]|Collection $readNotifications
|
|
|
|
* @property-read Notification[]|Collection $unreadNotifications
|
|
|
|
* @property-read string $handle - user handle, including @
|
|
|
|
*/
|
|
|
|
class User extends BaseModel implements
|
|
|
|
AuthenticatableContract,
|
|
|
|
AuthorizableContract,
|
|
|
|
CanResetPasswordContract
|
|
|
|
{
|
|
|
|
use Authenticatable, Authorizable, CanResetPassword;
|
|
|
|
use Reportable;
|
|
|
|
use Notifiable;
|
|
|
|
use InstanceCache;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The attributes that are mass assignable.
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
protected $fillable = [
|
|
|
|
'name', 'title', 'email', 'password', 'bio', 'website', 'confirmed'
|
|
|
|
];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The attributes that should be hidden for arrays.
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
protected $hidden = [
|
|
|
|
'password', 'remember_token',
|
|
|
|
];
|
|
|
|
|
|
|
|
protected static function boot()
|
|
|
|
{
|
|
|
|
parent::boot();
|
|
|
|
|
|
|
|
static::deleting(function(User $self) {
|
|
|
|
// comments are deleted via cascade
|
|
|
|
|
|
|
|
$self->reportsOf()->delete();
|
|
|
|
|
|
|
|
// manually delete proposals to ensure row refcounts are updated
|
|
|
|
foreach ($self->proposals as $proposal) {
|
|
|
|
$proposal->delete();
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($self->tables as $table) {
|
|
|
|
$table->delete();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Owned tables */
|
|
|
|
public function tables()
|
|
|
|
{
|
|
|
|
return $this->hasMany(Table::class, 'owner_id');
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Assigned OAuth identities */
|
|
|
|
public function socialIdentities()
|
|
|
|
{
|
|
|
|
return $this->hasMany(OAuthIdentity::class);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Authored proposals */
|
|
|
|
public function proposals()
|
|
|
|
{
|
|
|
|
return $this->hasMany(Proposal::class, 'author_id');
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Authored comments */
|
|
|
|
public function tableComments()
|
|
|
|
{
|
|
|
|
return $this->hasMany(TableComment::class, 'author_id');
|
|
|
|
}
|
|
|
|
|
|
|
|
/** User's favourite tables (personal collection) */
|
|
|
|
public function favouriteTables()
|
|
|
|
{
|
|
|
|
return $this->belongsToMany(Table::class, 'table_favourites');
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Tables whose discussions user follows (this is similar to favourites) */
|
|
|
|
public function followedDiscussions()
|
|
|
|
{
|
|
|
|
return $this->belongsToMany(Table::class, 'discussion_follows');
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Reports sent by this user */
|
|
|
|
public function authoredReports()
|
|
|
|
{
|
|
|
|
return $this->hasMany(ContentReport::class, 'author_id');
|
|
|
|
}
|
|
|
|
|
|
|
|
/** User's e-mail confirmations */
|
|
|
|
public function emailConfirmations()
|
|
|
|
{
|
|
|
|
return $this->hasMany(EmailConfirmation::class);
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------- Relation Helpers ---------
|
|
|
|
|
|
|
|
public function addFavourite(Table $table)
|
|
|
|
{
|
|
|
|
$this->favouriteTables()->attach($table);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function removeFavourite(Table $table)
|
|
|
|
{
|
|
|
|
$this->favouriteTables()->detach($table);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function followDiscussion(Table $table)
|
|
|
|
{
|
|
|
|
$this->followedDiscussions()->attach($table);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function unfollowDiscussion(Table $table)
|
|
|
|
{
|
|
|
|
$this->followedDiscussions()->detach($table);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resolve user by a given name, id, or e-mail
|
|
|
|
*
|
|
|
|
* @param string $ident
|
|
|
|
* @return User
|
|
|
|
*/
|
|
|
|
public static function resolve($ident)
|
|
|
|
{
|
|
|
|
$ident = "$ident";
|
|
|
|
if (strlen($ident) == 0) throw new ArgumentException("Can't find user by empty string.");
|
|
|
|
|
|
|
|
if (is_numeric($ident)) {
|
|
|
|
$u = static::find((int)$ident);
|
|
|
|
}
|
|
|
|
else if ($ident[0] == '@') {
|
|
|
|
$u = static::where('name', substr($ident, 1))->first();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$u = static::where('email', $ident)->first();
|
|
|
|
|
|
|
|
if (!$u) {
|
|
|
|
$u = static::where('name', $ident)->first();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$u) throw new NotExistException("No user found matching \"$ident\"");
|
|
|
|
|
|
|
|
return $u;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function __get($name)
|
|
|
|
{
|
|
|
|
if ($name == 'handle') return "@$this->name";
|
|
|
|
|
|
|
|
return parent::__get($name);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function ownsTable(Table $table)
|
|
|
|
{
|
|
|
|
return $table->owner_id == $this->id;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function favouritesTable(Table $table)
|
|
|
|
{
|
|
|
|
return \DB::table('table_favourites')
|
|
|
|
->where('user_id', $this->id)
|
|
|
|
->where('table_id', $table->id)->exists();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function sendEmailConfirmationLink($newEmail)
|
|
|
|
{
|
|
|
|
// delete old confirmation tokens
|
|
|
|
$this->emailConfirmations()->delete();
|
|
|
|
|
|
|
|
$confirmation = EmailConfirmation::create([
|
|
|
|
'user_id' => $this->id,
|
|
|
|
'email' => $newEmail,
|
|
|
|
'token' => Hash::make(Str::random(40)),
|
|
|
|
]);
|
|
|
|
|
|
|
|
$this->notify(new ConfirmEmail($newEmail, $confirmation->token));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if this user has a table with the givenname
|
|
|
|
*
|
|
|
|
* @param string $name
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function hasTable(string $name)
|
|
|
|
{
|
|
|
|
return $this->tables()->where('name', $name)->exists();
|
|
|
|
}
|
|
|
|
}
|