215 lines
5.6 KiB
PHP
215 lines
5.6 KiB
PHP
<?php
|
|
|
|
/**
|
|
* ============================================================
|
|
*
|
|
* @project biiproject
|
|
*
|
|
* @author Andika Debi Putra
|
|
*
|
|
* @email andikadebiputra@gmail.com
|
|
*
|
|
* @website https://biiproject.com
|
|
*
|
|
* @copyright Copyright (c) 2026 Andika Debi Putra
|
|
* @license Proprietary - All Rights Reserved
|
|
*
|
|
* @version 1.0.0
|
|
*
|
|
* @created 2026-05-01
|
|
* ============================================================
|
|
*
|
|
* Unauthorized copying, modification, distribution, or use
|
|
* of this file is strictly prohibited without prior written
|
|
* permission from the author.
|
|
* ============================================================
|
|
*/
|
|
|
|
namespace App\Models;
|
|
|
|
use App\Notifications\Auth\ResetPasswordNotification;
|
|
use App\Notifications\Auth\VerifyEmailNotification;
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
|
use Illuminate\Notifications\Notifiable;
|
|
use Laragear\WebAuthn\Contracts\WebAuthnAuthenticatable as WebAuthnContract;
|
|
use Laragear\WebAuthn\WebAuthnAuthentication;
|
|
use Laravel\Sanctum\HasApiTokens;
|
|
use Spatie\Activitylog\LogOptions;
|
|
use Spatie\Activitylog\Traits\LogsActivity;
|
|
use Spatie\MediaLibrary\HasMedia;
|
|
use Spatie\MediaLibrary\InteractsWithMedia;
|
|
use Spatie\Permission\Traits\HasRoles;
|
|
|
|
class User extends Authenticatable implements HasMedia, WebAuthnContract
|
|
{
|
|
use HasApiTokens, HasFactory, HasRoles, InteractsWithMedia, LogsActivity, Notifiable, SoftDeletes, WebAuthnAuthentication;
|
|
|
|
protected string $guard_name = 'web';
|
|
|
|
/**
|
|
* Spatie Activitylog configuration
|
|
*/
|
|
public function getActivitylogOptions(): LogOptions
|
|
{
|
|
return LogOptions::defaults()
|
|
->useLogName('user-management')
|
|
->logOnly(['name', 'username', 'email', 'phone_number', 'is_active'])
|
|
->logOnlyDirty()
|
|
->dontSubmitEmptyLogs();
|
|
}
|
|
|
|
/**
|
|
* The attributes that are mass assignable.
|
|
*/
|
|
protected $fillable = [
|
|
'name',
|
|
'username',
|
|
'email',
|
|
'phone_number',
|
|
'password',
|
|
'google_id',
|
|
'facebook_id',
|
|
'github_id',
|
|
'is_active',
|
|
'password_changed_at',
|
|
'last_session_id',
|
|
'created_by',
|
|
'updated_by',
|
|
];
|
|
|
|
/**
|
|
* Hidden fields
|
|
*/
|
|
protected $hidden = [
|
|
'password',
|
|
'remember_token',
|
|
'media', // Hide raw media relation
|
|
];
|
|
|
|
/**
|
|
* Appended attributes
|
|
*/
|
|
protected $appends = ['avatar'];
|
|
|
|
/**
|
|
* Avatar Accessor (using Media Library)
|
|
*/
|
|
public function getAvatarAttribute(): ?string
|
|
{
|
|
return $this->getFirstMediaUrl('avatar') ?: null;
|
|
}
|
|
|
|
/**
|
|
* The attributes that should be cast.
|
|
*/
|
|
protected $casts = [
|
|
'email_verified_at' => 'datetime',
|
|
'password' => 'hashed',
|
|
'is_active' => 'boolean',
|
|
'password_changed_at' => 'datetime',
|
|
];
|
|
|
|
/**
|
|
* Password History Relation
|
|
*/
|
|
public function passwordHistories()
|
|
{
|
|
return $this->hasMany(PasswordHistory::class);
|
|
}
|
|
|
|
/**
|
|
* Trusted Devices Relation
|
|
*/
|
|
public function trustedDevices()
|
|
{
|
|
return $this->hasMany(UserTrustedDevice::class);
|
|
}
|
|
|
|
/**
|
|
* ==========================================
|
|
* DISABLE RESET PASSWORD FOR SOCIAL USERS
|
|
* ==========================================
|
|
*/
|
|
public function sendPasswordResetNotification($token)
|
|
{
|
|
// ❌ OAuth user → BLOCK reset password
|
|
if ($this->google_id || $this->facebook_id || $this->github_id) {
|
|
return;
|
|
}
|
|
|
|
// ✅ User manual → normal
|
|
$this->notify(new ResetPasswordNotification($token));
|
|
}
|
|
|
|
/**
|
|
* Use our branded email verification template.
|
|
*/
|
|
public function sendEmailVerificationNotification()
|
|
{
|
|
$this->notify(new VerifyEmailNotification);
|
|
}
|
|
|
|
/**
|
|
* Helpers
|
|
*/
|
|
public function isSocialUser(): bool
|
|
{
|
|
return ! is_null($this->google_id) || ! is_null($this->facebook_id) || ! is_null($this->github_id);
|
|
}
|
|
|
|
/**
|
|
* Consents Relation
|
|
*/
|
|
public function consents()
|
|
{
|
|
return $this->hasMany(UserConsent::class);
|
|
}
|
|
|
|
/**
|
|
* Check if user has agreed to the current version of a document.
|
|
*/
|
|
public function hasAgreedToCurrentLegal(string $type): bool
|
|
{
|
|
// Settings use 'pdp' prefix for Privacy Policy, while code uses 'privacy' type
|
|
$keyPrefix = ($type === 'privacy' || $type === 'pdp') ? 'pdp' : $type;
|
|
$currentVersion = (int) get_setting("{$keyPrefix}_document_version", 1);
|
|
|
|
return $this->consents()
|
|
->where(function ($q) use ($type) {
|
|
$q->where('consent_type', $type)
|
|
->orWhere('consent_type', 'privacy')
|
|
->orWhere('consent_type', 'pdp');
|
|
})
|
|
->where('version_id', '>=', $currentVersion)
|
|
->exists();
|
|
}
|
|
|
|
/**
|
|
* Notifications this user has interacted with (including personal read/deleted status).
|
|
*/
|
|
public function broadcastNotifications()
|
|
{
|
|
return $this->belongsToMany(Notification::class, 'system_notification_user')
|
|
->withPivot('read_at', 'deleted_at')
|
|
->withTimestamps();
|
|
}
|
|
|
|
/**
|
|
* Get the user who created this record.
|
|
*/
|
|
public function creator()
|
|
{
|
|
return $this->belongsTo(User::class, 'created_by');
|
|
}
|
|
|
|
/**
|
|
* Get the user who last updated this record.
|
|
*/
|
|
public function updater()
|
|
{
|
|
return $this->belongsTo(User::class, 'updated_by');
|
|
}
|
|
}
|