mirror of
https://github.com/avecms/AVE.cms.git
synced 2026-08-01 08:45:44 +00:00
204 lines
5.9 KiB
PHP
204 lines
5.9 KiB
PHP
<?php
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------------------
|
|
| AVE.cms
|
|
|--------------------------------------------------------------------------------------
|
|
| @package AVE.cms
|
|
| @file system/App/Content/Revisions/JsonRevisionStore.php
|
|
| @author AVE.cms <support@ave-cms.ru>
|
|
| @copyright 2007-2026 (c) AVE.cms
|
|
| @link https://ave-cms.ru
|
|
| @version 3.3
|
|
*/
|
|
|
|
namespace App\Content\Revisions;
|
|
|
|
defined('BASEPATH') || die('Direct access to this location is not allowed.');
|
|
|
|
use App\Common\SystemTables;
|
|
use App\Helpers\Json;
|
|
use DB;
|
|
|
|
/**
|
|
* Общее хранилище JSON-ревизий для сущностей с числовым владельцем.
|
|
*
|
|
* Класс не знает, как получить или применить снимок. Эти бизнес-правила
|
|
* остаются в репозитории конкретной сущности.
|
|
*/
|
|
class JsonRevisionStore
|
|
{
|
|
protected $table;
|
|
protected $ownerColumn;
|
|
protected $labels;
|
|
|
|
public function __construct($table, $ownerColumn, array $labels = array())
|
|
{
|
|
$this->table = self::identifier($table);
|
|
$this->ownerColumn = self::identifier($ownerColumn);
|
|
$this->labels = $labels;
|
|
}
|
|
|
|
public function listing($ownerId, $limit = 50)
|
|
{
|
|
return DB::query(
|
|
'SELECT * FROM ' . $this->table . ' WHERE ' . $this->ownerColumn
|
|
. '=%i ORDER BY created_at DESC,id DESC LIMIT %i',
|
|
(int) $ownerId,
|
|
max(1, min(200, (int) $limit))
|
|
)->getAll() ?: array();
|
|
}
|
|
|
|
public function one($id)
|
|
{
|
|
return DB::query(
|
|
'SELECT * FROM ' . $this->table . ' WHERE id=%i LIMIT 1',
|
|
(int) $id
|
|
)->getAssoc() ?: null;
|
|
}
|
|
|
|
public function capture($ownerId, $action, array $snapshot, $authorId = 0, $comment = '', array $extra = array(), $deduplicate = true)
|
|
{
|
|
$ownerId = (int) $ownerId;
|
|
if ($ownerId <= 0 || !$snapshot) {
|
|
return 0;
|
|
}
|
|
|
|
$json = Json::encode($snapshot, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
if (!is_string($json)) {
|
|
throw new \RuntimeException('Не удалось создать JSON-снимок');
|
|
}
|
|
|
|
$hash = sha1($json);
|
|
if ($deduplicate) {
|
|
$last = DB::query(
|
|
'SELECT snapshot_hash FROM ' . $this->table . ' WHERE ' . $this->ownerColumn
|
|
. '=%i ORDER BY created_at DESC,id DESC LIMIT 1',
|
|
$ownerId
|
|
)->getValue();
|
|
if ($last && hash_equals((string) $last, $hash)) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
$data = array(
|
|
$this->ownerColumn => $ownerId,
|
|
'action' => (string) $action,
|
|
'snapshot_hash' => $hash,
|
|
'snapshot_json' => $json,
|
|
'comment' => trim((string) $comment),
|
|
'author_id' => (int) $authorId,
|
|
'author_name' => self::authorName((int) $authorId),
|
|
'created_at' => time(),
|
|
);
|
|
foreach ($extra as $column => $value) {
|
|
$column = self::identifier($column);
|
|
if (array_key_exists($column, $data)) {
|
|
throw new \InvalidArgumentException('Нельзя переопределить системное поле ревизии');
|
|
}
|
|
|
|
$data[$column] = $value;
|
|
}
|
|
|
|
DB::Insert($this->table, $data);
|
|
return (int) DB::insertId();
|
|
}
|
|
|
|
public function delete($id)
|
|
{
|
|
$row = $this->one($id);
|
|
if (!$row) {
|
|
return false;
|
|
}
|
|
|
|
DB::Delete($this->table, 'id=%i', (int) $id);
|
|
return isset($row[$this->ownerColumn]) ? (int) $row[$this->ownerColumn] : 0;
|
|
}
|
|
|
|
public function deleteFor($ownerId)
|
|
{
|
|
$ownerId = (int) $ownerId;
|
|
if ($ownerId <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
$count = (int) DB::query(
|
|
'SELECT COUNT(*) FROM ' . $this->table . ' WHERE ' . $this->ownerColumn . '=%i',
|
|
$ownerId
|
|
)->getValue();
|
|
DB::Delete($this->table, $this->ownerColumn . '=%i', $ownerId);
|
|
return $count;
|
|
}
|
|
|
|
public function formatRow(array $row, $withSnapshot = false)
|
|
{
|
|
$action = isset($row['action']) ? (string) $row['action'] : 'update';
|
|
$meta = isset($this->labels[$action]) ? $this->labels[$action] : array();
|
|
$label = isset($meta['label']) ? $meta['label'] : (isset($meta[0]) ? $meta[0] : $action);
|
|
$badge = isset($meta['badge']) ? $meta['badge'] : (isset($meta[1]) ? $meta[1] : 'badge-gray');
|
|
$created = isset($row['created_at']) ? (int) $row['created_at'] : 0;
|
|
$row['id'] = isset($row['id']) ? (int) $row['id'] : 0;
|
|
$row[$this->ownerColumn] = isset($row[$this->ownerColumn]) ? (int) $row[$this->ownerColumn] : 0;
|
|
$row['action'] = $action;
|
|
$row['action_label'] = (string) $label;
|
|
$row['badge'] = (string) $badge;
|
|
$row['author_id'] = isset($row['author_id']) ? (int) $row['author_id'] : 0;
|
|
$row['author_name'] = isset($row['author_name']) ? (string) $row['author_name'] : '';
|
|
$row['created_at'] = $created;
|
|
$row['created_label'] = $created > 0 ? date('d.m.Y H:i:s', $created) : '-';
|
|
$row['snapshot'] = $withSnapshot
|
|
? Json::toArray(isset($row['snapshot_json']) ? (string) $row['snapshot_json'] : '')
|
|
: null;
|
|
return $row;
|
|
}
|
|
|
|
public static function formatBytes($bytes)
|
|
{
|
|
$bytes = (int) $bytes;
|
|
if ($bytes < 1024) {
|
|
return $bytes . ' Б';
|
|
}
|
|
|
|
if ($bytes < 1048576) {
|
|
return round($bytes / 1024, 1) . ' KB';
|
|
}
|
|
|
|
return round($bytes / 1048576, 1) . ' MB';
|
|
}
|
|
|
|
protected static function authorName($id)
|
|
{
|
|
if ((int) $id <= 0) {
|
|
return '';
|
|
}
|
|
|
|
$row = DB::query(
|
|
'SELECT name,login,email FROM ' . SystemTables::table('users') . ' WHERE id=%i LIMIT 1',
|
|
(int) $id
|
|
)->getAssoc();
|
|
if (!$row) {
|
|
return '#' . (int) $id;
|
|
}
|
|
|
|
if (!empty($row['name'])) {
|
|
return (string) $row['name'];
|
|
}
|
|
|
|
if (!empty($row['login'])) {
|
|
return '@' . (string) $row['login'];
|
|
}
|
|
|
|
return !empty($row['email']) ? (string) $row['email'] : '#' . (int) $id;
|
|
}
|
|
|
|
protected static function identifier($value)
|
|
{
|
|
$value = (string) $value;
|
|
if ($value === '' || !preg_match('/^[a-zA-Z0-9_]+$/', $value)) {
|
|
throw new \InvalidArgumentException('Некорректный идентификатор хранилища ревизий');
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
}
|