Drupal + Omega + Bootstrap: fast creation of completely custom themes without layout (Part 2. Bootstrap)

  • Tutorial
First part, introduction

In the first part of the article, I talked about the excellent Omega framework for Drupal, which allows you to create your own custom themes based on the grid, arbitrarily configure and change the location and size of regions without any modifications to the HTML code of the templates. We could stop here, but there is a wonderful powerful Bootstrap CSS framework with its own implementation of the grid, a large number of ready-made CSS and JS components that are very easy to style, and also a huge community, thanks to which we get the highest quality and “licked” code compared to competitors.

In this part of the article, I’ll show you how to bind Bootstrap to Omega. But if for some reason Bootstrap does not suit you, then in this way you can tie any other framework of your choice to Omega. As in the first part of the article, I do not pretend to be any uniqueness, I just describe those things that can be learned from the documentation. There will be a lot of code and few pictures.

First, again, a little theory to get an idea of ​​what we have to work with. So, the structure of the Omega framework:

  1. The basic Alpha theme, which contains the very functionality that allows you to set the location of zones and regions through the theme settings, as well as introducing at our disposal a basic grid of various types (Fixed and Fluid) and the number of columns (12, 16, 24).
  2. The Omega Core Theme, a sub-theme of the Alpha Core Theme, in which the core Drupal HTML5 templates are redefined.
  3. Starter kit, on the basis of which we are invited to create sub-themes based on Omega either manually or automatically using the Omega Tools module.

Thus, in order to use Bootstrap with Omega, you need to do the following:

  1. Create a basic theme, which is an Omega sub-theme, that will connect Bootstrap and redefine the CSS classes of the Omega grid (grid- *) in Bootstrap (span *), as well as add the necessary CSS classes for standard Drupal components (for example, buttons, system messages, "Breadcrumbs", pagination, etc.).
  2. Create a starter kit for our basic theme to quickly create sub-themes using Omega Tools.

We will create the basic theme from the Omega HTML5 Startkit.

Preparation of the basic theme and starter kit

  1. Copy the directory omega/starterkits/omega-html5next to the directory omegaand rename it to omega_bootstrap.
  2. Rename the files starterkit_omega_html5.infoomega_bootstrap.info, css/global.csscss/omega-bootstrap.cssand delete YOURTHEME-alpha-default-narrow.css, YOURTHEME-alpha-default-normal.css, YOURTHEME-alpha-default-wide.css, YOURTHEME-alpha-default.css.
  3. Download the Bootsrtap and dump files bootstrap.cssand bootstrap-responsive.cssthe directory cssand the file bootstrap.jscreate a directory js. There is no need to use minified versions, we will be able to compress CSS and JS in the future using standard Drupal tools.

Next, we start editing the theme settings file omega_bootstrap.info. First, change the parameters name = Omega with Bootstrapand starterkit = FALSE.

For convenience, the parameters in this file are divided into sections.

In the sections ADDITIONAL REGIONSand ZONESwe can add the zones or regions we need.

In the section, OPTIONAL STYLESHEETSwe change everything [global.css]to [omega-bootstrap.css]and set namesomething like this in the parameter Omega Bootstrap custom styles. Here we can also add additional CSS (for example, enable FontAwesome), which we can then turn on / off in the theme settings in the Toggle styles section (enable / disable styles).

Below the section OPTIONAL STYLESHEETSwe create sections OPTIONAL LIBRARIESand CSS GRID SYSTEMS:

; OPTIONAL LIBRARIES
libraries[bootstrap][name] = 'Bootstrap'
libraries[bootstrap][description] = 'Sleek, intuitive, and powerful front-end framework for faster and easier web development.'
libraries[bootstrap][css][0][file] = bootstrap.css
libraries[bootstrap][css][0][options][weight] = 1
libraries[bootstrap][css][1][file] = bootstrap-responsive.css
libraries[bootstrap][css][1][options][weight] = 2
libraries[bootstrap][js][0][file] = bootstrap.js
libraries[bootstrap][js][0][options][weight] = -1
; CSS GRID SYSTEMS
grids[bootstrap][name] = Bootstrap
grids[bootstrap][layouts][normal] = Normal
grids[bootstrap][columns][12] = 12 Columns

