Pages, Articles & News
Tools & Plugins
Example / Placeholder Title


Description as an example placeholder. Sample text content for further customization of the "Index36" template.

You are free to edit and customize the template however you like. If you don’t have time or enough knowledge — you can always order template adaptation by contacting me via GitHub or private messages on the digital goods marketplace.

2025-07-07 19:16

Продолжая искать интересные решения по модификации кода Cotonti там, где разработчики не реализовали удобные элементы интерфейса, было интересно сделать фильтр по категориям в статьях и быстрый поиск по слову или фразу в строке поиска, в админке.

Все эти лайфхаки придется выполнять повторно при обновлении движка на своем сайте.

Все выполнялось на последней версии движка с репозитория, по состоянию на 07.07.2025
Вам понадобится мой шаблон админки, точнее тема админ панели управления сайтом с репозитория, которая уже содержит подключенную библиотеку Select2.

И так, кому не терпится, - скриншот прикреплен, того, что из этого вышло.
Предполагается, что скин "cotcp" уже установлен. (как установить “CotCP” и другие вопросы в этом разделе)

 

1. Открывае свой файл page.functions.php

/modules/page/inc/page.functions.php

и после всего кода, в самый конец добавляем код

/**
* Renders structure dropdown for cot_page_selectbox_structure_select2 with Select2(https://select2.org/) support and indented subcategories
*
* @param string $extension Extension code
* @param string $check Selected value
* @param string $name Dropdown name
* @param string $subcat Show only subcats of selected category
* @param bool $hidePrivate Hide private categories
* @param bool $isModule TRUE for modules, FALSE for plugins
* @param bool $addEmpty Allow empty choice
* @param mixed $attrs Additional attributes as an associative array or a string
* @param string $customRC Custom resource string name
* @return string
*/
function cot_page_selectbox_structure_select2(
   $extension,
   $check,
   $name,
   $subcat = '',
   $hidePrivate = true,
   $isModule = true,
   $addEmpty = false,
   $attrs = '',
   $customRC = ''
) {
   $categories = is_array(Cot::$structure[$extension]) ? Cot::$structure[$extension] : [];

   $options = [];

   foreach ($categories as $code => $category) {
       $display = ($hidePrivate && $isModule) ? cot_auth($extension, $code, 'W') : true;

       if ($display && !empty($subcat) && isset(Cot::$structure[$extension][$subcat])) {
           $mtch = Cot::$structure[$extension][$subcat]['path'] . '.';
           $mtchlen = mb_strlen($mtch);
           $display = (mb_substr($category['path'], 0, $mtchlen) == $mtch || $code === $subcat);
       }

       if ((!$isModule || cot_auth($extension, $code, 'R')) && $code !== 'all' && $display) {
           $depth = substr_count($category['path'], '.');
           $selected = ($code === $check) ? ' selected' : '';
           $attrs_str = is_array($attrs) ? cot_rc_attr_string($attrs) : $attrs;

           $options[] = '<option value="' . htmlspecialchars($code) . '" data-depth="' . $depth . '"' . $selected . ' ' . $attrs_str . '>' .
                        htmlspecialchars($category['title']) . '</option>';
       }
   }

   if ($addEmpty) {
       array_unshift($options, '<option value="">---</option>');
   }

   /* === Hook === */
   foreach (cot_getextplugins('selectBox.structure') as $pl) {
       include $pl;
   }
   /* ===== */

   return '<select name="' . htmlspecialchars($name) . '" class="form-select">' . implode("\n", $options) . '</select>';
}

