'string', 'email' => 'string', 'url' => 'string', 'company_id' => 'integer', 'avatar' => 'string', 'screenshots_state_locked' => 'boolean', 'manual_time' => 'integer', 'computer_time_popup' => 'integer', 'blur_screenshots' => 'boolean', 'web_and_app_monitoring' => 'boolean', 'screenshots_interval' => 'integer', 'active' => 'integer', 'password' => 'string', 'timezone' => 'string', 'important' => 'integer', 'change_password' => 'int', 'user_language' => 'string', 'type' => 'string', 'invitation_sent' => 'boolean', 'nonce' => 'integer', 'client_installed' => 'integer', 'efficiency' => 'float', ]; /** * The attributes that should be mutated to dates. * * @var array */ protected $dates = [ 'created_at', 'updated_at', 'deleted_at', 'last_activity', ]; protected $hidden = [ 'password', ]; protected static function boot(): void { parent::boot(); static::addGlobalScope(new UserAccessScope); } protected $appends = [ 'online', 'can_view_team_tab', 'can_create_task', ]; public function projects(): BelongsToMany { return $this->belongsToMany(Project::class, 'projects_users', 'user_id', 'project_id') ->withPivot('role_id'); } public function projectsRelation(): HasMany { return $this->hasMany(ProjectsUsers::class, 'user_id', 'id'); } public function tasks(): BelongsToMany { return $this->belongsToMany(Task::class, 'tasks_users', 'user_id', 'task_id'); } public function timeIntervals(): HasMany { return $this->hasMany(TimeInterval::class, 'user_id'); } public function properties(): MorphMany { return $this->morphMany(Property::class, 'entity'); } public function universalReports(): HasMany { return $this->hasMany(UniversalReport::class, 'user_id'); } /** * Send the password reset notification. * * @param string $token * @return void */ public function sendPasswordResetNotification($token): void { $this->notify(new ResetPassword($this->email, $token)); } protected function online(): Attribute { return Attribute::make( get: static fn($value, $attributes) => ($attributes['last_activity'] ?? false) && Carbon::parse($attributes['last_activity'])->diffInSeconds(Carbon::now()) < config('app.user_activity.online_status_time'), ); } protected function password(): Attribute { return Attribute::make( set: static fn($value) => Hash::needsRehash($value) ? Hash::make($value) : $value, ); } protected function canViewTeamTab(): Attribute { $self = $this; return Attribute::make( get: static fn() => $self->hasRole([Role::ADMIN, Role::MANAGER, Role::AUDITOR]) || $self->hasRoleInAnyProject([Role::MANAGER, Role::AUDITOR]), ); } protected function canCreateTask(): Attribute { $self = $this; return Attribute::make( get: static fn() => $self->hasRole([Role::ADMIN, Role::MANAGER]) || $self->hasRoleInAnyProject(Role::MANAGER, Role::USER), ); } /** * Always returns correct state in case env or app settings should override it. */ protected function screenshotsState(): Attribute { return Attribute::make( get: static function (mixed $value, array $attributes): ScreenshotsState { $userState = ScreenshotsState::withGlobalOverrides($value); $showOriginalValues = Auth::user() !== null && Auth::user()->hasRole(Role::ADMIN) && (int)Auth::id() !== (int)$attributes['id']; if ($showOriginalValues) { return match ($userState) { null => ScreenshotsState::REQUIRED, ScreenshotsState::ANY => ScreenshotsState::OPTIONAL, default => $userState, }; } return match ($userState) { null, ScreenshotsState::ANY, ScreenshotsState::OPTIONAL => ScreenshotsState::REQUIRED, default => $userState, }; }, set: static fn ($value) => (string)ScreenshotsState::getNormalizedValue($value), )->shouldCache(); } public function scopeAdmin(EloquentBuilder $query): EloquentBuilder { return $query->where('role_id', '=', Role::ADMIN->value, 'or'); } public function scopeManager(EloquentBuilder $query): EloquentBuilder { return $query->where('role_id', '=', Role::MANAGER->value, 'or'); } public function scopeActive(EloquentBuilder $query): EloquentBuilder { return $query->where('active', true); } }