We took the format for the description of these sections from alpha.infoand omega.info.

We also create the starter kit from Omega HTML5 Startkit:

  1. Copy the directory omega/starterkits/omega-html5next to omega_bootstrap, rename to omega-html5-bootstrap.
  2. Rename starterkit_omega_html5.infoto starterkit_omega_html5_bootstrap.info, delete YOURTHEME-alpha-default-narrow.css, YOURTHEME-alpha-default-normal.css, YOURTHEME-alpha-default-wide.css, YOURTHEME-alpha-default.css.
  3. In the file starterkit_omega_html5_bootstrap.infowe change the parameters name = Omega HTML5 Starterkit with Bootstrapand base theme = omega_bootstrap.

Our basic theme and starter kit are ready to use. But this is not the end, but rather only the beginning.

Sub Subject Creation

For a more visual representation of the process that will happen next, we will create a new theme in the manner described in the  first part of the article using Omega Tools, only in the Base theme (base theme) we select Omega with Bootstrap , and in Starterkit (starter set) our Omega HTML5 Starterkit with Bootstrap (Figure 1).

1.  image2. image3.  image4.  image5.  Install our theme by default and go to the settings.image



  1. On the Grid settings tab in the Grid system (grid type), a new Bootstrap type has appeared , and select it (Fig. 2).
  2. On the Zone and region configuration tab, add Bootstrap classes to zones and regions. In the Zone configuration section, in the Additional zone classes field, set the value row, and in the Additional wrapper classes field - value container. In the region settings, in the Additional region classes field, set the desired class span*for the required number of columns. Omega allows you to set arbitrary classes for zones and regions, which we have used now, but in the future we will automate this process (Fig. 3).
  3. On the Toggle libraries tab (enable / disable libraries), turn off all standard Omega libraries and enable our Bootstrap (Fig. 4).
  4. On the Toggle styles tab (enable / disable styles), also disable all styles in the Enable optional stylesheets section , except for Omega Bootstrap custom styles (all) - omega-bootstrap.css and Your custom global styles (all) - global. css (fig. 5).

We set all other settings in accordance with the recommendations from the first part of the article and save them. Then we can click the Export theme settings button , copy the settings and paste them into the file omega-html5-bootstrap/starterkit_omega_html5_bootstrap.info, replacing them with the settings in the section THEME SETTINGS (DEFAULTS). In this case, all new themes from our starter kit will be immediately configured in the way we need.

Theming

Bootstrap uses classes to style elements that are different from the standard Drupal classes. Therefore, you must add the Bootsrtap classes to the output of the standard Drupal elements. To do this, we will use the standard features of Drupal's theming, namely, we will redefine the necessary theme_functions for us . You can learn more about the possibilities of Drupal theming from the documentation section Using the theme layer ( http://drupal.org/node/933976 ).

We will redefine the theming functions in the file template.phpat the root of our base topic omega_bootstrap, in which case they will be inherited by all of our subtopics.

To get started, get rid of the need to manually add classes containerand rowzones, as well as classesspan*regions. To do this, add to the template.phpfunction omega_bootstrap_process():

omega_bootstrap_process ()
/**
 * Implements hook_process().
 */functionomega_bootstrap_process(&$vars, $hook){
  if (!empty($vars['elements']['#grid']) || !empty($vars['elements']['#data']['wrapper_css'])) {
    if (!empty($vars['elements']['#grid'])) {
      foreach (array('prefix', 'suffix', 'push', 'pull') as $quality) {
        if (!empty($vars['elements']['#grid'][$quality])) {
          array_unshift($vars['attributes_array']['class'], 'offset' . $vars['elements']['#grid'][$quality]); # Добавляем класс offset* региону
        }
      }
      array_unshift($vars['attributes_array']['class'], 'span' . $vars['elements']['#grid']['columns']); # Добавляем класс span* региону
    }
    $vars['attributes'] = $vars['attributes_array'] ? drupal_attributes($vars['attributes_array']) : '';
  }
  if (!empty($vars['elements']['#grid_container']) || !empty($vars['elements']['#data']['css'])) {
    if (!empty($vars['elements']['#grid_container'])) {
      $vars['content_attributes_array']['class'][] = 'container'; # Добавляем класс container зоне
    }
    $vars['content_attributes'] = $vars['content_attributes_array'] ? drupal_attributes($vars['content_attributes_array']) : '';
  }
  alpha_invoke('process', $hook, $vars);
}

