<?php

$cms["modules"]["pages.mod.php"] = array(
    "name"        => "pages_module_name",
    "description" => "pages_module_description",
    "files"       => array(
        ".cms/mod/pages.mod.php",
        ".cms/js/pages.js",
        ".cms/css/pages.less",
        ".cms/css/pages.css",
        ".cms/lang/ru_RU.UTF-8/pages.mod.php",
        ".cms/lang/en_US.UTF-8/pages.mod.php",
        ".cms/lang/uk_UA.UTF-8/pages.mod.php",
        ".cms/lang/translit.php",
        ".cms/lib/codemirror",
        ".cms/man/ru_RU.UTF-8/book.html",
        ".cms/man/ru_RU.UTF-8/codemirror.html",
        ".cms/man/ru_RU.UTF-8/example.html",
        ".cms/man/ru_RU.UTF-8/hint_css.html",
        ".cms/man/ru_RU.UTF-8/hint_html.html",
        ".cms/man/ru_RU.UTF-8/hint_php.html",
    ),
);

// Если модуль отключен
if ( ! empty( $cms["config"]["pages.mod.php"]["disabled"] ) ) {
    return;
}

if ( is_admin() ) {
    if ( empty( $_COOKIE["pages_pager"] ) ) {
        cms_set_sess_cookie( "pages_pager", 100 );
    }
    
    hook_add_fn( "create_tables", "cms_pages_create_tables" );
    hook_add_fn( "admin", "cms_pages_admin" );
    hook_add_fn( "admin_header", "cms_pages_admin_header" );
    hook_add_fn( "api", "cms_pages_api" );
    hook_add_fn( "mod_menu_item", "cms_pages_to_menu_item" );
    hook_add_fn( "mod_menu_pages_for_selector", "cms_pages_for_selector" );
}
hook_add_fn( "query", "cms_pages_query" );
hook_add_fn( "menu", "cms_pages_menu" );

// Модуль меню через хук запрашивает информацию о станице
function cms_pages_to_menu_item( &$item ) {
    global $cms;
    
    if ( ! isset( $item["page_id"] ) ) {
        return;
    }

    if ( empty( $item["found"] ) && ! empty( $cms["pdo"] ) ) {
        try {
            $stmt = $cms["pdo"]->prepare( "SELECT id, title, url, published FROM pages WHERE id = ?" );
            $stmt->execute( array( $item["page_id"] ) );
            $page = $stmt->fetch( PDO::FETCH_ASSOC );

            if ( $page ) {
                $item["link_title"] = $page["title"];
                $item["select_title"] = $page["title"];
                $item["url"] = $page["url"];
                $item["found"] = true;
                $item["published"] = $page["published"];
            } else {
                $item["link_title"] = __( "deleted_page" ); // Страница не найдена
                $item["select_title"] = __( "deleted_page" ); // Страница не найдена
                $item["found"] = false;
            }
        } catch ( PDOException $e ) {
            error_log( $e->getMessage() );
        }
    }
}


function cms_pages_for_selector( &$data ) {
    global $cms;
    if ( empty( $cms["pdo"] ) ) {
        return;
    }
    $s = preg_replace( "/\s/u", "", $_POST["search"] );
    $s = preg_split( '//u', $s, -1, PREG_SPLIT_NO_EMPTY );
    $s = implode( "%", $s );
    $search_param = "%{$s}%";
    $limit = (int) $data["limit"];
    try {
        $stmt = $cms["pdo"]->prepare( "SELECT id, title, url, pin, published FROM pages WHERE title LIKE ? OR url LIKE ? ORDER BY pin DESC, modified DESC LIMIT {$limit}" );
        $stmt->execute( array( $search_param, $search_param ) );

        while ( $page = $stmt->fetch( PDO::FETCH_ASSOC ) ) {
            if ( $page["published"] ) {
                $classes = "";
            } else {
                $classes = "unpublished";
            }
            $title = htmlspecialchars( $page["title"] );
            $data["pages"][] = array(
                "id" => $page["id"],
                "url" => $page["url"],
                "title" => $page["title"],
                "classes" => $classes,
            );
        }
    } catch ( PDOException $e ) {
        error_log( $e->getMessage() );
    }
}


// Нужно ли выводить скрипты и стили в админку.
// Определяется это видимостью пункта и секции.
function cms_pages_admin_header() {
    global $cms;
    // Вывод через echo более гибкий - позволяет делать вычисляемые стили.
    if ( is_admin_header_on( "pages.mod.php" ) ) {
        echo "        <link rel=stylesheet href='{$cms["base_path"]}css/pages.css'>\n";
        echo "        <script src='{$cms["base_path"]}js/pages.js'></script>\n";
    }
}


