'Неизвестное действие', 'action' => $handleRequestAction, 'post_data' => $_POST, 'get_data' => $_GET, 'server' => $_SERVER['REQUEST_URI'] ]); } } /** * @brief Обрабатывает входящий JSON-RPC запрос * @return string JSON-ответ с результатом выполнения метода или ошибкой */ function jsonrpcRequest() { $raw = file_get_contents("php://input"); $data = json_decode($raw, true); $id = $data["id"] ?? null; try { if (json_last_error() !== JSON_ERROR_NONE) { throw new Exception("Parse error", -32700); } $method = $data["method"] ?? ""; $params = $data["params"] ?? []; if (!function_exists($method)) { throw new Exception("Method not found", -32601); } $result = $method($params); header("Content-Type: application/json"); echo json_encode(["jsonrpc" => "2.0", "id" => $id, "result" => $result]); } catch (Exception $e) { header("Content-Type: application/json"); echo json_encode(["jsonrpc" => "2.0", "id" => $id, "error" => ["code" => $e->getCode(), "message" => $e->getMessage()]]); } } /** * @brief Подключает плагин или плагины из директории main_plugin * @param array $params Параметры подключения, ключ 'plugin' содержит имя плагина * @return string HTML-код подключённых плагинов */ function includePlugin($params) { global $path, $config; $html = ''; $pluginDir = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'main_plugin' . DIRECTORY_SEPARATOR; $requested = isset($params['plugin']) ? trim($params['plugin']) : null; if ($requested !== null && ($requested === '' || strpos($requested, '..') !== false)) { throw new Exception("Invalid plugin name", -32602); } if (!is_dir($pluginDir)) return $html; $dirs = $requested ? [$requested] : array_diff(scandir($pluginDir), ['.', '..']); foreach ($dirs as $dir) { $dirPath = $pluginDir . $dir; if (!is_dir($dirPath)) continue; $file = $dirPath . '/plug.php'; if (is_file($file)) { ob_start(); include $file; $html .= ob_get_clean(); } } return $html; } /** * @brief Получает список файлов и DOM-элементов плагина * @param array $params Параметры, ключ 'plugin' содержит имя плагина * @return array Массив ссылок на CSS, JS файлы и id элементов плагина */ function removePluginDom($params) { global $path; $plugin = isset($params['plugin']) ? trim($params['plugin']) : null; if (!$plugin || strpos($plugin, '..') !== false || !preg_match('/^[A-Za-z0-9_\-]+$/', $plugin) || !is_dir($path . 'main_plugin/' . $plugin)) { throw new Exception("Invalid plugin name"); } $pluginDir = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'main_plugin' . DIRECTORY_SEPARATOR . $plugin; $plugFile = $pluginDir . '/plug.php'; if (!is_file($plugFile)) throw new Exception("plug.php not found"); ob_start(); include $plugFile; $html = ob_get_clean(); $files = []; if (preg_match_all('/]+href=["\']([^"\']+)["\']/i', $html, $m)) $files = array_merge($files, $m[1]); if (preg_match_all('/]+src=["\']([^"\']+)["\']/i', $html, $m)) $files = array_merge($files, $m[1]); $doc = new DOMDocument(); libxml_use_internal_errors(true); $doc->loadHTML('
' . $html . '
', LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); libxml_clear_errors(); $wrapper = $doc->getElementById('wrapper'); foreach ($wrapper->childNodes as $child) { if ($child instanceof DOMElement && $child->hasAttribute('id')) { $files[] = '#' . $child->getAttribute('id'); } } return array_values($files); } /** * @brief Подключает все PHP-файлы функций из папки main_plugin * @return int Количество подключённых файлов */ function includePluginsPhp() { global $path; $count = 0; $pluginDir = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'main_plugin' . DIRECTORY_SEPARATOR; if (!is_dir($pluginDir)) return $count; $dirs = array_diff(scandir($pluginDir), ['.', '..']); foreach ($dirs as $dir) { $fullDir = $pluginDir . $dir . DIRECTORY_SEPARATOR; if (!is_dir($fullDir)) continue; foreach (glob($fullDir . 'func.*.php') as $file) { if (is_file($file)) { include_once $file; $count++; } } } return $count; } /** * @brief Устанавливает язык пользователя в сессии * @param array $params Параметры, ключ 'lng' содержит код языка * @return string Строка "true" при успешной установке */ function setLng($params) { global $_SESSION; if (!isset($params['lng'])) { throw new Exception("Missing parameter: lng", -32602); } $_SESSION['lng'] = $params['lng']; return "true"; } /** * @brief Завершает сессию пользователя * @param array $params Параметры, ключ 'logoff' инициирует выход * @return string Строка "true" при успешной разлогине */ function logoutUser($params) { if (!isset($params['logoff'])) { throw new Exception("Missing parameter: logoff", -32602); } session_destroy(); return "true"; } /** * @brief Выполняет вход пользователя с проверкой логина и пароля * @param array $params Параметры, ключи 'log', 'login' и 'pass' для авторизации * @return string "true" если вход успешен, "false" если нет */ function loginUser($params) { global $_SESSION; if (!isset($params['log']) || $params['log'] !== "Войти" || !isset($params['login']) || !isset($params['pass'])) { throw new Exception("Missing login action or credentials", -32602); } if (check($params['login'], md5($params['pass']))) { $_SESSION['username'] = $params['login']; $_SESSION['pass'] = $params['pass']; $_SESSION['Login'] = 'true'; setcookie('Login', 'true', time() + 2419200, "/"); $_SESSION['log_in'] = false; return "true"; } else { $_SESSION['Login'] = 'false'; setcookie('Login', 'false', time() + 2419200, "/"); return "false"; } } /** * @brief Проверяет соответствие логина и пароля с данными пользователей * @param string $login Имя пользователя * @param string $pass Хеш пароля пользователя * @return bool true если пользователь найден и пароль совпадает, false иначе */ function check($login, $pass) { global $config, $uxml, $path; $xmlstr = simplexml_load_file($path . $config['users']); $result = false; foreach ($xmlstr->users->user as $user) { if ((string)$user['name'] === $login && (string)$user['pass'] === $pass) { $result = true; } } return $result; } /** * @brief Загружает конфигурацию сайта из XML и пользователей * @return void Возвращает значения в глобальный массив $config */ function SetConfig() { global $config, $path; $xmlstr = simplexml_load_file($path . 'config/config_site.php'); $config['icon'] = $xmlstr->general->icon; $config['encoding'] = $xmlstr->general->encoding; $config['users'] = $xmlstr->general->users; $config['usersRequest'] = $xmlstr->general->usersrequest; $xmlstr = simplexml_load_file($path . 'data/users.php'); $config['emailAdmin'] = ''; foreach ($xmlstr->users->user as $user) { $access = explode(',', (string)$user['access']); $access = array_map('trim', $access); if (in_array('creatingAccounts', $access)) { $config['emailAdmin'] = (string)$user['email']; break; } } } /** * @brief Получает список пользователей с правами администратора * @return array Массив имён пользователей с правами Admin */ function adminsConfig() { global $path; $xml = simplexml_load_file($path . 'data/users.php'); $admins = []; foreach ($xml->users->user as $user) { $accessList = array_map('trim', explode(', ', (string)$user['access'])); if (in_array('Admin', $accessList, true)) { $admins[] = (string)$user['name']; } } return $admins; } /** * @brief Генерирует HTML-ссылки для меню * @param array $menuVar Массив пунктов меню с ключами 'url', 'title', 'name' * @return string Сформированные HTML-ссылки для меню */ function GetMenuItems($menuVar){ global $config; $menu = ''; for ($i = 0; $i <= count($menuVar)-1; $i+=1) { $menu.= ''.$menuVar[$i]['name'] . ' '; if ($i <= count($menuVar)-2) { $menu.= ':: '; } } return $menu; } /** * @brief Формирует HTML-блоки для отображения на странице * @param array $BlockVar Массив блоков с ключами 'url', 'title', 'tclass', 'bclass' * @param string $side Сторона или тип блока * @return string Сформированные HTML-блоки */ function GetBlock ($BlockVar, $side) { global $path, $ansv, $REQUEST_URI,$menu, $config, $EditPage; $Block = ''; if (is_countable($BlockVar) && count($BlockVar) > 0){ for ($i = 0; $i <= count($BlockVar)-1; $i+=1){ ob_start(); include $path . $BlockVar[$i]['url'].'plug.php'; $Xblock = ob_get_contents(); ob_end_clean(); $Block.='
'; if ($BlockVar[$i]['title']!=''){ $Block.='
'.$BlockVar[$i]['title'].'
'; } $Block.='
'.$Xblock.'
'; $Block.= '
'; $Block.='
'; } } return $Block; } /** * @brief Обрабатывает ошибку 404 и выводит страницу ошибки * @param bool $pageout Если true, выводит содержимое файла 404.shtml * @param string $encoding Кодировка страницы * @return void Завершает выполнение скрипта */ function error404($pageout = false, $encoding = 'utf-8') { header('Cache-Control: no-cache, no-store'); header('Content-Type: text/html; charset=' . $encoding); header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found'); if ($pageout) readfile('404.shtml'); die; } /** * @brief Обрабатывает ошибку 405 и выводит страницу ошибки * @param bool $pageout Если true, выводит содержимое файла 405.shtml * @param string $encoding Кодировка страницы * @return void Завершает выполнение скрипта */ function error405($pageout = false, $encoding = 'utf-8') { header('Cache-Control: no-cache, no-store'); header('Content-Type: text/html; charset=' . $encoding); header($_SERVER['SERVER_PROTOCOL'] . ' 405 Not Found'); if ($pageout) readfile('405.shtml'); die; } # Функция HTTP авторизации. Логин и пароль задаются в файле users.xml # Форма авторизации /* function Log_Form($Vector,$act) { if ($Vector=='h') $sep = ' '; else $sep = '
'; $string = '
'; $string .= ''; switch ($act){ case 'log_on':{ $string .= ''; $string .= $sep; $string .= ''; $string .= $sep; $string .= ''; $string .= $sep; $string .= ''; $string .= $sep; $string .= '
'; break; } case 'log_off':{ $string .= '
'; break; } case 'log_err':{ $string .= ''; $string .= $sep; $string .= '
'; break; } } $string .= ''; $_SESSION['Login'] =''; return $string; } */ /** * @brief Генерирует HTML-меню для выбора языка с кнопками для каждого доступного языка * @return string Сформированный HTML-код меню выбора языка с встроенным скриптом для отправки запроса */ function LngMenu() { $s = ''; $s .= ' '; return $s; } /** * @brief Определяет текущий язык пользователя и сохраняет его в сессии * @return string Текущий язык (например, 'en', 'ru', 'lv') */ function SetLanguage(){ global $_SESSION, $path; if (isset($_POST['lng']) && $_POST['lng'] != '') $_SESSION['lng'] = $_POST['lng']; if (empty($_SESSION['lng'])){ $s = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']); $a = explode('-', $s[0]); $_SESSION['lng'] = $a[0]; } return $_SESSION['lng']; } /** * @brief Разбирает URL запроса и определяет действие и путь к странице * @param string $URL URL запроса * @return array Массив с ключами 'act' и 'str', где 'act' — действие, 'str' — путь к странице */ function GetRequestURL($URL){ $c=explode('.',$URL); if ($c[1]=='html'&&count($c)!=1){ $mURL['act'] ='view'; $mURL['str'] = explode('/',$c[0]); $mURL['str'][0]="index"; if ($mURL['str'][1]=='index'){ $mURL['str'][1]=''; } if ($mURL['str'][count($mURL['str'])-1]==''){ array_pop($mURL['str']); } } else{ if ($c[1]=='xml'&&count($c)!=1){ $mURL['act'] ='edit'; $mURL['str'] = explode('/',$c[0]); $mURL['str'][0]="index"; } else{ $mURL['str']='error'; } } return $mURL; } /** * @brief Получает новости из XML-файла и формирует HTML-блоки для отображения * @param array $BlockVar Массив блоков с настройками отображения * @param string $side Сторона страницы, для которой формируются новости * @return string Сформированные HTML-блоки с новостями */ function getNews($BlockVar, $side) { global $path, $_SESSION; $lng = $_SESSION['lng'] ?? 'en'; $file = $path . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'filepath.' . $lng . '.php'; $content = @file_get_contents($file); if (!$content) $content = ''; $content = preg_replace('/^\s*<\?php.*?\?>\s*/s', '', $content); $xml = @simplexml_load_string($content); $rootReal = realpath(rtrim($path, DIRECTORY_SEPARATOR)); $html = ''; $tplClass = isset($BlockVar[0]['tclass']) ? $BlockVar[0]['tclass'] : 'btitle'; $bclass = isset($BlockVar[0]['bclass']) ? $BlockVar[0]['bclass'] : 'bfloat'; $now = new DateTime(); $walk = function($node) use (&$walk, &$html, $path, $side, $lng, $rootReal, $tplClass, $bclass, $now) { $newsAttr = trim((string)$node['news']); if ($newsAttr === '') { foreach ($node->children() as $child) $walk($child); return; } $parts = explode(',', $newsAttr); if (count($parts) === 0) { foreach ($node->children() as $child) $walk($child); return; } $period = array_shift($parts); $dates = explode('/', $period); if (count($dates) !== 2) { foreach ($node->children() as $child) $walk($child); return; } $parseDate = function($str) { $parts = explode('.', $str); if (count($parts) < 5) return false; list($Y, $m, $d, $H, $i) = $parts; $dateStr = sprintf('%04d-%02d-%02d %02d:%02d:00', $Y, $m, $d, $H, $i); return DateTime::createFromFormat('Y-m-d H:i:s', $dateStr); }; $start = $parseDate($dates[0]); $end = $parseDate($dates[1]); if (!$start || !$end || $now < $start || $now > $end) { foreach ($node->children() as $child) $walk($child); return; } $blocks = array_map('trim', $parts); if (!empty($blocks) && !in_array($side, $blocks)) { foreach ($node->children() as $child) $walk($child); return; } $urlAttrRaw = trim((string)$node['url']); $titleRaw = (string)$node['title']; $urlPart = trim($urlAttrRaw, "/\\"); $pageFile = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $urlPart . '.page.php'; $pageFileReal = realpath($pageFile); $pageContent = ''; if ($pageFileReal && strpos($pageFileReal, $rootReal) === 0 && is_file($pageFileReal) && is_readable($pageFileReal)) { $pageXml = @simplexml_load_file($pageFileReal); if ($pageXml && isset($pageXml->content->{$lng})) { $pageContent = strip_tags((string)$pageXml->content->{$lng}); $maxLength = ($side === 'center') ? 300 : 100; $pageContent = mb_substr($pageContent, 0, $maxLength) . (mb_strlen($pageContent) > $maxLength ? '...' : ''); } } $nameRaw = trim((string)$node['name']); $tagPath = []; $n = $node; while ($n) { $tag = $n->getName(); if ($tag !== 'site' && $tag !== 'index') { $tagPath[] = $tag; } $n = $n->xpath('..') ? $n->xpath('..')[0] : null; } $tagPath = array_reverse($tagPath); if (empty($tagPath)) { $link = '/'; } else { $link = implode('/', $tagPath) . '.html'; } $link = htmlspecialchars($link); $readMore = 'читать дальше'; $title = $titleRaw !== '' ? htmlspecialchars($titleRaw) : ''; $html .= '
'; if ($side === "center") $html .= '
'; if ($title !== '') $html .= '
' . $title . '
'; $html .= '
' . $pageContent . ' ' . $readMore . '
'; if ($side !== 'center') $html .= '
'; $html .= '
'; foreach ($node->children() as $child) $walk($child); }; if ($xml && isset($xml->index)) $walk($xml->index); return $html; } /** * @brief Генерирует HTML-строку горизонтального меню из XML-структуры * @param object $menuVar XML-объект меню * @param array $RURLstr Массив сегментов URL для определения вложенности * @param int $ItemNo Индекс текущего элемента в URL * @param string $Vector Вертикальное ('v') или горизонтальное отображение * @return string Сформированное HTML-меню */ function GetXMLMenu($menuVar,$RURLstr,$ItemNo,$Vector){ global $config, $path, $_SESSION; $child_menu = ""; $topURL = ''; if ($ItemNo!=0) { for ($i = 1; $i <= $ItemNo; $i+=1) { $topURL .=$RURLstr[$i].'/'; $menuVar =$menuVar->{$RURLstr[$i]}; } } $i = 0; foreach ($menuVar->children() as $child_page) { if (FindPageUser($child_page['users'],$_SESSION['username'])) { if ($i!=0) { if ($Vector!='v') { $child_menu .=' :: '; } else { $child_menu .='
'; } } $child_menu .= '' . $child_page['title'] . ''; $i=$i+1; } } return $child_menu; } /** * @brief Генерирует HTML-блок бокового меню из XML-структуры * @param object $menuVar XML-объект меню * @param array $RURLstr Массив сегментов URL для определения вложенности * @param int $ItemNo Индекс текущего элемента в URL * @return string Сформированное HTML боковое меню */ function GetSideXMLMenu($menuVar,$RURLstr,$ItemNo){ global $config, $path, $_SESSION; $child_menu = ""; $topURL = ''; if ($ItemNo!=0) { for ($i = 1; $i <= $ItemNo; $i+=1) { $topURL .=$RURLstr[$i].'/'; $menuVar =$menuVar->{$RURLstr[$i]}; } } $child_menu.='
'; $child_menu.=''; $child_menu.='Home'; $child_menu.='
'; foreach ($menuVar->children() as $child_page) { if (FindPageUser($child_page['users'],$_SESSION['username'])) { $child_menu.=''.$child_page['title'].''; } } return $child_menu; } /** * @brief Проверяет, имеет ли пользователь доступ к странице * @param string $PageUser Список пользователей, имеющих доступ, через запятую * @param string $user Имя пользователя * @return bool true если доступ разрешён, false если нет */ function FindPageUser($PageUser,$user){ if ($PageUser==''){ $test =true; } else{ $test =false; $PageUser =explode(',',$PageUser); for ($i = 0; $i <= count($PageUser)-1; $i+=1){ if ($PageUser[$i]==$user){ $test =true; } } } return $test; } /** * @brief Разбирает файл XML страницы и возвращает информацию о запрошенном URL * @param string $FPfile Путь к XML-файлу с описанием страниц * @param mixed $RURLstr Массив сегментов URL или строка 'error' * @return array Массив с информацией о странице, включая URL, шаблон, заголовок и плагины */ function URLstr($FPfile,$RURLstr){ global $path, $server, $config; $xmlstr =simplexml_load_file($FPfile); if (!isset($xmlstr->index)) { $index = $xmlstr->addChild('index', "\n"); $index->addAttribute('url', 'data/createSite'); $lang = isset($config['lng']) ? $config['lng'] : 'ru'; $titles = [ 'ru' => 'Страница создание нового сайта', 'en' => 'New site creation page', 'lv' => 'Jaunas vietnes izveides lapa' ]; $index->addAttribute('title', isset($titles[$lang]) ? $titles[$lang] : $titles['ru']); $index->addAttribute('name', 'index'); $index->addAttribute('template', 'MedWait'); $index->addAttribute('PageMenu', '0,1,2'); $index->addAttribute('users', ''); $index->addAttribute('group', ''); $index->addAttribute('news', ''); $index->addAttribute('plugins', ''); $xmlstr->asXML($FPfile); } $ansv['sitename'] =$xmlstr->sitename; $ansv['XML'] =$xmlstr; $ansv['pageURL'] =$xmlstr->sitename; $fileURL ='http://'.$_SERVER['HTTP_HOST']; $ansv['URLLine'] =""; if ($RURLstr!='error'){ for ($i = 0; $i <= count($RURLstr)-1; $i+=1){ if ($xmlstr->{$RURLstr[$i]}['name']!=''){ if ($i!=count($RURLstr)-1) { if ($i!=0){ $fileURL .='/'.$xmlstr->{$RURLstr[$i]}->getName(); $end ='.html'; } else{ $end ='/index.html'; } $ansv['URLLine'] .= ''.$xmlstr->{$RURLstr[$i]}['name'].'>>'; } else{ $ansv['URLLine'] .= $xmlstr->{$RURLstr[$i]}['name']; $ansv['FileURL'] = $xmlstr->{$RURLstr[$i]}['url']; $ansv['template'] = $xmlstr->{$RURLstr[$i]}['template']; $ansv['title'] = $xmlstr->{$RURLstr[$i]}['title']; $ansv['page'] = $xmlstr->{$RURLstr[$i]}; $ansv['page_plugins'] = (string)($xmlstr->{$RURLstr[$i]}['plugins'] ?? ''); } $xmlstr = $xmlstr->{$RURLstr[$i]}; } else { $ansv['URLLine'] = 'error'; $ansv['FileURL'] = 'error'; } } } return $ansv; } /** * @brief Разбирает файл XML страницы и возвращает информацию о запрошенном URL * @param string $FPfile Путь к XML-файлу с описанием страниц * @param mixed $RURLstr Массив сегментов URL или строка 'error' * @return array Массив с информацией о странице, включая URL, шаблон, заголовок и плагины */ function loadPluginsInCenterBlock() { global $_SESSION, $path, $config; if ($_SESSION['Login'] == 'true') { $availablePlugins = ['dgrm', 'SvgEditorM', 'form_editor']; $pluginDir = $path . 'main_plugin/'; if (is_dir($pluginDir)) { $dirs = array_diff(scandir($pluginDir), ['.', '..']); foreach ($dirs as $dir) { if (is_dir($pluginDir . $dir)) { if (!in_array($dir, $availablePlugins) || strpos($config['page_plugins'] ?? '', $dir) !== false) { if ($dir === 'SvgEditorM' || $dir === 'dgrm' || $dir === 'form_editor') { $html .= includePlugin(['plugin' => $dir]); } } } } } } return $html; } ?>