Создание логичного меню в Drupal

Главные вкладки

Аватар пользователя SaBoNim SaBoNim 29 марта 2008 в 17:41

Наверное, не изобрету велосипед, но задача подчас встает и нашла много нового и полезного в поисках решения, безусловно. Конечно опыт нельзя оставлять в себе, так как он имеет свойство улетучиваться. Поэтому приступим.

Возьмем обычный ничем не примечательный корпоративный сайт не на Drupal - sponda.fi. Меню на нем организовано очень логично и просто. Вверху строка основных разделов (первого уровня), слева при выборе раздела появляется меню подразделов, причем уровней несколько - это второй и третий уровни меню (иногда четвертый). Также мы видим что все пункты, через которые мы пришли на данную страницу (родительские пункты - и в первом уровне и в последующих) активны вместе с текущим активным пунктом меню. Как в Drupal организовать подобную структуру?

Обращаю внимание, что данные методы работают на сайте с Drupal 5.*, для Drupal 6.* не работает!

Метод №1

Есть хороший и очень удобный способ - сделать одно меню, создавая структуру через выбор родительских пунктов (все пункты меню можно оставить неразвернутыми), а потом в настройках меню выбрать основным (primary) и дополнительным (secondary) одно и то же это самое меню, а в page.tpl.php прописать расположение этих самых меню в виде 'links'.
Все это по шагам можно описать так:

  1. Перейти: Админка → Конструкция → Меню
  2. Создать меню (можно создать необходимые пункты прямо в primary links - меню уже создано по умолчанию). Чтобы сделать его многоуровневым, выбирайте у пунктов родителя. Не нужно создавать 2 отдельных меню.
  3. Зайти на вкладку Настройки - выбрать:
    Меню, содержащее основные ссылки: ваше меню (Primary links)
    Меню, содержащее дополнительные ссылки: ваше меню (Primary links)
  4. Написать в нужном месте в page.tpl.php такой код:
    Для primary_links
    <?php if (isset($primary_links)) { ?><?php print theme('links', $primary_links) ?><?php } ?>

    Для secondary_links:

    <?php if (isset($secondary_links)) { ?><?php print theme('links', $secondary_links) ?><?php } ?>

Но недостаток этого метода в том, что он применим только при двухуровневой иерархии, то есть существуют только разделы вверху и подразделы слева, а под-подразделов уже быть не может, так как их secondary menu не покажет.

Метод №2

