Логотип

Документация по макросам и шаблонам UMI.CMS

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