Глава 8. Шаблонизатор XTemplate SE и разработка скинов (тем)

07-07-26 14:09

В этой главе рассматривается шаблонизатор XTemplate SE (Seditio Edition), встроенный в ядро Seditio CMS, описываются его синтаксические конструкции (блоки, логические условия, циклы, фильтры), файловая архитектура тем оформления (скинов), PHP API для интеграции шаблонов в код, а также механизмы кастомизации интерфейса модулей и плагинов.


8.1. Введение и концепция XTemplate SE

Шаблонизатор Seditio CMS обеспечивает полное отделение логики приложения от его визуального представления.

8.1.1. Происхождение и назначение

Класс XTemplate в Seditio CMS (файл system/templates.php) представляет собой глубокую адаптацию шаблонизатора CoTemplate, используемого в проекте Cotonti, который, в свою очередь, наследует и развивает классическую концепцию XTemplate. Основные цели использования шаблонизатора в системе:

  • Безопасность разработки: Верстальщик работает исключительно с HTML-разметкой и плейсхолдерами шаблонизатора, не имея возможности напрямую выполнять произвольный PHP-код в шаблонах.
  • Чистота кода: Контроллеры ядра, модулей и плагинов формируют данные в PHP, а шаблонизатор занимается исключительно их рендерингом.
  • Быстродействие: Шаблонизатор компилирует исходные TPL-файлы в промежуточную структуру (структуру блоков), которая кэшируется классом XtplCache. При последующих запросах кэшированный шаблон считывается моментально, минуя ресурсоемкую стадию парсинга регулярными выражениями.

8.1.2. Механизм компиляции и кэширования

При первом вызове шаблона ядро считывает исходный .tpl файл, обрабатывает директивы динамического включения файлов, удаляет BOM-символы и компилирует шаблон в промежуточную структуру (структуру блоков), которая кэшируется классом XtplCache. При последующих запросах кэшированный шаблон считывается моментально, минуя ресурсоемкую стадию парсинга регулярными выражениями.

8.1.3. Отличия XTemplate SE от CoTemplate и преимущества

Несмотря на общую кодовую базу с CoTemplate (Cotonti), версия XTemplate SE (Seditio) имеет ключевые архитектурные отличия:

  1. Безопасность (Фильтрация функций):

    • XTemplate SE: Ограничивает вызовы функций-фильтров в шаблонах (режимы whitelist/blacklist). По умолчанию разрешены только безопасные хелперы (например, trim, date, crop, resize).
    • CoTemplate: Позволяет верстальщику вызывать любые PHP-функции без ограничений, что несет риски RCE.
  2. Арифметические выражения в тегах вывода:

    • XTemplate SE: Поддерживает вычисление простых выражений непосредственно внутри плейсхолдеров переменных: {SOME_TAG + 5} или {VAR_1 * VAR_2}.
    • CoTemplate: Ограничивает использование арифметики только тегами условий <!-- IF ... -->.
  3. Чистота и декомпозиция кода компиляции (compile):

    • XTemplate SE: Логика разбора блоков (XtplBlock::compile()) разбита на компактные методы (detectNextConstruct, extractConstruct, compileConstruct).
    • CoTemplate: Компиляция представляет собой монолитный 150-строчный метод с вложенными циклами и регулярными выражениями.
  4. Оптимальный RPN-парсер выражений (XtplExpr):

    • XTemplate SE: Хранит операторы в RPN-стеке в виде строк.
    • CoTemplate: Использует более 20 глобальных констант для кодирования операторов, усложняя код и засоряя глобальную область видимости.
  5. Оптимизированное дисковое кэширование:

    • XTemplate SE: Класс XtplCache сериализует скомпилированный шаблон в один файл на диске.
    • CoTemplate: Создает три отдельных файла (.tpl, .idx, .tags) на каждый шаблон, увеличивая нагрузку на файловую систему (I/O).
  6. Выделенная отладка (XtplDebugger):

    • XTemplate SE: Сбор отладочной информации вынесен в отдельный класс XtplDebugger.
    • CoTemplate: Логика отладки жестко встроена в методы рендеринга parse() и out().
  7. Глобальная конфигурация:

    • XTemplate SE: Настройка всех опций выполняется через единую статическую точку входа XTemplate::configure().
    • CoTemplate: Конфигурируется через метод init().

