Создание шаблонов для такого меню — хороший пример для тех, кто только начинает осваивать XSLT-шаблонизатор.
Это наиболее простой, но достаточно распространенный способ вывода меню, на примере которого можно изучить основы подключения и обработки результатов макросов UMI.CMS при помощи XSLT.
Предположим, мы уже подключили шаблон разметки сайта, полученный в виде HTML (см. «Создание и подключение XSLT-шаблона»):
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8" method="html" indent="yes"/>
<xsl:template match="/">
<html>
<head></head>
<body>
<div class="menu">
// здесь мы хотим поместить меню
</div>
<div class="content">
// здесь располагается контент страницы
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
И наша задача вывести в элемент <div class="menu"> следующий список:
<ul>
<li><a href="/page1/">Название страницы 1</a></li>
<li><a href="/page2/">Название страницы 2</a></li>
<li><a href="/page3/" class="active">Название страницы 3</a></li>
// ... и так далее
</ul>
При этом мы бы хотели, чтобы текущая страница помечалась class="active".
Для всех страниц, которые попадут в меню, должна быть проставлена опция "Отображать в меню". В этом случае вызов макроса %content menu()% по протоколу UData вернет нам XML примерно следующего содержания (для просмотра можно набрать в адресной строке браузера http://ваш_сайт/udata/content/menu):
<udata module="content" method="menu" generation-time="0.031503">
<items>
<item id="95" link="/page1/" name="Название страницы 1" xlink:href="upage://95">Название страницы 1</item>
<item id="98" link="/page2/" name="Название страницы 2" xlink:href="upage://98">Название страницы 2</item>
<item id="99" link="/page3/" name="Название страницы 3" xlink:href="upage://99">Название страницы 3</item>
// ... и так далее
</items>
</udata>
Нашей задачей является обработка этого XML и вывод данных в виде HTML в нужное место в шаблоне. Отметим также, что у элемента udata есть атрибуты module и method, говорящие о том, что этот XML получен от макроса %content menu()%.
Для включения ответа макроса в обработку необходимо воспользоваться функцией document(), которая получит данные по протоколу UData; тогда как инструкция apply-templates укажет, что эти XML-данные необходимо обработать по доступным шаблонам.
Таким образом, получаем ответ макроса в виде XML — document('udata://content/menu'), и указываем место, куда должны быть выведены результаты обработки — инструкция apply-templates:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8" method="html" indent="yes"/>
<xsl:template match="/">
<html>
<head></head>
<body>
<div class="menu">
<xsl:apply-templates select="document('udata://content/menu/')/udata"/>
</div>
<div class="content">
// здесь располагается контент страницы
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
select в инструкции apply-templates сообщает шаблонизатору, что мы выбрали в полученном XML элемент udata (он указан последним) и теперь шаблонизатор должен искать среди всех доступных шаблонов, шаблон для этого элемента. Наша задача — создать такой шаблон:
<xsl:template match="udata[@module = 'content'][@method = 'menu']">
<ul>
// здесь мы хотим увидеть элементы списка
</ul>
</xsl:template>
match = "udata" — и есть тот самый способ, который назначает именно этот шаблон для обработки элемента udata.
Важно
Шаблонизатор будет искать шаблон для обработки элемента среди всех доступных шаблонов (заданных инструкцией <xsl:template>) и выберет только тот, у которого подходит условие match.
Пока что таких шаблонов у нас только 2: <xsl:template match="/"> и <xsl:template match="udata[...][...]">. Первый — не подходит. Зато подходит второй: так как мы выбрали udata (select=".../udata") а у шаблона задано соотвествие udata (match="udata[...][...]").
Уточняющее условие udata[@module = 'content'][@method = 'menu'] говорит о том, что это шаблон необходимо применять только к элементу udata с атрибутами module="content" и method="menu" и, следовательно, этот шаблон будет применен только к результатам этого макроса. Таким образом мы можем отличать результат работы этого макроса от результатов работы других макросов, также возвращающих XML с элементом udata.
Замечание
Для тех, кому интересно: на самом деле запись udata[@module = 'content'][@method = 'menu'] означает следующее: из множества элементов udata — те, у которых атрибут module равен 'content', и среди этих уже те — у которых атрибут method равен 'menu'.
Если мы уверены, что в процессе обработки не будет больше никаких элементов udata c атрибутом method равным 'menu', то можно смело написать match="udata[@method = 'menu']".
Для того, чтобы вывести элементы списка, воспользуемся инструкцией apply-templates еще раз, внутри этого шаблона. Нас интересуют элементы item, так как в них и в их атрибутах содержится информация о страницах — поэтому выберем их при помощи select. Так как они расположены внутри элемента items, следует это явно указать:
<xsl:template match="udata[@module = 'content'][@method = 'menu']">
<ul>
<xsl:apply-templates select="items/item" mode="menu"/>
</ul>
</xsl:template>
select в инструкции apply-templates сообщает шаблонизатору, что мы выбрали в полученном XML элементы item, и теперь шаблонизатор должен искать шаблон последовательно для каждого элемента списка. Режим mode="menu" добавлен для того, чтобы эти элементы item не обрабатывались по другим шаблонам (обрабатывающим результаты других макросов и т.п.).
Далее, наша задача описать сам шаблон для элемента item (и задать ему mode="menu"):
<xsl:template match="item" mode="menu">
<li>
<a href="{@link}">
<xsl:value-of select="@name"/>
</a>
</li>
</xsl:template>
Инструкция value-of выводит значение атрибута name в виде текста, а запись {@link} выводит значение атрибута link внутри атрибута href тега <a>.
По правилам языка XSLT этот шаблон будет применен ко всем элементам item, выбранным при помощи <xsl:apply-templates select="items/item" mode="menu"/> в том порядке, в каком они находятся в исходном XML-ответе макроса %content menu()%.
Осталась одна задача — описать шаблон для активного пункта меню. Для того, чтобы это было возможно, в ответе макроса %content menu()% этот элемент помечается атрибутом status = 'active'. В таком случае, нетрудно представить себе уточняющее условие для создания еще одного шаблона — match="item[@status = 'active']". Сам шаблон может тогда выглядеть так:
<xsl:template match="item[@status = 'active']" mode="menu">
<li>
<a href="{@link}" class="active">
<xsl:value-of select="@name"/>
</a>
</li>
</xsl:template>
Этот шаблон будет выбран для активного элемента так как он является более точным, нежели match="item", и по правилам языка XSLT имеет более высокий приоритет.
В результате должен получиться файл следующего содержания:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8" method="html" indent="yes"/>
// шаблон разметки страницы
<xsl:template match="/">
<html>
<head></head>
<body>
<div class="menu">
<xsl:apply-templates select="document('udata://content/menu/')/udata"/>
</div>
<div class="content">
// здесь располагается контент страницы
</div>
</body>
</html>
</xsl:template>
// шаблон для всего меню
<xsl:template match="udata[@module = 'content'][@method = 'menu']">
<ul>
<xsl:apply-templates select="items/item" mode="menu"/>
</ul>
</xsl:template>
// шаблон для неактивного пункта меню
<xsl:template match="item" mode="menu">
<li>
<a href="{@link}">
<xsl:value-of select="@name"/>
</a>
</li>
</xsl:template>
// шаблон для активного пункта меню
<xsl:template match="item[@status = 'active']" mode="menu">
<li>
<a href="{@link}" class="active">
<xsl:value-of select="@name"/>
</a>
</li>
</xsl:template>
</xsl:stylesheet>


