214 lines
6.6 KiB
PHP
214 lines
6.6 KiB
PHP
<?php
|
|
namespace App\Model\Behavior;
|
|
|
|
use ArrayObject;
|
|
use Cake\Datasource\EntityInterface;
|
|
use Cake\Event\EventInterface;
|
|
use Cake\ORM\Behavior;
|
|
use Cake\ORM\Entity;
|
|
use Cake\ORM\Query;
|
|
use Cake\Utility\Text;
|
|
use Cake\Utility\Security;
|
|
use \Cake\Http\Session;
|
|
use Cake\Core\Configure;
|
|
use Cake\ORM\TableRegistry;
|
|
use App\Model\Table\AuditLogTable;
|
|
|
|
class AuditLogBehavior extends Behavior
|
|
{
|
|
/** @var array */
|
|
private $config;
|
|
|
|
/** @var array|null */
|
|
private $old;
|
|
|
|
/** @var AuditLog|null */
|
|
private $AuditLogs;
|
|
|
|
// Hash is faster that in_array
|
|
private $skipFields = [
|
|
'id' => true,
|
|
'lastpushedid' => true,
|
|
'timestamp' => true,
|
|
'revision' => true,
|
|
'modified' => true,
|
|
'date_modified' => true, // User
|
|
'current_login' => true, // User
|
|
'last_login' => true, // User
|
|
'newsread' => true, // User
|
|
'proposal_email_lock' => true, // Event
|
|
];
|
|
|
|
public function initialize(array $config): void
|
|
{
|
|
$this->config = $config;
|
|
}
|
|
|
|
public function beforeSave(EventInterface $event, EntityInterface $entity, ArrayObject $options)
|
|
{
|
|
$fields = $entity->extract($entity->getVisible(), true);
|
|
$skipFields = $this->skipFields;
|
|
$fieldsToFetch = array_filter($fields, function($key) use ($skipFields) {
|
|
return strpos($key, '_') !== 0 && !isset($skipFields[$key]);
|
|
}, ARRAY_FILTER_USE_KEY);
|
|
// Do not fetch old version when just few fields will be fetched
|
|
$fieldToFetch = [];
|
|
if (!empty($options['fieldList'])) {
|
|
foreach ($options['fieldList'] as $field) {
|
|
if (!isset($this->skipFields[$field])) {
|
|
$fieldToFetch[] = $field;
|
|
}
|
|
}
|
|
if (empty($fieldToFetch)) {
|
|
$this->old = null;
|
|
return true;
|
|
}
|
|
}
|
|
if ($entity->id) {
|
|
$this->old = $this->_table->find()->where(['id' => $entity->id])->contain($fieldToFetch)->first();
|
|
} else {
|
|
$this->old = null;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public function afterSave(EventInterface $event, EntityInterface $entity, ArrayObject $options)
|
|
{
|
|
if ($entity->id) {
|
|
$id = $entity->id;
|
|
} else {
|
|
$id = null;
|
|
}
|
|
|
|
if ($entity->isNew()) {
|
|
$action = $entity->getConstant('ACTION_ADD');
|
|
} else {
|
|
$action = $entity->getConstant('ACTION_EDIT');
|
|
if (isset($entity['deleted'])) {
|
|
if ($entity['deleted']) {
|
|
$action = $entity->getConstant('ACTION_SOFT_DELETE');
|
|
} else if (!$entity['deleted'] && $this->old['deleted']) {
|
|
$action = $entity->getConstant('ACTION_UNDELETE');
|
|
}
|
|
}
|
|
}
|
|
$changedFields = $this->changedFields($entity, isset($options['fieldList']) ? $options['fieldList'] : null);
|
|
if (empty($changedFields)) {
|
|
return;
|
|
}
|
|
|
|
$modelTitleField = $this->_table->getDisplayField();
|
|
if (is_callable($modelTitleField)) {
|
|
$modelTitle = $modelTitleField($entity, isset($this->old) ? $this->old : []);
|
|
} else if (isset($entity[$modelTitleField])) {
|
|
$modelTitle = $entity[$modelTitleField];
|
|
} else if ($this->old[$modelTitleField]) {
|
|
$modelTitle = $this->old[$modelTitleField];
|
|
}
|
|
$this->auditLogs()->insert([
|
|
'request_action' => $action,
|
|
'model' => $entity->getSource(),
|
|
'model_id' => $id,
|
|
'model_title' => $modelTitle,
|
|
'changed' => $changedFields
|
|
]);
|
|
}
|
|
|
|
public function beforeDelete(EventInterface $event, EntityInterface $entity, ArrayObject $options)
|
|
{
|
|
$this->old = $this->_table->find()->where(['id' => $entity->id])->first();
|
|
return true;
|
|
}
|
|
|
|
public function afterDelete(EventInterface $event, EntityInterface $entity, ArrayObject $options)
|
|
{
|
|
$modelTitleField = $this->_table->getDisplayField();
|
|
if (is_callable($modelTitleField)) {
|
|
$modelTitle = $modelTitleField($entity, []);
|
|
} else if (isset($entity[$modelTitleField])) {
|
|
$modelTitle = $entity[$modelTitleField];
|
|
}
|
|
|
|
$this->auditLogs()->insert([
|
|
'request_action' => $entity->getConstant('ACTION_DELETE'),
|
|
'model' => $entity->getSource(),
|
|
'model_id' => $this->old->id,
|
|
'model_title' => $modelTitle,
|
|
'changed' => $this->changedFields($entity)
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @param Model $model
|
|
* @param array|null $fieldsToSave
|
|
* @return array
|
|
*/
|
|
private function changedFields(EntityInterface $entity, $fieldsToSave = null)
|
|
{
|
|
$dbFields = $this->_table->getSchema()->typeMap();
|
|
$changedFields = [];
|
|
foreach ($entity->extract($entity->getVisible()) as $key => $value) {
|
|
if (isset($this->skipFields[$key])) {
|
|
continue;
|
|
}
|
|
if (!isset($dbFields[$key])) {
|
|
continue;
|
|
}
|
|
if ($fieldsToSave && !in_array($key, $fieldsToSave, true)) {
|
|
continue;
|
|
}
|
|
if (isset($entity[$key]) && isset($this->old[$key])) {
|
|
$old = $this->old[$key];
|
|
} else {
|
|
$old = null;
|
|
}
|
|
// Normalize
|
|
if (is_bool($old)) {
|
|
$old = $old ? 1 : 0;
|
|
}
|
|
if (is_bool($value)) {
|
|
$value = $value ? 1 : 0;
|
|
}
|
|
$dbType = $dbFields[$key];
|
|
if ($dbType === 'integer' || $dbType === 'tinyinteger' || $dbType === 'biginteger' || $dbType === 'boolean') {
|
|
$value = (int)$value;
|
|
if ($old !== null) {
|
|
$old = (int)$old;
|
|
}
|
|
}
|
|
if ($value == $old) {
|
|
continue;
|
|
}
|
|
if ($key === 'password' || $key === 'authkey') {
|
|
$value = '*****';
|
|
if ($old !== null) {
|
|
$old = $value;
|
|
}
|
|
}
|
|
|
|
if ($old === null) {
|
|
$changedFields[$key] = $value;
|
|
} else {
|
|
$changedFields[$key] = [$old, $value];
|
|
}
|
|
}
|
|
return $changedFields;
|
|
}
|
|
|
|
/**
|
|
* @return AuditLogs
|
|
*/
|
|
public function auditLogs()
|
|
{
|
|
if ($this->AuditLogs === null) {
|
|
$this->AuditLogs = TableRegistry::getTableLocator()->get('AuditLogs');
|
|
}
|
|
return $this->AuditLogs;
|
|
}
|
|
|
|
public function log()
|
|
{
|
|
|
|
}
|
|
}
|