'boolean', 'email_verified_at' => 'datetime', 'inactive_at' => 'datetime', 'deleted_at' => 'datetime', ]; /** * Get the admin's user(s) that can manage admin profiles. * Many-to-many. */ public function users() { return $this->belongsToMany(User::class); } /** * Get all related languages of the admin. * Many-to-many polymorphic. */ public function languages() { return $this->morphToMany(Language::class, 'languagable')->withPivot('competence'); } /** * Get all related the locations of the admin. * One-to-many polymorph. */ public function locations() { return $this->morphMany(Location::class, 'locatable'); } /** * Get all related accounts of the user. * One-to-many polymorphic. * NOTE: At the moment admins do not use accounts, but to keep methods consistent * with the other profile models, we keep this method. * Removing this method will break generic methods that will are used on all profile models! */ public function accounts() { return $this->morphMany(Account::class, 'accountable'); } /** * Get all of the admin's message settings. */ public function message_settings() { return $this->morphMany(MessageSetting::class, 'message_settingable'); } public function sendEmailVerificationNotification() { \Mail::to($this->email)->send(new \App\Mail\VerifyProfileEmailMailable($this)); } /** * Send the password reset notification. * * @param string $token * @return void */ public function sendPasswordResetNotification($token) { // Construct the full reset URL using the named route // The route 'non-user.password.reset' expects 'profileType' and 'token' parameters. // It's also good practice to include the email in the query string for the reset form. $resetUrl = route('non-user.password.reset', [ 'profileType' => 'admin', // Hardcode 'admin' for the Admin model 'token' => $token, 'email' => $this->getEmailForPasswordReset(), // Method from CanResetPassword trait ]); // Pass the fully constructed URL to your notification $this->notify(new NonUserPasswordResetNotification($resetUrl, $this)); } /** * Configure the activity log options for the User model. * * This method sets up the logging to: * - Only log changes to the 'name', 'password', and 'last_login_ip' attributes. * - Log only when these attributes have been modified (dirty). * - Prevent submission of empty logs. * - Use 'user' as the log name. * * @return \Spatie\Activitylog\LogOptions */ public function getActivitylogOptions(): LogOptions { return LogOptions::defaults() ->logOnly([ 'name', 'last_login_ip', 'inactive_at', 'deleted_at', ]) ->logOnlyDirty() // Only log attributes that have been changed ->dontSubmitEmptyLogs() ->useLogName('Admin'); } /** * Wirechat: Returns the URL for the user's cover image (avatar). * Adjust the 'avatar_url' field to your database setup. */ public function getCoverUrlAttribute(): ?string { return Storage::url($this->profile_photo_path) ?? null; } /** * Wirechat:Returns the URL for the user's profile page. * Adjust the 'profile' route as needed for your setup. */ public function getProfileUrlAttribute(): ?string { return route('profile.show_by_type_and_id', ['type' => __('admin'), 'id' => $this->id]); } /** * Wirechat: Returns the display name for the user. * Modify this to use your preferred name field. */ public function getDisplayNameAttribute(): ?string { if ($this->full_name !== $this->name) { return $this->full_name . ' (' . $this->name . ')'; } elseif ($this->name) { return $this->name; } else { return __('Admin'); } } /** * Wirechat: Search for users when creating a new chat or adding members to a group. * Customize the search logic to limit results, such as restricting to friends or eligible users only. */ public function searchChatables(string $query): ?\Illuminate\Support\Collection { $searchableFields = ['name', 'full_name']; // Search across all profile types $users = \App\Models\User::where(function ($queryBuilder) use ($searchableFields, $query) { foreach ($searchableFields as $field) { $queryBuilder->orWhere($field, 'LIKE', '%'.$query.'%'); } })->limit(6)->get(); $organizations = \App\Models\Organization::where(function ($queryBuilder) use ($searchableFields, $query) { foreach ($searchableFields as $field) { $queryBuilder->orWhere($field, 'LIKE', '%'.$query.'%'); } })->limit(6)->get(); $banks = \App\Models\Bank::where(function ($queryBuilder) use ($searchableFields, $query) { foreach ($searchableFields as $field) { $queryBuilder->orWhere($field, 'LIKE', '%'.$query.'%'); } })->limit(6)->get(); $admins = Admin::where(function ($queryBuilder) use ($searchableFields, $query) { foreach ($searchableFields as $field) { $queryBuilder->orWhere($field, 'LIKE', '%'.$query.'%'); } })->limit(6)->get(); // Combine all results into a base Collection to avoid serialization issues $results = collect() ->merge($users->all()) ->merge($organizations->all()) ->merge($banks->all()) ->merge($admins->all()); // Filter out profiles based on configuration return $results->filter(function ($profile) { // Check inactive profiles if (timebank_config('profile_inactive.messenger_hidden') && !$profile->isActive()) { return false; } // Check email unverified profiles if (timebank_config('profile_email_unverified.messenger_hidden') && !$profile->isEmailVerified()) { return false; } // Check incomplete profiles if ( timebank_config('profile_incomplete.messenger_hidden') && method_exists($profile, 'hasIncompleteProfile') && $profile->hasIncompleteProfile($profile) ) { return false; } return true; })->take(6)->values(); } /** * Wirechat: Determine if the user can create new groups. */ public function canCreateGroups(): bool { return timebank_config('profiles.bank.messenger_can_create_groups'); } }