8.2. Синтаксис шаблонов: блоки, циклы и логические условия

Шаблоны в Seditio используют стандартное расширение .tpl. Синтаксис состоит из плейсхолдеров (тегов) и управляющих комментариев.

8.2.1. Именованные блоки

Вся разметка в XTemplate SE строится на иерархической структуре блоков. Блок объявляется специальными HTML-комментариями:

<!-- BEGIN: MAIN -->
<div class="wrapper">
    <h1>{PAGE_TITLE}</h1>
    
    <!-- BEGIN: SUBBLOCK -->
    <p>Дополнительный текст подблока: {SUB_INFO}</p>
    <!-- END: SUBBLOCK -->
</div>
<!-- END: MAIN -->
  • Корневым (главным) блоком в файле почти всегда является блок MAIN.
  • Блоки могут быть вложенными на любую глубину.
  • В PHP-коде для вывода блока используется его путь относительно корня (например, MAIN или MAIN.SUBBLOCK).

8.2.2. Логические условия (IF/ELSE)

Условия позволяют управлять отображением элементов в зависимости от переданных переменных:

<!-- IF {PAGE_AUTHOR} == 'Admin' -->
    <span class="badge red">Администратор</span>
<!-- ELSE -->
    <span>Автор: {PAGE_AUTHOR}</span>
<!-- ENDIF -->

Внутри управляющего тега <!-- IF {EXPR} --> используется анализатор выражений XtplExpr. Поддерживаются следующие операции:

  • Арифметические: +, -, *, /, %.
  • Сравнения: == (эквивалентно), === (тождественно), !=, !==, <, >, <=, >=.
  • Логические: AND (или &&), OR (или ||), XOR, ! (логическое отрицание).
  • Специфические для Seditio:
    • HAS — проверка вхождения элемента в массив (аналог in_array() в PHP). Например: <!-- IF {USER_GROUP_ID} HAS 5 -->...<!-- ENDIF -->.
    • ~= — проверка вхождения подстроки (аналог strpos() !== false). Например: <!-- IF {PAGE_TITLE} ~= 'Важно' -->...<!-- ENDIF -->.

8.2.3. Циклы (FOR)

Шаблонизатор XTemplate SE поддерживает автоматический обход массивов:

<ul>
<!-- FOR {VAL} IN {PHP.cfg.menu_links} -->
    <li><a href="{VAL.url}">{VAL.title}</a></li>
<!-- ENDFOR -->
</ul>

Также поддерживается получение ключа ассоциативного массива:

<!-- FOR {KEY}, {VAL} IN {PHP.usr.auth_groups} -->
    <li>Группа ID: {KEY}, Название: {VAL}</li>
<!-- ENDFOR -->
</ul>

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

8.2.4. Директивы включения файлов (FILE)

Для сборки страницы из составных частей (например, подключение общей шапки, подвала или виджета) используется тег FILE:

{FILE "skins/default/header.tpl"}

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


8.3. Синтаксис плейсхолдеров (тегов) и фильтры

Плейсхолдеры — это переменные в фигурных скобках, которые во время выполнения заменяются на конкретные текстовые или HTML-данные.

8.3.1. Базовые переменные и массивы

  • Простой тег: {PAGE_TITLE}.
  • Элемент многомерного массива: {PAGE_ROW.page_cat} или {PAGE_ROW.page_owner.user_name}.

8.3.2. Применение функций-фильтров (Callbacks)

Значения плейсхолдеров можно изменять «на лету» с помощью цепочек фильтров. Фильтр отделяется символом вертикальной черты |:

<p>{PAGE_TEXT|strip_tags|trim}</p>

Фильтры могут принимать аргументы, которые передаются в скобках через запятую:

<h2>{PAGE_TITLE|strtolower|ucfirst}</h2>
<p>Отрезок текста: {PAGE_TEXT|strip_tags|substr(0, 150)}</p>
<p>Дата публикации: {PAGE_DATE|date("d.m.Y H:i", $this)}</p>

