Files
dockerfluxodecaixapro/app/Models/ActivityLog.php
T
2026-09-01 10:46:29 -03:00

386 lines
11 KiB
PHP
Executable File

<?php
/**
* SysCMS - Content Management System
*
* Copyright (c) 2025 Marco Costa
* All rights reserved.
*
* Author: Marco Costa
* Email: contato@syspanel.com.br
* Project: SysCMS
*
* This software is licensed, not sold.
* Permission is granted to use this software
* subject to the terms of the SysCMS Commercial License.
*
* Redistribution, sublicensing, resale, or
* disclosure of this software, in whole or in part,
* is strictly prohibited without prior written permission.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
*/
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Modules\Company\Models\Company;
use App\Helpers\Log;
use Illuminate\Database\Capsule\Manager as Capsule;
class ActivityLog extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'activity_logs';
/**
* The primary key associated with table.
*
* @var string
*/
protected $primaryKey = 'activity_logs_id';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'activity_logs_user_id',
'activity_logs_bus_id',
'activity_logs_action',
'activity_logs_resource_type',
'activity_logs_resource_id',
'activity_logs_old_values',
'activity_logs_new_values',
'activity_logs_ip_address',
'activity_logs_user_agent'
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'activity_logs_old_values' => 'array',
'activity_logs_new_values' => 'array',
'created_at' => 'datetime',
'updated_at' => 'datetime'
];
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $dates = [
'created_at',
'updated_at'
];
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
/**
* Get the user that performed the activity.
*/
public function user(): BelongsTo
{
return $this->belongsTo(\Modules\Auth\Models\User::class, 'activity_logs_user_id');
}
/**
* Get the company where the activity occurred.
*/
public function company(): BelongsTo
{
return $this->belongsTo(Company::class, 'activity_logs_bus_id');
}
/**
* Scope to get logs for a specific user.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param int|null $userId
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeForUser($query, ?int $userId)
{
return $query->where('activity_logs_user_id', $userId);
}
/**
* Scope to get logs for a specific company.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param int|null $companyId
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeForCompany($query, ?int $companyId)
{
return $query->where('activity_logs_bus_id', $companyId);
}
/**
* Scope to get logs for a specific action.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $action
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeForAction($query, string $action)
{
return $query->where('activity_logs_action', $action);
}
/**
* Scope to get logs for a specific resource type.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $resourceType
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeForResourceType($query, string $resourceType)
{
return $query->where('activity_logs_resource_type', $resourceType);
}
/**
* Scope to get logs for a specific resource.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $resourceType
* @param int $resourceId
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeForResource($query, string $resourceType, int $resourceId)
{
return $query->where('activity_logs_resource_type', $resourceType)
->where('activity_logs_resource_id', $resourceId);
}
/**
* Scope to get logs within a date range.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \DateTime|string $startDate
* @param \DateTime|string $endDate
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeBetweenDates($query, $startDate, $endDate)
{
return $query->whereBetween('created_at', [$startDate, $endDate]);
}
/**
* Scope to order logs by creation date.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $direction
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeOrderByDate($query, string $direction = 'desc')
{
return $query->orderBy('created_at', $direction);
}
/**
* Log an activity.
*
* @param int|null $userId
* @param int|null $companyId
* @param string $action
* @param string $resourceType
* @param int|null $resourceId
* @param array|null $oldValues
* @param array|null $newValues
* @param string|null $ipAddress
* @param string|null $userAgent
* @return static
*/
public static function log(
?int $userId,
?int $companyId,
string $action,
string $resourceType,
?int $resourceId = null,
?array $oldValues = null,
?array $newValues = null,
?string $ipAddress = null,
?string $userAgent = null
): self {
// Also log to file for monitoring
Log::info("Activity: {$action}", [
'user_id' => $userId,
'company_id' => $companyId,
'resource' => "{$resourceType}:{$resourceId}",
'ip' => $ipAddress ?? ($_SERVER['REMOTE_ADDR'] ?? '127.0.0.1')
]);
$resolvedCompanyId = $companyId ?? ($_SESSION['current_company_id'] ?? ($_SESSION['user_idbus'] ?? null));
// Validate company id exists to satisfy FK constraints
if ($resolvedCompanyId !== null) {
$exists = Capsule::table('bus')->where('bus_id', $resolvedCompanyId)->exists();
if (!$exists) {
$resolvedCompanyId = null;
}
}
$attributes = [
'activity_logs_user_id' => $userId,
'activity_logs_bus_id' => $resolvedCompanyId,
'activity_logs_action' => $action,
'activity_logs_resource_type' => $resourceType,
'activity_logs_resource_id' => $resourceId,
'activity_logs_old_values' => $oldValues,
'activity_logs_new_values' => $newValues,
'activity_logs_ip_address' => $ipAddress ?? ($_SERVER['REMOTE_ADDR'] ?? '127.0.0.1'),
'activity_logs_user_agent' => $userAgent ?? ($_SERVER['HTTP_USER_AGENT'] ?? 'Console')
];
// If company id is invalid/null and DB enforces NOT NULL or FK, avoid DB insert
if ($resolvedCompanyId === null) {
return new self($attributes);
}
return self::create($attributes);
}
/**
* Get the action attribute.
*
* @return string
*/
public function getActionAttribute(): string
{
return $this->activity_logs_action;
}
/**
* Get the resource type attribute.
*
* @return string
*/
public function getResourceTypeAttribute(): string
{
return $this->activity_logs_resource_type;
}
/**
* Get the resource id attribute.
*
* @return int|null
*/
public function getResourceIdAttribute(): ?int
{
return $this->activity_logs_resource_id;
}
/**
* Get the user name attribute.
*
* @return string
*/
public function getUserNameAttribute(): string
{
return $this->user ? ($this->user->users_name ?: $this->user->users_email) : __('activity.unknown_user');
}
/**
* Get the action color attribute.
*
* @return string
*/
public function getActionColorAttribute(): string
{
$colors = [
'created' => 'success',
'updated' => 'primary',
'deleted' => 'danger',
'login' => 'success',
'logout' => 'warning',
'password_changed' => 'info',
'profile_updated' => 'info',
'module_enabled' => 'success',
'module_disabled' => 'secondary',
'permission_granted' => 'success',
'permission_revoked' => 'danger'
];
return $colors[$this->activity_logs_action] ?? 'secondary';
}
/**
* Get the formatted action description.
*
* @return string
*/
public function getFormattedActionAttribute(): string
{
$actions = [
'created' => __('activity.created'),
'updated' => __('activity.updated'),
'deleted' => __('activity.deleted'),
'login' => __('activity.login'),
'logout' => __('activity.logout'),
'password_changed' => __('activity.password_changed'),
'profile_updated' => __('activity.profile_updated'),
'module_enabled' => __('activity.module_enabled'),
'module_disabled' => __('activity.module_disabled'),
'permission_granted' => __('activity.permission_granted'),
'permission_revoked' => __('activity.permission_revoked')
];
return $actions[$this->activity_logs_action] ?? $this->activity_logs_action;
}
/**
* Get the formatted resource description.
*
* @return string
*/
public function getFormattedResourceAttribute(): string
{
if ($this->activity_logs_resource_type && $this->activity_logs_resource_id) {
return "{$this->activity_logs_resource_type} #{$this->activity_logs_resource_id}";
}
return $this->activity_logs_resource_type ?: __('activity.unknown_resource');
}
/**
* Get the formatted changes description.
*
* @return string
*/
public function getFormattedChangesAttribute(): string
{
if (!$this->activity_logs_old_values && !$this->activity_logs_new_values) {
return '';
}
$changes = [];
if ($this->activity_logs_old_values && $this->activity_logs_new_values) {
foreach ($this->activity_logs_old_values as $key => $oldValue) {
if (isset($this->activity_logs_new_values[$key])) {
$newValue = $this->activity_logs_new_values[$key];
$changes[] = "{$key}: '{$oldValue}' → '{$newValue}'";
}
}
}
return implode(', ', $changes);
}
}