Совместимость с новыми логами

This commit is contained in:
n1rwana 2023-08-09 23:17:14 +03:00
parent 1ef08ae1b7
commit 34d2e32f3c
No known key found for this signature in database
GPG key ID: 184A60085ABF17D8
6 changed files with 10 additions and 328 deletions

View file

@ -1,190 +0,0 @@
<?php declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use Chandler\Database\DatabaseConnection;
use openvk\Web\Models\Repositories\GeodbCities;
use openvk\Web\Models\Repositories\GeodbCountries;
use openvk\Web\Models\Repositories\GeodbEducation;
use openvk\Web\Models\Repositories\GeodbSpecializations;
use openvk\Web\Models\Repositories\Users;
use openvk\Web\Models\RowModel;
use openvk\Web\Util\DateTime;
class Log extends RowModel
{
protected $tableName = "logs";
function getId(): int
{
return (int) $this->getRecord()->id;
}
function getUser(): ?User
{
return (new Users)->get((int) $this->getRecord()->user);
}
function getObjectTable(): string
{
return $this->getRecord()->object_table;
}
function getObjectId(): int
{
return $this->getRecord()->object_id;
}
function getObject()
{
$model = $this->getRecord()->object_model;
return new $model(DatabaseConnection::i()->getContext()->table($this->getObjectTable())->get($this->getObjectId()));
}
function getTypeRaw(): int
{
return $this->getRecord()->type;
}
function getType(): string
{
return ["добавил", "отредактировал", "удалил", "восстановил"][$this->getTypeRaw()];
}
function getTypeNom(): string
{
return ["Создание", "Редактирование", "Удаление", "Восстановление"][$this->getTypeRaw()];
}
function getObjectType(): string
{
return [
"albums" => "Альбом",
"groups" => "Сообщество",
"profiles" => "Профиль",
"comments" => "Комментарий",
"ip" => "IP-адрес",
"posts" => "Запись",
"tickets" => "Вопрос",
"tickets_comments" => "Комментарий к тикету",
][$this->getRecord()->object_table] ?? $this->getRecord()->object_model;
}
function getObjectName(): string
{
$object = $this->getObject();
if (method_exists($object, 'getCanonicalName'))
return $object->getCanonicalName();
else return "[#" . $this->getObjectId() . "] " . $this->getObjectType();
}
function getLogsText(): string
{
return $this->getRecord()->logs_text;
}
function getObjectURL(): string
{
$object = $this->getObject();
if (method_exists($object, "getURL") && $this->getObjectTable() !== "videos")
return $this->getObject()->getURL();
else
return "#";
}
function getObjectAvatar(): ?string
{
$object = $this->getObject();
if (method_exists($object, 'getAvatarURL'))
return $object->getAvatarURL("normal");
else return NULL;
}
function getOldValue(): ?array
{
return (array) json_decode($this->getRecord()->xdiff_old, true, JSON_UNESCAPED_UNICODE) ?? null;
}
function getNewValue(): ?array
{
return (array) json_decode($this->getRecord()->xdiff_new, true, JSON_UNESCAPED_UNICODE) ?? null;
}
function getTime(): DateTime
{
return new DateTime($this->getRecord()->ts);
}
function diff($old, $new): array
{
$matrix = array();
$maxlen = 0;
foreach ($old as $oindex => $ovalue) {
$nkeys = array_keys($new, $ovalue);
foreach ($nkeys as $nindex) {
$matrix[$oindex][$nindex] = isset($matrix[$oindex - 1][$nindex - 1]) ?
$matrix[$oindex - 1][$nindex - 1] + 1 : 1;
if ($matrix[$oindex][$nindex] > $maxlen) {
$maxlen = $matrix[$oindex][$nindex];
$omax = $oindex + 1 - $maxlen;
$nmax = $nindex + 1 - $maxlen;
}
}
}
if ($maxlen == 0) return array(array('d' => $old, 'i' => $new));
return array_merge(
$this->diff(array_slice($old, 0, $omax), array_slice($new, 0, $nmax)),
array_slice($new, $nmax, $maxlen),
$this->diff(array_slice($old, $omax + $maxlen), array_slice($new, $nmax + $maxlen)));
}
function htmlDiff($old, $new): string
{
$ret = '';
$diff = $this->diff(preg_split("/[\s]+/", $old), preg_split("/[\s]+/", $new));
foreach ($diff as $k) {
if (is_array($k))
$ret .= (!empty($k['d']) ? "<del>" . implode(' ', $k['d']) . "</del> " : '') .
(!empty($k['i']) ? "<ins>" . implode(' ', $k['i']) . "</ins> " : '');
else $ret .= $k . ' ';
}
return $ret;
}
function getChanges(): array
{
$result = $this->getOldValue();
$_changes = [];
if ($this->getTypeRaw() === 1) { // edit
$changes = $this->getNewValue();
foreach ($changes as $field => $value) {
$new_value = xdiff_string_patch((string) $result[$field], (string) $value);
$_changes[$field] = [
"field" => $field,
"old_value" => $result[$field],
"new_value" => strlen($new_value) > 0 ? $new_value : "(empty)",
"ts" => $this->getTime(),
"diff" => $this->htmlDiff((string) $result[$field], (string) $new_value)
];
}
} else if ($this->getTypeRaw() === 0) { // create
foreach ($result as $field => $value) {
$_changes[$field] = [
"field" => $field,
"old_value" => $value,
"ts" => $this->getTime()
];
}
} else if ($this->getTypeRaw() === 2) { // delete
$_changes[] = [
"field" => "deleted",
"old_value" => 0,
"new_value" => 1,
"ts" => $this->getTime(),
"diff" => $this->htmlDiff("0", "1")
];
}
return $_changes;
}
}