We borrowed the code from a function alpha_process()that can be found in the file omega/alpha/template.php. As you see, you still need to add row, for this we copy the template files from omega/alpha/templates/zone.tpl.phpand omega/omega/templates/zone--content.tpl.phpto the directory omega_bootstrap/templatesand edit it as follows.

Zone.tpl.php file
<?php/**
 * @file
 * Alpha's theme implementation to display a zone.
 */?><?phpif ($wrapper): ?><div<?phpprint $attributes; ?>><?phpendif; ?>  
  <div<?phpprint $content_attributes; ?>><div class="row">
    <?phpprint $content; ?>
  </div></div>
<?phpif ($wrapper): ?></div><?phpendif; ?>

Zone file - content.tpl.php
<?phpif ($wrapper): ?><div<?phpprint $attributes; ?>><?phpendif; ?>
  <div<?phpprint $content_attributes; ?>><div class="row">
    <?phpif ($breadcrumb): ?>
      <divid="breadcrumb" class="grid-<?phpprint $columns; ?>"><?phpprint $breadcrumb; ?></div>
    <?phpendif; ?>    
    <?phpif ($messages): ?>
      <divid="messages" class="grid-<?phpprint $columns; ?>"><?phpprint $messages; ?></div>
    <?phpendif; ?>
    <?phpprint $content; ?>
  </div></div>
<?phpif ($wrapper): ?></div><?phpendif; ?>

Now you can remove unnecessary classes from the theme settings and set the number of columns in the region in the usual way.

Now we proceed to the decorations. We take the code from the corresponding functions, slightly modify it to our needs and add the Bootstrap classes.

"Breadcrumbs"
Так как для вывода системных элементов мы используем модуль Delta Blocks, то переопределяем его функцию темизации.

/**
 * Implements theme_delta_blocks_breadcrumb().
 */functionomega_bootstrap_delta_blocks_breadcrumb($variables){
  $output = '';
  if (!empty($variables['breadcrumb'])) {  
    if ($variables['breadcrumb_current']) {
      $variables['breadcrumb'][] = l(drupal_get_title(), current_path(), array('html' => TRUE));
    }
    $output = '<div id="breadcrumb" class="clearfix"><ul class="breadcrumb">';
    $switch = array('odd' => 'even', 'even' => 'odd');
    $zebra = 'even';
    $last = count($variables['breadcrumb']) - 1;    
    foreach ($variables['breadcrumb'] as $key => $item) {
      $zebra = $switch[$zebra];
      $attributes['class'] = array('depth-' . ($key + 1), $zebra);
      if ($key == 0) {
        $attributes['class'][] = 'first';
      }
      if ($key == $last) {
        $attributes['class'][] = 'last';
      }
      else {
        $item .= '<span class="divider">/</span>';
      }
      $output .= '<li' . drupal_attributes($attributes) . '>' . $item . '</li>';
    }
    $output .= '</ul></div>';
  }
  return $output;
}

System messages
Классы messages меняем на alert.

/**
 * Implements theme_status_messages().
 */functionomega_bootstrap_status_messages($variables){
  $display = $variables['display'];
  $output = '';
  $status_heading = array(
    'status' => t('Status message'), 
    'error' => t('Error message'), 
    'warning' => t('Warning message'),
  );
  $class = array(
    'status' => 'alert alert-success', 
    'error' => 'alert alert-error', 
    'warning' => 'alert',
  );
  foreach (drupal_get_messages($display) as $type => $messages) {
    $output .= "<div class=\"{$class[$type]}\">\n";
    if (!empty($status_heading[$type])) {
      $output .= '<h2 class="element-invisible">' . $status_heading[$type] . "</h2>\n";
    }
    if (count($messages) > 1) {
      $output .= " <ul>\n";
      foreach ($messages as $message) {
        $output .= '  <li>' . $message . "</li>\n";
      }
      $output .= " </ul>\n";
    }
    else {
      $output .= $messages[0];
    }
    $output .= "</div>\n";
  }
  return $output;
}