[!IMPORTANT] Фильтрация функций (Function Filtering): Для обеспечения безопасности в классе XTemplate реализован белый список разрешенных функций (whitelist). В шаблоне можно вызывать только одобренные ядром безопасные функции (по умолчанию: trim, nl2br, date, htmlspecialchars, strip_tags, substr, sprintf, count, resize, crop и др.). Попытка вызвать системные функции вроде eval, system, file_get_contents приведет к ошибке безопасности или их блокировке.

8.3.3. Глобальный контекст (Префикс PHP)

Шаблонизатор Seditio предоставляет прямой доступ к глобальным массивам и переменным ядра CMS через специальный префикс PHP.:

  • {PHP.cfg.mainurl} — адрес главной страницы сайта.
  • {PHP.usr.name} — имя текущего авторизованного пользователя.
  • {PHP.usr.id} — ID текущего пользователя (0 для гостей).
  • {PHP.L.Yes} / {PHP.L.No} — языковые строки локализации.
  • {PHP.out.subtitle} — текущий подзаголовок страницы.

8.4. Структура скинов и шаблонов

Пользовательские темы оформления (скины) сайта хранятся в каталоге /skins/. При этом стандартные шаблоны модулей и плагинов по умолчанию поставляются в их собственных каталогах: /modules/{имя_модуля}/tpl/ и /plugins/{код_плагина}/ (или /plugins/{код_плагина}/tpl/).

Скин в Seditio представляет собой набор переопределяющих шаблонов (.tpl), файлов стилей (.css), скриптов (.js) и медиа-ресурсов, которые позволяют полностью изменить интерфейс CMS без изменения исходных файлов модулей и плагинов.

8.4.1. Локализация папки скина

Папка конкретного скина имеет вид: /skins/{код_скина}/ (например, /skins/default/).

8.4.2. Системные шаблоны и шаблоны модулей

В Seditio шаблоны могут храниться как непосредственно в папке активного скина (/skins/{код_скина}/), так и в папке соответствующего модуля (/modules/{имя_модуля}/tpl/). Функция sed_skinfile() выполняет автоматический поиск шаблона, отдавая приоритет файлам в папке скина, а при их отсутствии загружая стандартный шаблон из папки модуля.

Ниже приведен список реальных файлов шаблонов, сгруппированных по их назначению:

Общие системные шаблоны (Общие компоненты)

  • header.tpl — общая шапка сайта. Содержит HTML-заголовок, метатеги, тег {HEADER_STYLESHEET} для подключения стилей и верхнюю панель навигации.
  • footer.tpl — общий подвал сайта. Содержит закрывающие теги, счетчики посещаемости, информацию о разработчиках и тег {FOOTER_DEVINFO} (время генерации, количество SQL-запросов и использование ОЗУ).
  • index.tpl — макет главной страницы сайта.
  • message.tpl — вывод системных предупреждений, сообщений об успешных операциях и ошибок.
  • popup.tpl — разметка для всплывающих окон (например, для вставки смайликов или BB-кодов).
  • plugin.tpl — стандартная обёртка для standalone-плагинов, не имеющих собственного шаблона.
  • breadcrumbs.tpl — шаблон для оформления хлебных крошек (навигационной цепочки).
  • maintenance.tpl — экран заглушки при проведении технических работ.

Модуль «Страницы» (Page)

  • page.tpl — отображение полной версии отдельной страницы.
  • page.add.tpl — форма добавления страницы пользователем с фронтенда.
  • page.edit.tpl — форма редактирования и обновления страницы.
  • page.print.tpl — упрощенный шаблон для версии печати страницы.
  • list.tpl — отображение списка страниц внутри выбранной категории.
  • list.group.tpl — вывод списка страниц с группировкой по подразделам.
  • admin.page.tpl — управление страницами и списком в панели администрирования.
  • admin.page.add.tpl / admin.page.edit.tpl — формы создания/редактирования страниц в панели администрирования.
  • admin.page.manager.tpl — инструмент быстрого менеджмента страниц в админке.

Модуль «Форумы» (Forums)

  • forums.sections.tpl — главная страница форумов (таблица категорий и разделов).
  • forums.topics.tpl — список тем внутри конкретного раздела форума.
  • forums.posts.tpl — список сообщений и форма ответа внутри конкретной темы.
  • forums.newtopic.tpl — форма создания новой темы.
  • forums.editpost.tpl — форма редактирования сообщений.
  • admin.forums.tpl / admin.forums.structure.tpl — управление структурой и настройками форумов в панели администрирования.