Поэтому расположим слева в page.tpl.php левую колонку (sidebar_left). Далее создадим блок в админке и поместим туда вот такой PHP код (http://drupal.org/node/77099 комментарий №249180 - источник):

<?php
$primary_menu_id = variable_get('menu_primary_menu', 0);
if (menu_in_active_trail($primary_menu_id)){
  // get the menu items that lead to the current menu item
  $active_trail =_menu_get_active_trail();
  // get the menu id of the active top-level link
  $mid = $active_trail[1];
  $menu_tree = menu_tree($mid);
print '<ul class="ВАШ КЛАСС ДЛЯ ЛЕВОГО МЕНЮ">'.$menu_tree.'</ul>';
}
?>

Тестируем, действительно, работает. Показывает ссылки слева в левой колонке.

Теперь прикрепим class="active" к родительскому пункту в самом верхнем уровне меню (primary links). Для этого воспользуемся TemplatePHP сниппетом с этой страницы. Вставляем в template.php данный код. Для моей ситуации ссылка на главную страницу не имеет дочерних меню, поэтому можно сократить код до такого состояния:

function phptemplate_links($links, $attributes = array()) {
  if (!count($links)) {
    return '';
  }
  $level_tmp = explode('-', key($links));
  $level = $level_tmp[0];
  $output = "<ul class=\"links-$level ".$attributes['class']. "\">\n";
  foreach ($links as $index => $link) {
    $output .= '<li';
    if (stristr($index, 'active')) {
      $output .= ' class="active"';
    }
    $output .= ">". l($link['title'], $link['href'], $link['attributes'], $link['query'], $link['fragment']) ."</li>\n";
}
  $output .= '</ul>';
  return $output;
}

Код для page.tpl.php также не обязательно повторять за автором статьи, так как он оптимизирован для его меню, нам будет достаточно:

<?php if (isset($primary_links)) : ?>
 <?php print theme('links', $primary_links) ?>
<?php endif; ?>

Теперь самая сложная для нас задача. Если бы я была программистом, то я бы с легкостью написала какой-нибудь TemplatePHP сниппет, и автоматом все родительские ссылки имели class="active", однако я не программист, поэтому решим эту проблему с помощью CSS.
Такое меню генерирует код из блока.

<ul class="sbtree">
 <li class="leaf"><a href="/Company_History">Company History</a></li>
 <li class="expanded"><a href="/Business_Units">Business Units</a>
  <ul class="menu">
   <li class="expanded"><a href="/Property_Development">Property Development</a>
    <ul class="menu">
     <li class="leaf"><a href="/board_committees" class="active">Board Committees</a></li >
     <li class="leaf"><a href="/Rules_of_Procedure">Rules of Procedure</a></li>
    </ul>
   </li>
   <li class="leaf"><a href="/Real_Estate_Funds">Real Estate Funds</a></li>
   <li class="leaf"><a href="/Russia">Russia</a></li>
  </ul>
 </li>
 <li class="leaf"><a href="/Corporate_Governance">Corporate Governance</a></li>
</ul>

Такие классы меню нужны мне чтобы полностью проследить 4 уровня вложенного меню.

ul.sbtree li a.active - просто активное меню второго уровня
ul.sbtree li.expanded a - родительский пункт развернутого меню, содержащего активный пункт - хотим чтобы он был как активный
ul.sbtree li.expanded ul.menu li a - дочерний неактивный пункт меню третьего уровня - хотим чтобы он был как неактивный
ul.sbtree li ul.menu li a.active - дочерний активный пункт меню третьего уровня
ul.sbtree li.expanded ul.menu li.expanded a - родительский пункт развернутого меню в развернутом меню - хотим чтобы он был как активный
ul.sbtree li.expanded ul.menu li.expanded ul.menu a - дочерний неактивный пункт развернутого меню в развернутом меню - хотим чтобы он был как неактивный
ul.sbtree li.expanded ul.menu li.expanded ul.menu a.active - - дочерний активный пункт меню четвертого уровня

Итак, используя 2 сниппета и одно меню, стилизованное через CSS мы получили меню, которое удовлетворяет нашим требованиям:

  • Разделение расположения основных и второстепенных разделов меню.
  • Второстепенные разделы показываются только при выборе одного из основных.
  • Выделение активным классом всех родительских пунктов на всех уровнях.
  • Возможность вывода дерева меню (много уровней) в подразделах (чего нельзя добиться при использовании secondary_links)

Ссылки по теме:
http://www.drupal.ru/node/3573, http://www.drupal.ru/node/3335, http://www.drupal.ru/node/8581, http://www.drupal.ru/node/12210, http://www.drupal.ru/node/10894.

Хотелось бы узнать ваше мнение по поводу этих способов, может быть вы знаете методы получше?

Комментарии

Аватар пользователя SaBoNim SaBoNim 1 апреля 2008 в 13:46

Drupal CookBook - Готовить может каждый!Решение было сохранено на сайте DrupalCookBook.ru:
Использование primary/secondary links для построения разделенного двухуровневого меню, Использование сниппетов для построения разделенного многоуровневого меню
Авторы, предложившие решения, также указаны в сохранённой статье.

Аватар пользователя player player 22 апреля 2008 в 0:10

ПРОБЛЕМА Localizer + Pathauto + Secondary links
2-е решение c localizer не работает к сожалению. menu_in_active_trail($primary_menu_id) не работает при переключении языка. Пытался побится с этой проблемой но решил не совсем. В моем случае не требовалось большого уровня вложенности (точнее требовалось вывести одни лишь secondary links). Вывел я их через темизацию
в page.tpl.php темы luxline добавив

<?php print theme('luxline_links', $secondary_links, array('class' =>'links_sec', 'id' => 'subnavlist')) ?>

и в template.php дописав

function theme_luxline_links($links, $attributes = array('class' => 'links')) {
 $out = '';
  if (count($links) > 0) {
    $out .= '<ul'. drupal_attributes($attributes) .'>';
    $num_links = count($links);
    $i = 1;
    foreach ($links as $key => $link) {
      $out .= '<li>';
      $html = isset($link['html']) && $link['html'];
      $link['query'] = isset($link['query']) ? $link['query'] : NULL;
      $link['fragment'] = isset($link['fragment']) ? $link['fragment'] : NULL;

      if (isset($link['href'])) {
        $out .= l($link['title'], $link['href'], $link['attributes'], $link['query'], $link['fragment'], FALSE, $html);
      }
      else if ($link['title']) {
      if (!$html) {
          $link['title'] = check_plain($link['title']);
        }
        $out .= '<span'. drupal_attributes($link['attributes']) .'>'. $link['title'] .'</span>';
      }
      $i++;
      $out .= "</li>\n";
    }
    $out .= '</ul>';
  }
  return $out;
}

Но! Когда включаем pathauto все летит к чертям. Это меню попросту не отображается. Эксперементальным путем определил что если ставить верные ссылки типа
/язык/node/1 - /язык/o-nas
а не
/node/1 - /o-nas
то все работает правильно.
Осталось узнать есть ли параметр языка в pathauto чтоб ручками не прописывать языки в ссылках.

Аватар пользователя player player 22 апреля 2008 в 1:30

Нет к сожалению. Всем рассмотреным тут способам это не помогло. Вот только модуль который romantaran предложил не смотрел.

Аватар пользователя ААндрей ААндрей (не проверено) 2 июня 2008 в 23:48

добрый день

> Далее создадим блок в админке и поместим туда вот такой PHP

создаю блок через "Add block" вставляю пхп код и привязываю к левой колонке, но там печатается сам пхп код, а не результат его выполнения

подскажите как быть
спасибо

Аватар пользователя SanchezR SanchezR 13 июля 2008 в 13:49

"...Написать в нужном месте в page.tpl.php такой код:..."
Скажите, пожалуйста, в каком именно нужном месте?
Для темы garland уже есть строки для primary и secondary.

Аватар пользователя SanchezR SanchezR 15 июля 2008 в 13:34

Для primary и secondary уже есть строки, но они не совпадают с теми, которые рекомендуют. Если вместо них вставить рекомендованные - то меню не будет двухуровневым....и с уже имеющимися тоже не получается двухуровневое меню.
Подскажите, где как же его сделать двухуровневым? Все делаю, как описанно.

Аватар пользователя nyck@drupal.org nyck@drupal.org 1 октября 2008 в 23:56

SanchezR wrote:
Подскажите, где как же его сделать двухуровневым? Все делаю, как описанно.

надо зайти в настройки темы(admin/build/themes/settings/garland) и в "Toggle display" включить "Primary links" и "Secondary links".
при этом в настйроках блоков "Primary links" и "Secondary links" можно(и даже нужно) выключить.
после можно зайти в шаблон и поместить код вывода меню

<?php if (isset($secondary_links)) { ?><?php print theme('links', $secondary_links) ?><?php } ?>

в нужный блок.


p.s. второй метод не работает в 6-м друпале, т.к. в menu.inc отсутвует функция menu_in_active_trail()

Аватар пользователя maxneb maxneb 7 октября 2008 в 13:24

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

Аватар пользователя maxneb maxneb 13 октября 2008 в 13:04

Спасибо, уже решил при помощи модуля menublock и привязывания материала к определенному пункту меню, сразу при создании.

Аватар пользователя bobroff bobroff 4 января 2009 в 23:52

maxneb wrote:
Спасибо, уже решил при помощи модуля menublock и привязывания материала к определенному пункту меню, сразу при создании.

Напиши по-подробнее, как ты это решил.

Аватар пользователя bobroff bobroff 5 января 2009 в 12:01

После добавления этого кода в блок:

<?php
$primary_menu_id = variable_get('menu_primary_menu', 0);
if (menu_in_active_trail($primary_menu_id)){
// get the menu items that lead to the current menu item
$active_trail =_menu_get_active_trail();
// get the menu id of the active top-level link
$mid = $active_trail[1];
$menu_item = menu_get_item($mid);
$menu_tree = menu_tree($mid);
print '

    '.$menu_tree.'

';
}
?>

Сайт вообще перестал загружаться. Как быть?

Аватар пользователя sigma sigma 17 марта 2009 в 20:00

для 6го друпала необходимо создать блок и поместить туда следующий код PHP

<?php$tree = menu_tree_page_data('primary-links');
      foreach ($tree as $link_data) {
        if ($link_data['link']['in_active_trail']) {
          $m = array();
          $m = $link_data['below'];
          if (isset($m) and is_array($m) and count($m)) 
              print menu_tree_output( $m );
          break;
        }
      }?>