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