- Installed p7h/nas-file-manager package via private VCS repo - Published config/nas-file-manager.php with super_admin middleware restriction - Added NAS env vars to .env.example - Created admin/nas-storage page with connection info panel and file browser widget - Added NAS Storage link to admin sidebar (super_admin only) - Added SuperAdminController@nasStorage method and admin.nas-storage route - Includes all accumulated branch changes: profile wall, 2FA, audit logs, settings panel, country/phone/timezone components, posts, slideshow, playlist shares, video downloads/shares, comment likes, notifications, social links, and more Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
222 lines
5.2 KiB
PHP
Executable File
222 lines
5.2 KiB
PHP
Executable File
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use App\Notifications\VerifyEmail;
|
|
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
|
use Illuminate\Notifications\Notifiable;
|
|
use Illuminate\Support\Str;
|
|
use Laravel\Sanctum\HasApiTokens;
|
|
|
|
class User extends Authenticatable implements MustVerifyEmail
|
|
{
|
|
use HasApiTokens, HasFactory, Notifiable;
|
|
|
|
protected $fillable = [
|
|
'name',
|
|
'username',
|
|
'email',
|
|
'password',
|
|
'avatar',
|
|
'role',
|
|
'bio',
|
|
'website',
|
|
'twitter',
|
|
'instagram',
|
|
'facebook',
|
|
'youtube',
|
|
'linkedin',
|
|
'tiktok',
|
|
'birthday',
|
|
'location',
|
|
'gender',
|
|
'nationality',
|
|
'phone_code',
|
|
'phone_number',
|
|
'timezone',
|
|
'whatsapp',
|
|
'google_location',
|
|
'social_phone',
|
|
'social_email',
|
|
'two_factor_secret',
|
|
'two_factor_enabled',
|
|
'banner',
|
|
];
|
|
|
|
protected $hidden = [
|
|
'password',
|
|
'remember_token',
|
|
];
|
|
|
|
protected $casts = [
|
|
'email_verified_at' => 'datetime',
|
|
'password' => 'hashed',
|
|
'two_factor_enabled' => 'boolean',
|
|
];
|
|
|
|
protected $appends = ['avatar_url', 'banner_url'];
|
|
|
|
// Auto-generate a unique slug-based username when creating a user without one
|
|
protected static function boot(): void
|
|
{
|
|
parent::boot();
|
|
static::creating(function (User $user) {
|
|
if (empty($user->username)) {
|
|
$user->username = static::generateUniqueUsername($user->name);
|
|
}
|
|
});
|
|
}
|
|
|
|
public static function generateUniqueUsername(string $name): string
|
|
{
|
|
$base = substr(Str::slug(trim($name)), 0, 18);
|
|
if ($base === '') {
|
|
$base = 'user';
|
|
}
|
|
do {
|
|
$slug = $base . '-' . Str::lower(Str::random(6));
|
|
} while (static::where('username', $slug)->exists());
|
|
|
|
return $slug;
|
|
}
|
|
|
|
// Returns the channel URL slug — the username, auto-generated and saved if missing
|
|
public function getChannelAttribute(): string
|
|
{
|
|
if (empty($this->username)) {
|
|
$this->username = static::generateUniqueUsername($this->name);
|
|
$this->saveQuietly();
|
|
}
|
|
return $this->username;
|
|
}
|
|
|
|
// Relationships
|
|
public function videos()
|
|
{
|
|
return $this->hasMany(Video::class);
|
|
}
|
|
|
|
public function likes()
|
|
{
|
|
return $this->belongsToMany(Video::class, 'video_likes')->withTimestamps();
|
|
}
|
|
|
|
public function views()
|
|
{
|
|
return $this->belongsToMany(Video::class, 'video_views')->withTimestamps();
|
|
}
|
|
|
|
public function comments()
|
|
{
|
|
return $this->hasMany(Comment::class);
|
|
}
|
|
|
|
public function playlists()
|
|
{
|
|
return $this->hasMany(Playlist::class);
|
|
}
|
|
|
|
public function posts()
|
|
{
|
|
return $this->hasMany(\App\Models\Post::class);
|
|
}
|
|
|
|
public function getAvatarUrlAttribute()
|
|
{
|
|
if ($this->avatar) {
|
|
return asset('storage/avatars/'.$this->avatar);
|
|
}
|
|
|
|
return 'https://i.pravatar.cc/150?u='.$this->id;
|
|
}
|
|
|
|
public function getBannerUrlAttribute(): ?string
|
|
{
|
|
if ($this->banner) {
|
|
return asset('storage/banners/'.$this->banner);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public function sendEmailVerificationNotification(): void
|
|
{
|
|
$this->notify(new VerifyEmail);
|
|
}
|
|
|
|
// Role helper methods
|
|
public function isSuperAdmin()
|
|
{
|
|
return $this->role === 'super_admin';
|
|
}
|
|
|
|
public function isAdmin()
|
|
{
|
|
return in_array($this->role, ['admin', 'super_admin']);
|
|
}
|
|
|
|
public function isUser()
|
|
{
|
|
return $this->role === 'user' || $this->role === null;
|
|
}
|
|
|
|
// Users who subscribe TO this channel
|
|
public function subscribers()
|
|
{
|
|
return $this->belongsToMany(
|
|
User::class,
|
|
'user_subscriptions',
|
|
'channel_id',
|
|
'subscriber_id'
|
|
)->withPivot('created_at');
|
|
}
|
|
|
|
// Channels this user subscribes to
|
|
public function subscriptions()
|
|
{
|
|
return $this->belongsToMany(
|
|
User::class,
|
|
'user_subscriptions',
|
|
'subscriber_id',
|
|
'channel_id'
|
|
)->withPivot('created_at');
|
|
}
|
|
|
|
public function isSubscribedTo(User $channel): bool
|
|
{
|
|
return $this->subscriptions()->where('channel_id', $channel->id)->exists();
|
|
}
|
|
|
|
public function getSubscriberCountAttribute(): int
|
|
{
|
|
return $this->subscribers()->count();
|
|
}
|
|
|
|
// Check if user has any social links
|
|
public function socialLinks()
|
|
{
|
|
return $this->hasMany(UserSocialLink::class)->orderBy('sort_order');
|
|
}
|
|
|
|
public function hasSocialLinks()
|
|
{
|
|
return $this->socialLinks()->exists();
|
|
}
|
|
|
|
// Get formatted website URL
|
|
public function getWebsiteUrlAttribute()
|
|
{
|
|
if (! $this->website) {
|
|
return null;
|
|
}
|
|
|
|
// Add https:// if no protocol is specified
|
|
if (! preg_match('/^https?:\/\//', $this->website)) {
|
|
return 'https://'.$this->website;
|
|
}
|
|
|
|
return $this->website;
|
|
}
|
|
}
|