Модуль «Персональная файловая система» (PFS)

  • pfs.tpl — личный кабинет файлового хранилища (загрузка файлов, список созданных папок).
  • pfs.edit.tpl — форма редактирования описания и параметров файла/папки.
  • pfs.standalone.tpl — интерфейс PFS во всплывающем окне (для прикрепления файлов/изображений в редакторе).
  • pfs.edit.standalone.tpl — форма редактирования файла во всплывающем окне.
  • admin.pfs.tpl — управление файловой системой пользователей в админке.

Модуль «Пользователи» (Users)

  • users.tpl — общая таблица (список) зарегистрированных участников.
  • users.details.tpl — страница просмотра публичного профиля пользователя.
  • users.profile.tpl — форма редактирования собственного профиля текущим пользователем.
  • users.register.tpl — форма онлайн-регистрации нового аккаунта.
  • users.auth.tpl — страница авторизации (входа на сайт).
  • users.edit.tpl — редактирование чужого профиля (для модераторов/администраторов).
  • admin.users.tpl — управление учетными записями и правами в панели администрирования.

Модуль «Личные сообщения» (PM)

  • pm.tpl — списки входящих, исходящих и архивных личных сообщений.
  • pm.send.tpl — форма отправки нового личного сообщения.
  • admin.pm.tpl / admin.pm.stat.tpl — управление и статистика ЛС в админке.

Модуль «Опросы» (Polls)

  • polls.tpl — вывод результатов опроса.
  • polls.poll.tpl — форма для голосования.
  • polls.standalone.tpl — вывод опросов в отдельном всплывающем окне или блоке.
  • admin.polls.tpl — управление опросами в панели администрирования.

Модуль «Галерея» (Gallery)

  • gallery.home.tpl — главная страница галереи (список фотоальбомов).
  • gallery.browse.tpl — сетка эскизов изображений в выбранном альбоме.
  • gallery.details.tpl — страница детального просмотра изображения с комментариями/оценками.
  • admin.gallery.tpl / admin.config.gallery.tpl — управление галереей и альбомами в админке.

8.5. PHP API шаблонизатора (для разработчиков)

Разработчики модулей и плагинов взаимодействуют с шаблонизатором через экземпляр класса XTemplate.

8.5.1. Создание экземпляра и компиляция

Конструктор принимает путь к файлу шаблона:

$t = new XTemplate(sed_skinfile('index'));

Функция sed_skinfile('имя_файла') автоматически проверяет наличие файла шаблона в активном скине сайта, подставляя правильный системный путь.

8.5.2. Присвоение значений тегам

Метод assign() осуществляет передачу переменных в область видимости шаблона:

// Передача одиночной переменной
$t->assign('PAGE_TITLE', $page_title);

// Массовое назначение через ассоциативный массив
$t->assign(array(
    'PAGE_ID' => $row['page_id'],
    'PAGE_CAT' => $row['page_cat'],
    'PAGE_OWNER' => $row['page_owner']
));

// Использование префиксов (например, для вывода массива с префиксом PAGE_ROW_)
$t->assign($row, null, 'PAGE_ROW_');

8.5.3. Парсинг блоков

После заполнения тегов внутри конкретного блока данными, этот блок отправляется на рендеринг методом parse():

// Рендеринг вложенного блока внутри цикла
foreach ($items as $item) {
    $t->assign(array(
        'ITEM_ID' => $item['id'],
        'ITEM_NAME' => $item['name']
    ));
    $t->parse('MAIN.ITEMS_LIST.ITEM_ROW');
}

// Парсинг родительских блоков
$t->parse('MAIN.ITEMS_LIST');
$t->parse('MAIN');
  • Блоки условий IF и циклов FOR внутри шаблонов вычисляются автоматически при рендеринге их родительских блоков. Вызывать метод parse() для блоков IF/FOR в PHP-коде не нужно.

8.5.4. Получение HTML-кода и вывод

  • Метод $t->text('MAIN') возвращает сгенерированный HTML-код в виде PHP-строки (удобно для передачи HTML в буфер или кэш).
  • Метод $t->out('MAIN') выводит сгенерированный блок напрямую в поток вывода (браузер).

