Panels: Un layout flexible con Bootstrap

La meta

Lograr que el mantenimiento de un sitio construido con Drupal sea más simple y rápido. Evitar la proliferación de tpl específicos dentro del theme para ditintos tipos de contenido intentando resolver todo con Panels. Más o menos la misma idea que Panels Everywhere, pero no tan drástica.

Un subtheme de Bootstrap

Bootstrap es un buen inicio. Aquí partimos de un subtheme. Limpio. Sin complicaciónes. El template predeterminado que viene con el paquete es el siguiente:

<header id="navbar" role="banner" class="<?php print $navbar_classes; ?>">
  <div class="<?php print $container_class; ?>">
    <div class="navbar-header">
      <?php if ($logo): ?>
        <a class="logo navbar-btn pull-left" href="<?php print $front_page; ?>" title="<?php print t('Home'); ?>">
          <img src="<?php print $logo; ?>" alt="<?php print t('Home'); ?>" />
        </a>
      <?php endif; ?>

      <?php if (!empty($site_name)): ?>
        <a class="name navbar-brand" href="<?php print $front_page; ?>" title="<?php print t('Home'); ?>"><?php print $site_name; ?></a>
      <?php endif; ?>

      <?php if (!empty($primary_nav) || !empty($secondary_nav) || !empty($page['navigation'])): ?>
        <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
          <span class="sr-only"><?php print t('Toggle navigation'); ?></span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
        </button>
      <?php endif; ?>
    </div>

    <?php if (!empty($primary_nav) || !empty($secondary_nav) || !empty($page['navigation'])): ?>
      <div class="navbar-collapse collapse">
        <nav role="navigation">
          <?php if (!empty($primary_nav)): ?>
            <?php print render($primary_nav); ?>
          <?php endif; ?>
          <?php if (!empty($secondary_nav)): ?>
            <?php print render($secondary_nav); ?>
          <?php endif; ?>
          <?php if (!empty($page['navigation'])): ?>
            <?php print render($page['navigation']); ?>
          <?php endif; ?>
        </nav>
      </div>
    <?php endif; ?>
  </div>
</header>

<div class="main-container <?php print $container_class; ?>">

  <header role="banner" id="page-header">
    <?php if (!empty($site_slogan)): ?>
      <p class="lead"><?php print $site_slogan; ?></p>
    <?php endif; ?>

    <?php print render($page['header']); ?>
  </header> <!-- /#page-header -->

  <div class="row">

    <?php if (!empty($page['sidebar_first'])): ?>
      <aside class="col-sm-3" role="complementary">
        <?php print render($page['sidebar_first']); ?>
      </aside>  <!-- /#sidebar-first -->
    <?php endif; ?>

    <section<?php print $content_column_class; ?>>
      <?php if (!empty($page['highlighted'])): ?>
        <div class="highlighted jumbotron"><?php print render($page['highlighted']); ?></div>
      <?php endif; ?>
      <?php if (!empty($breadcrumb)): print $breadcrumb; endif;?>
      <a id="main-content"></a>
      <?php print render($title_prefix); ?>
      <?php if (!empty($title)): ?>
        <h1 class="page-header"><?php print $title; ?></h1>
      <?php endif; ?>
      <?php print render($title_suffix); ?>
      <?php print $messages; ?>
      <?php if (!empty($tabs)): ?>
        <?php print render($tabs); ?>
      <?php endif; ?>
      <?php if (!empty($page['help'])): ?>
        <?php print render($page['help']); ?>
      <?php endif; ?>
      <?php if (!empty($action_links)): ?>
        <ul class="action-links"><?php print render($action_links); ?></ul>
      <?php endif; ?>
      <?php print render($page['content']); ?>
    </section>

    <?php if (!empty($page['sidebar_second'])): ?>
      <aside class="col-sm-3" role="complementary">
        <?php print render($page['sidebar_second']); ?>
      </aside>  <!-- /#sidebar-second -->
    <?php endif; ?>

  </div>
