Mercurial
view .cms/mod/admin.mod.php @ 0:78edf6b517a0 draft
24.10
author | Coffee CMS <info@coffee-cms.ru> |
---|---|
date | Fri, 11 Oct 2024 22:40:23 +0000 |
parents | |
children |
line wrap: on
line source
<?php $cms["modules"]["admin.mod.php"] = array( "name" => "admin_module_name", "description" => "admin_module_description", "files" => array( ".cms/admin.cms/html.php", ".cms/mod/admin.mod.php", ".cms/js/admin.js", ".cms/css/admin.css", ".cms/lang/ru_RU.UTF-8/admin.mod.php", ".cms/lang/en_US.UTF-8/admin.mod.php", ".cms/lang/uk_UA.UTF-8/admin.mod.php", ), ); // Return if module disabled if ( ! empty( $cms["config"]["admin.mod.php"]["disabled"] ) ) { return; } else { if ( ! empty( $cms["config"]["locale"] ) ) { $translit = "{$cms['cms_dir']}/lang/{$cms['config']['locale']}/translit.php"; if ( is_file( $translit ) ) { include_once( $translit ); } } // on install admin_url == base_path if ( ! isset( $cms["config"]["admin.mod.php"]["admin_url"] ) ) { $cms["config"]["admin.mod.php"]["admin_url"] = ""; } if ( empty( $cms["config"]["admin.mod.php"]["api_url"] ) ) { $cms["config"]["admin.mod.php"]["api_url"] = "api" . cms_uid(); //cms_save_config(); } if ( empty( $cms["config"]["admin.mod.php"]["cron_url"] ) ) { $cms["config"]["admin.mod.php"]["cron_url"] = "cron" . cms_uid(); //cms_save_config(); } if ( ! empty( $cms["config"]["admin.mod.php"]["clear_cache"] ) ) { hook_add_fn( "template", "cms_admin_clear_cache", 4 ); } $cms["urls"]["^{$cms['base_path']}{$cms['config']['admin.mod.php']['admin_url']}$"] = "admin"; $cms["urls"]["^{$cms['base_path']}{$cms['config']['admin.mod.php']['api_url']}$"] = "api"; $cms["urls"]["^{$cms['base_path']}{$cms['config']['admin.mod.php']['cron_url']}$"] = "cron"; // Authorized admin and not authorized admin if ( is_admin() ) { hook_add_fn( "admin_header", "cms_admin_admin_header" ); hook_add_fn( "api", "cms_admin_api" ); } hook_add_fn( "admin", "cms_admin_admin", 9999 ); } function cms_admin_clear_cache() { global $cms; header( "Clear-Site-Data: \"cache\"" ); unset( $cms["config"]["admin.mod.php"]["clear_cache"] ); cms_save_config(); } function cms_admin_admin_header() { global $cms; echo "<script src='{$cms['base_path']}js/admin.js'></script>"; } function is_admin() { global $cms; return isset( $_COOKIE["sess"] ) && isset( $cms["config"]["logged"][ $_COOKIE["sess"] ] ); } // Боковое меню в админке function cms_admin_menu() { global $cms; // Сохраненные настройки боковой панели: $cms["config"]["admin_sections"] // Текущее состояние настроек формируемое динамически: $cms["admin_sections"] if ( ! empty( $cms["config"]["admin_sections"] ) ) { $cms["admin_sections"] = $cms["config"]["admin_sections"]; } else { $cms["admin_sections"] = array(); } // Настройки пунктов сохраняются в настройках модулей, а не в секции, // чтобы отключение модуля прятало пункт из боковой панели foreach( $cms["config"] as $mod => $mod_cfg ) { if ( substr( $mod, -8, 8 ) === ".mod.php" && empty( $mod_cfg["disabled"] ) && isset( $mod_cfg["menu"] ) ) { foreach( $mod_cfg["menu"] as $menu => $menu_cfg ) { $section = $menu_cfg["section"]; $menu_cfg["module"] = $mod; // для последующей локализации $cms["admin_sections"][$section]["items"][$menu] = $menu_cfg; // Локализация заголовка секции $locale = $cms["config"]["locale"]; if ( ! isset( $cms["admin_sections"][$section]["title"] ) && isset( $cms["lang"][$mod][$locale][$section] ) ) { $cms["admin_sections"][$section]["title"] = $cms["lang"][$mod][$locale][$section]; } // default section sort if ( isset( $menu_cfg["section_sort"] ) && ! isset( $cms["admin_sections"][$section]["sort"] ) ) { $cms["admin_sections"][$section]["sort"] = $menu_cfg["section_sort"]; } } } } // Затем эти данные используются в .cms/admin.cms/html.php // Sort & Translate $default_sort = array( "content" => 100, "settings" => 1000, ); foreach( $cms["admin_sections"] as $section => $sec_conf ) { // Сортировка. Установить порядковый номер если не установлен. if ( ! isset( $cms["admin_sections"][$section]["sort"] ) ) { if ( isset( $default_sort[$section] ) ) { $cms["admin_sections"][$section]["sort"] = $default_sort[$section]; } else { $cms["admin_sections"][$section]["sort"] = 500; } } // translate if ( ! isset( $cms["admin_sections"][$section]["title"] ) ) { $cms["admin_sections"][$section]["title"] = __( $section ); } } // Sort sections cms_asort( $cms["admin_sections"] ); // Sort items inside sections foreach( $cms["admin_sections"] as $section_name => $section ) { cms_asort( $cms["admin_sections"][$section_name]["items"] ); } } function cms_admin_admin() { global $cms; $cms["template"] = "admin.cms"; $cms["status"] = "200"; $cms["hooks"]["write"]["disabled"] = true; // Not required code if not authorized if ( ! is_admin() ) return; $conf = $cms["config"]["admin.mod.php"]["menu"]["auth"]; if ( empty( $conf["hide"] ) && empty( $cms["config"]["admin_sections"][ $conf["section"] ]["hide"] ) ) { // Save settings if ( ! empty( $_POST["change_admin_login"] ) ) { $login = trim( $_POST["admin_login"] ); $password = trim( $_POST["admin_password"] ); $cms["config"]["admin.mod.php"]["admin_login"] = $login; $cms["config"]["admin.mod.php"]["admin_password"] = $password; $cms["config"]["admin.mod.php"]["admin_url"] = trim( $_POST["admin_url"] ); cms_save_config(); header( "Location: {$cms['base_path']}{$cms['config']['admin.mod.php']['admin_url']}" ); $cms["hooks"]["admin"]["next"] = ""; // Предотвратить выдачу админки return; } $rows = ""; foreach ( $cms["config"]["logged"] as $login => $attr ) { if ( $login === $_COOKIE["sess"] ) { $current = "class=current"; } else { $current = ""; } $rows .= " <div {$current}> <div class=del-sess data-login='{$login}'></div> <div class=date>{$attr['date']}</div> <div class=ip>{$attr['ip']}</div> <div class=agent>{$attr['user_agent']}</div> </div>"; } $logouts = ""; if ( ! empty( $cms["config"]["logouted"] ) ) { // Перебираем закрытые сессии в прямом порядки // и очередную добавляем в начало списка, // чтобы получить обратный порядок отображения. foreach ( $cms["config"]["logouted"] as $attr ) { $logouts = " <div> <div></div> <div class=date>{$attr['date']}</div> <div class=ip>{$attr['ip']}</div> <div class=agent>{$attr['user_agent']}</div> </div> {$logouts}"; } } // Display settings $tr_login = __( "login_or_password" ); $tr_passw = __( "password" ); $tr_set = __( "set_password" ); $tr_current = __( "current_logins" ); $tr_history = __( "logins_history" ); $tr_admin_url = __( "admin_url" ); $form = " <form method=post> <div class=admin-url>{$tr_admin_url}</div> <input name=admin_url type=text value='{$cms['config']['admin.mod.php']['admin_url']}' autocomplete=off size=10> <div class=login-title>{$tr_login}</div> <input name=admin_login type=text value='{$cms['config']['admin.mod.php']['admin_login']}' autocomplete=off size=22> <div class=passwd-title>{$tr_passw}</div> <div class=password-widget> <input name=admin_password type=password value='{$cms['config']['admin.mod.php']['admin_password']}' autocomplete=off size=10> <div class=password-eye></div> </div> <div></div> <button value=save name=change_admin_login>{$tr_set}</button> </form> <div class=current-sess> <div class=table-title>{$tr_current}</div> <div class=sess-table>{$rows}</div> </div> <div class=history-sess> <div class=table-title>{$tr_history}</div> <div class=sess-table>{$logouts}</div> </div> "; // Create menu item if not exists if ( empty( $cms["config"]["admin.mod.php"]["menu"]["auth"] ) ) { $cms["config"]["admin.mod.php"]["menu"]["auth"] = array( "title" => "auth", "sort" => 10, "section" => "settings", ); cms_save_config(); } $cms["admin_pages"]["auth"] = $form; } $conf = $cms["config"]["admin.mod.php"]["menu"]["phpinfo"]; if ( empty( $conf["hide"] ) && empty( $cms["config"]["admin_sections"][ $conf["section"] ]["hide"] ) ) { // PHP Info if ( empty( $cms["config"]["admin.mod.php"]["menu"]["phpinfo"]["hide"] ) ) { ob_start(); phpinfo(); preg_match( "/<body>(.+)<\/body>/us", ob_get_clean(), $m ); $page = $m[1]; // Create menu item if not exists if ( empty( $cms["config"]["admin.mod.php"]["menu"]["phpinfo"] ) ) { $cms["config"]["admin.mod.php"]["menu"]["phpinfo"] = array( "title" => "php_info", "sort" => 30, "section" => "settings", ); cms_save_config(); } $cms["admin_pages"]["phpinfo"] = $page; } } // Modules List $conf = $cms["config"]["admin.mod.php"]["menu"]["modules"]; if ( empty( $conf["hide"] ) && empty( $cms["config"]["admin_sections"][ $conf["section"] ]["hide"] ) ) { if ( empty( $cms["config"]["admin.mod.php"]["update"]["last_version"] ) ) { $last_version = __( "unknown" ); } else { $last_version = $cms["config"]["admin.mod.php"]["update"]["last_version"]; if ( $cms["kernel_compat"] !== $cms["config"]["admin.mod.php"]["update"]["compat"] ) { $last_version .= ". " . __( "incompatible_v" ); } } if ( empty( $cms["config"]["admin.mod.php"]["last_check"] ) ) { $last_check = __( "never" ); } else { $last_check = date( "d.m.Y H:i", $cms["config"]["admin.mod.php"]["last_check"] ); } if ( file_exists( $cms["cms_dir"] . "/man/" . $cms["config"]["locale"] . "/book.html" ) ) { $book_href = $cms["base_path"] . "man/" . $cms["config"]["locale"] . "/book.html"; $book_link = "<a href={$book_href} target=_blank>" . __( "book" ) . "</a>"; } else { $book_link = ""; } $admin_menu = " <div class=header> <div class=update-window> <div>" . __( "update_core" ) . "</div> <div class=check-answer> <p>" . __( "current_v" ) . " {$cms['kernel_version']}</p> <p>" . __( "last_v" ) . " {$last_version}</p> <p>" . __( "checked" ) . " {$last_check}</p> </div> <div class=buttons> <button data-fn=cms_check_update>" . __( "check" ) . "</button> <button data-fn=cms_update>" . __( "update" ) . "</button> {$book_link} <button data-show-dev>" . __( "for_dev" ) . "</button> </div> </div> <div class='dev-window developers_only'> <div class=buttons> <button data-fn=cms_check_dev_update>" . __( "check_dev" ) . "</button> <button data-fn=cms_changed_files>" . __( "changed_files" ) . "</button> <button data-fn=create_zip style='display:none'>" . __( "create_zip" ) . "</button> </div> </div> <div class=changed-files style='display:none'></div> <div class=upload_dnd> <input id=module-upload type=file name='myfile[]' multiple class=files> " . __( "mod_install" ) . " </div> </div> <div class=modules-grid>"; // Сортировка модулей function modules_sort( $a, $b ) { return ( $a["name"] < $b["name"] ) ? -1 : 1; } uasort( $cms["modules"], "modules_sort" ); $tr_on = __( "mod_on" ); $tr_off = __( "mod_off" ); $tr_del = __( "mod_del" ); foreach( $cms["modules"] as $mod => $mod_cfg ) { if ( ! empty( $cms["config"][$mod]["disabled"] ) ) { $status = "disabled"; $tr_sw = $tr_on; } else { $status = "enabled"; $tr_sw = $tr_off; } if ( empty( $mod_cfg["version"] ) ) { $mod_version = ""; } else { $mod_version = "<div class=module-version>{$mod_cfg['version']}</div>"; } // переводы имен модулей вынесены сюда чтобы не было варнингов при установке $admin_menu .= " <div class={$status} data-module={$mod}> <div class=module-name>" . __( $mod_cfg['name'], $mod ) . "</div> {$mod_version} <div class=module-description>" . __( $mod_cfg['description'], $mod ) . "</div> <a class=module-sw-btn>{$tr_sw}</a> <a class=module-del-btn>{$tr_del}</a> </div>"; } $admin_menu .= "</div>"; // Create menu item if not exists if ( empty( $cms["config"]["admin.mod.php"]["menu"]["modules"] ) ) { $cms["config"]["admin.mod.php"]["menu"]["modules"] = array( "title" => "modules", "sort" => 50, "section" => "settings", ); cms_save_config(); } $cms["admin_pages"]["modules"] = $admin_menu; } // Admin // Create menu item if not exists if ( empty( $cms["config"]["admin.mod.php"]["menu"]["admin_menu"] ) ) { $cms["config"]["admin.mod.php"]["menu"]["admin_menu"] = array( "title" => "admin_module_name", "sort" => 40, "section" => "settings", ); cms_save_config(); } cms_admin_menu(); $conf = $cms["config"]["admin.mod.php"]["menu"]["admin_menu"]; if ( empty( $conf["hide"] ) && empty( $cms["config"]["admin_sections"][ $conf["section"] ]["hide"] ) ) { $tr_show = __( "show" ); $tr_hide = __( "hide" ); $tr_save = __( "save" ); $tr_del_section = __( "delete" ); $tr_reset = __( "reset" ); $options = ""; foreach( $cms["admin_sections"] as $section_name => $section ) { $options .= "<div class=option value='{$section_name}'>{$section["title"]}</div>"; } // Admin Sections $admin_menu = " <div class=main-main> <div class=am-grid>"; foreach( $cms["admin_sections"] as $section_name => $section ) { if ( empty( $section["hide"] ) ) { $status = "showed"; $tr_sw = $tr_hide; } else { $status = "hidden"; $tr_sw = $tr_show; } $admin_menu .= " <div> <div class={$status} data-am-type=section data-am-item='{$section_name}'> <input name=title type=text value='{$section['title']}'> <input name=sort type=text value='{$section['sort']}'> <div class=buttons> <a data-am-save>{$tr_save}</a> <a data-am-delete>{$tr_del_section}</a> <a data-am-sw>{$tr_sw}</a> </div> </div> <div class=items-grid data-am-childs='{$section_name}'>"; // Admin Items if ( ! empty( $cms["admin_sections"][$section_name]["items"] ) ) foreach( $cms["admin_sections"][$section_name]["items"] as $iname => $item ) { if ( empty( $item["hide"] ) ) { $status = "showed"; $tr_sw = $tr_hide; } else { $status = "hidden"; $tr_sw = $tr_show; } $admin_menu .= " <div class={$status} data-am-type=item data-am-module={$item['module']} data-am-item={$iname}> <div class=item-name>" . __( $item["title"], $item["module"] ) . "</div> <input name=sort type=text value='{$item['sort']}'> <div class=section-select-grid> <div class=field-select data-section='{$section_name}'> <div class=value>{$cms['admin_sections'][$section_name]['title']}</div> <div class=icon></div> </div> <div class=field-options> {$options} </div> </div> <div class=buttons> <a data-am-save>{$tr_save}</a> <a data-am-save data-am-reset>{$tr_reset}</a> <a data-am-sw>{$tr_sw}</a> </div> </div>"; } $admin_menu .= " </div> <!-- class=status --> </div> <!-- class= -->"; } $admin_menu .= " </div> <!-- class=am-grid --> </div> <!-- class=main-main --> <div class=main-footer> <div class=add-section>" . __( "add_section" ) . "</div> <div class=reset-all>" . __( "reset_all" ) . "</div> </div>"; $cms["admin_pages"]["admin_menu"] = $admin_menu; } } function cms_admin_api() { global $cms; // Cookie, Logout, Admin, Modules if ( ! empty( $_POST["fn"] ) && is_admin() ) { switch ( $_POST["fn"] ) { case "logout": $cms["status"] = "200"; if ( empty( $_POST["sess"] ) ) { if ( PHP_VERSION_ID < 70300 ) { setcookie( "sess", "", 365 * 24 * 60 * 60 ); } else { setcookie( "sess", "", array( "SameSite" => "Lax", "expires" => time() + 365 * 24 * 60 * 60 ) ); } $login = $_COOKIE["sess"]; $result = "refresh"; } else { $login = $_POST["sess"]; if ( $_POST["sess"] === $_COOKIE["sess"] ) { $result = "refresh"; } else { $result = "ok"; } } if ( ! isset($cms["config"]["logouted"]) ) { $cms["config"]["logouted"] = array(); } // Добавляем закрытую сессию в конец массива в историю array_push( $cms["config"]["logouted"], $cms["config"]["logged"][$login] ); // Оставим только последние 10 выходов в истории $cms["config"]["logouted"] = array_slice( $cms["config"]["logouted"], -10 ); unset( $cms["config"]["logged"][$login] ); cms_save_config(); echo( json_encode( array( "info_text" => __( "logout_completed" ), "info_class" => "info-success", "info_time" => 5000, "result" => $result, ) ) ); return; break; case "admin_menu_save": switch ( $_POST["type"] ) { case "section": if ( empty( $_POST["title"] ) ) { unset ( $cms["config"]["admin_sections"][ $_POST["item"] ]["title"] ); } else { $cms["config"]["admin_sections"][ $_POST["item"] ]["title"] = $_POST["title"]; } $n = (int) $_POST["sort"]; if ( $n ) { $cms["config"]["admin_sections"][ $_POST["item"] ]["sort"] = $n; } else { unset( $cms["config"]["admin_sections"][ $_POST["item"] ]["sort"] ); } break; // Настройки пунктов сохраняются в настройках модулей, а не в секции case "item": if ( $_POST["reset"] === "true" ) { unset( $cms["config"][ $_POST["module"] ]["menu"][ $_POST["item"] ] ); } else { $cms["config"][ $_POST["module"] ]["menu"][ $_POST["item"] ]["section"] = $_POST["section"]; $cms["config"][ $_POST["module"] ]["menu"][ $_POST["item"] ]["sort"] = (int) $_POST["sort"]; } break; } cms_save_config(); echo( json_encode( array( "ok" => "true", ) ) ); return; break; case "admin_menu_hide": $hide = $_POST["hide"] === "true"; switch ( $_POST["type"] ) { case "section": $cms["config"]["admin_sections"][ $_POST["item"] ]["hide"] = $hide; break; case "item": $cms["config"][ $_POST["module"] ]["menu"][ $_POST["item"] ]["hide"] = $hide; break; } cms_save_config(); echo( json_encode( array( "ok" => "true", ) ) ); return; break; case "admin_menu_add_section": $n = 1; while ( isset( $cms["config"]["admin_sections"]["section_".$n] ) ) { $n++; } $cms["config"]["admin_sections"]["section_".$n] = array( "title" => __( "add_section" ), "sort" => 99, ); cms_save_config(); echo( json_encode( array( "info_text" => __( "section_added" ), "info_class" => "info-success", "info_time" => 3000, ) ) ); return; break; case "admin_menu_del": $cms["status"] = "200"; unset( $cms["config"]["admin_sections"][ $_POST["item"] ]); cms_save_config(); echo( json_encode( array( "info_text" => __( "deleted" ), "info_class" => "info-success", "info_time" => 1000, ) ) ); return; break; case "module_disable": if ( $_POST["module"] == "admin.mod.php" || $_POST["module"] == "template.mod.php" ) { echo( json_encode( array( "info_text" => __( "not_disabled" ), "info_class" => "info-error", "info_time" => 5000, ) ) ); return; } if ( $_POST["disable"] === "true" ) { $cms["config"][$_POST["module"]]["disabled"] = true; } else { unset( $cms["config"][$_POST["module"]]["disabled"] ); } cms_save_config(); echo( json_encode( array( "ok" => "true", ) ) ); return; break; case "module_del": $text = __( "delete_this_files" ); foreach( $cms["modules"][$_POST["module"]]["files"] as $file ) { $text .= "<br>{$file}"; } echo( json_encode( array( "info_text" => $text, "info_class" => "info-error", "info_time" => 60000, ) ) ); return; break; case "install_module": $success = true; foreach ( $_FILES["myfile"]["name"] as $n => $name ) { if ( $_FILES["myfile"]["error"][$n] ) { $success = false; $text = str_replace( "xxx", $name, __( "upload_error_xxx" ) ); break; } else { // Unpack Module // Object Oriented Style for future compability with PHP 8 $zip = new ZipArchive; $zip_file = $_FILES["myfile"]["tmp_name"][$n]; if ( $zip->open( $zip_file ) === TRUE ) { $ok = $zip->extractTo( $cms["site_dir"] ); $zip->close(); if ( ! $ok ) { $success = false; $text = str_replace( "xxx", $name, __( "cant_extract_xxx" ) ); break; } else { // Load modules // В одном zip может быть множество модулей foreach( glob( "{$cms['cms_dir']}/mod/*.mod.php" ) as $mod_file ) { include_once( $mod_file ); $module_name = str_replace( ".mod.php", "", basename( $mod_file ) ); $sql_file = "{$cms['cms_dir']}/mod/{$module_name}.update.sql"; if ( is_file( $sql_file ) ) { $q = file_get_contents( $sql_file ); $res = mysqli_multi_query( $cms["base"], $q ); } } do_hook( "create_tables" ); $text = __( "install_success" ); } } else { $success = false; $text = str_replace( "xxx", $name, __( "cant_open_zip_xxx" ) ); break; } } } if ( $success ) { if ( function_exists( "cms_clear_cache" ) ) { cms_clear_cache(); } $cms["config"]["admin.mod.php"]["clear_cache"] = true; cms_save_config(); echo( json_encode( array( "info_text" => $text, "info_class" => "info-success", "info_time" => 5000, ) ) ); return; } else { echo( json_encode( array( "info_text" => $text, "info_class" => "info-error", "info_time" => 10000, ) ) ); return; } break; case "no_translation": if ( ! empty( $cms["config"]["debug"] ) ) { $new_debug = array( "translate to {$cms["config"]["locale"]}" => array( "{$_POST["module"]}:js" => $_POST["str"], ), ); $debug_file = $cms["cms_dir"] . "/debug.log.php"; if ( $handle = fopen( $debug_file, "r" ) ) { flock( $handle, LOCK_SH ); include( $debug_file ); $cms["debug"] = array_replace_recursive( $cms["debug"], $new_debug ); flock( $handle, LOCK_UN ); file_put_contents( $debug_file, '<?php $cms["debug"] = ' . var_export( $cms["debug"], true) . ";\n", LOCK_EX ); } else { file_put_contents( $debug_file, '<?php $cms["debug"] = ' . var_export( $cms["debug"], true) . ";\n", LOCK_EX ); } } echo( '{ "result": "ok" }' ); return; break; case "reset_admin_menu_items": reset_admin_menu_items(); echo( json_encode( array( "info_text" => __( "reset_all_ok" ), "info_class" => "info-success", "info_time" => 3000, ) ) ); return; break; case "create_zip": cms_update_create_zip(); break; case "cms_check_update": cms_update_check( "https://coffee-cms.ru/update.json" ); break; case "cms_check_dev_update": cms_update_check( "https://dev.coffee-cms.ru/update_dev.json" ); break; case "cms_update": cms_update_cms_update(); break; case "cms_changed_files": cms_changed_files(); break; } } } function reset_admin_menu_items() { global $cms; unset( $cms["config"]["admin_sections"] ); foreach( $cms["modules"] as $mod => $val ) { if ( isset( $cms["config"][$mod]["menu"] ) ) { unset( $cms["config"][$mod]["menu"] ); } } cms_save_config(); }//unset( $cms["config"][ $_POST["module"] ]["menu"][ $_POST["item"] ] ); function cms_update_create_filelist() { global $cms; $logs = ""; $queue = array( "{$cms['site_dir']}/uploads/.keep", "{$cms['site_dir']}/.htaccess", "{$cms['site_dir']}/.nginx.conf", "{$cms['site_dir']}/.cms/admin.cms", "{$cms['site_dir']}/.cms/css/base.css", "{$cms['site_dir']}/.cms/css/menu.css", "{$cms['site_dir']}/.cms/css/pages.css", "{$cms['site_dir']}/.cms/css/sitemap.css", "{$cms['site_dir']}/.cms/css/rss.css", "{$cms['site_dir']}/.cms/css/template.css", "{$cms['site_dir']}/.cms/css/admin.css", "{$cms['site_dir']}/.cms/filelist.php", "{$cms['site_dir']}/.cms/img", "{$cms['site_dir']}/.cms/js/admin.js", "{$cms['site_dir']}/.cms/js/menu.js", "{$cms['site_dir']}/.cms/js/pages.js", "{$cms['site_dir']}/.cms/js/sitemap.js", "{$cms['site_dir']}/.cms/js/rss.js", "{$cms['site_dir']}/.cms/js/template.js", "{$cms['site_dir']}/.cms/lang/ru_RU.UTF-8/admin.mod.php", "{$cms['site_dir']}/.cms/lang/ru_RU.UTF-8/base.mod.php", "{$cms['site_dir']}/.cms/lang/ru_RU.UTF-8/menu.mod.php", "{$cms['site_dir']}/.cms/lang/ru_RU.UTF-8/pages.mod.php", "{$cms['site_dir']}/.cms/lang/ru_RU.UTF-8/sitemap.mod.php", "{$cms['site_dir']}/.cms/lang/ru_RU.UTF-8/rss.mod.php", "{$cms['site_dir']}/.cms/lang/ru_RU.UTF-8/template.mod.php", "{$cms['site_dir']}/.cms/lang/ru_RU.UTF-8/translit.php", "{$cms['site_dir']}/.cms/lang/en_US.UTF-8/admin.mod.php", "{$cms['site_dir']}/.cms/lang/en_US.UTF-8/base.mod.php", "{$cms['site_dir']}/.cms/lang/en_US.UTF-8/menu.mod.php", "{$cms['site_dir']}/.cms/lang/en_US.UTF-8/pages.mod.php", "{$cms['site_dir']}/.cms/lang/en_US.UTF-8/sitemap.mod.php", "{$cms['site_dir']}/.cms/lang/en_US.UTF-8/rss.mod.php", "{$cms['site_dir']}/.cms/lang/en_US.UTF-8/template.mod.php", "{$cms['site_dir']}/.cms/lang/uk_UA.UTF-8/admin.mod.php", "{$cms['site_dir']}/.cms/lang/uk_UA.UTF-8/base.mod.php", "{$cms['site_dir']}/.cms/lang/uk_UA.UTF-8/menu.mod.php", "{$cms['site_dir']}/.cms/lang/uk_UA.UTF-8/pages.mod.php", "{$cms['site_dir']}/.cms/lang/uk_UA.UTF-8/sitemap.mod.php", "{$cms['site_dir']}/.cms/lang/uk_UA.UTF-8/rss.mod.php", "{$cms['site_dir']}/.cms/lang/uk_UA.UTF-8/template.mod.php", "{$cms['site_dir']}/.cms/lang/uk_UA.UTF-8/translit.php", "{$cms['site_dir']}/.cms/lib/codemirror", "{$cms['site_dir']}/.cms/man/ru_RU.UTF-8/book.html", "{$cms['site_dir']}/.cms/man/ru_RU.UTF-8/codemirror.html", "{$cms['site_dir']}/.cms/man/ru_RU.UTF-8/example.html", "{$cms['site_dir']}/.cms/man/ru_RU.UTF-8/hint_css.html", "{$cms['site_dir']}/.cms/man/ru_RU.UTF-8/hint_html.html", "{$cms['site_dir']}/.cms/man/ru_RU.UTF-8/hint_php.html", "{$cms['site_dir']}/.cms/man/ru_RU.UTF-8/menu.html", "{$cms['site_dir']}/.cms/man/css/prism.css", "{$cms['site_dir']}/.cms/man/css/styles.css", "{$cms['site_dir']}/.cms/man/js/prism.js", // Файлы шаблона "{$cms['site_dir']}/.cms/mini/html.php", "{$cms['site_dir']}/.cms/mini/404.en_US.UTF-8.php", "{$cms['site_dir']}/.cms/mini/404.ru_RU.UTF-8.php", "{$cms['site_dir']}/.cms/mini/404.uk_UA.UTF-8.php", "{$cms['site_dir']}/.cms/mini/banner.jpg", "{$cms['site_dir']}/.cms/mini/blog.php", "{$cms['site_dir']}/.cms/mini/favicon.svg", "{$cms['site_dir']}/.cms/mini/frontpage.php", "{$cms['site_dir']}/.cms/mini/instruction.ru_RU.UTF-8.html", "{$cms['site_dir']}/.cms/mini/page.php", "{$cms['site_dir']}/.cms/mini/page.teaser.php", "{$cms['site_dir']}/.cms/mini/post.php", "{$cms['site_dir']}/.cms/mini/styles.css", "{$cms['site_dir']}/.cms/mini/template.settings.php", "{$cms['site_dir']}/.cms/mini/tpl.mod.php", "{$cms['site_dir']}/.cms/mod/admin.mod.php", "{$cms['site_dir']}/.cms/mod/base.mod.php", "{$cms['site_dir']}/.cms/mod/menu.mod.php", "{$cms['site_dir']}/.cms/mod/pages.mod.php", "{$cms['site_dir']}/.cms/mod/sitemap.mod.php", "{$cms['site_dir']}/.cms/mod/rss.mod.php", "{$cms['site_dir']}/.cms/mod/template.mod.php", "{$cms['site_dir']}/.cms/index.fn.php", "{$cms['site_dir']}/.cms/index.php", "{$cms['site_dir']}/.cms/update.sql", "{$cms['site_dir']}/.cms/update.php", "{$cms['site_dir']}/.cms/.unlicense.txt", ); $no_sha1 = array( "{$cms['site_dir']}/.cms/filelist.php", ); $list = array(); while ( $cur = array_shift( $queue ) ) { if ( is_dir( $cur ) ) { $queue = array_merge( $queue, glob( $cur . "/*" ) ); } elseif ( is_file( $cur ) ) { if ( in_array( $cur, $no_sha1 ) ) { $sha1 = ""; $size = ""; } else { $sha1 = sha1_file( $cur ); $size = filesize( $cur ); } $file = str_replace( "{$cms['site_dir']}/", "", $cur ); $list[$file] = array( "sha1" => $sha1, "size" => $size, ); } else { $logs .= "<p>" . __( "file_or_dir_not_exists" ) . " '{$cur}'</p>"; } } // Список файлов которые разрешено редактировать. // И если они отредактированы, то не будут обновляться. $allow_change = array( ".htaccess", ); file_put_contents( "{$cms['cms_dir']}/filelist.php", "<?php\n\$allow_change = " . var_export( $allow_change, true ) . ";\n" . '$list = ' . var_export( $list, true ) . ";\n" ); $logs .= "<p>".__( "files" )." " . count( $list ) . "</p>"; return $logs; } function cms_update_check( $url ) { global $cms; // prevent warning if ( empty( $cms["config"]["admin.mod.php"]["last_check"] ) ) { $cms["config"]["admin.mod.php"]["last_check"] = 0; } $t = time(); $dt = $t - $cms["config"]["admin.mod.php"]["last_check"]; $time_min = 1; if ( $dt < $time_min * 60 ) { $n = ceil( ( $time_min * 60 - $dt ) / 60 ); $msg = __( "try_later" ); $msg = str_replace( "xxx", $n, $msg ); echo( json_encode( array( "info_text" => $msg, "info_class" => "info-error", "info_time" => 5000, ) ) ); return; } if ( ! $update = file_get_contents( $url ) ) { echo( json_encode( array( "info_text" => __( "cant_get" ) . " " . $url, "info_class" => "info-error", "info_time" => 5000, ) ) ); return; } if ( ! $update = json_decode( $update, true ) ) { echo( json_encode( array( "info_text" => __( "json_error" ) . " " . $url, "info_class" => "info-error", "info_time" => 5000, ) ) ); return; } $cms["config"]["admin.mod.php"]["last_check"] = $t; $cms["config"]["admin.mod.php"]["update"] = $update; cms_save_config(); $last_check = date( "d.m.Y H:i", $cms["config"]["admin.mod.php"]["last_check"] ); $last_version = $cms["config"]["admin.mod.php"]["update"]["last_version"]; if ( $cms["kernel_compat"] !== $cms["config"]["admin.mod.php"]["update"]["compat"] ) { $last_version .= ". " . __( "incompatible_v" ); } $update_info = "<p>" . __( "current_v" ) . " {$cms['kernel_version']}</p>"; $update_info .= "<p>" . __( "last_v" ) . " {$last_version}</p>"; $update_info .= "<p>" . __( "checked" ) . " {$last_check}</p>"; echo( json_encode( array( "info_text" => __( "last_v" ) . " {$last_version}", "info_class" => "info-success", "info_time" => 5000, "answer" => $update_info, ) ) ); return; } function cms_changed_files() { global $cms; $fl = "{$cms['cms_dir']}/filelist.php"; require( $fl ); $errors = array(); $max_errors = 100; // Проверка файлов текущей версии // по чексуммам и размерам foreach( $list as $fn => $file ) { $f = $cms["site_dir"] . "/" . $fn; if ( ! is_file( $f ) ) { if ( count( $errors ) >= $max_errors ) { break; } else { array_push( $errors, __( "file_not_exists" ) . " " . $f ); } } $sha1 = sha1_file( $f ); $size = filesize( $f ); if ( ! empty( $file["sha1"] ) && ! empty( $file["size"] ) && is_file( $f ) ) { if ( $file["sha1"] !== $sha1 || $file["size"] !== $size ) { if ( ! in_array( $fn, $allow_change ) ) { if ( count( $errors ) >= $max_errors ) { break; } else { array_push( $errors, __( "file_changed" ) . " " . $f ); } } } } } // Возвращаем список недочетов if ( count( $errors ) ) { $files = implode( "<br>", $errors ); } else { $files = __( "no_files_changed" ); } echo( json_encode( array( "answer" => $files, ) ) ); return; } function cms_update_cms_update() { global $cms; // Не проверялись обновления вообще if ( empty( $cms["config"]["admin.mod.php"]["update"] ) ) { echo( json_encode( array( "info_text" => __( "check_updates" ), "info_class" => "info-error", "info_time" => 5000, ) ) ); return; } // Отсутствует файл со списком файлов $fl = "{$cms['cms_dir']}/filelist.php"; if ( ! file_exists( $fl ) ) { echo( json_encode( array( "info_text" => __( "file_list_missing" ) . " " . $fl, "info_class" => "info-error", "info_time" => 5000, ) ) ); return; } require( $fl ); $update = $cms["config"]["admin.mod.php"]["update"]; // Может нечего обновлять if ( $update["last_version"] <= $cms["kernel_version"] ) { echo( json_encode( array( "info_text" => __( "already_updated" ), "info_class" => "info-success", "info_time" => 5000, ) ) ); return; } // Может несовместимая версия if ( $update["compat"] !== $cms["kernel_compat"] ) { echo( json_encode( array( "info_text" => __( "impossible" ), "info_class" => "info-error", "info_time" => 15000, ) ) ); return; } // Копить по 10 ошибок // чтобы не мучить по одной $errors = array(); $max_errors = 10; // Проверка файлов текущей версии // по чексуммам и размерам foreach( $list as $fn => $file ) { $f = $cms["site_dir"] . "/" . $fn; if ( ! is_file( $f ) ) { if ( count( $errors ) >= $max_errors ) { break; } else { array_push( $errors, __( "file_not_exists" ) . " " . $f ); } } $sha1 = sha1_file( $f ); $size = filesize( $f ); if ( ! empty( $file["sha1"] ) && ! empty( $file["size"] ) && is_file( $f ) ) { if ( $file["sha1"] !== $sha1 || $file["size"] !== $size ) { if ( ! in_array( $fn, $allow_change ) ) { if ( count( $errors ) >= $max_errors ) { break; } else { array_push( $errors, __( "file_changed" ) . " " . $f ); } } } } } // Возвращаем список недочетов if ( count( $errors ) ) { echo( json_encode( array( "info_text" => implode( "<br>", $errors ), "info_class" => "info-error", "info_time" => 50000, ) ) ); return; } // Check Rights $rights = array(); foreach( $list as $fn => $file ) { $file_path = $cms["site_dir"] . "/" . $fn; $e = ! is_file( $file_path ) || ! is_writable( $file_path ); if ( $e ) { if ( count( $rights ) >= $max_errors ) { break; } else { array_push( $rights, $file_path ); } } } // Возвращаем список недочетов if ( count( $rights ) ) { echo( json_encode( array( "info_text" => __( "update_error_rights" ) . "<br>" . implode( "<br>", $rights ), "info_class" => "info-error", "info_time" => 50000, ) ) ); return; } if ( empty( $cms["config"]["admin.mod.php"]["last_update"] ) ) { $cms["config"]["admin.mod.php"]["last_update"] = 0; } $t = time(); $dt = $t - $cms["config"]["admin.mod.php"]["last_update"]; $time_min = 1; if ( $dt < $time_min * 60 ) { $n = ceil( ( $time_min * 60 - $dt ) / 60 ); $msg = __( "try_later" ); $msg = str_replace( "xxx", $n, $msg ); echo( json_encode( array( "info_text" => $msg, "info_class" => "info-error", "info_time" => 5000, ) ) ); return; } // Download New Version if ( ! $content = file_get_contents( $update["download"] ) ) { echo( json_encode( array( "info_text" => __( "download_error" ), "info_class" => "info-error", "info_time" => 5000, ) ) ); return; } $tmp = $cms["site_dir"] . "/.tmp"; if ( is_dir( $tmp ) ) { recurse_rm( $tmp ); } if ( ! mkdir( $tmp ) ) { echo( json_encode( array( "info_text" => __( "cant_create_tmp_dir" ) . " " . $tmp, "info_class" => "info-error", "info_time" => 5000, ) ) ); return; } $fn = preg_replace( "/.*\//u", "", $update["download"] ); $file = $tmp . "/" . $fn; if ( ! file_put_contents( $file, $content ) ) { echo( json_encode( array( "info_text" => __( "cant_write_file" ) . " " . $file, "info_class" => "info-error", "info_time" => 50000, ) ) ); return; } // Unpack New Version // Object Oriented Style for future compability with PHP 8 $zip = new ZipArchive; if ( $zip->open( $file ) === TRUE ) { $ok = $zip->extractTo( $tmp ); $zip->close(); if ( ! $ok ) { echo( json_encode( array( "info_text" => __( "cant_extract" ) . " " . $file, "info_class" => "info-error", "info_time" => 5000, ) ) ); return; } } else { echo( json_encode( array( "info_text" => __( "cant_open_zip" ) . " " . $file, "info_class" => "info-error", "info_time" => 5000, ) ) ); return; } // Check New Version $fl = "{$tmp}/.cms/filelist.php"; if ( ! is_file( $fl ) ) { echo( json_encode( array( "info_text" => __( "file_list_missing" ) . " " . $fl, "info_class" => "info-error", "info_time" => 5000, ) ) ); return; } $oldlist = $list; require( $fl ); foreach( $list as $fn => $file ) { $f = $tmp . "/" . $fn; if ( ! is_file( $f ) ) { echo( json_encode( array( "info_text" => __( "file_not_exists" ) . " " . $f, "info_class" => "info-error", "info_time" => 5000, ) ) ); return; } $sha1 = sha1_file( $f ); $size = filesize( $f ); if ( ! empty( $file["sha1"] ) ) { if ( $file["sha1"] !== $sha1 ) { echo( json_encode( array( "info_text" => __( "file_changed" ) . " " . $f, "info_class" => "info-error", "info_time" => 5000, ) ) ); return; } } if ( ! empty( $file["size"] ) ) { if ( $file["size"] !== $size ) { echo( json_encode( array( "info_text" => __( "file_changed" ) . " " . $f, "info_class" => "info-error", "info_time" => 5000, ) ) ); return; } } } // Удаление файлов текущей цмс $removed = true; foreach( $oldlist as $fn => $file ) { $from = $cms["site_dir"] . "/" . $fn; if ( in_array( $fn, $allow_change ) and sha1_file( $from ) !== $oldlist[$fn]["sha1"] ) { // Если файл в списке разрешенных на изменение // и sha1 не совпадает, то оставляем его не тронутым } else { $c = unlink( $from ); $removed = $removed && $c; } // try remove dir $rdir = preg_replace( "/\/[^\/]+$/u", "", $from ); if ( is_dir_and_empty( $rdir ) ) { rmdir( $rdir ); } } if ( ! $removed ) { echo( json_encode( array( "info_text" => __( "update_error_remove" ), "info_class" => "info-error", "info_time" => 5000, ) ) ); return; } // Перемещение новой версии в текущую // Разрешенные измененные файлы не перезаписываются $moved = true; // по умолчанию нет ошибок foreach( $list as $file_name => $none ) { $old_file_path = $cms["site_dir"] . "/" . $file_name; if ( is_file( $old_file_path ) and in_array( $file_name, $allow_change ) and sha1_file( $old_file_path ) !== $oldlist[$file_name]["sha1"] ) { // Если файл в списке разрешенных на изменение // и sha1 не совпадает, то оставляем его не тронутым } else { $from = $tmp . "/" . $file_name; $to = $cms["site_dir"] . "/" . $file_name; $ndir = preg_replace( "/\/[^\/]+$/u", "", $to ); if ( ! is_dir( $ndir ) ) { mkdir( $ndir, 0777, true ); } $c = copy( $from, $to ); $moved = $moved && $c; } } if ( ! $moved ) { echo( json_encode( array( "info_text" => __( "update_error_move" ), "info_class" => "info-error", "info_time" => 5000, ) ) ); return; } recurse_rm( $tmp ); // Выполнить запросы в БД, находящиеся в файле update.sql // Не будем обращать внимания на результат запроса, // ведь если идет многократное обновление из dev-ветки, // то скорее всего будут ошибки его выполнения. $update_sql = "{$cms['cms_dir']}/update.sql"; $q = ""; if ( is_file( $update_sql ) ) { $q = file_get_contents( $update_sql ); } if ( $q ) { cms_base_connect(); if ( $cms["base"] ) { $res = mysqli_multi_query( $cms["base"], $q ); } } $cms["config"]["admin.mod.php"]["last_update"] = time(); // Обновить конфиг $update_config = "{$cms['cms_dir']}/update.php"; if ( is_file( $update_config ) ) { include( $update_config ); } $cms["config"]["admin.mod.php"]["clear_cache"] = true; cms_save_config(); cms_clear_cache(); echo( json_encode( array( "info_text" => __( "successfull_update" ), "info_class" => "info-success", "info_time" => 5000, "reload" => true, ) ) ); return; } function cms_update_create_zip() { global $cms; // Данная функция сообщит об отсутствующих файлах $files = cms_update_create_filelist(); require( "{$cms['cms_dir']}/filelist.php" ); $zip = new ZipArchive(); $v = $cms['kernel_version'] . date( "_Y.m.d_Hi" ); $name = "coffee-cms-{$v}.zip"; $download_url = "{$cms['url']['scheme']}://{$cms['url']['host']}/{$name}"; $zipname = "{$cms["site_dir"]}/{$name}"; if ( $zip->open( $zipname, ZipArchive::CREATE ) !== true ) { echo( json_encode( array( "answer" => __( "cant_create_zip" ) . " {$zipname}", ) ) ); return; } foreach( $list as $fn => $file ) { $from = "{$cms['site_dir']}/{$fn}"; $zip->addFile( $from, $fn ); } $zip->close(); file_put_contents( "{$cms["site_dir"]}/update_dev.json", json_encode( array( "last_version" => $v, "compat" => $cms["kernel_compat"], "download" => $download_url, ) ) ); echo( json_encode( array( "answer" => $files . "<p>" . __( "archive_created" ) . ": <a href='{$download_url}'>{$download_url}</a></p>", ) ) ); return; }