/**
* Select page cat for search form. Используется с Select2 (https://select2.org/)
*
* @global array $structure
* @param string $check Selected category code
* @param string $name Name of the select input
* @param string $subcat Parent category code for filtering subcategories
* @param bool $hideprivate Hide private categories
* @return string
*/
function cot_page_selectcat_select2($check, $name, $subcat = '', $hideprivate = true)
{
   // Доступ к глобальной структуре категорий
   global $structure;

   // Проверяем, что массив категорий существует, иначе инициализируем пустым
   $structure['page'] = is_array($structure['page']) ? $structure['page'] : [];

   // Переменная для накопления всех option'ов
   $options = '';

   // Перебираем все категории в разделе 'page'
   foreach ($structure['page'] as $i => $x) {
       // Проверяем, разрешён ли просмотр категории (если нужно скрывать приватные)
       $display = $hideprivate ? cot_auth('page', $i, 'R') : true;

       // Если нужно фильтровать подкатегории, проверяем, входит ли текущая категория в фильтр
       if ($display && !empty($subcat) && isset($structure['page'][$subcat])) {
           // Формируем строку пути родительской категории с точкой на конце
           $mtch = $structure['page'][$subcat]['path'] . ".";
           // Длина этого пути
           $mtchlen = mb_strlen($mtch);
           // Проверяем, что путь текущей категории начинается с пути родителя или совпадает с ним
           $display = (mb_substr($x['path'], 0, $mtchlen) == $mtch || $i === $subcat);
       }

       // Если есть права на чтение категории, она не "all" и подходит по фильтру
       if (cot_auth('page', $i, 'R') && $i !== 'all' && $display) {
           // Считаем глубину категории — количество точек в пути
           $depth = substr_count($x['path'], '.');

           // Определяем, выбрана ли эта категория в данный момент
           $selected = ($i == $check) ? ' selected' : '';

           // Формируем тег option с value, data-depth и текстом
           $options .= '<option value="' . htmlspecialchars($i) . '" data-depth="' . $depth . '"' . $selected . '>' .
                       htmlspecialchars($x['title']) . '</option>';
       }
   }

   // Возвращаем полный select с классом Bootstrap
   return '<select name="' . htmlspecialchars($name) . '" class="form-select">' . $options . '</select>';
}


/**
* Select page cat for search form
* 
* @global array $structure
* @param type $check
* @param type $name
* @param type $subcat
* @param type $hideprivate
* @return string
*/
function cot_page_selectcat($check, $name, $subcat = '', $hideprivate = true)
{
    global $structure;

    $structure['page'] = (is_array($structure['page'])) ? $structure['page'] : array();

    $result_array = array();
    foreach ($structure['page'] as $i => $x)
    {
        $display = ($hideprivate) ? cot_auth('page', $i, 'R') : true;
        if ($display && !empty($subcat) && isset($structure['page'][$subcat]))
        {
            $mtch = $structure['page'][$subcat]['path'].".";
            $mtchlen = mb_strlen($mtch);
            $display = (mb_substr($x['path'], 0, $mtchlen) == $mtch || $i === $subcat);
        }

        if (cot_auth('page', $i, 'R') && $i != 'all' && $display) {
            $result_array[$i] = $x['tpath'];
        }
    }

   return cot_selectbox($check, $name, array_keys($result_array), array_values($result_array), true);
}

 

пояснение по назначению функций:
function cot_page_selectcat_select2 - выбор категории в списках через select2
function cot_page_selectcat - обычный выбор категории (на случай, если select2 не используется)
function cot_page_selectbox_structure_select2 - не используется, это подготовка для выбора категорий через select2 при создании и редактировании статей

 

2. Открываем page.admin.php

/modules/page/page.admin.php

или добавляете строки из файла page.admin.php, который в архиве, с заменой полностью, или строку за строкой, сравнивая исходный и модифицированный файл при помощи программы ExamDiff 

Файл page.admin.php - как его модифицировать "по-строчно"?

1. находим код (~ line 56)

$filter = empty($filter) ? 'valqueue' : $filter;

 

меняем на

$filter = empty($filter) ? 'all' : $filter; // лучше "все" по умолчанию. забываешь выбрать потом недоумеваешь

 

2. сразу после массива (~ lines 57-63):

$filter_type = [
    'all' => Cot::$L['All'],
    'valqueue' => Cot::$L['adm_valqueue'],
    'validated' => Cot::$L['adm_validated'],
    'expired' => Cot::$L['adm_expired'],
    'drafts' => Cot::$L['page_drafts'],
];

 

добавляем две строки:

$sq = cot_import('sq', 'G', 'TXT'); // Добавлено: импорт строки поиска
$c = cot_import('c', 'G', 'ALP'); // Добавлено: импорт категории для фильтрации

 


3.  находим код (~ lines 72-74)

if ($filter != 'valqueue') {
   $urlParams['filter'] = $filter;
}

 

и сразу за ними добавляем код:

if (!empty($sq)) { // Добавлено: добавление строки поиска в параметры URL
   $urlParams['sq'] = $sq;
}
if (!empty($c)) { // Добавлено: добавление категории в параметры URL
   $urlParams['c'] = $c;
}

 

4.  находим код (~ lines 98-101)

$catsub = cot_structure_children('page', '');
if (count($catsub) < count(Cot::$structure['page'])) {
    $sqlwhere .= " AND page_cat IN ('" . implode("','", $catsub) . "')";
}

 

меняем на