8.6. Переопределение и кастомизация TPL модулей и плагинов

В Seditio CMS реализован гибкий механизм кастомизации визуального стиля плагинов и модулей без изменения их системного кода.

8.6.1. Приоритеты переопределения шаблонов плагинов

При вызове шаблона плагина (в его direct, standalone или popup частях) ядро выполняет поиск файла .tpl в следующем приоритетном порядке:

  1. Каталог активной темы (скина): /skins/{имя_активного_скина}/plugins/{код_плагина}.tpl (наивысший приоритет — позволяет верстальщику переопределить стандартный вид плагина под дизайн конкретной темы).
  2. Каталог плагина по умолчанию: /plugins/{код_плагина}/tpl/{код_плагина}.tpl (базовый шаблон, поставляемый разработчиком плагина).

Для реализации этого поиска в коде плагина вызов шаблона должен осуществляться через функцию sed_skinfile():

// Загрузит шаблон из скина, если он там есть, иначе из папки плагина
$t = new XTemplate(sed_skinfile('myplugin', true)); 

Второй аргумент true указывает функции sed_skinfile, что ищется шаблон плагина.

8.6.2. Кастомизация шаблонов модулей

Шаблоны страниц и списков контента в модуле page могут гибко переопределяться для разных категорий сайта:

  • Если в скине создан файл page.news.tpl, модуль страниц автоматически подключит его для отображения контента категории news.
  • Если специального файла категории нет, модуль подключит стандартный шаблон page.tpl.

8.7. Практическое руководство по созданию скина

Для создания новой темы оформления (скина) в Seditio CMS необходимо придерживаться структуры стандартного скина sympfy. Рассмотрим процесс создания темы с кодовым именем mytheme.

8.7.1. Структура файлов скина

Создайте в папке /skins/ каталог mytheme/. Внутри него расположите следующие папки и файлы:

  • mytheme.php — конфигурационный файл темы и метаданные.
  • mytheme.ru.lang.php — файл русской локализации интерфейса темы.
  • mytheme.png — превью скина для панели администратора (рекомендуемый размер — 240x180 пикселей).
  • header.tpl — общая шапка сайта.
  • footer.tpl — общий подвал сайта.
  • index.tpl — шаблон главной страницы.
  • css/style.css — основной файл стилей.
  • js/ — каталог для JavaScript-скриптов.

[!NOTE] Скин в Seditio CMS не обязан содержать шаблоны модулей (например, page.tpl, forums.tpl, users.tpl). Если они отсутствуют в папке скина, система автоматически подгрузит стандартные шаблоны из каталогов самих модулей (например, /modules/page/tpl/page.tpl).

8.7.2. Конфигурация темы (mytheme.php)

Этот файл содержит обязательный информационный блок SED для движка и переопределяет глобальные массивы настроек пагинации, хлебных крошек и масок плагинов:

<?php
/* ====================
Seditio - Website engine
Copyright © Seditio Team
https://seditio.org

[BEGIN_SED]
File=skins/mytheme/mytheme.php
Version=1.0.0
Updated=2026-jul-06
Type=Skin
Name=My Custom Theme
Author=Developer
Url=https://mysite.com
Description=My custom responsive skin for Seditio CMS
[END_SED]
==================== */

// Переопределение стилей пагинации
$cfg['pagination'] = '<li class="page-item">%s</li>';
$cfg['pagination_cur'] = '<li class="page-item active"><span class="page-link">%s</span></li>';
$cfg['pagination_arrowleft'] = '<i class="icon-chevron-left"></i>';
$cfg['pagination_arrowright'] = '<i class="icon-chevron-right"></i>';

// Настройка разделителя хлебных крошек
$cfg['separator'] = '<i class="icon-arrow-right"></i>';

// Иконки элементов интерфейса (поддерживаются Font Awesome или другие иконочные шрифты)
$out['ic_arrow_up'] = '<i class="icon-arrow-up"></i>';
$out['ic_folder'] = '<i class="icon-folder"></i>';
$out['ic_checked'] = '<i class="icon-check"></i>';
$out['ic_close'] = '<i class="icon-close"></i>';

