Изменения темизации в drupal 7 (часть 3)

Продолжение первой и второй частей. Мы рассматриваем изменения в темизации drupal 7.

Присоединение JavaScript и CSS для drupal_render

Теперь мы можем отдельным элементам указывать, какие js и css файлы с ними связаны.

Как было в Drupal 6:

<?php
function example_admin_settings() {
  // Добавляем example.admin.css
  drupal_add_css(drupal_get_path('module', 'example') .'/example.admin.css');
  // Добавляем inline-JavaScript
  drupal_add_js('alert("Вы познакомились с тестовой формой.");', 'inline');
  // Добавляем js настройки.
  drupal_add_js(array('mymodule' => 'example'), 'setting');
  $form['example'] = array(
    '#type' => 'fieldset',
    '#title' => t('Example');
  );
  return $form;
}
?>

Как будет в Drupal 7:

<?php
function example_admin_settings() {
  $form['#attached_css'] = array(
    // Добавляем example.admin/css.
    drupal_get_path('module', 'example') . '/example.admin.css'
  ),
  $form['#attached_js'] = array(
    // Добавляем inline JavaScript.
    'alert("Вы познакомились с тестовой формой.");' => 'inline',
    // Add a JavaScript setting. Note that when the key is a number, the 'data' property will be used.
    array(
      'data' => array('mymodule' => array(...)),
      'type' => 'setting'
    ),
  );
  $form['example'] = array(
    '#type' => 'fieldset',
    '#title' => t('Example');
  );
  return $form;
}
?>

$closure станет $page_bottom, новый регион $page_top и скрытые регионы

Drupal 6 располагал специальной переменной $closure, которую выводили обычно в конце HTML body и которая могла быть темизована через theme_footer() . В Drupal 7 пошли путем приведения вывода к одному общему стандарту для различных областей страницы. Что это значит? Что теперь у нас есть регион page_bottom, в котором и выводится переменная $closure, вам не надо теперь прописывать ее специально (ура! бывало, о ней забывали в процессе создания темы, что вызывало минуты размышления "а что тут не так). Также появился регион page_top. Рассмотрим различия в коде:

Drupal 6:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
...
<body class="<?php print $body_classes; ?>">
...
    <?php print $closure; ?>
</body>
</html>

Drupal 7:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN"
  "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">
...
<body class="<?php print $classes; ?>">
  <?php print $page_top; ?>
...
  <?php print $page_bottom; ?>
</body>
</html>

Если вы создаете свои произвольные регионы, важно помнить что нужно включить page_top, page_bottom в ваш .info файл (и вообще сделать включение обязательных регионов привычкой при создании тем).

regions[content] = Content
regions[help] = Help
regions[page_top] = Page top
regions[page_bottom] = Page bottom
regions[indicators] = Indicators
regions_hidden[] = indicators

page_top и page_botoom являются скрытыми регионами (hidden), таким образом они не будут отображаться в админке в разделе администрирования блоков. Может оказаться полезным создание большего количества спрятанных регионов для ваших тем (к примеру для вывода ваших самописных модулей), что вы можете теперь делать в .info файле, массивом regions_hidden[] :

regions[content] = Content
regions[help] = Help
regions[page_top] = Page top
regions[page_bottom] = Page bottom
regions[indicators] = Indicators
regions_hidden[] = indicators

$left и $right переменные теперь называются $sidebar_first и $sidebar_second; CSS ID соответственно изменились

В drupal 6 сайдбары (колонки, если хотите) назывались $left и $right. В drupal 7 они называются $sidebar_first и $sidebar_second.

Drupal 6

  <?php if (!empty($left)): ?>
        <div id="sidebar-left" class="column sidebar">
          <?php print $left; ?>
        </div> <!-- /sidebar-left -->
      <?php endif; ?>
...
      <?php if (!empty($right)): ?>
        <div id="sidebar-right" class="column sidebar">
          <?php print $right; ?>
        </div> <!-- /sidebar-right -->
      <?php endif; ?>

Drupal 7

   <?php if ($sidebar_first): ?>
        <div id="sidebar-first" class="column sidebar"><div class="section region">
          <?php print $sidebar_first; ?>
        </div></div> <!-- /.section, /#sidebar-first -->
      <?php endif; ?>
 
      <?php if ($sidebar_second): ?>
        <div id="sidebar-second" class="column sidebar"><div class="section region">
          <?php print $sidebar_second; ?>
        </div></div> <!-- /.section, /#sidebar-second -->
      <?php endif; ?>

CSS идентификаторы тоже изменились:
.sidebar-left --> .sidebar-first
.sidebar-right --> .sidebar-second

$picture стала $user_picture, и CSS класс 'picture' изменился на 'user-picture'

$picture выводила изображение (аватарку) из профиля пользователя. Теперь, видимо, решили сделать имя более конкретным и отвечающим нуждам - $user_picture.

Добавились новые классы для скрытия содержимого

Этот пункт - особое "Вау!" от drupal 7 для верстальщика. Добавились 2 класса: .element-hidden и .element-invisible. У каждого свое назначение:
.element-hidden - назначение этого класса скрыть элемент от всех пользователей. Используется для элементов, которые не должны быть показаны сразу после открытия страницы. К примеру это выплывающие/проявляющиеся/еще-как-то-анимированные блоки, меню и т.п. К ним удобно применять jquery show() и hide() функции.

.element-invisible - предназначен для сокрытия элементов визуально. В документации было очень непонятно описано что же это обозначает, и мне пришлось покопаться в коде и обсуждениях на drupal.org. Основная идея в том, чтобы для полновесных браузеров этот элемент не был виден, но обозначался к примеру для парсеров, которые отметают элементы с display:none, или спецефичных устройств с звуковым выводом(их еще называют срин-ридеры, устройства которые озвучивают содержание сайта, часто используются людьми с нарушениями зрения). Чтобы было совсем понятнее, посмотрим как это реализовано в коде (system.css) в drupal 7:

.element-hidden {
  display: none;
}
 
.element-invisible {
  height: 0;
  overflow: hidden;
  position: absolute;
}

JavaScript переменная Drupal.jsEnabled удалена

По той причине что больше нет смысла ее использовать. Либо jquery работает, либо нет :) Процет браузеров, который поддерживает джаваскрипт, но не поддерживает jquery исчезающе мал.

