mirror of
https://github.com/avecms/AVE.cms.git
synced 2026-08-01 08:45:44 +00:00
526 lines
15 KiB
PHP
526 lines
15 KiB
PHP
<?php
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------------------
|
|
| AVE.cms
|
|
|--------------------------------------------------------------------------------------
|
|
| @package AVE.cms
|
|
| @file adminx/modules/Blocks/Model.php
|
|
| @author AVE.cms <support@ave-cms.ru>
|
|
| @copyright 2007-2026 (c) AVE.cms
|
|
| @link https://ave-cms.ru
|
|
| @version 3.3
|
|
*/
|
|
|
|
namespace App\Adminx\Blocks;
|
|
|
|
defined('BASEPATH') || die('Direct access to this location is not allowed.');
|
|
|
|
use DB;
|
|
use App\Common\FileCacheInvalidator;
|
|
use App\Common\SystemTables;
|
|
use App\Content\ContentTables;
|
|
|
|
class Model
|
|
{
|
|
public static function table()
|
|
{
|
|
return ContentTables::table('sysblocks');
|
|
}
|
|
|
|
public static function groupsTable()
|
|
{
|
|
return ContentTables::table('sysblocks_groups');
|
|
}
|
|
|
|
public static function all($q = '', $groupId = 0, $state = '')
|
|
{
|
|
$sql = 'SELECT b.*, g.title AS group_title, u.name AS author_name, u.login AS author_login, u.email AS author_email'
|
|
. ' FROM ' . self::table() . ' b'
|
|
. ' LEFT JOIN ' . self::groupsTable() . ' g ON g.id = b.sysblock_group_id'
|
|
. ' LEFT JOIN ' . SystemTables::table('users') . ' u ON u.id = b.sysblock_author_id'
|
|
. ' WHERE 1=1';
|
|
$args = array();
|
|
|
|
$q = trim((string) $q);
|
|
if ($q !== '') {
|
|
$sql .= ' AND (b.sysblock_name LIKE %ss OR b.sysblock_alias LIKE %ss OR b.sysblock_description LIKE %ss OR b.sysblock_text LIKE %ss)';
|
|
$args[] = $q;
|
|
$args[] = $q;
|
|
$args[] = $q;
|
|
$args[] = $q;
|
|
}
|
|
|
|
$groupId = (int) $groupId;
|
|
if ($groupId > 0) {
|
|
$sql .= ' AND b.sysblock_group_id = %i';
|
|
$args[] = $groupId;
|
|
}
|
|
|
|
if ($state === 'active') {
|
|
$sql .= ' AND b.sysblock_active = %s';
|
|
$args[] = '1';
|
|
} elseif ($state === 'inactive') {
|
|
$sql .= ' AND b.sysblock_active = %s';
|
|
$args[] = '0';
|
|
} elseif ($state === 'external') {
|
|
$sql .= ' AND b.sysblock_external = %s';
|
|
$args[] = '1';
|
|
} elseif ($state === 'ajax') {
|
|
$sql .= ' AND b.sysblock_ajax = %s';
|
|
$args[] = '1';
|
|
} elseif ($state === 'visual') {
|
|
$sql .= ' AND b.sysblock_visual = %s';
|
|
$args[] = '1';
|
|
}
|
|
|
|
$sql .= ' ORDER BY COALESCE(g.position, 9999) ASC, b.id ASC';
|
|
$rows = call_user_func_array(array('DB', 'query'), array_merge(array($sql), $args))->getAll();
|
|
$out = array();
|
|
foreach ($rows as $row) {
|
|
$out[] = self::row($row);
|
|
}
|
|
|
|
return $out;
|
|
}
|
|
|
|
public static function one($id)
|
|
{
|
|
$row = DB::query('SELECT * FROM ' . self::table() . ' WHERE id = %i LIMIT 1', (int) $id)->getAssoc();
|
|
return $row ? self::row($row) : null;
|
|
}
|
|
|
|
public static function raw($id)
|
|
{
|
|
$row = DB::query('SELECT * FROM ' . self::table() . ' WHERE id = %i LIMIT 1', (int) $id)->getAssoc();
|
|
return $row ? $row : null;
|
|
}
|
|
|
|
public static function revisionFields()
|
|
{
|
|
return array(
|
|
'id',
|
|
'sysblock_group_id',
|
|
'sysblock_name',
|
|
'sysblock_description',
|
|
'sysblock_alias',
|
|
'sysblock_text',
|
|
'sysblock_active',
|
|
'sysblock_eval',
|
|
'sysblock_external',
|
|
'sysblock_ajax',
|
|
'sysblock_visual',
|
|
'sysblock_editor',
|
|
'sysblock_author_id',
|
|
'sysblock_created',
|
|
);
|
|
}
|
|
|
|
public static function snapshot(array $row)
|
|
{
|
|
$out = array();
|
|
foreach (self::revisionFields() as $field) {
|
|
$out[$field] = isset($row[$field]) ? $row[$field] : '';
|
|
}
|
|
|
|
$out['id'] = (int) $out['id'];
|
|
$out['sysblock_group_id'] = (int) $out['sysblock_group_id'];
|
|
$out['sysblock_author_id'] = (int) $out['sysblock_author_id'];
|
|
$out['sysblock_created'] = (int) $out['sysblock_created'];
|
|
if ($out['sysblock_editor'] === '') {
|
|
$out['sysblock_editor'] = self::legacyEditorMode($out);
|
|
}
|
|
|
|
return $out;
|
|
}
|
|
|
|
public static function applySnapshot($id, array $snapshot, $authorId)
|
|
{
|
|
$id = (int) $id;
|
|
$current = self::raw($id);
|
|
if (!$current) {
|
|
throw new \RuntimeException('Системный блок не найден');
|
|
}
|
|
|
|
$data = self::snapshot($snapshot);
|
|
unset($data['id']);
|
|
$data['sysblock_author_id'] = (int) $authorId > 0 ? (int) $authorId : (int) $data['sysblock_author_id'];
|
|
DB::Update(self::table(), $data, 'id = %i', $id);
|
|
self::clearCache($id, isset($current['sysblock_alias']) ? $current['sysblock_alias'] : '');
|
|
self::clearCache($id, isset($data['sysblock_alias']) ? $data['sysblock_alias'] : '');
|
|
return $id;
|
|
}
|
|
|
|
public static function groups()
|
|
{
|
|
$rows = DB::query(
|
|
'SELECT g.*, COUNT(b.id) AS blocks_count'
|
|
. ' FROM ' . self::groupsTable() . ' g'
|
|
. ' LEFT JOIN ' . self::table() . ' b ON b.sysblock_group_id = g.id'
|
|
. ' GROUP BY g.id, g.position, g.title, g.description'
|
|
. ' ORDER BY g.position ASC, g.id ASC'
|
|
)->getAll();
|
|
$out = array();
|
|
foreach ($rows as $row) {
|
|
$out[] = array(
|
|
'id' => (int) $row['id'],
|
|
'position' => (int) $row['position'],
|
|
'title' => (string) $row['title'],
|
|
'description' => (string) $row['description'],
|
|
'blocks_count' => (int) $row['blocks_count'],
|
|
);
|
|
}
|
|
|
|
return $out;
|
|
}
|
|
|
|
public static function groupMap()
|
|
{
|
|
$out = array();
|
|
foreach (self::groups() as $group) {
|
|
$out[$group['id']] = $group;
|
|
}
|
|
|
|
return $out;
|
|
}
|
|
|
|
public static function groupedBlocks(array $blocks, array $groups)
|
|
{
|
|
$out = array();
|
|
foreach ($groups as $group) {
|
|
$out[(int) $group['id']] = array(
|
|
'id' => (int) $group['id'],
|
|
'title' => (string) $group['title'],
|
|
'description' => (string) $group['description'],
|
|
'position' => (int) $group['position'],
|
|
'items' => array(),
|
|
);
|
|
}
|
|
|
|
$out[0] = array(
|
|
'id' => 0,
|
|
'title' => 'Без группы',
|
|
'description' => 'Блоки без привязки к группе.',
|
|
'position' => 99999,
|
|
'items' => array(),
|
|
);
|
|
|
|
foreach ($blocks as $block) {
|
|
$id = isset($out[(int) $block['sysblock_group_id']]) ? (int) $block['sysblock_group_id'] : 0;
|
|
$out[$id]['items'][] = $block;
|
|
}
|
|
|
|
$out = array_filter($out, function ($group) {
|
|
return !empty($group['items']);
|
|
});
|
|
|
|
uasort($out, function ($a, $b) {
|
|
if ((int) $a['position'] === (int) $b['position']) {
|
|
return strcmp((string) $a['title'], (string) $b['title']);
|
|
}
|
|
|
|
return (int) $a['position'] < (int) $b['position'] ? -1 : 1;
|
|
});
|
|
|
|
return array_values($out);
|
|
}
|
|
|
|
public static function group($id)
|
|
{
|
|
$row = DB::query('SELECT * FROM ' . self::groupsTable() . ' WHERE id = %i LIMIT 1', (int) $id)->getAssoc();
|
|
if (!$row) {
|
|
return null;
|
|
}
|
|
|
|
return array(
|
|
'id' => (int) $row['id'],
|
|
'position' => (int) $row['position'],
|
|
'title' => (string) $row['title'],
|
|
'description' => (string) $row['description'],
|
|
);
|
|
}
|
|
|
|
public static function stats()
|
|
{
|
|
return array(
|
|
'total' => (int) DB::query('SELECT COUNT(*) FROM ' . self::table())->getValue(),
|
|
'active' => (int) DB::query('SELECT COUNT(*) FROM ' . self::table() . ' WHERE sysblock_active = %s', '1')->getValue(),
|
|
'external' => (int) DB::query('SELECT COUNT(*) FROM ' . self::table() . ' WHERE sysblock_external = %s', '1')->getValue(),
|
|
'ajax' => (int) DB::query('SELECT COUNT(*) FROM ' . self::table() . ' WHERE sysblock_ajax = %s', '1')->getValue(),
|
|
'groups' => (int) DB::query('SELECT COUNT(*) FROM ' . self::groupsTable())->getValue(),
|
|
);
|
|
}
|
|
|
|
public static function aliasExists($alias, $excludeId = 0)
|
|
{
|
|
return (bool) DB::query(
|
|
'SELECT id FROM ' . self::table() . ' WHERE sysblock_alias = %s AND id != %i LIMIT 1',
|
|
(string) $alias,
|
|
(int) $excludeId
|
|
)->getValue();
|
|
}
|
|
|
|
public static function save($id, array $input, $authorId)
|
|
{
|
|
$data = self::input($input);
|
|
if ((int) $id > 0) {
|
|
DB::Update(self::table(), $data, 'id = %i', (int) $id);
|
|
self::clearCache((int) $id, $data['sysblock_alias']);
|
|
Revisions::capture((int) $id, 'update', (int) $authorId, 'Сохранение блока');
|
|
return (int) $id;
|
|
}
|
|
|
|
$data['sysblock_author_id'] = (int) $authorId > 0 ? (int) $authorId : 1;
|
|
$data['sysblock_created'] = time();
|
|
DB::Insert(self::table(), $data);
|
|
$newId = (int) DB::insertId();
|
|
Revisions::capture($newId, 'create', (int) $authorId, 'Создание блока');
|
|
return $newId;
|
|
}
|
|
|
|
public static function delete($id, $authorId = 0)
|
|
{
|
|
$raw = self::raw($id);
|
|
if (!$raw) {
|
|
return false;
|
|
}
|
|
|
|
Revisions::capture((int) $id, 'delete', (int) $authorId, 'Удаление блока', $raw);
|
|
$row = self::row($raw);
|
|
if (!$row) {
|
|
return false;
|
|
}
|
|
|
|
DB::Delete(self::table(), 'id = %i', (int) $id);
|
|
self::clearCache((int) $id, $row['sysblock_alias']);
|
|
return true;
|
|
}
|
|
|
|
public static function copy($id, $name, $authorId)
|
|
{
|
|
$row = self::one($id);
|
|
if (!$row) {
|
|
throw new \RuntimeException('Системный блок не найден');
|
|
}
|
|
|
|
$name = trim((string) $name);
|
|
if ($name === '') {
|
|
$name = $row['sysblock_name'] . ' (копия)';
|
|
}
|
|
|
|
$alias = self::uniqueAlias($row['sysblock_alias'] . '-copy');
|
|
$data = array(
|
|
'sysblock_group_id' => (int) $row['sysblock_group_id'],
|
|
'sysblock_name' => $name,
|
|
'sysblock_description' => (string) $row['sysblock_description'],
|
|
'sysblock_alias' => $alias,
|
|
'sysblock_text' => (string) $row['sysblock_text'],
|
|
'sysblock_active' => '0',
|
|
'sysblock_eval' => (string) $row['sysblock_eval'],
|
|
'sysblock_external' => (string) $row['sysblock_external'],
|
|
'sysblock_ajax' => (string) $row['sysblock_ajax'],
|
|
'sysblock_visual' => (string) $row['sysblock_visual'],
|
|
'sysblock_editor' => isset($row['sysblock_editor']) ? (string) $row['sysblock_editor'] : self::legacyEditorMode($row),
|
|
'sysblock_author_id' => (int) $authorId > 0 ? (int) $authorId : 1,
|
|
'sysblock_created' => time(),
|
|
);
|
|
DB::Insert(self::table(), $data);
|
|
$newId = (int) DB::insertId();
|
|
Revisions::capture($newId, 'copy', (int) $authorId, 'Копия блока #' . (int) $id);
|
|
return $newId;
|
|
}
|
|
|
|
public static function saveGroup($id, array $input)
|
|
{
|
|
$title = trim(isset($input['title']) ? (string) $input['title'] : '');
|
|
if ($title === '') {
|
|
throw new \RuntimeException('Укажите название группы');
|
|
}
|
|
|
|
$data = array(
|
|
'title' => $title,
|
|
'description' => trim(isset($input['description']) ? (string) $input['description'] : ''),
|
|
);
|
|
if ((int) $id <= 0) {
|
|
$data['position'] = (int) DB::query('SELECT COALESCE(MAX(position), 0) + 1 FROM ' . self::groupsTable())->getValue();
|
|
}
|
|
|
|
if ((int) $id > 0) {
|
|
DB::Update(self::groupsTable(), $data, 'id = %i', (int) $id);
|
|
return (int) $id;
|
|
}
|
|
|
|
DB::Insert(self::groupsTable(), $data);
|
|
return (int) DB::insertId();
|
|
}
|
|
|
|
public static function reorderGroups(array $ids)
|
|
{
|
|
$clean = array();
|
|
foreach ($ids as $id) {
|
|
$id = (int) $id;
|
|
if ($id > 0 && !in_array($id, $clean, true)) {
|
|
$clean[] = $id;
|
|
}
|
|
}
|
|
|
|
if (empty($clean)) {
|
|
throw new \RuntimeException('Не передан порядок групп');
|
|
}
|
|
|
|
$position = 1;
|
|
foreach ($clean as $id) {
|
|
DB::Update(self::groupsTable(), array('position' => $position), 'id = %i', $id);
|
|
$position++;
|
|
}
|
|
|
|
return count($clean);
|
|
}
|
|
|
|
public static function deleteGroup($id)
|
|
{
|
|
$id = (int) $id;
|
|
if ($id <= 0) {
|
|
return false;
|
|
}
|
|
|
|
DB::Update(self::table(), array('sysblock_group_id' => 0), 'sysblock_group_id = %i', $id);
|
|
DB::Delete(self::groupsTable(), 'id = %i', $id);
|
|
return true;
|
|
}
|
|
|
|
public static function clearCache($id, $alias = '')
|
|
{
|
|
FileCacheInvalidator::sysblock($id, $alias);
|
|
}
|
|
|
|
protected static function input(array $input)
|
|
{
|
|
$editor = isset($input['sysblock_editor'])
|
|
? self::editorMode($input['sysblock_editor'])
|
|
: self::legacyEditorMode($input);
|
|
|
|
return array(
|
|
'sysblock_group_id' => (int) (isset($input['sysblock_group_id']) ? $input['sysblock_group_id'] : 0),
|
|
'sysblock_name' => trim(isset($input['sysblock_name']) ? (string) $input['sysblock_name'] : ''),
|
|
'sysblock_description' => trim(isset($input['sysblock_description']) ? (string) $input['sysblock_description'] : ''),
|
|
'sysblock_alias' => trim(isset($input['sysblock_alias']) ? (string) $input['sysblock_alias'] : ''),
|
|
'sysblock_text' => isset($input['sysblock_text']) ? (string) $input['sysblock_text'] : '',
|
|
'sysblock_active' => !empty($input['sysblock_active']) ? '1' : '0',
|
|
'sysblock_eval' => $editor === 'php' ? '1' : '0',
|
|
'sysblock_external' => !empty($input['sysblock_external']) ? '1' : '0',
|
|
'sysblock_ajax' => !empty($input['sysblock_ajax']) ? '1' : '0',
|
|
'sysblock_visual' => $editor === 'rich' ? '1' : '0',
|
|
'sysblock_editor' => $editor,
|
|
);
|
|
}
|
|
|
|
protected static function row(array $row)
|
|
{
|
|
$author = '';
|
|
if (!empty($row['author_name'])) {
|
|
$author = (string) $row['author_name'];
|
|
} elseif (!empty($row['author_login'])) {
|
|
$author = '@' . (string) $row['author_login'];
|
|
} elseif (!empty($row['author_email'])) {
|
|
$author = (string) $row['author_email'];
|
|
}
|
|
|
|
$row['id'] = (int) $row['id'];
|
|
$row['sysblock_group_id'] = (int) $row['sysblock_group_id'];
|
|
$row['sysblock_author_id'] = (int) $row['sysblock_author_id'];
|
|
$row['sysblock_created'] = (int) $row['sysblock_created'];
|
|
$row['author'] = $author !== '' ? $author : ('#' . $row['sysblock_author_id']);
|
|
$row['group_title'] = isset($row['group_title']) && $row['group_title'] !== null ? $row['group_title'] : 'Без группы';
|
|
$row['created_label'] = $row['sysblock_created'] > 0 ? date('d.m.Y H:i', $row['sysblock_created']) : '-';
|
|
$row['text_size'] = strlen((string) $row['sysblock_text']);
|
|
$row['text_size_label'] = self::formatBytes($row['text_size']);
|
|
$row['sysblock_editor'] = isset($row['sysblock_editor']) && $row['sysblock_editor'] !== ''
|
|
? self::editorMode($row['sysblock_editor'])
|
|
: self::legacyEditorMode($row);
|
|
return $row;
|
|
}
|
|
|
|
protected static function editorMode($mode)
|
|
{
|
|
$mode = strtolower(trim((string) $mode));
|
|
return in_array($mode, array('php', 'html', 'rich', 'text'), true) ? $mode : 'html';
|
|
}
|
|
|
|
protected static function legacyEditorMode(array $row)
|
|
{
|
|
if (!empty($row['sysblock_eval'])) {
|
|
return 'php';
|
|
}
|
|
|
|
return !empty($row['sysblock_visual']) ? 'rich' : 'html';
|
|
}
|
|
|
|
protected static function uniqueAlias($base)
|
|
{
|
|
$base = preg_replace('/[^A-Za-z0-9_-]+/', '-', (string) $base);
|
|
$base = trim($base, '-_');
|
|
if ($base === '' || is_numeric($base)) {
|
|
$base = 'sysblock';
|
|
}
|
|
|
|
$base = substr($base, 0, 20);
|
|
$alias = $base;
|
|
$i = 2;
|
|
while (self::aliasExists($alias, 0)) {
|
|
$suffix = '-' . $i;
|
|
$alias = substr($base, 0, 20 - strlen($suffix)) . $suffix;
|
|
$i++;
|
|
}
|
|
|
|
return $alias;
|
|
}
|
|
|
|
protected 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 removeDir($dir)
|
|
{
|
|
if (!is_dir($dir)) {
|
|
return;
|
|
}
|
|
|
|
$base = realpath(BASEPATH . '/tmp/cache/sql/sysblocks');
|
|
$real = realpath($dir);
|
|
if (!$base || !$real || strpos($real, $base) !== 0) {
|
|
return;
|
|
}
|
|
|
|
$items = @scandir($real);
|
|
if ($items === false) {
|
|
return;
|
|
}
|
|
|
|
foreach ($items as $item) {
|
|
if ($item === '.' || $item === '..') {
|
|
continue;
|
|
}
|
|
|
|
$path = $real . DIRECTORY_SEPARATOR . $item;
|
|
if (is_dir($path)) {
|
|
self::removeDir($path);
|
|
} elseif (is_file($path)) {
|
|
@unlink($path);
|
|
}
|
|
}
|
|
|
|
@rmdir($real);
|
|
}
|
|
}
|