Button Buttons
Мне больше нравятся nav-pills, можно заменить на nav-tabs.

/**
 * Implements theme_menu_local_tasks().
 */functionomega_bootstrap_menu_local_tasks(&$variables){
  $output = '';
  if (!empty($variables['primary'])) {
    $variables['primary']['#prefix'] = '<h2 class="element-invisible">' . t('Primary tabs') . '</h2>';
    $variables['primary']['#prefix'] .= '<ul class="nav nav-pills">';
    $variables['primary']['#suffix'] = '</ul>';
    $output .= drupal_render($variables['primary']);
  }
  if (!empty($variables['secondary'])) {
    $variables['secondary']['#prefix'] = '<h2 class="element-invisible">' . t('Secondary tabs') . '</h2>';
    $variables['secondary']['#prefix'] .= '<ul class="nav nav-pills">';
    $variables['secondary']['#suffix'] = '</ul>';
    $output .= drupal_render($variables['secondary']);
  }
  return $output;
}

Form Buttons
Добавляем класс btn.

/**
 * Implements theme_button().
 */functionomega_bootstrap_button($variables){
  $element = $variables['element'];
  $element['#attributes']['type'] = 'submit';
  element_set_attributes($element, array('id', 'name', 'value'));
  $element['#attributes']['class'][] = 'btn';
  switch($element['#id']) { # Разукрашиваем основные кнопкиcase strpos($element['#id'], 'edit-submit') === 0: $element['#attributes']['class'][] = 'btn-primary'; break;
    case'edit-preview': $element['#attributes']['class'][] = 'btn-warning'; break;
    case'edit-delete': $element['#attributes']['class'][] = 'btn-danger'; break;
  }
  $element['#attributes']['class'][] = 'form-' . $element['#button_type'];
  if (!empty($element['#attributes']['disabled'])) {
    $element['#attributes']['class'][] = 'form-button-disabled btn-disabled';
  }
  return' <input' . drupal_attributes($element['#attributes']) . ' /> ';
}

Pagination
Класс pagination.