PHPTemplate обзавелся wildcard

Нет, правда, я не знаю как это адекватно перевести :) В общем wildcard - групповой символ, когда вы хотите обозначить множественное значение. В поиске зачастую "some*" звездочка это wildcard. В Drupal 6 мы таким образом именовали файлы шаблонов: page-user.tpl.php или page-user-1.tpl.php. И если мы хотели изменить все страницы пользователей мы меняли page-user.tpl.php, невольно изменяя и страницу входа на сайт (login page). В Drupal 7 же будет доступно такое решение в названиях файлов шаблонов: page-user-%.tpl.php. Символ % тут заменяет целочисленные аргументы - т.е. он будет действовать для page-user-1.tpl.php, page-user-2.tpl.php и т.п., но не будет работать для page-user-edit.tpl.php. Что несколько облегчит наши тяжкие раздумья насчет архитектуры сайта :)

Изменена system_elements()

Причина - появившиеся при тестировании баги, связанные с drupal_render().

Дополнительные изменения видимости элементов

В связи с тем что на вооружение drupal7 взят описанный выше класс .element-invisible, теперь при отключенном css будет видно, как проходит инсталляция, на каком этапе сейчас находится установка сайта (ранее при отключенном css все это было проблематично увидеть, аналогично со скрин-ридерами дело обстоит), а также текущее положение пользователя в хлебных крошках.

Изменены атрибуты alt и title у RSS-иконки

В drupal 6 атрибут alt был статично прописан как "Syndicate content", а title параметр брался из переменной $title. В drupal 7 и alt и title содержат один текст, который представляет из себя "Subscribe to " + $title.

Изменился вывод $search_box

Если в drupal 6 форма поиска выводилась в любом месте темы через $search_box, то в drupal 7 эта возможность убрана. Теперь поиск это часть системы блоков. Т.е. форма поиска - элемент блока поиска, и темизоваться будет как соответствующий блок. Что гарантирует дополнительную работу при обновлении d6->d7 людям, которые использовали $search_box.

Изменение в построении меню, ссылок и табов

Изменения связаны с появлением drupal_render(), соответственно функция menu_tree_output() выводит теперь массив элементов для рендера. Функции theme_menu_item_link() и theme_menu_item() были удалены, теперь их заменяют элементы theme_menu_link().

