MaxSite CMS для чайников. Разбираем шаблоны
Вторник, 5 января 2010 г.
Просмотров: 943
Подписаться на комментарии по RSS
Продолжим наши изыскания. Сегодня речь пойдет о шаблонах MaxSite CMS. Общую информацию вы уже получили, теперь мы окунемся в детали и подробности.
Шаблон Mini
Давайте распотрошим какой-нибудь шаблон и я предлагаю начать с Mini, посколько он самый маленький и не содержит ничего лишнего.
Как вы уже знаете, MaxSite CMS всегда передает управление в шаблонный index.php (верно ведь?!). В нашем случае этот файл - диспетчер типов данных. То есть в нем происходит анализ типов, сегментов и по результату подключается нужный type-файл.
Защита от прямого вызова php-файла
В первой строчке мы видим:
- <?php if (!defined('BASEPATH')) exit('No direct script access allowed');
Сие условие (if) проверяет существование константы BASEPATH и если её нет, то происходит завершение скрипта. Это очень важная конструкция и она должна присутствовать в любом php-файле MaxSite CMS. Дело в том, что любой php-файл по-идее можно вызвать напрямую через браузер. Предположим какой-то php-скрипт принимает входящий адрес и на его основе выполняет определенные действия. Таким образом нет никакой сложности вручную набрать такой адрес в браузере и вуаля! скрипт выполнил всё, что нужно. Именно по такому алгоритму совершается большинство взломов WordPress - прямой вызов уязвимого скрипта.
В MaxSite CMS (точнее в CodeIgniter) выполняется такая проверка и если вы попытаетесь явно вызвать файл, произойдет завершение его работы. Таким образом в MaxSite CMS эта строчка является обязательной и напрямую связана с безопасностью.
Хуки глобального кэширования
Следующая строчка:
- if (mso_hook('global_cache_start', false)) return;
выполняет хук «global_cache_start» и если функция вернула true, то это значит, что текущая страница будет выведена из кэша (поэтому сразу выходим по return). У функции mso_hook() второй параметр определяет что будет, если такого хука не существует. Например мы запретили глобальное кэширование, то есть хук «global_cache_start» не определен. Следовательно функция mso_hook() вернет false и условие не будет выполнено (пойдет обычная генерация страницы).
Что такое глобальное кэширование? В MaxSite CMS кэширование часть системы, поэтому вам не нужно заботиться об этом. Есть несколько уровней кэша. Первый - встроенный. В этом случае в кэш попадают результаты отработки функций или каких-то готовых блоков. Например нет смысла каждый раз получать список рубрик или опций. Они один раз получаются, после этого сохраняются в кэше и последующие обращения к этим функциям отдают уже готовый результат.
Что касается опций, то они еще сохраняются в глобальной переменной с тем, чтобы при активном использовании не выполнять операции чтения из файла, а сразу брать из памяти.
Второй уровень кэширования - это кэширование SQL-запросов CodeIgniter. В этом случае все запросы сохраняются в виде готового результата и при последующих сразу берутся из файла. Данный вид кэширования позволяет полностью убрать запросы к БД и тем самым дополнительно ускорить работу сайта. Правда у данного вида кэширования есть и некоторые недостатки. Например кэшируются запросы со случайной выборкой.
Третий вид кэширования предназначен для совсем уж слабых серверов или при очень высокой посещаемости. Это глобальное кэширование. В этом случае в кэш попадает полностью готовая html-страница, минуя работу всех функций шаблона и БД. У этого вида кэширования много тонкостей и прежде всего из-за того, что перестают работать все динамические «фишки».
Отмечу, что не стоит кидаться и включать все эти виды кэширования. Как показывает практика для 99% случаев стандартного кэширования более чем достаточно. Обратите внимание на время загрузки и количество SQL-запросов в подвале сайта. Если оно очень велико, то тогда уже и стоит об этом призадумываться.
Вернемся к нашей строчке. Наш шаблон поддерживает глобальное кэширование, которое реализовано на двух хуках: «global_cache_start» и «global_cache_end». Само кэширование реализовано в виде плагина «Глобальное кэширование».
RSS-лента
Следующая наша функция
- is_feed()
В данном случае функция проверяет наличие в последнем сегменте «feed» и если она есть, то возвращает true. Как вы уже поняли - это rss-лента. И мы должны проверить это условие до всех прочих. Почему?
RSS - это специальный XML-формат. То есть нам нельзя отдавать в нём наше произвольное оформление, тексты и т.д. Там используется строгий синтаксис и наш шаблон просто для этого не годится. Именно поэтому мы и подключаем для feed свои отдельные type-файлы.
- if (is_type('page')) $mso_type_file = 'feed-page';
В данном случае, если у нас тип page, type-файл будет feed-page.
Иногда возникает задача сделать нестандартную rss-ленту. Например для Яндекс-новостей. В этом случае за основу можно взять наиболее близкий type-файл для feed и модифицировать под свою задачу.
Получение информации через getinfo()
Обратите внимание на функцию getinfo(). Данная функция предназначена для быстрого получения какого-то значения. Например:
- getinfo('template_dir');
возвратит каталог текущего шаблона на сервере. А
- getinfo('template_url');
возвратит адрес (URL) текущего шаблона.
В дальнейшем, когда вы начнете делать свои плагины и шаблоны, вам следует использовать именно getinfo() для определения путей и прочих настроек. Полное описание функции вы можете посмотреть в common.php.
Подключение файлов
Для того, чтобы подключить произвольный файл, используется примерно такая конструкция:
- if (file_exists($fn1)) require($fn1);
Функция file_exists() проверяет существование файла. Функция require() подключает этот файл. Если же не делать проверку на существование файла, то может возникнуть ситуация, когда файла нет, но вы его пытаетесь подключить. В таких случаях возникает ошибка PHP.
С помощью require() в MaxSite CMS подключаются файлы шаблона, плагинов и функций. Иногда возникает необходимость подключить файл только один раз. Например функции рубрик могут потребоваться в разных частях шаблона, которые могут сами по себе сложно комбинироваться. Таким образом отследить было ли подключение функций довольно сложно. Для таких случаев предназначена стандартная php-функция require_once(). PHP сам проверит было ли подключение и если было, то ничего не подключит. Очень удобно. ![]()
Функции системы
Строчки:
- require_once(getinfo('common_dir') . 'page.php');
- require_once(getinfo('common_dir') . 'category.php');
подключают к шаблону функции для работы с рубриками и записями.
В MaxSite CMS не принято подключать всё скопом, как это делается в WordPress. Действительно, зачем подключать функции меток, если эти метки используются только для одной определенной страницы? Именно за счет такого экономного подхода, моя система и обладает довольно скромными запросами по ресурсам.
Но в данном случае мы подключаем фукции страниц и рубрик, потому что они используются практически во всех файлах шаблона.
Ядро системы - это файл common.php. Он подключается всегда. Когда я принимаю решение где разместить новую функцию, то учитываю сразу несколько факторов. Например, будет ли эта функция использоваться часто? Нужна ли она в шаблонах и плагинах? Если да, то есть смысл включить в ядро. Некоторые функции выполняют роль хелперов (помощников). Они небольшие, но могут значительно упростить конечный код. Если же функция имеет явную направленность, например при выводе записей, то ей самое место в page.php.
Если вы захотите виртуозно научиться работать с MaxSite CMS, то вам придется окунуться в исходные php-файлы. Я знаю, что для новичков удобно было бы все разжевать и показать на примерах как делаются определенные вещи. Но для такого мануала потребуется очень много сил и времени не одного человека. Поэтому наиболее оптимальный вариант, научиться читать исходный код. К тому же я каждую функцию и большинство строчек комментрирую.
Общий алгоритм определения типа данных
Вернемся к нашим баранам.
Дальнейший код - реализация довольно интересного алгоритма. Любой программист перед тем как приступить к написанию кода, обязательно продумывает как и что он хочет получить в итоге.
Когда-то давно, компьютеров никто и в глаза не видел, нам давали несколько уроков по языку РАЯ - русский алгоритмический язык. На доске рисовали всякие алгоритмы и придумывали способы решения задач. Сейчас всё немного по другому, но суть осталась прежней.
1. Задаем переменную $mso_type_file, которая содержит текущий тип данных, которая определила система на основе URL. Здесь нюанс в том, что имя type-файла совпадает с именем типа данных. Например для типа page, файл будет page.php.
2. Вводим переменную $mso_type_file_404, которая сигнализирует о том, что у нас может быть неопределенный тип.
3. Если наш тип равен «page_404» (страница не найдена, а точнее - тип не известен системе!), то проверяем существование хука «custom_page_404» и если он есть, то выполняем его (mso_hook) и ставим $mso_type_file равным false (чтобы после не подключать файл типа).
Зачем нам нужен хук «custom_page_404»? Дело в том, что в MaxSite CMS можно подключить к шаблону произвольный файл. Например в плагине вы делаете свой type-файл и вот, чтобы его выполнить, нужно как-то «прицепиться» к шаблону. Посколько система не смогла определить тип и выставила его как «page_404», то мы и проверяем это условие и наличие этого хука (mso_hook_present). Если он есть, то выполняем хук (на нем, собственно и «сидит» наш тип) и сбрасываем $mso_type_file, чтобы не выполнять дальнейших действий.
4. Если у нас тип «page_404», но хука «custom_page_404» нет, то мы попытаемся определить тип из первого сегмента. Для этого служит функция mso_segment(), где указывается номер интересующего нас сегмента.
5. Некоторые типы данных могут обрабатываться разными php-файлами, в зависимости от неких условий. Например если нам нужно получить страницу комюзера, то адрес будет http://сайт/users/номер. Для редактирования данных: http://сайт/users/номер/edit. Для восстановления пароля: http://сайт/users/номер/lost. Если номер вообще не указан (http://сайт/users), то выводится список всех комюзеров. Таким образом мы анализируем сегменты и подключаем соответствующие файлы.
6. Теперь, когда мы выполнили все проверки, нужно непосредственно подключить type-файл. Делать это следует только при условии, что $mso_type_file не равна false (это происходит в случае отстутствия хука «custom_page_404»).
7. Дальше просто. На основе $mso_type_file формируем два имени type-файла. Первый в текущем шаблоне в каталоге /type/, второй в дефолтном.
8. Если у нас оказалась определена $mso_type_file_404 (мы его определили по сегменту), то дополнительно определяем имена файлов и на основе этой переменной.
Имена файлов следует задавать либо полные (на сервере), либо если вы точно знаете, что он в текущем каталоге.
9. Конечное действие: проверяем существование type-файла во всех случаях и если они есть, то подключаем как обычно с помощью require().
Надеюсь, что вы не запутались в моем описании, и если такое вдруг случилось, то еще раз пройдитесь по коду и тексту.
Что дает такой алгоритм? Прежде всего мы можем подключать произвольные type-файлы без переделки index.php. Пример из прошлой статьи. Вы решили сделать свою форму заказа. Адрес http://сайт/order. Все ваши действия - загрузить order.php в каталог /type/ своего шаблона.
Другой пример. Вы решили переписать дефолтный type-файл page.php. Ваши действия аналогичны - сохраняем свой вариант в каталоге /type/ своего шаблона.
До версии MaxSite CMS 0.37 приходилось явно указывать подключаемые type-файлы в шаблонном index.php. C одной стороны это довольно просто, с другой приходилось постоянно править файлы.
Еще один момент, о котором следует рассказать. При таком алгоритме могут появиться какие-то новые типы данных на уровне системы (в дефолтном шаблоне) и все чужие шаблоны, автоматически его «подхватят». То есть разработчики шаблонов могут упростить себе жизнь и не править файлы под каждую версию системы.
Что же делать тем, кому нужно четкое и явное определение типа. Например ваш сайт не должен «понимать» рубрики. В этом случае вы берете за основу index-old.php. Надеюсь, что вы также посмотрите этот файл. Разобраться в нем не составит большого труда.
Где type-файлы?
Если вы невнимательно прочитали и изучили предудущий раздел, то у вас возникнет недоумение, а где же type-файлы? В шаблоне-то их нет!
Конечно, наш алгоритм вначале проверил существование файлов в текущем шаблоне и, поскольку их там не оказалось, взял из дефолтного. Просто, не правда ли?
Что происходит в type-файле?
Мы условились, что пока будем рассматривать type-файлы, как черные ящики. Нам важно пока усвоить последовательнность действий.
1. В шаблонном index.php мы подключили type-файл.
2. В type-файле вначале получаются все данные.
3. Подключается шаблонный main-start.php.
4. Выводятся данные этого типа, например записи.
5. Подключается шаблонный main-end.php.
Таким образом в в нашем шаблоне нас интересует только два файла main-start.php и main-end.php.
Файлы main-start.php и main-end.php
Откроем эти файлы и увидим, что они почти полностью состоят из html-тэгов. Действительно имено в них и заключен наш дизайн и html-структура. Главное - это разбить исходный html на две части: до и после type-файла.
Секция head
С давних времен я выношу html-секцию head в отдельный файл исключительно из удобства. Иногда бывают такие задачи, где сайт должен иметь раздельные дизайны для главной и прочих страниц. Таким образом main-start.php и main-end.php будут разными, но а header.php один. Давайте его рассмотрим поподробней.
Строчка «DOCTYPE» указывает какой html мы используем. В MaxSite CMS принято использование «HTML 4.01 Transitional», который наиболее близок в HTML5 (надеемся, что он повится очень скоро).
Посмотрим внимательно на строчку:
- <title><?= mso_head_meta('title') ?></title>
Понятно, что это титул страницы и система генерирует его самостоятельно. Вот такая php-конструкция <?= эквивалентна echo. То есть строчку можно было бы записать и так:
- <title><?php echo mso_head_meta('title') ?></title>
Таким образом мы просто используем короткую php-запись.
Функция mso_head_meta() принимает первым параметром title - для титула, keywords - для ключевых слов и description - для короткого описания. Эти meta получаются в «черном ящике» type-файла после того, как получены все данные. Когда мы доберемся до type-файлов вы увидите как это происходит и как произвольно менять формат вывода.
Дальше мы подключаем иконку, и css-стили. Для этого мы используем
- getinfo('stylesheet_url')
которая возвращает адрес к текущему шаблону.
Обратите внимание на один момент. В MaxSite CMS принято все адреса и пути возвращать с завершающим слэшем «/».
Для подключения rss, точнее указание его адреса используется
- getinfo('rss_url')
Следующая строчка немного интересней:
- <?= mso_load_jquery() ?>
Данным действием мы подключаем библиотеку jQuery. Так уж случилось, что MaxSite CMS поставляется сразу с этой прекрасной библиотекой, поэтому вам не нужно её дополнительно скачивать и устанавливать. Но mso_load_jquery() выполняет еще одну полезную функцию: она позволяет избежать двойного подключения одного js-файла. Например в вашем шаблоне вы подключили jQuery. Потом активировали плагин, в котором также используется jQuery. А потом еще один. Естественно, что плагины также подключат jQuery и получится полная белиберда. Вот для таких ситуаций мы просто указываем, что хотим подключить jQuery, а дальше пусть система сама разбирается.
Но mso_load_jquery() можно использовать и для подключения других файлов jQuery. В этом случае в её параметре нужно указать путь до этого файла относительно каталога common/jquery, например:
- <?= mso_load_jquery('cornerz.js') ?>
- <?= mso_load_jquery('ui/effects.core.packed.js') ?>
- <?= mso_load_jquery('ui/effects.highlight.packed.js') ?>
Теперь, если какой-то другой плагин подключит эти файлы, то его добавление будет проигнорировано и дублирования не произойдет.
Последний, но очень важный код
- <?php mso_hook('head') ?>
В данном случае выполняется хук «head» (чуть ниже еще и «body_start»). Когда какой-то плагин хочет «прицепить» к шаблону свои css-стили, meta или js-файл, то он делает это через этот хук.
Первое знакомства с опциями
В файле main-start.php (с header.php мы разобрались) мы увидим получение вывод опций. В частности это используется при формировании названия сайта в шапке (тэг h1).
Для получения опции используется функция mso_get_option(). Пользоваться её очень просто. Первым парметром указывается имя опции. Вторым - группа опции. Третьим - значение, если такой опции нет.
Каждая опция может входить в группу. Таким образом имя опции дожно быть уникально только в пределах своей группы. Сделано это для того, чтобы разрешить нормальное именование для разных настроек. Например для плагина можно задать свою группу (по имени плагина), а имя опции задавать привычными именами: title, header и т.д. Если бы групп не было, то пришлось бы строго следить за именами опций, чтобы случайно не затереть чужие. Если вы не указываете группу, то она по-умолчанию равна general.
Опции в MaxSite CMS шикарная вещь, потому что их можно задавать даже без программирования. Загляните ради интереса в дефолтном шаблоне в файл options.ini, а также в «application/maxsite/admin/plugins/admin_options/general.ini» и вы сразу увидите как все работает.
Впрочем об опциях мы еще поговорим, сейчас мы просто видим, что в нашем шаблоне вывод шапки формируется из полученных опций.
Обычно на сайтах принято располагать главное меню по аналогии с windows-программами. MaxSite CMS предоставляет в ваше распоряжение два способа. Первый, (старый вариант) - когда вы задаете пункты меню в админке в опциях (в специальном формате), а потом выводим готовое меню с помощью функции «mso_menu_build()».
Второй вариант появился в MaxSite CMS гораздо позже, зато позволяет делать «изысканные» варианты. Реализуется это на уровне хука в отдельном плагине, в частности в Main Menu. В этом плагине можно задавать не только пункты меню, но и группы, и тем самым формировать подменю. Также используется плагин jQuery, который создает анимацию при работе меню. Правда для индивидуального шаблона придется настраивать свой main-menu.css, что для неподготовленного пользователя является не совсем простой задачей.
Подключение сайдбаров
В Файле «main-end.php» мы видим подключение сайдбара в виде отдельного файла. На самом деле совершенно не обязательно выносить одну функцию в отдельный файл, скорее это так «исторически сложилось».
Сайдбар выводится с помощью функции mso_show_sidebar(). Первым параметром указывается номер сайдбара, вторым - html до каждого виджета, третьим - после. В предыдущем рассказе я подробно рассказал о сайдбарах и виджетах, поэтому вы можете освежить эти знания там.
Сами сайдбары необходимо зарегистрировать. Тут такой нюанс. Сайдбары - это такая штука, о которой должен знать не только шаблон, но и админ-панель. Поэтому существует специальный файл шаблона functions.php который автоматически подключается не только в шаблоне, но и в админ-панели. В нем можно размещать функции, которые должны присутствовать в админ-панели. Здесь-то и регистрируются сайдбары:
- mso_register_sidebar('1', t('Первый сайдбар', 'templates'));
Первым параметром указываем номер сайдабра, вторым его название. Функция t() - функция перевода. Если вы например включите украинский язык, то вместо «Первый сайдбар» получите «Перша бічна панель».
Подвал
Файл footer.php подключается в main-end.php и также не является обязательным. В нем формируются служебные строчки о времени генерации страницы, количестве SQL-запросов и т.д.
Мы почти все функции уже рассмотрели, отмечу только такую строчку:
- $CI = & get_instance();
Подобное вы будете встречать очень часто. Это получение ссылки на «сам» CodeIgniter. В нем, как вы наверное знаете, есть много функций и библиотек, которые можно использовать как для MaxSite CMS, так и для ваших задач. CodeIgniter очень хороший фреймворк и мы с удовольствием будет по максимуму использовать его возможности.
После того, как мы получили ссылку к CodeIgniter ($CI - имя произвольно, просто так удобно для меня), мы получаем доступ к его функциям.
- $mq = $CI->db->query_count;
Получаем количество SQL-запросов. Как видите все просто.
Ушки
Ушки - это плагин. Если у вас WordPress, то возможно вы также пользуетесь моим же плагином. Ушки позволяют выводить произвольный код. То есть в шаблоне прописывается имя ушки, а самим наполнением занимаемся уже в админ-панели.
Хотя это и не в моих правилах делать для кого-то исключения, но для ушек я решил, что они достойны занять почетное место.
Данный код делает очень простую вещь: выводит код гугл-аналитика.
- if (function_exists('ushka')) echo ushka('google_analytics');
То есть мы проверем есть функция «ushka» и если есть, выводим содержимое ушки «google_analytics». Код GA отличается от остальных тем, что его следует размещать в конце страницы и он не выводит никакого мусора, вроде картинок (и не блокируется банерорезалками). Поэтому вероятность, что вы также будете использовать GA, велика. И вам (вашим клиентам) будет приятно, что о такой мелочи уже подумали.
Заключение
Итак мы разобрали шаблон по косточкам. Думаю, что с файлом info.php у вас никаких проблем не возникнет, а разбор css-стилей немного выходит за рамки статьи.



Комментариев: 1
]]>
Макс, спасибо! После прошлой статьи и общения с modx наконец поперло разобраться с твоей системой
Очень не хватает справочника по функциям которые используются в системе в формате:
То есть статьи это тоже классно, но иногда нужно быстро посмотреть где собака зарыта... ;)