ПОДРОБНОЕ РУКОВОДСТВО: Как регулировать диапазон лет в датах при редактировании страниц в Cotonti
(Без хаков, без правки ядра, с полным объяснением — как, где, почему, и что происходит под капотом)
Введение: Проблема, которую мы решаем
При редактировании статей - страницы в Cotonti (модуль Page), у нас есть поля:
- Дата создания
- Начало публикации
- Окончание публикации
В каждом из них — выпадающие списки: День | Месяц | Год | Час | Минута
Проблема по умолчанию:
$max_year = 2030;
$min_year = 2000;→ Жёстко прописано в функции cot_selectbox_date()
→ Не меняется со временем
→ Через 5 лет (в 2031) — нельзя будет выбрать будущие даты
→ Через 10 лет — список устареет полностью
→ Довольно большой диапазон в годах между верхним и нижним порогом при выборе года как компонента даты
Цель
Сделать динамический и в тоже время контролируемый диапазон лет, например:
- 2022 – 2026 (в 2025 году)
- 2023 – 2027 (в 2026 году)
- Автоматически обновляется каждый год
Без правки ядра. Без плагинов. Через тему.
Где и как используется cot_selectbox_date()?
Функция лежит в system/forms.php (можно глянуть здесь):
/**
* Generates date part dropdown
*
* @param int $utime Selected timestamp
* @param string $mode Display mode: 'short' or complete
* @param string $name Variable name preffix
* @param int $max_year Max. year possible
* @param int $min_year Min. year possible
* @param bool $usertimezone Use user timezone
* @param string $custom_rc Custom resource string name
* @return string
*/
function cot_selectbox_date($utime, $mode = 'long', $name = '', $max_year = 2030, $min_year = 2000, $usertimezone = true, $custom_rc = '')
{
global $L, $R, $usr;
if (function_exists('cot_selectbox_date_custom'))
{
return cot_selectbox_date_custom($utime, $mode, $name, $max_year, $min_year, $usertimezone, $custom_rc);
}
$result = NULL;
/* === Hook === */
foreach (cot_getextplugins('form.date') as $pl)
{
include $pl;
}
/* ===== */
if($result !== NULL) return $result;
$rc_name = preg_match('#^(\w+)\[(.*?)\]$#', $name, $mt) ? $mt[1] : $name;
$utime = ($usertimezone && $utime > 0) ? ($utime + $usr['timezone'] * 3600) : $utime;
if ($utime == 0) {
list($s_year, $s_month, $s_day, $s_hour, $s_minute) = [null, null, null, null, null];
$buffered = cot_import_buffered($name, null);
if (is_array($buffered)) {
$s_year = $buffered['year'];
$s_month = $buffered['month'];
$s_day = $buffered['day'];
$s_hour = ($buffered['hour'] ?? 0) > 0 ? $buffered['hour'] : 1;
$s_minute = $buffered['minute'] ?? 0;
}
} else {
list($s_year, $s_month, $s_day, $s_hour, $s_minute) = explode('-', @date('Y-m-d-H-i', $utime));
}
$months = [];
$months[1] = $L['January'];
$months[2] = $L['February'];
$months[3] = $L['March'];
$months[4] = $L['April'];
$months[5] = $L['May'];
$months[6] = $L['June'];
$months[7] = $L['July'];
$months[8] = $L['August'];
$months[9] = $L['September'];
$months[10] = $L['October'];
$months[11] = $L['November'];
$months[12] = $L['December'];
$year = cot_selectbox($s_year, $name.'[year]', range($max_year, $min_year, -1));
$month = cot_selectbox($s_month, $name.'[month]', array_keys($months), array_values($months));
$day = cot_selectbox($s_day, $name.'[day]', range(1, 31));
$range = [];
for ($i = 0; $i < 24; $i++) {
$range[] = sprintf('%02d', $i);
}
$hour = cot_selectbox($s_hour, $name.'[hour]', $range);
$range = [];
for ($i = 0; $i < 60; $i++) {
$range[] = sprintf('%02d', $i);
}
$minute = cot_selectbox($s_minute, $name.'[minute]', $range);
$rc = empty($R["input_date_{$mode}"]) ? 'input_date' : "input_date_{$mode}";
$rc = empty($R["input_date_{$rc_name}"]) ? $rc : "input_date_{$rc_name}";
$rc = empty($custom_rc) ? $rc : $custom_rc;
$result = cot_rc($rc, [
'day' => $day,
'month' => $month,
'year' => $year,
'hour' => $hour,
'minute' => $minute
]);
return $result;
}
| Параметр | Что делает | По умолчанию |
$utime | Unix-время выбранной даты | — |
$mode | 'long' — полные месяцы, 'short' — без | 'long' |
$name | Префикс полей (rpagedate, rpagebegin) | — |
$max_year | Максимальный год в списке | 2030 |
$min_year | Минимальный год в списке | 2000 |
$usertimezone | Учитывать часовой пояс пользователя | true |
$custom_rc | Кастомный шаблон вывода | — |
Именно
$max_yearи$min_year— наши цели.
Где вызывается в редактировании страниц?
В modules/page/page.edit.php (можно глянуть здесь):
'PAGEEDIT_FORM_DATE' => cot_selectbox_date($pag['page_date'], 'long', 'rpagedate').' '.Cot::$usr['timetext'],
'PAGEEDIT_FORM_BEGIN' => cot_selectbox_date($pag['page_begin'], 'long', 'rpagebegin').' '.Cot::$usr['timetext'],
'PAGEEDIT_FORM_EXPIRE' => cot_selectbox_date($pag['page_expire'], 'long', 'rpageexpire').' '.Cot::$usr['timetext'],→ Эти теги ({PAGEEDIT_FORM_DATE} и т.д.) выводятся в шаблоне page.edit.tpl. (можно посмотреть здесь)
Решение: Переопределить $max_year и $min_year через шаблон + ресурсы $R
Без правки
system/functions.php. Без плагинов. Через тему (например, Carbon).
ШАГ 1: Задаём динамические пороги в файле темы
Файл: themes/carbon/carbon.php
<?php
defined('COT_CODE') or die('Wrong URL');
// === ДИНАМИЧЕСКИЕ ДИАПАЗОНЫ ГОДОВ ДЛЯ ДАТ РЕДАКТИРОВАНИЯ ===
// Текущий год
$current_year = (int)date('Y');
// Минимальный год: -3 года от текущего
$R['page_years_min_range_threshold'] = $current_year - 3;
// Максимальный год: +1 год от текущего
$R['page_years_max_range_threshold'] = $current_year + 1;Почему так?
$R— глобальный массив ресурсов, доступный в шаблонах через{PHP.R.название}date('Y')— текущее время, всегда актуально$Rзадаётся один раз при загрузке темы → работает везде
ШАГ 2: Переопределяем вывод в шаблоне
Файл: themes/carbon/page.edit.tpl
Заменяем стандартные теги:
{PAGEEDIT_FORM_DATE}
{PAGEEDIT_FORM_BEGIN}
{PAGEEDIT_FORM_EXPIRE}на модификатор с новыми параметрами:
<div class="card mb-4">
<div class="card-header d-flex align-items-center bg-none fw-bold">
{PHP.L.Date}
</div>
<div class="card-body">
<div class="row">
<!-- ДАТА СОЗДАНИЯ -->
<div class="mb-lg-0 mb-3 col-md-4">
<label class="form-label">{PHP.L.pageDateCreated}</label>
{PAGEEDIT_FORM_DATE|cot_selectbox_date(
'{PHP.pag.page_date}',
'short',
'rpagedate',
'{PHP.R.page_years_max_range_threshold}',
'{PHP.R.page_years_min_range_threshold}',
false,
'input_date_short'
)}
</div>
<!-- НАЧАЛО ПУБЛИКАЦИИ -->
<div class="mb-lg-0 mb-3 col-md-4">
<label class="form-label">{PHP.L.Begin}</label>
{PAGEEDIT_FORM_BEGIN|cot_selectbox_date(
'{PHP.pag.page_begin}',
'short',
'rpagebegin',
'{PHP.R.page_years_max_range_threshold}',
'{PHP.R.page_years_min_range_threshold}',
false,
'input_date_short'
)}
</div>
<!-- ОКОНЧАНИЕ ПУБЛИКАЦИИ -->
<div class="mb-lg-0 mb-3 col-md-4">
<label class="form-label">{PHP.L.Expire}</label>
{PAGEEDIT_FORM_EXPIRE|cot_selectbox_date(
'{PHP.pag.page_expire}',
'short',
'rpageexpire',
'{PHP.R.page_years_max_range_threshold}',
'{PHP.R.page_years_min_range_threshold}',
false,
'input_date_short'
)}
</div>
</div>
</div>
</div>
Что происходит под капотом?
| Шаг | Объяснение |
| 1 | При загрузке темы → $R['page_years_...'] заполняются |
| 2 | В шаблоне {PHP.R.page_years_max_range_threshold} → подставляет 2026 |
| 3 | {PAGEEDIT_FORM_DATE|...} → перехватывает вывод и вызывает функцию заново |
| 4 | Функция получает новые $max_year и $min_year → генерирует правильный список |
Дополнительно: Кастомный вид (без часов/минут)
$R['input_date_short'] = '<div class="row g-2">
<div class="col-4">{$day}</div>
<div class="col-4">{$month}</div>
<div class="col-4">{$year}</div>
</div>';
→ Это переопределяет шаблон вывода
→ input_date_short — используется вместо стандартного
→ Убирает часы и минуты → чище
Почему работает?
cot_rc()ищет$R["input_date_{$mode}"]→ если есть — использует его.
Как задать СВОИ диапазоны?
Пример 1: Только будущие даты (для begin и expire)
$R['page_years_min_future'] = (int)date('Y');
$R['page_years_max_future'] = (int)date('Y') + 10;В шаблоне:
{PAGEEDIT_FORM_BEGIN|cot_selectbox_date(
'{PHP.pag.page_begin}',
'short',
'rpagebegin',
'{PHP.R.page_years_max_future}',
'{PHP.R.page_years_min_future}',
false
)}Пример 2: Разные диапазоны для разных полей
// Дата создания: можно назад
$R['page_date_min'] = (int)date('Y') - 10;
$R['page_date_max'] = (int)date('Y') + 1;
// Периоды: только вперёд
$R['page_period_min'] = (int)date('Y');
$R['page_period_max'] = (int)date('Y') + 20;
Почему этот способ — ЛУЧШИЙ?
| Плюс | Объяснение |
| Без правки ядра | Обновления Cotonti не сломают |
| Работает в любой теме | Просто копируем в theme.php |
| Автообновление | Каждый год — новый диапазон |
| Гибкость | Любой диапазон, разные для полей |
| Чистый код | Никаких Cot::$sys, date() в .tpl |
| Кастомизация вида | Через $R['input_date_...'] |
Частые ошибки и как их избежать
| Ошибка | Как исправить |
Cot::$sys['now'] в шаблоне | → Нельзя! Используй только {PHP.R....} |
Забыл кавычки в '{PHP.pag.page_date}' | → Без кавычек — не сработает |
Используем long вместо short | → Появятся часы/минуты → уберираем через $R['input_date_short'] |
Не переопределил $R | → Проверяем файл инициализации темы сайта carbon.php |
Готово!
Теперь имеем:
- Динамические списки годов
- Красивый, чистый интерфейс
- Без багов, без хаков
- Работает вечно
закрепим мысль, - файл инициализации темы как пример в themes/carbon/carbon.php:
<?php
/* ====================
[BEGIN_COT_THEME]
Name=Carbon
Version=1.0.2
Schemes=default:Default
[END_COT_THEME]
==================== */
/**
* Cotonti Model Theme
*
* @package Carbon
* @copyright (c) 2025 webitproff https://github.com/webitproff
* @license BSD
*/
defined('COT_CODE') or die('Wrong URL');
global $R, $sys;
// нижний и верхний порог выбора года (диапазона лет) при редактировании страниц и статей
// для функции cot_selectbox_date
/*
{PAGEEDIT_FORM_DATE|cot_selectbox_date(
'{PHP.pag.page_date}',
'short',
'rpagedate',
'{PHP.R.page_years_max_range_threshold}',
'{PHP.R.page_years_min_range_threshold}',
false,
false)} // false - это использовать ли шаблон $R['input_date_long'] или $R['input_date_short'] вашей темы
*/
$R['page_years_min_range_threshold'] = (int) cot_date('Y', Cot::$sys['now']) - 3;
$R['page_years_max_range_threshold'] = (int) cot_date('Y', Cot::$sys['now']) + 1;
$R['input_date_long'] = '<div class="row g-2">
<div class="col-2">{$day}</div>
<div class="col-3">{$month}</div>
<div class="col-2">{$year}</div>
<div class="col-2">{$hour}</div>
<div class="col-1 text-center">:</div>
<div class="col-2">{$minute}</div>
</div>';
$R['input_date_short'] = '<div class="row g-2">
<div class="col-4">{$day}</div>
<div class="col-4">{$month}</div>
<div class="col-4">{$year}</div>
</div>';Как настроить диапазон лет в выпадающих списках даты для редактирования страниц в Cotonti (на примере темы Carbon)
Цель
Сделать так, чтобы при редактировании страницы (в полях Дата создания, Начало публикации, Окончание)
выпадающий список годов показывал не фиксированный диапазон 2000–2030, а динамический:
Минимальный год: текущий год - 3
Максимальный год: текущий год + 1
Пример на 2025 год:
→ выбор от 2022 до 2026
Как переопределить диапазон выбора элементов даты без правки ядра?
Решение: Используем ресурсы $R + модификатор шаблона
Шаг 1: Откройте файл темы
Путь: themes/carbon/carbon.php
Это главный файл темы, он подключается всегда, когда тема активна.
Шаг 2: Добавить код
// === ДИНАМИЧЕСКИЕ ДИАПАЗОНЫ ГОДОВ ДЛЯ РЕДАКТИРОВАНИЯ СТРАНИЦ ===
// Минимальный год: текущий - 3 года
$R['page_years_min_range_threshold'] = (int)date('Y') - 3;
// Максимальный год: текущий + 1 год
$R['page_years_max_range_threshold'] = (int)date('Y') + 1;
Почему date('Y')?Cot::$sys['now'] — это текущее время в секундах.date('Y') — проще, быстрее, и работает всегда.
Шаг 3: Измените шаблон редактирования страницы
Файл: themes/carbon/modules/page/page.edit.tpl
Найдите блоки:
<tr>
<td>{PHP.L.Date}:</td>
<td>
{PAGEEDIT_FORM_DATE}
<p class="small" style="margin-top: 6px">{PAGEEDIT_FORM_DATENOW} {PHP.L.page_date_now}</p>
</td>
</tr>
<tr>
<td>{PHP.L.Begin}:</td>
<td>{PAGEEDIT_FORM_BEGIN}</td>
</tr>
<tr>
<td>{PHP.L.Expire}:</td>
<td>{PAGEEDIT_FORM_EXPIRE}</td>
</tr>
Замените их на:
<div class="mb-lg-0 mb-3">
<label class="form-label">{PHP.L.pageDateCreated}</label>
{PAGEEDIT_FORM_DATE|cot_selectbox_date('{PHP.pag.page_date}', 'short', 'rpagedate', '{PHP.R.page_years_max_range_threshold}', '{PHP.R.page_years_min_range_threshold}', false)}
</div>
<div class="mb-lg-0 mb-3">
<label class="form-label">{PHP.L.Begin}</label>
{PAGEEDIT_FORM_BEGIN|cot_selectbox_date('{PHP.pag.page_begin}', 'short', 'rpagebegin', '{PHP.R.page_years_max_range_threshold}', '{PHP.R.page_years_min_range_threshold}', false)}
</div>
<div class="mb-lg-0 mb-3">
<label class="form-label">{PHP.L.Expire}</label>
{PAGEEDIT_FORM_EXPIRE|cot_selectbox_date('{PHP.pag.page_expire}', 'short', 'rpageexpire', '{PHP.R.page_years_max_range_threshold}', '{PHP.R.page_years_min_range_threshold}', false)}
</div>Решение универсальное и можно использовать не только со страницами, а везде, где есть выпадающий список селектора выбора элементов даты.
конечно можно. просто немного допилить сам модуль товаров потребуется.
добавить импорт данных (дата) в создание и редактирование товара, вывести в шаблон, и затем строковыми ресурсами кастомизировать под себя
а к товарам для модуля "маркет" тоже так можно сделать?