DateAgo
У меня возникли сложно с DateAgo, поэтому решил написать свой снипет, может кому пригодиться.
В чанке
format:
<?php
/**
* Форматирует дату публикации в относительном или полном формате с поддержкой мультиязычности
*/
if (!defined('MODX_CORE_PATH')) die('Unauthorized access');
$debug = (bool)$modx->getOption('debug', $scriptProperties, true);
if ($debug) {
$modx->setLogLevel(modX::LOG_LEVEL_INFO);
$modx->log(modX::LOG_LEVEL_INFO, 'formatDateRussian: Начало работы сниппета');
}
$date = $modx->getOption('publishedon', $scriptProperties, '');
$lang = htmlspecialchars($modx->getOption('lang', $scriptProperties, $modx->getOption('cultureKey', null, 'ru')), ENT_QUOTES, 'UTF-8');
$forceAbsolute = (bool)$modx->getOption('forceAbsolute', $scriptProperties, false);
$showYear = (bool)$modx->getOption('showYear', $scriptProperties, true);
$useCurrentDateFallback = (bool)$modx->getOption('useCurrentDateFallback', $scriptProperties, true);
$defaultOutput = $modx->getOption('defaultOutput', $scriptProperties, 'Нет даты');
// Валидация языка
$allowedLangs = ['ru', 'en'];
if (!in_array($lang, $allowedLangs)) {
$lang = 'ru';
if ($debug) $modx->log(modX::LOG_LEVEL_WARN, "formatDateRussian: Некорректный язык '$lang', установлен 'ru'");
}
if ($debug) {
$modx->log(modX::LOG_LEVEL_INFO, "formatDateRussian: Параметры scriptProperties: " . print_r($scriptProperties, true));
$modx->log(modX::LOG_LEVEL_INFO, "formatDateRussian: Получены параметры - Date: '$date'");
}
if (empty($date) && $useCurrentDateFallback) {
$date = date('Y-m-d H:i:s');
if ($debug) $modx->log(modX::LOG_LEVEL_WARN, "formatDateRussian: Использована текущая дата: '$date'");
}
$locales = [
'ru' => [
'months' => [
'01' => ['января', 'янв'], '02' => ['февраля', 'фев'],
'03' => ['марта', 'мар'], '04' => ['апреля', 'апр'],
'05' => ['мая', 'мая'], '06' => ['июня', 'июн'],
'07' => ['июля', 'июл'], '08' => ['августа', 'авг'],
'09' => ['сентября', 'сен'], '10' => ['октября', 'окт'],
'11' => ['ноября', 'ноя'], '12' => ['декабря', 'дек']
],
'relative' => [
'now' => 'Только что',
'yesterday' => 'Вчера',
'ago' => 'назад',
'units' => [
'minutes' => ['минуту', 'минуты', 'минут'],
'hours' => ['час', 'часа', 'часов'],
'days' => ['день', 'дня', 'дней']
]
]
],
'en' => [
'months' => [
'01' => ['January', 'Jan'], '02' => ['February', 'Feb'],
'03' => ['March', 'Mar'], '04' => ['April', 'Apr'],
'05' => ['May', 'May'], '06' => ['June', 'Jun'],
'07' => ['July', 'Jul'], '08' => ['August', 'Aug'],
'09' => ['September', 'Sep'], '10' => ['October', 'Oct'],
'11' => ['November', 'Nov'], '12' => ['December', 'Dec']
],
'relative' => [
'now' => 'Just now',
'yesterday' => 'Yesterday',
'ago' => 'ago',
'units' => [
'minutes' => ['minute', 'minutes', 'minutes'],
'hours' => ['hour', 'hours', 'hours'],
'days' => ['day', 'days', 'days']
]
]
]
];
$locale = $locales[$lang] ?? $locales['ru'];
$pluralForm = function($number, $unit) use ($locale) {
$forms = $locale['relative']['units'][$unit] ?? ['','',''];
$n = abs($number) % 100;
$remainder = $n % 10;
if ($n > 10 && $n < 20) return $forms[2];
if ($remainder > 1 && $remainder < 5) return $forms[1];
if ($remainder == 1) return $forms[0];
return $forms[2];
};
$timestamp = 0;
if (is_numeric($date) && $date > 0) {
$timestamp = (int)$date;
} elseif (!empty($date)) {
$timestamp = strtotime($date);
}
if ($timestamp <= 0) {
if ($debug) $modx->log(modX::LOG_LEVEL_WARN, "formatDateRussian: Некорректная дата. Input='$date'");
return $defaultOutput;
}
$now = time();
$diff = $now - $timestamp;
$days = floor($diff / 86400);
$formatAbsoluteDate = function($timestamp, $locale) use ($showYear) {
$monthIndex = date('m', $timestamp);
$monthNames = $locale['months'][$monthIndex] ?? ['', ''];
return date('d', $timestamp) . ' ' . $monthNames[0] . ($showYear ? ' ' . date('Y', $timestamp) : '');
};
if ($forceAbsolute || $timestamp > $now || $days > 7) {
$result = $formatAbsoluteDate($timestamp, $locale);
if ($debug) $modx->log(modX::LOG_LEVEL_INFO, "formatDateRussian: Абсолютная дата, результат: '$result'");
return $result;
}
if ($diff < 60) {
if ($debug) $modx->log(modX::LOG_LEVEL_INFO, "formatDateRussian: Менее минуты назад");
return $locale['relative']['now'];
}
if ($diff < 3600) {
$minutes = floor($diff/60);
if ($minutes == 1 && $lang === 'en') {
$result = "a minute {$locale['relative']['ago']}";
} else {
$unit = $pluralForm($minutes, 'minutes');
$result = "$minutes $unit {$locale['relative']['ago']}";
}
if ($debug) $modx->log(modX::LOG_LEVEL_INFO, "formatDateRussian: Минуты, результат: '$result'");
return $result;
}
if ($diff < 86400) {
$hours = floor($diff/3600);
if ($hours == 1) {
$result = $lang === 'en' ? "an hour {$locale['relative']['ago']}" : "час {$locale['relative']['ago']}";
} else {
$unit = $pluralForm($hours, 'hours');
$result = "$hours $unit {$locale['relative']['ago']}";
}
if ($debug) $modx->log(modX::LOG_LEVEL_INFO, "formatDateRussian: Часы, результат: '$result'");
return $result;
}
if ($days == 1) {
$result = $lang === 'en' ? "a day {$locale['relative']['ago']}" : $locale['relative']['yesterday'];
if ($debug) $modx->log(modX::LOG_LEVEL_INFO, "formatDateRussian: Один день, результат: '$result'");
return $result;
}
$unit = $pluralForm($days, 'days');
$result = "$days $unit {$locale['relative']['ago']}";
if ($debug) $modx->log(modX::LOG_LEVEL_INFO, "formatDateRussian: Дни, результат: '$result'");
return $result;Вывод на основании системных настроек{$_modx->resource.publishedon | formatDateRussian}В чанке
{'formatDateRussian' | snippet : [
'publishedon' => $publishedon,
'format' => 'full',
'lang' => 'en',
'showYear' => true,
'debug' => 1
]}Описаниеformat:
- relative (по умолчанию): выводит относительные интервалы («Только что», «5 минут назад», «Вчера» и т.д.).
- full: выводит полную дату («09 мая, 2025»).
- true (по умолчанию): включает год в полном формате.
- false: убирает год (например, «09 мая»).
- ru (по умолчанию через cultureKey): русский язык.
- en: английский язык.
Комментарии: 7
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
там же какой формат укажешь такой и отобразится
добавьте у себя тоже возможность указания формата
можно использовать пространства имён и автозагрузку, но проверка MODX_CORE_PATH остаётся простым и надёжным способом защиты.
(просто как доп. уровень, это наверно из разряда xss csp)
погодите, а разве параметр dateFormat не закрывает этот момент?
Все верно вы говорите, просто я его еще не доделал. Изначально я хотел что при текущем годе показа года не было.
Вот сырой код, если хотите посмотреть, для понимания
Такие штуки не должны быть доступны пользователю через http запрос. php код может выполнится, что небезопасно, а код шаблона вообще просто отобразится или файл скачается.
Их нужно хранить или в директории core, которая должна быть закрыта от доступа через правила веб-сервера, или же вообще в идеале выше, чем public каталог.
ну и тогда не нужно парится насчет «Если кто-то попытается обратиться к файлу напрямую»