MaxSite.org 11 лет
Блог вебмастера о сайтостроении
Внимание! Данная запись отмечена как устаревшая и может содержать неточную или неактуальную информацию!

Построчно разбираем шаблон MaxSite CMS

MaxSite CMS / Шаблоны (архив)Просмотров: 15849 (165)

Продолжим наши изыскания. Сегодня речь пойдет о шаблонах 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.phpheader.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-стилей немного выходит за рамки статьи.

Комментариев: 9 RSS

1Attlant06-01-2010 00:22

Макс, спасибо! После прошлой статьи и общения с modx наконец поперло разобраться с твоей системой :) Очень не хватает справочника по функциям которые используются в системе в формате:

Название функции, назначение, параметры, пример использования.

То есть статьи это тоже классно, но иногда нужно быстро посмотреть где собака зарыта... ;)

2RitWok06-10-2010 18:37

Ну наконец то, нашел сайт по Максите. На самом форуме по этой системе даже пару раз возмущался, мол, нет шаблонов.

На днях закралась мысль, может разобраться, как делать то их, шаблоны эти.

И вот случайно на этот сайт напоролся сегодня...

Как то странно, но теперь знаю, что у автора есть 3 сайта и по всем трем инфа интересная раскинута!

3mr_rain24-12-2010 02:43

Спасибо, Макс за MaxSite. Сам люблю писать сайты на чистом php, потому как большинтсво распространенных CMS нерационально тратят ресурсы. Думаю это потому что их разрабатывает целая куча людей, и у всех свой взгляд на вещи. Разбираясь в MaxSite - сразу почувствовал, что его делал в основном один человек, так как система очень целостная по структуре и нет противоречий.

4Андрей19-06-2012 09:20

Виноват, прошлый коммент отправил с ошибкой)))

Код GA отличается от остальных тем, что его следует размещать в конце страницы

Фиг поймёшь red face То ГуглАналитикс говорит, что надо размещать код "непосредственно перед тегом

</body>
" (http://support.google.com/googleanalytics/bin/answer.py?hl=ru&answer=55488), то рекомендует размещать код отслеживания "непосредственно перед закрывающим тегом
</head>
"? (https://support.google.com/analytics/bin/answer.py?hl=ru&utm_id=ad&answer=1008080).

Как быть?

5MAX19-06-2012 10:08

У гугла есть несколько вариантов размещения кода. Старая версия ставилась перед /BODY, новые перед /HEAD. Ставьте туда, где рекомендуется на странице кода счетчика.

6Андрей19-06-2012 18:36

Нубская просьба: Максим, ткните, пожалуйста, пальцем, куда нужно вставить ушку

if (function_exists('ushka')) echo ushka('google_analytics');
, чтобы код ГуглАналитикса выводился перед закрывающим тегом HEAD на каждой странице. Поставил Вашу CMS на локалхост (Денвер), исходил админку вдоль и поперёк, но куда воткнуть код GA, так и не нашёл. Не там искал?

7MAX19-06-2012 19:33

Используйте ушку head.

В новых версиях, сейчас доступных в prelatest, можно использовать ушку google_analytics_top.

8Андрей19-06-2012 20:16

Максим, чтобы Вас лишний раз не дёргать по пустякам, скажите, плиз, где можно узнать полный список ушек и их назначений?

Оставьте комментарий!

Комментарий будет опубликован после проверки. Вы соглашаетесь с правилами сайта.

(обязательно)

О сайте

Здесь вы получите самую полную информацию о создании сайтов на MaxSite CMS.