View file

@ -1,96 +0,0 @@
<?php declare(strict_types=1);
namespace openvk\Web\Models\Repositories;
use Chandler\Database\DatabaseConnection;
use Nette\Database\Table\ActiveRow;
use openvk\Web\Models\Entities\Country;
use openvk\Web\Models\Entities\Editor;
use openvk\Web\Models\Entities\Log;
use openvk\Web\Models\Entities\User;
class Logs
{
private $context;
private $logs;
function __construct()
{
$this->context = DatabaseConnection::i()->getContext();
$this->logs = $this->context->table("logs");
}
private function toLog(?ActiveRow $ar): ?Log
{
return is_null($ar) ? NULL : new Log($ar);
}
function get(int $id): ?Log
{
return $this->toLog($this->logs->get($id));
}
function create(int $user, string $table, string $model, int $type, $object, $changes, ?string $ip = NULL, ?string $useragent = NULL): void
{
if (OPENVK_ROOT_CONF["openvk"]["preferences"]["logs"] === true) {
$fobject = (is_array($object) ? $object : $object->unwrap());
$nobject = [];
$_changes = [];
if ($type === 1) {
foreach ($changes as $field => $value) {
$nobject[$field] = $fobject[$field];
}
foreach (array_diff_assoc($nobject, $changes) as $field => $value) {
if (str_starts_with($field, "rate_limit")) continue;
if ($field === "online") continue;
$_changes[$field] = xdiff_string_diff((string)$nobject[$field], (string)$changes[$field]);
}
if (count($_changes) === 0) return;
} else if ($type === 0) { // if new
$nobject = $fobject;
foreach ($fobject as $field => $value) {
$_changes[$field] = xdiff_string_diff("", (string)$value);
}
} else if ($type === 2 || $type === 3) { // if deleting or restoring
$_changes["deleted"] = (int)($type === 2);
}
$log = new Log;
$log->setUser($user);
$log->setType($type);
$log->setObject_Table($table);
$log->setObject_Model($model);
$log->setObject_Id(is_array($object) ? $object["id"] : $object->getId());
$log->setXdiff_Old(json_encode($nobject));
$log->setXdiff_New(json_encode($_changes));
$log->setTs(time());
$log->setIp($ip ?? CurrentUser::i()->getIP());
$log->setUserAgent($useragent ?? CurrentUser::i()->getUserAgent());
$log->save();
}
}
function find(string $query, array $pars = [], string $sort = "id DESC", int $page = 1, ?int $perPage = NULL): \Traversable
{
$query = "%$query%";
$result = $this->logs->where("id LIKE ? OR object_table LIKE ?", $query, $query);
return new Util\EntityStream("Log", $result->order($sort));
}
function search($filter): \Traversable
{
foreach ($this->logs->where($filter)->order("id DESC") as $log)
yield new Log($log);
}
function getTypes(): array
{
$types = [];
foreach ($this->context->query("SELECT DISTINCT(`object_model`) AS `object_model` FROM `logs`")->fetchAll() as $type)
$types[] = str_replace("openvk\\Web\\Models\\Entities\\", "", $type->object_model);
return $types;
}
}

View file