theme_links() обзавелся новым параметром $heading для большей доступности

Вывод будет теперь более гибким. Пока сложно понять, насколько это будет полезным. Выглядеть будет так:

<?php print theme('links', $secondary_menu, array('id' => 'secondary-menu', 'class' => array('links', 'clearfix')), array('text' => t('Secondary menu'), 'level' => 'h2', 'class' => array('element-invisible'))); ?>

Улучшены theme_get_setting() и THEME_settings()

В Drupal 6 можно было добавлять собственные формы (настройки) в admin/build/themes/settings/имятемы. Нужно было создавать файл theme-settings.php в директории темы и использовать следующее решение:

<?php
/**
* Implementation of THEMEHOOK_settings() function.
*
* @param $saved_settings
*   array An array of saved settings for this theme.
* @return
*   array A form array.
*/
function phptemplate_settings($saved_settings) { }
?>

В drupal 7 все стало несколько более гибким, и теперь в theme-settings.php вы можете использовать функцию THEMENAME_form_system_theme_settings_alter(&$form, $form_state). К примеру, у вас есть тема "foo" и вы хотите добавить текстовое поле с значением по умолчанию "hi all".

<?php
function foo_form_system_theme_settings_alter(&$form, $form_state) {
  $form['caberet_example'] = array(
    '#type'          => 'textfield',
    '#title'         => t('Widget'),
    '#default_value' => theme_get_setting('foo_example'),
    '#description'   => t("Place this text in the widget spot on your site."),
  );
}
?>

Для того чтобы установить значение по умолчанию, вы просто добавляете в ваш .info файл строчку:

settings[foo_example] = hi all

и в любом php файле темы вызвать

<?php
$foo_example = theme_get_setting('foo_example');
?>

Маркер для обязательных полей

Теперь для того чтобы отметить какое-либо поле как "обязательное", вы можете использовать новую функцию theme_form_required_marker() . В drupal 6 надо было использовать theme_form_element(), т.е. переопределять эту довольно емкую функцию - теперь эти лишние телодвижения упростили, сделав для такой довольно популярной задачи theme_form_required_marker() .

Новая функция theme_link()

Сделана для упрощения переопределения ссылок. Для тех, кто пользовался hook_preprocess_link() вполне знакома конструкция (для drupal 6):

<?php
function mytheme_preprocess_link(&$variables) {
  // In order to style links differently depending on what they are linking to,
  // add classes that contain information about the link path.
  if (strpos($variables['path'], ':') !== FALSE) {
    // For external links, add a class indicating an external link and a class
    // indicating the scheme (e.g., for 'mailto:...' links, add a 'link-mailto'
    // class).
    $variables['options']['attributes']['class'][] = 'link-external';
    $variables['options']['attributes']['class'][] = 'link-' . parse_url($variables['path'], PHP_URL_SCHEME);
  }
  else {
    // For internal paths, add a class indicating an internal link.
    $variables['options']['attributes']['class'][] = 'link-internal';
    if (empty($variables['path']) || $variables['path'] == '<front>') {
      // Add a class indicating a link to the front page.
      $variables['options']['attributes']['class'][] = 'link-front';
    }
    else {
      // For internal links not to the front page, add a class for each part
      // of the path. For example, for a link to 'admin/structure/block', add
      // the classes 'link-path-admin', 'link-path-admin-structure', and
      // 'link-path-admin-structure-block'.
      $class = 'link-path';
      foreach (explode('/', $variables['path']) as $path_part) {
        $class .= '-' . $path_part;
        $variables['options']['attributes']['class'][] = $class;
      }
    }
  }
}
?>

Как это может выглядеть сейчас в drupal 7? Так:

<?php
function mytheme_link($variables) {
  // Place a span within and outside the anchor tag in order to implement some
  // special styling.
  return '<span class="link-wrapper"><a href="' . check_plain(url($path, $options)) . '"' . drupal_attributes($options['attributes']) . '><span class="link-content-wrapper">' . ($options['html'] ? $text : check_plain($text)) . '</span></a></span>';
}
?>

Для "родных тем" ядра будет облегчен доступ к основным ссылкам

Для тех кто работает только с клавиатурой или использует устройства голосового воспроизведения контента(скрин-ридеры).