$catsub = cot_structure_children('page', $c); // Изменено: использование $c для фильтрации по категории
if (count($catsub) < count(Cot::$structure['page'])) {
    $sqlwhere .= " AND page_cat IN ('" . implode("','", $catsub) . "')";
}

if (!empty($sq)) { // Добавлено: логика фильтрации по строке поиска
   $words = explode(' ', $sq);
   $sqlsearch = '%' . implode('%', $words) . '%';
   $sqlwhere .= " AND (page_title LIKE '" . Cot::$db->prep($sqlsearch) . "' OR page_text LIKE '" . Cot::$db->prep($sqlsearch) . "')";
}

 

5. находим код (~ line 423)

'ADMIN_PAGE_ON_PAGE' => $ii,

 

и сразу после неё добавляем код:

'SEARCH_ACTION_URL' => cot_url('admin', 'm=page', '', true), // Добавлено: URL для формы поиска
'SEARCH_SQ' => cot_inputbox('text', 'sq', $sq, 'class="schstring"'), // Добавлено: строка поиска
'SEARCH_CAT_SELECT2' => cot_page_selectcat_select2($c, 'c'), // Добавлено: выбор категории через select2
'SEARCH_CAT' => cot_page_selectcat($c, 'c'), // Добавлено: обычный выбор категории (на случай, если select2 не используется)
'CATTITLE' => (!empty($c)) ? ' / ' . htmlspecialchars(Cot::$structure['page'][$c]['title']) : '' // Добавлено: заголовок категории

 

то есть весь массив должен иметь вид:

$t->assign([
    'ADMIN_PAGE_URL_CONFIG' => cot_url('admin', 'm=config&n=edit&o=module&p=page'),
    'ADMIN_PAGE_URL_ADD' => cot_url('page', 'm=add'),
    'ADMIN_PAGE_URL_EXTRAFIELDS' => cot_url('admin', 'm=extrafields&n=' . $db_pages),
    'ADMIN_PAGE_URL_STRUCTURE' => cot_url('admin', 'm=structure&n=page'),
    'ADMIN_PAGE_FORM_URL' => cot_url('admin', $common_params.'&a=update_checked&d=' . $durl),
    'ADMIN_PAGE_ORDER' => cot_selectbox($sorttype, 'sorttype', array_keys($sort_type), array_values($sort_type), false),
    'ADMIN_PAGE_WAY' => cot_selectbox($sortway, 'sortway', array_keys($sort_way), array_values($sort_way), false),
    'ADMIN_PAGE_FILTER' => cot_selectbox($filter, 'filter', array_keys($filter_type), array_values($filter_type), false),
    'ADMIN_PAGE_TOTALDBPAGES' => $totaldbpages,
   'ADMIN_PAGE_ON_PAGE' => $ii,
    'SEARCH_ACTION_URL' => cot_url('admin', 'm=page', '', true), // Добавлено: URL для формы поиска
    'SEARCH_SQ' => cot_inputbox('text', 'sq', $sq, 'class="schstring"'), // Добавлено: строка поиска
    'SEARCH_CAT_SELECT2' => cot_page_selectcat_select2($c, 'c'), // Добавлено: выбор категории через select2
    'SEARCH_CAT' => cot_page_selectcat($c, 'c'), // Добавлено: обычный выбор категории (на случай, если select2 не используется)
    'CATTITLE' => (!empty($c)) ? ' / ' . htmlspecialchars(Cot::$structure['page'][$c]['title']) : '' // Добавлено: заголовок категории
]);

 

Модификация page.admin.php завершена.
Напоминаю, сразу перед этим "докидуем" функции в /modules/page/inc/page.functions.php иначе насыпите на экран ошибок "фаталити".
Затем правим page.admin.php
а потом перезаливаем шаблон page.admin.tpl.
Разумеется бэкап сайта делать стоит перед любыми правками.

 

3. Шаблон админки, он прикреплен в архиве, 

его можно перезалить в папку /themes/admin/cotcp/modules/page/page.admin.tpl

все.

результат на скриншоте.
 

Сами три файла в архиве.

07_07_2025_19-50_page_admin_select2_ready_2.zip

This post was edited by webitproff (2025-07-07 21:34, 11 months ago)
2025-11-16 01:03

Вот это расклад. 💥

очень подробно расписано. я не настолько опытная, но потратила около получаса, - вышло все красиво.

жаль что под капотом из коробки нет такого быстрого поиска и фильтра в адмике. мое спасибо вам очень даже заслужено! 💋

Account