</div>

<?php if (!empty($page['footer'])): ?>
  <footer class="footer <?php print $container_class; ?>">
    <?php print render($page['footer']); ?>
  </footer>
<?php endif; ?>

Imaginemos por un momento que el layout que necesitamos sea más o menos como se muestra en la siguiente figura:

Drupal - Layout del sitio con sus regiones

Podrían utilizarse bloques dentro de la correspondiente región. Algunos bloques podrían hacerse con vistas. Algunos tipos de contenido podrían personalizarse con un archivo tpl particular. Todas estas son operaciones que convertirán el mantenimiento del sitio en un infierno.

Una manera de trabajar mejor es reducir el número de herramientas con una más potente. De esta manera, tendremos todo en un solo lugar. Es por eso que para trabajar con layouts más sofisticados que los que pueda requerir un monótono blog, construir con Panels nos aliviará muchísimo el trabajo. Ya sea en la etapa de desarrollo como también en la más aburrida y costosa: la del mantenimiento.

Observemos una vez más el layout del sitio pero esta vez indicando quien se ocupará de cubrir cada sección (o región en lenguaje Drupal)

Drupal - Layout del sitio

El encabezado

La idea es cargar todo el contenido del encabezado en un mini panel. Un mini panel es muy parecido a un panel con la diferencia que el mini panel no cubre una página si no un bloque. Pensemos a los elementos que ubicaremos en el encabezado:

  • Nombre del sitio
  • Logo
  • Menú principal
  • Formulario para la búsqueda
  • Menú para cambiar idioma?

Todos estos elemento son accesibles desde la interfaz de mini panels. Con este método se pierde el control en muchas de las configuraciones del subtheme de Bootstrap. En realidad, no las perdemos. Las estamos solo cambiando de lugar. Por ejemplo, no podremos cambiar la configuración de navbar desde un select, pero podremos modificar las classes que querramos desde el mini panel.

Drupal: Configuración del subtheme Bootstrap

Crear el layout personalizado con Bootstrap

Antes de comenzar, instalar el módulo Panels Extra Styles. Panels Extra Styles nos dará una mano en poder configurar fácilmente los contenedores con sus respectivas classes según ordene la constitución de Bootstrap y nuestras necesidades.

Los layouts personalizados para Panels y Mini panels se crean en un directorio dentro la ubicación del theme. Aconsejo crearlos dentro del theme de Bootstrap y no del subtheme para que sea más simple reutilizar los layouts entre distintos sitios en la misma instalación de Drupal. Seguramente será sites/all/themes/bootstrap/layouts. En el archivo .info del theme agregar la siguiente línea:

plugins[panels][layouts] = layouts

Ahora podemos crear un layout de ejemplo. Lo llamaremos navbar. Para crearlo necesitamos 4 archivos:

  • navbar.css
  • navbar.png
  • navbar.inc
  • panels-navbar.tpl.php

El archivo navbar.css lo dejamos vacio. Olvidemos navbar.png que no tienen importancia al momento. En el archivo navbar.inc insertamos las siguiente lineas:

<?php

/**
 * implementation of hook_panels_layouts()
 */
// Plugin definition
$plugin = array(
  'title' => t('navbar'),
  'category' => t('Bootstrap'),
  'icon' => 'navbar.png',
  'theme' => 'panels_navbar',
  'css' => 'navbar.css',
  'regions' => array(  
    'header'   => t('navbar header'),
    'collapse' => t('navbar collapse'),
  ),
);

Luego crear el archivo panels-navbar.tpl.php con el siguiente contenido:

<nav class="navbar navbar-default navbar-static-top">
  <div class="container">
    <?php print $content['header']; ?>
    <?php print $content['collapse']; ?>
  </div>
</nav>

Para que el nuevo layout personalizado esté disponible, debemos borrar la cache de Drupal en admin/config/development/performance