function cms_pages_api() {
    global $cms;
    
    if ( empty( $cms["pdo"] ) ) {
        return;
    }

    if ( ! empty( $_POST["fn"] ) ) {
        
        switch ( $_POST["fn"] ) {

            case "create_page":

                $t = "page";

                // Тексты для разных типов страниц
                $contents = __( "page_templates", $cms["config"]["template.mod.php"]["template"] );
                if ( ! is_array( $contents ) ) {
                    $contents = array();
                }
                
                // Default text for new page from template
                if ( ! empty( $contents[$t] ) ) {
                    $text = $contents[$t];
                } else {
                    $text = "";
                }
                
                // Чтобы не в UTC создало, а как положено
                $created = date( "Y-m-d H:i:sP" );

                try {
                    $stmt = $cms["pdo"]->prepare( "
                        INSERT INTO pages ( created, modified, t, text )
                        VALUES ( ?, ?, ?, ? )
                    " );
                    $stmt->execute( array( $created, $created, $t, $text ) );
                    $id = $cms["pdo"]->lastInsertId();
                } catch ( PDOException $e ) {
                    error_log( $e->getMessage() );
                    break;
                }

                // Create title, url and update in database
                $title     = __( "page_default_title" );
                $url       = "{$id}";
                
                // Если страница одна, то сделать ее главной
                try {
                    $count = $cms["pdo"]->query( "SELECT COUNT(*) FROM pages" )->fetchColumn();
                    if ( $count == 1 ) {
                        $url = "";
                        $title = __( "home_page" );
                    }
                } catch ( PDOException $e ) {
                    error_log( $e->getMessage() );
                    break;
                }
                
                try {
                    $stmt = $cms["pdo"]->prepare( "UPDATE pages SET title = ?, url = ? WHERE id = ?" );
                    $stmt->execute( array( $title, $url, $id ) );

                    // Чтобы не дублировать код используем функцию списка страниц
                    $_POST["new_page_id"] = $id;
                    $response = cms_pages_list_with_search();

                    if ( isset( $response["result"] ) ) {
                        $response["result"] = array_merge( $response["result"],
                            array(
                                "notify_html"  => __( "page_created" ),
                            )
                        );
                    }
                    echo( json_encode( $response ) );
                    return;
                } catch ( PDOException $e ) {
                    error_log( $e->getMessage() );
                    break;
                }
            break;

            case "save_page":

                $_POST["url"] = trim( $_POST["url"] );
                $_POST["url"] = ltrim( $_POST["url"], "/" );

                // Тексты для разных типов страниц
                $contents = __( "page_templates", $cms["config"]["template.mod.php"]["template"] );
                if ( ! is_array( $contents ) ) {
                    $contents = array();
                }

                // Если изменился шаблон, то возможно нужно поменять
                // текст по умолчанию, предоставляемый шаблоном.
                if ( $_POST["old_template"] !== $_POST["template"] ) {
                    // Текст предыдущего шаблона. Обязан быть.
                    if ( ! empty( $contents[ $_POST["old_template"] ] ) ) {

                        $old_text = $contents[ $_POST["old_template"] ];
                    
                        // Текст страницы в базе
                        try {
                            $stmt = $cms["pdo"]->prepare( "SELECT text FROM pages WHERE id = ?" );
                            $stmt->execute( array( $_POST["id"] ) );
                            $text_in_base = $stmt->fetchColumn();
                            
                            if ( $text_in_base === $old_text ) {
                                // Текст в базе и текст шаблона совпали.
                                // Теперь либо должен отсутствовать переданный текст,
                                // либо передан и совпадать со старым
                                if ( ! isset( $_POST["text"] ) || str_replace( "\r", "", $_POST["text"] ) === $old_text ) {
                                    if ( ! empty( $contents[ $_POST["template"] ] ) ) {
                                        $new_text = $contents[ $_POST["template"] ];
                                    } else {
                                        $new_text = "";
                                    }
                                }
                            }
                        } catch ( PDOException $e ) {
                            error_log( $e->getMessage() );
                            break;
                        }
                    }
                }

                // Не дублируется ли URL?
                try {
                    $stmt = $cms["pdo"]->prepare( "SELECT COUNT(*) FROM pages WHERE url = ? AND id <> ?" );
                    $stmt->execute( array( $_POST["url"], $_POST["id"] ) );
                    if ( $stmt->fetchColumn() > 0 ) {
                        $_POST["url"] = cms_uid();
                    }
                } catch ( PDOException $e ) {
                    error_log( $e->getMessage() );
                    break;
                }

                // INFO: вот это откатывает до целых секунд
                if ( empty( $_POST["date"] ) || empty( $_POST["time"] ) ) {
                    $created = date( "Y-m-d H:i:sP", time() );
                } else {
                    $created = date( "Y-m-d H:i:sP", strtotime( $_POST["date"] . " " . $_POST["time"] ) );
                }
                $modified = date( "Y-m-d H:i:sP", time() );

                $query = "UPDATE pages SET title=?, seo_title=?, description=?,
                        tags=?, created=?, modified=?, t=?, url=?";
                $params = array(
                    $_POST["title"], $_POST["seo_title"], $_POST["description"],
                    $_POST["tags"], $created, $modified, $_POST["template"], $_POST["url"]
                );

                if ( isset( $new_text ) ) {
                    $query .= ", text=?";
                    array_push( $params, $new_text );
                } elseif ( isset( $_POST["text"] ) ) {
                    $query .= ", text=?";
                    array_push( $params, $_POST["text"] );
                }
                
                $query .= " WHERE id=? AND modified=?";
                array_push( $params, $_POST["id"], $_POST["modified"] );

                // Очистка кеша перед сохранением чтобы при удалении
                // использовался старый url
                if ( function_exists( "cms_clear_cache" ) ) {
                    cms_clear_cache();
                }

                try {
                    $stmt = $cms["pdo"]->prepare( $query );
                    $stmt->execute( $params );

                    // Страница была изменена
                    $row_count = $stmt->rowCount();
                    if ( $row_count === 0 ) {
                        echo( json_encode( array(
                            "error" => array(
                                "notify_html" => __( "page_changed" ),
                            )
                        ) ) );
                        break;
                    }

                    if ( function_exists( "cms_sitemap_update" ) ) {
                        cms_sitemap_update();
                    }

                    if ( function_exists( "cms_rss_update" ) ) {
                        cms_rss_update();
                    }

                    $timestamp = strtotime( $created );
                    $title = htmlspecialchars( $_POST["title"] );
                    $current_template = $cms["config"]["template.mod.php"]["template"];
                    $page = array(
                        "t" => $_POST["template"],
                    );
                    $result = array(
                        "id"          => $_POST["id"],
                        "title"       => $title,
                        "url"         => $_POST["url"],
                        "base_path"   => $cms["base_path"],
                        "created"     => str_replace( ",", "<br>", format_date( $created ) ),
                        "planned"     => strtotime( $created ) > time(),
                        "modified"    => $modified,
                        "date"        => date( "Y-m-d", $timestamp ),
                        "time"        => date( "H:i", $timestamp ),
                        "t"           => $page["t"],
                        "options"     => template_options( $page ),
                        "t_tr"        => __( $page["t"], $current_template ) . " ({$page["t"]})",
                        "seo_title"   => $_POST["seo_title"],
                        "description" => $_POST["description"],
                        "tags"        => $_POST["tags"],
                    );

                    $result["notify_html"] = __( "updated" ) . '"' . $title . '"';

                    if ( isset( $new_text ) ) {
                        $result["new_text"] = $new_text;
                    }

                    // Узнать у модуля меню, задействована ли страница в нем и нужно ли обновить меню
                    $req = array(
                        "page_id" => $_POST["id"],
                        "update_menu" => false,
                    );
                    do_hook( "mod_pages_page_saved", $req );
                    $result["update_menu"] = $req["update_menu"];

                    echo( json_encode( array( "result" => $result ) ) );
                } catch ( PDOException $e ) {
                    error_log( $e->getMessage() );
                }
            break;

            case "page_pin":
                try {
                    $stmt = $cms["pdo"]->prepare( "UPDATE pages SET pin=? WHERE id=?" );
                    $stmt->execute( array( $_POST["pin"], $_POST["id"] ) );
                    echo( json_encode( array(
                        "result" => true,
                    ) ) );
                } catch ( PDOException $e ) {
                    error_log( $e->getMessage() );
                }
            break;
            
            case "page_publish":
                try {
                    $stmt = $cms["pdo"]->prepare( "UPDATE pages SET published=? WHERE id=?" );
                    $stmt->execute( array( $_POST["published"], $_POST["id"] ) );
                    echo( json_encode( array(
                        "result" => true,
                    ) ) );
                } catch ( PDOException $e ) {
                    error_log( $e->getMessage() );
                }
            break;

            case "get_page":
                try {
                    $stmt = $cms["pdo"]->prepare( "SELECT * FROM pages WHERE id = ?" );
                    $stmt->execute( array( $_POST["id"] ) );
                    $page = $stmt->fetch( PDO::FETCH_ASSOC );

                    if ( ! $page ) {
                        echo( json_encode( array(
                            "error" => array(
                                "notify_html" => __( "page_not_found" ),
                            )
                        ) ) );
                        break;
                    }

                    // Переводим в местное время
                    $timestamp = strtotime( $page["created"] . " UTC" );
                    $date = date( "Y-m-d", $timestamp );
                    $time = date( "H:i", $timestamp );

                    $current_template = $cms["config"]["template.mod.php"]["template"];
                    echo( json_encode( array(
                        "result" => array_merge( $page, array(
                            "base_path" => $cms["base_path"],
                            "flist"     => mediateka_flist( $page["id"] ),
                            "date"      => $date,
                            "time"      => $time,
                            "options"   => template_options( $page ),
                            "t_tr"      => __( $page["t"], $current_template ) . " ({$page["t"]})",
                        ) )
                    ) ) );
                } catch ( PDOException $e ) {
                    error_log( $e->getMessage() );
                }
            break;

            case "del_files":
                $count = 0;
                $upload_root = realpath( $cms["site_dir"] . "/uploads" );
                foreach ( $_POST["flist"] as $path_name ) {
                    // Чтобы не лезло выше $cms["site_dir"]
                    $rel_file = substr( $path_name, strlen( $cms["base_path"] ) );
                    $del_file = $cms["site_dir"]."/".$rel_file;
                    $real_del_file = realpath( $del_file );

                    // Безопасное удаление: проверяем, что файл существует и находится внутри /uploads/
                    if ( $real_del_file && is_file( $real_del_file ) && strpos( $real_del_file, $upload_root ) === 0 ) {
                        unlink( $real_del_file );
                        $count += 1;
                        // Попытка удалить родительскую директорию, если она пуста
                        $upload_dir = dirname( $real_del_file );
                        if ( is_dir_and_empty( $upload_dir ) ) {
                            rmdir( $upload_dir );
                        }
                    }
                }
                echo( json_encode( array(
                    "result" => array(
                        "notify_html"  => __( "files_deleted" ) . $count,
                    )
                ) ) );
            break;

            case "get_pages_list":
                $pages = cms_pages_list_with_search();
                if ( $pages ) {
                    echo( json_encode( $pages ) );
                }
                return;
            break;

            case "del_pages":
                // Clear cache
                if ( function_exists( "cms_clear_cache" ) ) {
                    cms_clear_cache();
                }

                // Создание корзины
                $trash = $cms["site_dir"] . "/uploads/.trash";
                if ( ! is_dir( $trash ) ) {
                    mkdir( $trash );
                }

                $update_menu = false;
                foreach( $_POST["ids"] as $id ) {
                    if ( $id = (int) $id ) {
                        // Вытаскивание и сохранение страницы
                        try {
                            $stmt = $cms["pdo"]->prepare( "SELECT * FROM pages WHERE id = ?" );
                            $stmt->execute( array( $id ) );
                            $p = $stmt->fetch( PDO::FETCH_ASSOC );

                            if ( $p ) {
                                file_put_contents( "{$trash}/{$id}.txt", "url {$p["url"]}\nid {$p["id"]}\npin {$p["pin"]}\npublished {$p["published"]}\ncreated {$p["created"]}\nmodified {$p["modified"]}\nt {$p["t"]}\ntitle {$p["title"]}\nseo_title {$p["seo_title"]}\ndescription {$p["description"]}\ntags {$p["tags"]}\ntext\n{$p["text"]}\n" );

                                // Delete page from base
                                $stmt = $cms["pdo"]->prepare( "DELETE FROM pages WHERE id = ?" );
                                $stmt->execute( array( $id ) );

                                // Перемещение файлов страницы
                                $files_dir = "{$cms["site_dir"]}/uploads/{$id}";
                                if ( is_dir( $files_dir ) ) {
                                    rename( $files_dir, "{$trash}/{$id}" );
                                }

                                $stmt = $cms["pdo"]->prepare( "SELECT * FROM menu WHERE page_id = ?" );
                                $stmt->execute( array( $id ) );
                                $m = $stmt->fetch( PDO::FETCH_ASSOC );
                                if ( $m ) {
                                    $update_menu = true;
                                    // Раз страница удалена - обновить меню целиком
                                }
                            }
                        } catch ( PDOException $e ) {
                            error_log( $e->getMessage() );
                            break;
                        }
                    }
                }

                // update sitemap and rss
                if ( function_exists( "cms_sitemap_update" ) ) {
                    cms_sitemap_update();
                }

                if ( function_exists( "cms_rss_update" ) ) {
                    cms_rss_update();
                }

                echo( json_encode( array(
                    "result" => array(
                        "notify_html" => __( "pages_deleted" ),
                        "update_menu" => $update_menu,
                    )
                ) ) );
            break;

            case "upload_files":
                $id = (int) $_POST["id"];
                $upload_dir = "{$cms["site_dir"]}/uploads/{$id}";
                
                // Создать папку
                if ( ! is_dir( $upload_dir ) && ! mkdir( $upload_dir, 0777, true ) ) {
                    echo( json_encode( array(
                        "error" => array(
                            "notify_html"  => __( "error_create_folder" ) . " " . $upload_dir,
                        )
                    ) ) );
                    break;
                }

                $success = true;
                foreach ( $_FILES["files"]["name"] as $n => $name ) {
                    if ( $_FILES["files"]["error"][$n] ) {
                        $success = false;
                        $text = __( "error_upload_file" ) . " \"{$name}\"";
                        break;
                    }

                    // Перемещение с заменой
                    if ( ! move_uploaded_file( $_FILES["files"]["tmp_name"][$n], "{$upload_dir}/{$name}" ) ) {
                        $success = false;
                        $text = __( "file_move_error" ) . " \"{$upload_dir}/{$name}\"";
                        break;
                    }
                }

                // Все файлы успешно загружены
                if ( $success ) {
                    try {
                        // Получить заголовок страницы
                        $stmt = $cms["pdo"]->prepare( "SELECT * FROM pages WHERE id = ?" );
                        $stmt->execute( array( $id ) );
                        $page = $stmt->fetch( PDO::FETCH_ASSOC );
                    } catch ( PDOException $e ) {
                        error_log( $e->getMessage() );
                        break;
                    }

                    $text = __( "files_uploaded" ) . '"' . $page["title"] . '"';
                    echo( json_encode( array(
                        "result" => array(
                            "notify_html" => $text,
                            "flist"       => mediateka_flist( $id ),
                        )
                    ) ) );
                } else {
                    echo( json_encode( array(
                        "error" => array(
                            "notify_html"  => $text,
                        )
                    ) ) );
                }
            break;

            case "replace_in_pages":
                $table  = preg_replace( "/[^a-zA-Z0-9_]/", "", $_POST["table"] );
                $id_col = preg_replace( "/[^a-zA-Z0-9_]/", "", $_POST["id_col"] );
                $column = preg_replace( "/[^a-zA-Z0-9_]/", "", $_POST["column"] );

                if ( empty( $table ) || empty( $id_col ) || empty( $column ) ) {
                    echo( json_encode( array(
                        "error" => array(
                            "notify_html"  => __( "invalid_table_or_column" ),
                        )
                    ) ) );
                    break;
                }

                $q = "SELECT {$id_col}, {$column} FROM {$table}";
                $t = trim( $_POST["search_regex"] );

                if ( ! empty( $t ) ) {
                    try {
                        // Escape / for regexp
                        $regex = preg_prepare( $_POST["search_regex"] );
                        
                        $stmt = $cms["pdo"]->prepare( $q );
                        $stmt->execute();
                        while( $page = $stmt->fetch( PDO::FETCH_ASSOC ) ) {

                            $new_text = preg_replace( $regex, $_POST["replace"], $page[$column] );
                            $id_val = $page[$id_col];

                            $q = "UPDATE {$table} SET {$column}=? WHERE {$id_col}=?";
                            $stmt2 = $cms["pdo"]->prepare( $q );
                            $stmt2->execute( array( $new_text, $id_val ) );
                        }
                    } catch ( PDOException $e ) {
                        error_log( $e->getMessage() );
                        break;
                    }
                }

                echo( json_encode( array(
                    "result" => array(
                        "info_text"  => __( "replace_ok" ),
                    )
                ) ) );
            break;
        }
    }
}

function template_options( $page ) {
    global $cms;
    // Типы страниц
    $type_list = array();
    $expr = "{$cms["cms_dir"]}/{$cms["config"]["template.mod.php"]["template"]}/*.php";
    foreach ( glob( $expr ) as $t ) {
        $name = preg_replace( "/.*\/([^\/]+)\.php/u", "$1", $t );
        // Имя типа не должно содержать точку
        if ( $name !== "html" && strpos( $name, "." ) === false ) {
            array_push( $type_list, $name );
        }
    }

    $current_template = $cms["config"]["template.mod.php"]["template"];

    $template_options = array();
    foreach( $type_list as $t ) {
        $tr_t = __( $t, $current_template );
        if ( $t !== $tr_t ) {
            $t_title = "{$tr_t} ({$t})";
        } else {
            $t_title = $t;
        }

        $template_options[] = "<div class=option value='{$t}'>{$t_title}</div>";
    }

    // Добавляем текущий подшаблон страницы в список если его там нет.
    // Такое может быть если переключен шаблон.
    $t = $page["t"];
    if ( ! in_array( $t, $type_list ) && ! empty( $t ) ) {
        $tr_t = __( $t, $current_template );
        if ( $t !== $tr_t ) {
            $t_title = "{$tr_t} ({$t})";
        } else {
            $t_title = $t;
        }

        $template_options[] = "<div class=option value='{$page["t"]}'>{$t_title}</div>";
    }

    $template_options = implode( "", $template_options );
    return $template_options;
}

function mediateka_flist( $page_id ) {
    global $cms;

    $farr = array();
    foreach ( glob( "{$cms["site_dir"]}/uploads/{$page_id}/*", GLOB_NOSORT ) as $path ) {
        if ( is_file( $path ) ) {
            $name = pathinfo( $path, PATHINFO_BASENAME );
            $html = mediateka_file( $page_id, $name );
            $farr[] = array(
                "path" => $path,
                "name" => $name,
                "html" => $html,
                "sort" => filemtime( $path ),
            );
        }
    }
    cms_asort( $farr );
    $farr = array_reverse( $farr );

    $flist = array();
    foreach ( $farr as $file ) {
        $flist[] = $file["html"];
    }
    $flist = implode( "", $flist );
    return $flist;
}

function mediateka_file( $page_id, $name ) {
    global $cms;
    
    $path = "{$cms["base_path"]}uploads/{$page_id}";
    $upload_dir  = "{$cms["site_dir"]}/uploads/{$page_id}";
    $ext = strtolower( pathinfo( $name, PATHINFO_EXTENSION ) );

    if ( file_exists( "{$cms["cms_dir"]}/img/icon.{$ext}.svg" ) ) {
        $icon = "{$cms["base_path"]}img/icon.{$ext}.svg";
    } else {
        $icon = "{$cms["base_path"]}img/icon.default.svg";
    }

    switch ( $ext ) {
        case "webp":
        case "tiff":
        case "jpeg":
        case "jpg":
        case "png":
        case "svg":
        case "gif":
        case "bmp":
        case "ico":
            $size = getimagesize( "{$upload_dir}/{$name}" );
            if ( ! empty( $size[3] ) ) {
                $size = $size[3];
            } else {
                $size = "";
            }
            $upd = time();
            $html = "
            <div class=file>
                <div class=name>{$name}</div>
                <img src='{$path}/{$name}?upd={$upd}' data-src='{$path}/{$name}' {$size}>
                <input type=checkbox name=del>
                <div class=ext>{$ext}</div>
            </div>";
        break;
        
        default:
            $html = "
            <div class=file>
                <div class=name>{$name}</div>
                <img src='{$icon}' data-src='{$path}/{$name}'>
                <input type=checkbox name=del>
                <div class=ext>{$ext}</div>
            </div>";
        break;
    }
    return $html;
}

// Create pages list
// $_POST["new_page_id"] = 123; нужно для момента создания страницы
// $_POST["count"] = "1000";
// $_POST["search"] = "test";
// $_POST["Shift"] = false;
// $_POST["Ctrl"] = false;
// $_COOKIE["pages_pager"] = 10;
function cms_pages_list_with_search() {
    global $cms;

    if ( empty( $cms["pdo"] ) ) {
        return;
    }
    
    // Переводы
    $tTitle           = __( "title" );
    $tDescription     = __( "description" );
    $tSeoTitle        = __( "seo_title" );
    $tT               = __( "t" );
    $tPublished       = __( "published" );
    $tUnPublished     = __( "unpublished" );
    $tDate            = __( "date" );
    $tTime            = __( "time" );
    $tSave            = __( "save" );
    $tProperties      = __( "properties" );
    $tEdit            = __( "edit" );
    $tr_pin           = __( "pin" );

    // Условия и значения поиска
    $search_cond   = array();
    $search_params = array();

    // Создана новая страница
    if ( ! empty( $_POST["new_page_id"] ) ) {
        array_push( $search_cond, "id = ?" );
        array_push( $search_params, (int) $_POST["new_page_id"] );
    }

    // Смещение
    if ( isset( $_POST["offset"] ) ) {
        $offset = (int) $_POST["offset"];
    } else {
        $offset = 0;
    }
    
    // Количество
    if ( ! empty( $_POST["count"] ) ) {
        $limit = (int) $_POST["count"];
    } elseif ( ! empty( $_COOKIE["pages_pager"] ) ) {
        $limit = (int) $_COOKIE["pages_pager"];
    } else {
        $limit = 100;
    }

    // Устанавливаем $search для возврата из функции
    if ( empty( $_POST["search"] ) ) {
        $search = "";
    } else {
        $search = trim( $_POST["search"] );
        $sql_search = $search;

        // Shift разбивает искомое на символы
        if ( $_POST["Shift"] === "true" ) {
            $sql_search = preg_replace( "/\s/u", "", $sql_search );
            $sql_search = preg_split( '//u', $sql_search, -1, PREG_SPLIT_NO_EMPTY );
            $sql_search = implode( "%", $sql_search );
        }

        $sql_search = "%" . $sql_search . "%";

        // Ctrl ищет только по тексту
        if ( $_POST["Ctrl"] === "true" ) {
            array_push( $search_cond, "text LIKE ?" );
            array_push( $search_params, $sql_search );
        } else {
            array_push( $search_cond, "( title LIKE ? OR url LIKE ? OR t LIKE ? OR tags LIKE ? )" );
            array_push( $search_params, $sql_search, $sql_search, $sql_search, $sql_search );
        }
    }

    // Количество страниц
    try {
        $query = "SELECT COUNT(*) FROM pages";
        if ( count( $search_cond ) > 0 ) {
            $query .= " WHERE " . implode( " AND ", $search_cond );
        }
        $stmt = $cms["pdo"]->prepare( $query );
        $stmt->execute( $search_params );
        $count = $stmt->fetchColumn();
    } catch ( PDOException $e ) {
        error_log( $e->getMessage() );
        return;
    }

    $current_template = $cms["config"]["template.mod.php"]["template"];

    $pages = array();
    $start = microtime( true );
    $overload = false;
    $notify_html = "";

    try {
        // Перечисляем все кроме text
        $query = "SELECT id, published, pin, title, seo_title, description,
            created, url, t, tags, modified FROM pages";
        if ( count( $search_cond ) > 0 ) {
            $query .= " WHERE " . implode( " AND ", $search_cond );
        }
        $query .= " ORDER BY pin DESC, GREATEST( created, modified ) DESC LIMIT ? OFFSET ?";
        
        if ( $cms["pdo"]->getAttribute( PDO::ATTR_DRIVER_NAME ) === "mysql" ) {
            $query = str_replace( "LIMIT ? OFFSET ?", "LIMIT {$limit} OFFSET {$offset}", $query );
        } else {
            array_push( $search_params, $limit, $offset );
        }
        $stmt = $cms["pdo"]->prepare( $query );
        $stmt->execute( $search_params );

        while ( $page = $stmt->fetch( PDO::FETCH_ASSOC ) ) {
            
            $time = strtotime( $page["created"] );
            if ( time() >= $time ) {
                $date_class = "past";
            } else {
                $date_class = "future";
            }

            $created = str_replace( ",", "<br>", format_date( $time ) );
            $date    = date( "Y-m-d", $time );
            $time    = date( "H:i", $time );

            $template_options = template_options( $page );

            if ( $page["published"] ) {
                $tpub = $tPublished;
            } else {
                $tpub = $tUnPublished;
            }

            $classes = array();
            if ( $page["url"] == "" ) {
                $classes[] = "home";
            }

            if ( empty( $page["t"] ) ) {
                $t_translated = __( "no_template" );
            } else {
                // Если есть локализация шаблона, то указываем оригинальное имя
                // латиницей в скобках чтобы искать по нему
                $tr_t = __( $page["t"], $current_template );
                if ( $tr_t !== $page["t"] ) {
                    $t_translated = "{$tr_t} ({$page["t"]})";
                } else {
                    $t_translated = $page["t"];
                }
            }

            $classes[] = "page";
            $classes = implode( " ", $classes );

            // Экранирование данных, выводимых в HTML
            $esc_title       = htmlspecialchars( $page["title"],       ENT_QUOTES );
            $esc_seo_title   = htmlspecialchars( $page["seo_title"],   ENT_QUOTES );
            $esc_description = htmlspecialchars( $page["description"], ENT_QUOTES );
            $esc_url         = htmlspecialchars( $page["url"],         ENT_QUOTES );
            $esc_t         = htmlspecialchars( $page["t"],         ENT_QUOTES );
            $esc_t_name    = htmlspecialchars( $t_translated,      ENT_QUOTES );
            $esc_tags        = htmlspecialchars( $page["tags"],        ENT_QUOTES );

            $html = "
<div class='{$classes}' data-id={$page["id"]} data-pin={$page["pin"]} data-published={$page["published"]} data-modified='{$page["modified"]}'>

    <a class=title href='{$cms["base_path"]}{$page["url"]}' target=_blank title='id={$page["id"]}'>{$esc_title}</a>
    <div class=pin title='" . $tr_pin . "'></div>
    <div class=eye title='{$tpub}'></div>
    <div class=buttons>
        <div class='button save'><span>{$tSave}</span></div>
        <div class='button prop'><span>{$tProperties}</span></div>
        <div class='button edit'><span>{$tEdit}</span></div>
    </div>
    <div class='date {$date_class}'>{$created}</div>
    <input type=checkbox name=del>
    
    <div class=pprop>
        
        <div class=left>
            <div class=title>{$tTitle}:</div>
            <input name=title type=text value='{$esc_title}'>

            <div class='url title'>URL:</div>
            <div class='url widget'>
                <input name=url type=text value='{$esc_url}' placeholder='" . __( "front_url" ) . "'>
                <div class='button translit' title='" . __( "url_translit" ) . "'><span></span></div>
            </div>

            <div class=title>{$tSeoTitle}:</div>
            <input name=seo_title type=text value='{$esc_seo_title}'>

            <div class='description title'>{$tDescription}:</div>
            <textarea name=description rows=3>{$esc_description}</textarea>
        </div>

        <div class=right>
            <div class=title>{$tT}:</div>
            <div class='template select widget'>
                <div class=selected data-value='{$esc_t}' data-old-value='{$esc_t}'>
                    <div class=value>{$esc_t_name}</div>
                    <div class='icon down button'><span></span></div>
                </div>
                <div class=options>
                    {$template_options}
                </div>
            </div>

            <div class=title>{$tDate}:</div>
            <input name=date type=date value='{$date}'>

            <div class=title>{$tTime}:</div>
            <input name=time type=time value='{$time}'>

            <div class=title>" . __( "tags" ) . ":</div>
            <textarea name=tags rows=3>{$esc_tags}</textarea>
        </div>

    </div>

</div>";
            array_push( $pages, array(
                "id" => (int) $page["id"],
                "html" => $html
            ) );
            // Следим за перегрузкой
            if ( microtime( true ) - $start > 1 ) {
                $overload = true;
                $notify_html = __( "server_overloaded_xxx" );
                $notify_html = str_replace( "xxx", count( $pages ), $notify_html );
                break;
            }
        }
    } catch ( PDOException $e ) {
        error_log( $e->getMessage() );
        return;
    }

    return array( "result" => array(
        "pages"    => $pages, 
        "offset"   => $offset, 
        "count"    => $count, 
        "overload" => $overload,
        "search"   => $search,
        "notify_html" => $notify_html,
    ) );
}


function cms_pages_query() {
    global $cms;
    
    // Запрашивается файл - незачем лезть в базу
    if ( is_file( $cms["url"]["file"] ) ) {
        return;
    }

    if ( empty( $cms["pdo"] ) ) return;
    
    // fix template warnings
    $cms["page"]["id"] = 0;
    $cms["page"]["t"] = "";
    
    try {
        // Три условия попадания на страницу:
        $variants = array(
            // полное совпадение
            $cms["url"]["path"],
            // в базе слеш, значит папка - попадание. На диске будет созада папка
            // и впоследствии веб-сервер сам отредиректит как это всегда бывает
            $cms["url"]["path"] . "/",
            // запросили папку перейдя по ссылке из поисковика, но мы переезжаем
            // на файлы и не хотим отдавать 404 чтобы не терять посетителя
            rtrim( $cms["url"]["path"], "/" )
        );
        $stmt = $cms["pdo"]->prepare( "SELECT * FROM pages WHERE url IN ( ?, ?, ? )" );
        $stmt->execute( $variants );
        $page = $stmt->fetch( PDO::FETCH_ASSOC );
        // Тут можно было бы редиректить на
        // $canonical = $page["url"];
        // if ($cms["url"]["path"] !== $canonical) {
        //     header("Location: " . $canonical, true, 301);
        //     exit;
        // }

        if ( $page ) {
            $cms["page"] = $page;
            if ( $page["created"] <= date( "Y-m-d H:i:sP" ) && $page["published"] ) {
                $cms["status"] = "200";

                // Ускорение для краулеров.
                // Но страницы админок и ЛК всегда динамичны.
                // Их нужно добавить в исключения в карту сайта.
                if ( isset( $_SERVER["HTTP_IF_MODIFIED_SINCE"] ) ) {
                    if ( $modified = max( strtotime( $page["created"] ), (int) $page["modified"] ) ) {
                        $if_modified_since = strtotime( trim( $_SERVER["HTTP_IF_MODIFIED_SINCE"] ) );
                        if ( $if_modified_since >= $modified ) {
                            $exclude = explode( "\n", $cms["config"]["sitemap.mod.php"]["exclude"] );
                            if ( ! in_array( $page["url"], $exclude ) ) {
                                $cms["status"] = "304";
                            }
                        }
                    }
                }
                // Получаем страницу и смотрим заголовки
                // curl -I https://yoursite.ru/page
                // HTTP/2 200 
                // server: nginx-reuseport/1.21.1
                // date: Thu, 26 Feb 2026 12:20:56 GMT
                // content-type: text/html; charset=UTF-8
                // vary: Accept-Encoding
                // x-powered-by: PHP/7.3.31
                // cache-control: max-age=0
                // Повторный запрос с If-Modified-Since
                // curl -I -H "If-Modified-Since: Wed, 25 Feb 2026 14:20:00 GMT" https://yoursite.ru/page
            } else {
                $cms["status"] = "404"; // disable write to disk but echo if admin
            }
        }
    } catch ( PDOException $e ) {
        error_log( $e->getMessage() );
    }
}

function cms_pages_create_tables() {
    global $cms;

    if ( empty( $cms["pdo"] ) ) {
        return;
    }

    $type = $cms["pdo"]->getAttribute( PDO::ATTR_DRIVER_NAME );

    if ( $type === "mysql" ) {
        $id_column = "id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY";
        $text_column = "text LONGTEXT DEFAULT NULL";
    } elseif ( $type === "pgsql" ) {
        $id_column = "id BIGSERIAL PRIMARY KEY";
        $text_column = "text TEXT DEFAULT NULL";
    } else {
        return; // Неизвестный тип БД
    }

    // Универсальный SQL-запрос
    // TIMESTAMP - общий знаменатель для mysql и pgsql
    $sql = "
    CREATE TABLE IF NOT EXISTS pages (
        {$id_column},
        pin BOOLEAN NOT NULL DEFAULT FALSE,
        published BOOLEAN NOT NULL DEFAULT TRUE,
        created DATETIME NOT NULL,
        modified DATETIME NOT NULL,
        t VARCHAR(64) DEFAULT NULL,
        title VARCHAR(255) DEFAULT NULL,
        seo_title VARCHAR(255) DEFAULT NULL,
        {$text_column},
        url VARCHAR(255) DEFAULT NULL,
        description VARCHAR(2048) NOT NULL DEFAULT '',
        tags VARCHAR(2048) NOT NULL DEFAULT ''
    );";

    try {
        $cms["pdo"]->exec( $sql );
    } catch ( PDOException $e ) {
        error_log( $e->getMessage() );
    }
    // В MySQL 8.0+: используется utf8mb4_0900_ai_ci.
    // В MySQL 5.7 и ниже: используется utf8mb4_general_ci.
}

#region cms_pages_admin
function cms_pages_admin() {
    global $cms;

    $conf = $cms["config"]["pages.mod.php"];
    $menu_pages = $conf["menu"]["pages"];
    $module_visible = empty( $menu_pages["hide"] );
    $section_visible = empty( $cms["config"]["admin_sections"][$menu_pages["section"]]["hide"] );
    $visible = $module_visible && $section_visible;

    if ( $visible ) {
    
        $tMaxUploadSize  = __( "max_size" ) . " " . number_format( file_upload_max_size(), 0, ".", " " ) . " " . __( "bytes" );

        $help_file = "{$cms["config"]["template.mod.php"]["template"]}/instruction.{$cms["config"]["locale"]}.html";
        if ( is_file( "{$cms["cms_dir"]}/{$help_file}" ) ) {
            $help_blanks = "<a target=_blank href='{$cms["base_path"]}{$help_file}'>" . __( "blanks" ) . "</a>";
        } else {
            $help_blanks = "";
        }

        $files_panel = "
<div class=pan>
    <div class=hscroll>
        <div class=files>
            
        </div>
    </div>
    
    <div class=buttons>
        <button class='button upload' title='{$tMaxUploadSize}'>
            <span>
                <input type=file name='myfile[]' multiple>
                <span>" . __( "upload" ) . "</span>
            </span>
        </button>

        <div class=link>
            <span class=tag></span>
            <span class='button copy'>
                <span>" . __( "copy" ) . "</span>
            </span>
        </div>

        <div class='button del disabled'>
            <span>" . __( "delete" ) . "</span>
        </div>
    </div>
    
</div>";

        $buttons  = "<div class='button save'><span>" . __( "save" ) . "</span></div>";
        $buttons .= "<div class='button prop'><span>" . __( "properties" ) . "</span></div>";
        $buttons .= "<div class='button media'><span>" . __( "mediateka" ) . "</span></div>";
        $buttons .= "<div class='button tags'><span>" . __( "tags" ) . "</span></div>";
        $buttons .= "<div class='button replace'><span>" . __( "replace" ) . "</span></div>";
        $buttons .= $help_blanks;
        $no_hotkey = __( "no_hotkey" );

        $page = "
<div class=header>

    <div class='add button'>
        <span class=mobile>
            <span class=x1></span>
            <span class=x2></span>
        </span>
        <span class=desktop>" . __( "add_page_title" ) . "</span>
    </div>
    
    " . widget( "search_widget.html" ) . "

    <div class='button trash icon' title='" . __( "delete_pages" ) . "'>
        <span></span>
    </div>
    
</div>

<div class=main>
    
    <div class=pages>
    </div>
    
</div>

" . widget( "footer_pager.html" ) . "

<!-- Ради изучения тега <template> -->
<template>
<div class=editor>
    <div class=panel>

        <div class=header>
            <div class='button close icon'>
                <span></span>
            </div>
            <div class='button minimize icon'>
                <span><span class=dash></span></span>
            </div>
            <a class=title target=_blank></a>
        </div>

        <div class=buttons>
            {$buttons}
        </div>
        
        <div class=pprop>

            <div class=left>
                <div class=title>" . __( "title" ) . ":</div>
                <input name=title type=text>

                <div class=title>URL:</div>
                <div class='url widget'>
                    <input name=url type=text placeholder='" . __( "front_url" ) . "'>
                    <div class='button translit' title='" . __( "url_translit" ) . "'><span></span></div>
                </div>

                <div class=title>" . __( "seo_title" ) . ":</div>
                <input name=seo_title type=text>

                <div class=title>" . __( "description" ) . ":</div>
                <textarea name=description rows=3></textarea>
            </div>

            <div class=right>
                <div class=title>" . __( "t" ) . ":</div>
                <div class='template select widget'>
                    <div class=selected data-template data-old-template>
                        <div class=value></div>
                        <div class='icon down button'><span></span></div>
                    </div>
                    <div class=options>
                        
                    </div>
                </div>

                <div class=title>" . __( "date" ) . ":</div>
                <input name=date type=date>

                <div class=title>" . __( "time" ) . ":</div>
                <input name=time type=time>

                <div class=title>" . __( "tags" ) . ":</div>
                <textarea name=tags rows=3></textarea>
            </div>

        </div>

        <div class=mediateka>
            <div class=progress></div>
            {$files_panel}
        </div>

        <div class=textarea>
            <textarea></textarea>
        </div>

        <div class=tags>
            <div class=buttons>

                <div class=button title='{$no_hotkey}' data-type=wrap data-otag='<h1>' data-ctag='</h1>' data-len=4>
                    <span>
                        <span>" . __( "h1" ) . "</span>
                        <span class=tag>&lt;h1></span>
                    </span>
                </div>

                <div class=button title='Ctrl + H' data-type=wrap data-otag='<h2>' data-ctag='</h2>' data-len=4>
                    <span>
                        <span>" . __( "h1" ) . "</span>
                        <span class=tag>&lt;h2></span>
                    </span>
                </div>

                <div class=button title='Ctrl + P' data-type=wrap data-otag='<p>' data-ctag='</p>' data-len=3>
                    <span>
                        <span>" . __( "p" ) . "</span>
                        <span class=tag>&lt;p></span>
                    </span>
                </div>

                <div class=button title='Ctrl + D' data-type=wrap data-otag='<div>' data-ctag='</div>' data-len=5>
                    <span>
                        <span>" . __( "div" ) . "</span>
                        <span class=tag>&lt;div></span>
                    </span>
                </div>

                <div class=button title='Ctrl + Shift + A' data-type=wrap-a>
                    <span>
                        <span>" . __( "link" ) . "</span>
                        <span class=tag>&lt;a></span>
                    </span>
                </div>

                <div class=button title='Ctrl + <' data-type=wrap data-otag='<code>' data-ctag='</code>' data-len=6>
                    <span>
                        <span>" . __( "code" ) . "</span>
                        <span class=tag>&lt;code></span>
                    </span>
                </div>

                <div class=button title='Ctrl + Shift + <' data-type=wrap data-otag='<pre><code>' data-ctag='</code></pre>' data-len=11>
                    <span>
                        <span>" . __( "code" ) . "</span>
                        <span class=tag>&lt;pre>&lt;code></span>
                    </span>
                </div>

                <div class=button title='Ctrl + Shift + S' data-type=wrap data-otag='<span>' data-ctag='</span>' data-len=6>
                    <span>
                        <span>" . __( "span" ) . "</span>
                        <span class=tag>&lt;span></span>
                    </span>
                </div>

                <div class=button title='Ctrl + >' data-type=wrap data-otag='<blockquote>' data-ctag='</blockquote>' data-len=12>
                    <span>
                        <span>" . __( "cite" ) . "</span>
                        <span class=tag>&lt;blockquote></span>
                    </span>
                </div>

                <div class=button title='Ctrl + U' data-type=wrap-list data-tag=ul>
                    <span>
                        <span>" . __( "ul" ) . "</span>
                        <span class=tag>&lt;ul></span>
                    </span>
                </div>

                <div class=button title='Ctrl + O' data-type=wrap-list data-tag=ol>
                    <span>
                        <span>" . __( "ol" ) . "</span>
                        <span class=tag>&lt;ol></span>
                    </span>
                </div>
                
                <div class=button title='Ctrl + L' data-type=wrap data-otag='<li>' data-ctag='</li>' data-len=4>
                    <span>
                        <span>" . __( "li" ) . "</span>
                        <span class=tag>&lt;li></span>
                    </span>
                </div>
                
                <div class=button title='Ctrl + M' data-type=wrap data-otag='<figure>' data-ctag='\\n    <figcaption></figcaption>\\n</figure>' data-ch=16 data-line=1>
                    <span>
                        <span>" . __( "figure" ) . "</span>
                        <span class=tag>&lt;figure></span>
                    </span>
                </div>
                
                <div class=button title='Ctrl + Shift + C' data-type=wrap data-otag='<!-- ' data-ctag=' -->' data-len=5>
                    <span>
                        <span class=tag>&lt;!--</span>
                        <span>" . __( "comment" ) . "</span>
                        <span class=tag>--></span>
                    </span>
                </div>
                
                <div class=button title='Ctrl + B' data-type=wrap data-otag='<b>' data-ctag='</b>' data-len=3>
                    <span>
                        <span>" . __( "bold" ) . "</span>
                        <span class=tag>&lt;b></span>
                    </span>
                </div>

                <div class=button title='Ctrl + K' data-type=wrap data-otag='<kbd>' data-ctag='</kbd>' data-len=5>
                    <span>
                        <span>" . __( "kbd" ) . "</span>
                        <span class=tag>&lt;kbd></span>
                    </span>
                </div>

                <div class=button title='Ctrl + Shift + >' data-type=wrap data-otag='<mark>' data-ctag='</mark>' data-len=6>
                    <span>
                        <span>" . __( "mark" ) . "</span>
                        <span class=tag>&lt;mark></span>
                    </span>
                </div>

            </div>
        </div>
    </div>
</div>
</template>
";

        // Create menu item if not exists
        $section_id = "pages";
        if ( empty( $cms["config"]["pages.mod.php"]["menu"][$section_id] ) ) {
            $cms["config"]["pages.mod.php"]["menu"][$section_id] = array(
                "title"    => "pages_module_name",
                "sort"     => 10,
                "section"  => "content",
            );
            cms_save_config();
        }
        $cms["admin_pages"][$section_id] = $page;
        
        // Пункт справки по редактору
        if ( empty( $cms["config"]["pages.mod.php"]["menu"]["help_pages"] ) ) {
            $man_file = "man/{$cms["config"]["locale"]}/codemirror.html";
            if ( is_file( $cms["cms_dir"] . "/" . $man_file ) ) {
                $cms["config"]["pages.mod.php"]["menu"]["help_pages"] = array(
                    "title"    => "page_editor_codemirror",
                    "sort"     => 10,
                    "section"  => "help",
                    "url"      => $cms["base_path"] . $man_file,
                );
                // Не сохраняем чтобы переключалось при изменении языка
                //cms_save_config();
            }
        }
    }
    
    // $conf = $cms["config"]["pages.mod.php"];
    $menu_utils = $conf["menu"]["pages"];
    $module_visible = empty( $menu_utils["hide"] );
    $section_visible = empty( $cms["config"]["admin_sections"][$menu_utils["section"]]["hide"] );
    $visible = $module_visible && $section_visible;
    
    if ( $visible ) {
    
        // Create menu item if not exists
        if ( empty( $cms["config"]["pages.mod.php"]["menu"]["pages-utils"] ) ) {
            $cms["config"]["pages.mod.php"]["menu"]["pages-utils"] = array(
                "title"    => "pages_utils",
                "sort"     => 15,
                "section"  => "content",
                "hide"     => true,
            );
            cms_save_config();
        }

        $page = "
<div class=main>
    <p>" . __( "replace_instruction" ) . "</p>
    <div class=fields>
        <div class=field>
            <div class=title>" . __( "replace_table" ) . "</div>
            <input type=text name=table autocomplete=off value=pages>
        </div>
        <div class=field></div>
        <div class=field>
            <div class=title>" . __( "replace_id_col" ) . "</div>
            <input type=text name=id_col autocomplete=off value=id>
        </div>
        <div class=field>
            <div class=title>" . __( "replace_column" ) . "</div>
            <input type=text name=column autocomplete=off value=text>
        </div>
        <div class=field>
            <div class=title>" . __( "regex_title" ) . "</div>
            <input type=text name=search_regex autocomplete=off>
        </div>
        <div class=field>
            <div class=title>" . __( "replace_title" ) . "</div>
            <input type=text name=replace autocomplete=off>
        </div>
    </div>
    <div class='button replace'>
        <span>" . __( "replace_btn" ) . "</span>
    </div>
</div>
    ";

        $cms["admin_pages"]["pages-utils"] = $page;
    }
}
#endregion cms_pages_admin

// Еще не подключено, хук "menu" не вызывается
function cms_pages_menu( &$menu ) {
    global $cms;

    foreach( $menu as $id => $item ) {
        if ( is_array( $item["items"] ) ) {
            cms_pages_menu_items( $menu[$id]["items"] );
        }
    }
}


function cms_pages_menu_items( &$items ) {
    global $cms;
    
    if ( empty( $cms["pdo"] ) ) {
        return;
    }

    try {
        // Подготавливаем запрос один раз перед циклом для производительности
        $stmt = $cms["pdo"]->prepare( "SELECT title, url FROM pages WHERE id = ?" );

        foreach( $items as $id => $item ) {
            if ( ! empty( $item["id"] ) ) {
                $stmt->execute( array( $item["id"] ) );
                $page = $stmt->fetch( PDO::FETCH_ASSOC );

                if ( $page ) {
                    if ( empty( $items[$id]["title"] ) ) {
                        $items[$id]["title"] = $page["title"];
                    }
                    $items[$id]["url"] = $page["url"];
                } else {
                    $items[$id]["title"] = __( "deleted_page" );
                }
            }
            if ( ! empty( $items[$id]["items"] ) && is_array( $items[$id]["items"] ) ) {
                cms_pages_menu_items( $items[$id]["items"] );
            }
        }
    } catch ( PDOException $e ) {
        error_log( $e->getMessage() );
    }
}