// Маски вывода плагина RecentItems (недавние публикации)
$cfg['plu_mask_pages'] = '<span class="date">%3\$s</span> %1\$s ' . $cfg['separator'] . ' %2\$s<br />';
$cfg['plu_mask_comments'] = '<span class="date">%3\$s</span> %1\$s ' . $cfg['separator'] . ' %2\$s (%4\$s)<br />';

8.7.3. Локализация темы (mytheme.ru.lang.php)

Файл содержит переводы статических фраз интерфейса темы, сгруппированные в массив $skinlang по именам шаблонов:

<?php
/* ====================
[BEGIN_SED]
File=skins/mytheme/mytheme.ru.lang.php
Version=1.0.0
Updated=2026-jul-06
Type=Skin
Name=My Custom Theme (Russian Localization)
[END_SED]
==================== */

// Локализация для header.tpl
$skinlang['header']['Login'] = "Вход на сайт";
$skinlang['header']['Register'] = "Регистрация";
$skinlang['header']['Lostyourpassword'] = "Забыли пароль?";

// Локализация для index.tpl
$skinlang['index']['Newinforums'] = "Новое на форумах";
$skinlang['index']['Recentadditions'] = "Последние обновления";
$skinlang['index']['Online'] = "Сейчас на сайте";

В шаблонах темы локализованные фразы выводятся через теги глобального массива: {PHP.skinlang.header.Login} или {PHP.skinlang.index.Online}.

8.7.4. Базовая верстка файлов темы

[!IMPORTANT] Автоматическое подключение шапки и подвала: В Seditio CMS шаблоны шапки (header.tpl) и подвала (footer.tpl) подключаются контроллерами ядра автоматически (через выполнение скриптов /system/header.php и /system/footer.php перед и после рендеринга основного шаблона). В файлах страниц (таких как index.tpl, page.tpl и др.) не нужно вручную использовать директиву {FILE ...} для подключения шапки и подвала.

Шапка сайта (header.tpl)

<!-- BEGIN: HEADER -->{HEADER_DOCTYPE}
<html>
<head>
    <title>{HEADER_TITLE}</title>
    {HEADER_METAS}
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="skins/{PHP.skin}/css/style.css" type="text/css" rel="stylesheet" />
    {HEADER_CSS}
</head>
<body>
    <!-- BEGIN: USER -->
    <div class="userpanel">
        <ul>
            <li><span>{HEADER_LOGSTATUS}</span></li>
            <!-- IF {HEADER_USER_ADMINPANEL} -->
            <li>{HEADER_USER_ADMINPANEL}</li>
            <!-- ENDIF -->
            <!-- IF {HEADER_USER_PROFILE} -->
            <li>{HEADER_USER_PROFILE}</li>
            <!-- ENDIF -->
            <!-- IF {HEADER_USER_LOGINOUT} -->
            <li>{HEADER_USER_LOGINOUT}</li>
            <!-- ENDIF -->
        </ul>
    </div>
    <!-- END: USER -->

    <header class="main-header">
        <div class="logo">
            <a href="{PHP.sys.dir_uri}" alt="{HEADER_TITLE}">{PHP.cfg.maintitle}</a>
        </div>
        <div class="menu">
            {PHP.sed_menu.1.childrens}
        </div>
    </header>
<!-- END: HEADER -->

Подвал сайта (footer.tpl)

<!-- BEGIN: FOOTER -->
    <footer class="main-footer">
        <div class="copyright">{FOOTER_BOTTOMLINE}</div>
        <div class="stats">
            {FOOTER_CREATIONTIME}<br />{FOOTER_SQLSTATISTICS}
        </div>
        {FOOTER_DEVMODE}
    </footer>
    {FOOTER_JAVASCRIPT}
</body>
</html>
<!-- END: FOOTER -->

Главная страница (index.tpl)

<!-- BEGIN: MAIN -->
<div class="content-wrapper">
    <section class="intro">
        <h1>{PHP.cfg.maintitle}</h1>
        <p>{PHP.cfg.subtitle}</p>
    </section>
    
    <aside class="sidebar-box">
        <h3>{PHP.skinlang.index.Online}</h3>
        <p><a href="{PHP.out.whosonline_link}">{PHP.out.whosonline}</a> : {PHP.out.whosonline_reg_list}</p>
    </aside>
</div>
<!-- END: MAIN -->

Оценка:
(0.00)

Пока комментариев нет