Una vez que tenemos el layout debemos limpiar el HTML original del subtheme de Bootstrap. Entonces, copiamos el archivo sites/all/themes/bootstrap/theme/system/page.tpl.php y lo pegamos en sites/dominio.com/themes/nombre_subtheme/system/page.tpl.php

Ahora, todo lo que está dentro <header> ya no nos sirve ya que necesitamos solo un contenedor para el mini panel. En lugar de <header>...</header> colocar las siguientes líneas:

<?php if (!empty($page['navigation'])): ?>
  <?php print render($page['navigation']); ?>
<?php endif; ?>

Crear el mini panel para navbar

Creamos un nuevo mini-panel utilizando el layout que creamos anteriormente. Vamos a encontrarnos con 2 regiones:

  1. navbar header
  2. navbar collapse

En la primera, configuraremos el contenedor como DIV con la classe navbar-header y en la segunda, configuraremos el contenedor como DIV con la classe collapse navbar-collapse

Drupal - Contenido del minipanel para navbar

Ahora ubicamos el mini panel en el bloque navigation y listo. Ya tenemos un navbar fácil de configurar.

Drupal - Navbar con Mini panels

Limpiar HTML de una región

Las regiones de un theme de Drupal cargan en forma predeterminada un grupo de contenedores. Por lo general es muy cómodo, pero en este caso es muy molesto. Para limpiar el HTML tenemos que crear un archivo de nombre region--[nombre de la region].tpl.php. El archivo original podemos encontrarlo en /modules/system/region.tpl.php. Su contenido en la última versión de Drupal 7 al día de hoy es el siguiente:

<?php if ($content): ?>
  <div class="<?php print $classes; ?>">
    <?php print $content; ?>
  </div>
<?php endif; ?>

El nuevo archivo, en este caso region--navigation.tpl.php, debería contener simplemente una línea:

<?php print $content; ?>

Este archivo tendremos que crearlo dentro del directorio theme, que se encuentra dentro el subtheme de Bootstrap. Por ejemplo: sites/dominio.com/themes/misubtheme/theme

Limpiar el HTML del block

De la misma manera que en el punto anterior, en este caso, será necesario crear un archivo llamado block--[nombre de la region].tpl-php

<?php print $content; ?>

En el sitio de Bootstrap encontraremos toda la ayuda necesaria para entender como construir correctamente una navbar. En el sitio de Drupal existe un artículo que explica más detalladamente como crear un layout personalizado. También en el sitio de Bootstrap podemos encontrar ejemplos de navbar.

Panels para el contenido principal

Para representar los distintos tipos de contenido utilizaremos Panels. Para poder trabajar con Panels en los distintos tipos de contenido será necesario antes activar el módulos Page manager (page_manager). Una vez activo, visitando admin/structure/pages podremos activar el submodulo node_view que sobreescribirá el proceso predeterminado de Drupal para todo lo que se encuentre en la ruta /node/%node

Drupal: Page manager

Una vez hecho esto, podremos entrar en el mundo de Panels. Encontraremos la tarea de configurar los siguientes puntos:

  • Variants
  • Selection rules
  • Layout
  • Content

La configuración de estos 4 puntos es el requerimiento mínimo necesario para poder exponer por ejemplo un tipo de contenido.

Variants es más que nada el grupo que contiene el resto de las configuraciones. Seleccion rules (que traducido al lenguaje de Views serían Filters) nos permite seleccionar o identificar que parte específica del sitio queremos representar. Una operación simple para quien ya esté acostumbrado a utilizar vistas. Layout nos permitirá seleccionar sobre que template ubicaremos más adelante los distintos elementos del contenido. De la misma manera que para el navbar, deberíamos crear un layout personalizado acorde a nuestras necesidades. Por último, la sección content nos permitirá insertar cualquier tipo de contenido dentro de cada región del layout (no confundir con las regiones del template).

Drupal: Dos grupos de variants para dos tipos de contenido distintos

Módulos para trabajar con Panels

Estos son algunos módulos que pueden facilitar el uso de Panels