@ -1,7 +1,9 @@
<?php declare(strict_types=1);
namespace openvk\Web\Presenters;
use Chandler\Database\Log;
use Chandler\Database\Logs;
use openvk\Web\Models\Entities\{Voucher, Gift, GiftCategory, User, BannedLink};
use openvk\Web\Models\Repositories\{ChandlerGroups, ChandlerUsers, Logs, Users, Clubs, Vouchers, Gifts, BannedLinks};
use openvk\Web\Models\Repositories\{Bans, ChandlerGroups, ChandlerUsers, Photos, Posts, Users, Clubs, Videos, Vouchers, Gifts, BannedLinks};
use Chandler\Database\DatabaseConnection;
final class AdminPresenter extends OpenVKPresenter
@ -14,7 +16,7 @@ final class AdminPresenter extends OpenVKPresenter
private $chandlerGroups;
private $logs;
function __construct(Users $users, Clubs $clubs, Vouchers $vouchers, Gifts $gifts, BannedLinks $bannedLinks, ChandlerGroups $chandlerGroups, Logs $logs)
function __construct(Users $users, Clubs $clubs, Vouchers $vouchers, Gifts $gifts, BannedLinks $bannedLinks, ChandlerGroups $chandlerGroups)
{
$this->users = $users;
$this->clubs = $clubs;
@ -22,7 +24,7 @@ final class AdminPresenter extends OpenVKPresenter
$this->gifts = $gifts;
$this->bannedLinks = $bannedLinks;
$this->chandlerGroups = $chandlerGroups;
$this->logs = $logs;
$this->logs = DatabaseConnection::i()->getContext()->table("ChandlerLogs");
parent::__construct();
}
@ -568,7 +570,7 @@ final class AdminPresenter extends OpenVKPresenter
$this->template->type = $type;
}
if ($this->queryParam("uid")) {
$user = (int) $this->queryParam("uid");
$user = $this->queryParam("uid");
$filter["user"] = $user;
$this->template->user = $user;
}
@ -578,17 +580,12 @@ final class AdminPresenter extends OpenVKPresenter
$this->template->obj_id = $obj_id;
}
if ($this->queryParam("obj_type") !== NULL && $this->queryParam("obj_type") !== "any") {
$obj_type = "openvk\\Web\\Models\\Entities\\" . $this->queryParam("obj_type");
$obj_type = CHANDLER_ROOT_CONF["preferences"]["logs"]["entitiesNamespace"] . $this->queryParam("obj_type");
$filter["object_model"] = $obj_type;
$this->template->obj_type = $obj_type;
}
if (count($filter) === 0) {
$this->template->logs = $this->searchResults($this->logs, $this->template->count);
} else {
$this->template->logs = $this->logs->search($filter);
}
$this->template->logs = (new Logs)->search($filter);
$this->template->object_types = (new Logs)->getTypes();
}
}

View file

@ -9,7 +9,6 @@
{/block}
{block content}
{var $logs = iterator_to_array($logs)}
{var $amount = sizeof($logs)}
<style>
@ -27,7 +26,7 @@
<option value="3" n:attr="selected => $type === 3">Восстановление</option>
</select>
<input class="text medium-field" type="number" id="id" name="id" placeholder="ID записи" n:attr="value => $id"/>
<input class="text medium-field" type="number" id="uid" name="uid" placeholder="ID пользователя" n:attr="value => $user"/>
<input class="text medium-field" type="text" id="uid" name="uid" placeholder="UUID пользователя" n:attr="value => $user"/>
</div>
<div style="margin: 8px 0;" />
<div>
@ -51,16 +50,10 @@
</tr>
</thead>
<tbody>
<tr n:foreach="$logs as $log">
<td>{$log->getId()}</td>
<td>
<span n:if="$log->getUser()->getAvatarURL()" class="aui-avatar aui-avatar-xsmall">
<span class="aui-avatar-inner">
<img src="{$log->getUser()->getAvatarURL()}" alt="{$log->getUser()->getCanonicalName()}" style="object-fit: cover;" role="presentation" />
</span>
</span>
<a href="{$log->getUser()->getURL()}">{$log->getUser()->getCanonicalName()}</a>
<a href="/admin/chandler/user/{$log->getUser()}" target="_blank">{$log->getUser()}</a>
</td>
<td>
<span n:if="$log->getObjectAvatar()" class="aui-avatar aui-avatar-xsmall">

View file

@ -49,4 +49,3 @@ services:
- openvk\Web\Models\Repositories\BannedLinks
- openvk\Web\Models\Repositories\ChandlerGroups
- openvk\Web\Presenters\MaintenancePresenter
- openvk\Web\Models\Repositories\Logs

View file

@ -1,21 +0,0 @@
CREATE TABLE `logs`
(
`id` bigint(20) UNSIGNED NOT NULL,
`user` bigint(20) UNSIGNED NOT NULL,
`type` int(11) NOT NULL,
`object_table` tinytext COLLATE utf8mb4_unicode_ci NOT NULL,
`object_model` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
`object_id` bigint(20) UNSIGNED NOT NULL,
`xdiff_old` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`xdiff_new` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`ts` bigint(20) NOT NULL,
`ip` tinytext NOT NULL,
`useragent` longtext NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
ALTER TABLE `logs`
ADD PRIMARY KEY (`id`);
ALTER TABLE `logs`
MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
COMMIT;