cerebrate/src/Model/Behavior/AuditLogBehavior.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()
{
}
}