/**
 * Implements theme_pager().
 */functionomega_bootstrap_pager($variables){
  $tags = $variables['tags'];
  $element = $variables['element'];
  $parameters = $variables['parameters'];
  $quantity = $variables['quantity'];
  global $pager_page_array, $pager_total;
  // Calculate various markers within this pager piece:// Middle is used to "center" pages around the current page.
  $pager_middle = ceil($quantity / 2);
  // current is the page we are currently paged to
  $pager_current = $pager_page_array[$element] + 1;
  // first is the first page listed by this pager piece (re quantity)
  $pager_first = $pager_current - $pager_middle + 1;
  // last is the last page listed by this pager piece (re quantity)
  $pager_last = $pager_current + $quantity - $pager_middle;
  // max is the maximum page number
  $pager_max = $pager_total[$element];
  // End of marker calculations.// Prepare for generation loop.
  $i = $pager_first;
  if ($pager_last > $pager_max) {
    // Adjust "center" if at end of query.
    $i = $i + ($pager_max - $pager_last);
    $pager_last = $pager_max;
  }
  if ($i <= 0) {
    // Adjust "center" if at start of query.
    $pager_last = $pager_last + (1 - $i);
    $i = 1;
  }
  // End of generation loop preparation.
  $li_first = theme('pager_first', array('text' => (isset($tags[0]) ? $tags[0] : t('« first')), 'element' => $element, 'parameters' => $parameters));
  $li_previous = theme('pager_previous', array('text' => (isset($tags[1]) ? $tags[1] : t('‹ previous')), 'element' => $element, 'interval' => 1, 'parameters' => $parameters));
  $li_next = theme('pager_next', array('text' => (isset($tags[3]) ? $tags[3] : t('next ›')), 'element' => $element, 'interval' => 1, 'parameters' => $parameters));
  $li_last = theme('pager_last', array('text' => (isset($tags[4]) ? $tags[4] : t('last »')), 'element' => $element, 'parameters' => $parameters));
  if ($pager_total[$element] > 1) {
    if ($li_first) {
      $items[] = array(
        'data' => $li_first,
      );
    }
    if ($li_previous) {
      $items[] = array(
        'data' => $li_previous,
      );
    }
    // When there is more than one page, create the pager list.if ($i != $pager_max) {
      if ($i > 1) {
        $items[] = array(
          'data' => '<span>…</span>',
        );
      }
      // Now generate the actual pager piece.for (; $i <= $pager_last && $i <= $pager_max; $i++) {
        if ($i < $pager_current) {
          $items[] = array(
            'data' => theme('pager_previous', array('text' => $i, 'element' => $element, 'interval' => ($pager_current - $i), 'parameters' => $parameters)),
          );
        }
        if ($i == $pager_current) {
          $items[] = array(
            'class' => array('active'), 
            'data' => "<span>$i</span>",
          );
        }
        if ($i > $pager_current) {
          $items[] = array(
            'data' => theme('pager_next', array('text' => $i, 'element' => $element, 'interval' => ($i - $pager_current), 'parameters' => $parameters)),
          );
        }
      }
      if ($i < $pager_max) {
        $items[] = array(
          'data' => '<span>…</span>',
        );
      }
    }
    // End generation.if ($li_next) {
      $items[] = array(
        'data' => $li_next,
      );
    }
    if ($li_last) {
      $items[] = array(
        'data' => $li_last,
      );
    }
    return'<h2 class="element-invisible">' . t('Pages') . '</h2><div class="pagination pagination-centered">' . theme('item_list', array(
      'items' => $items, 
    )) . '</div>';
  }
}

And so on according to the same principle. If you need to add Bootstrap classes to the standard Drupal output - just redefine the theming function (or template), all this is done using standard Drupal tools. You can borrow some additional features from the Bootstrap Drupal theme . You can also supplement these functions with additional parameters that can be changed in the theme settings. Examples of settings: the separator character in bread crumbs, nav-tabsor nav-pills, the size and location of the page numbering ( pagination-large/ pagination-small/ pagination-miniand pagination-centered/ pagination-right). You can read more about this in the documentation Creating advanced theme settings ( http://drupal.org/node/177868 ).

Using

Well, now let's move on to the practical part and see how all this can be used. For starters, I would recommend setting up an auxiliary overlay grid to fit the width of the Bootstrap columns. To do this, find PNG from the Alpha base theme, modify them in a graphical editor and redefine the class alpha-grid-debugin ours omega-bootstrap.css.

Please note that for the minimum version of jQuery for the Bootstrap JS components to work - 1.7, so you need to connect it (or more recent) using the jQuery Update module .

Using the Block Class module (which was mentioned in the first part of the article), it is very convenient to add the necessary classes to blocks, for example, the standard one well(see Fig. 6).

In the display settings of the Views module, there is a standard ability to set arbitrary classes to almost all components, which opens up very wide possibilities for using Bootstrap.

Example 1: mesh output. To do this, just add the class to the rowentire Other - CSS class view , and in the line class settings, the Format - Unformatted list - Settings - Row class output form set the class with the desired number of columns, for example span2.

Example 2: displaying news using the Bootstrap component of a Media object . To do this, add to the Format - Unformatted list - Settings - Row class classmedia, in the parameters of the field with the image Style settings - Customize field and label wrapper HTML - Create a CSS class add a class pull-left, and in the same parameters of the field with text add a class media-body. Other fields can be added to the same field through Rewrite results - Rewrite the output of this field , without forgetting to turn off these fields themselves for output (Exclude from display) .

Also, using the Views Bootstrap module (thanks to mrded ), Bootstrap components such as Thumbnails and Carousel can be used to style Views .

And finally, a small demonstration of some of the capabilities of the resulting topic blank.

Figure 6. Ready theme with debugging blocks enabled.



Thanks for attention! I hope this article is helpful and extends the scope of your use of Drupal.

Also popular now: