Theme and Template

There are two meanings of ‘theme’ in Drupal, first one is the folder under ‘themes’ and it’s the whole presentation layer; second one is the theme API.

For the second one, Drupal have its mechanism to cast the content into the mock-ups, which’s suffix is like ‘xxxx.tpl.php’. We call the mock up is templates. It looks likes that:

  /**
   * This is the block.tpl.php.
   */
  <div id="<?php print $block_html_id; ?>" class="<?php print $classes; ?>"<?php print $attributes; ?>>
  <?php print render($title_prefix); ?>
  <?php if ($title): ?>
    <h4<?php print $title_attributes; ?>><?php print $title; ?></h4>
  <?php endif; ?>
  <?php print render($title_suffix); ?>
  <?php print $content; ?>

Templates has the naming convention according to the different components, here’s some example:

  • Fields: field–.tpl.php

  • Nodetype: node–.tpl.php

  • Views: views-view–.tpl.php (we can check this in view admin UI)

The theme suggestion information could be checked using dpm to check the theme suggestion

What if you would like to use some existing template but not in the suggest list? you can use

What if you would like to add some template not use that naming convention? You can use theme function, but remember to register the theme before you use that.

/**
 * Implements hook_theme().
 */
function THEME_NAME_theme() {
  $path = drupal_get_path('theme', 'THEME_NAME') . '/templates';
  $templates['TEMPLATE_NAME'] = array(
    'template' => 'TEMPLATE-FILE',
    'path' => $path,
    'type' => 'theme',
  );
  return $templates;
}

Panelizer vs Panel

Panel is like a container to put all the different kind of components together, a component maybe a view with it’s own format, a field in the node, a block provided by the module, a custom html markup. In the panel, we call every component as pane

To organize the content better, we could use different layouts. In the layouts, we define several regions in grid system. It’s good for the responsive as well.

Here’s a sample of layout defined via ctool plugin.

$plugin = array(
  'title' => t('Full'),
  'category' => t('CATEGORY'),
  'icon' => 'full.png',
  'theme' => 'panels_full',
  'css' => 'full.css',
  'regions' => array(
    'banner' => t('Banner'),
    'body' => t('body'),
    'tabs' => t('Tabs'),
    'bottom' => t('Bottom'),
  ),
);

Panelizer is a super version of the panel. We could turn on panelizer for a content type, create several variation for this content type, and choose which node use a specific variation.

And even more, we could do some special handling base on that variation (e.g. content, layout, context) only for one node.

Ctools Plugins

Ctools module is a contributed module to provide a set of API and function, like Page Manager, Context tools, Access tools (visibility rules), configuration exportability, form wizards and so on.

And the Plugins is one of functions this module provide. Using this tool we could extend the modules or themes with `.inc’ file. There’re 3 kinds of include file: layouts (in theme), content type and styles (in modules, feature).

content_types ctools plugin exits in modules, it allow us to create our own panel pane that display dynamic content. In the pane, we could even setup a form to do the configuration.

In .module file, we could use HOOK_ctools_plugin_directory to register the plugin directory like this:

/**
 * Implements hook_ctools_plugin_directory().
 */
function MODULE_ctools_plugin_directory($owner, $plugin_type) {
  switch ("$owner:$plugin_type") {
    case 'panels:styles':
      return "plugins/$plugin_type";

    case 'panels:content_types':
      return "plugins/$plugin_type";

    case 'ctools:content_types':
      return "plugins/$plugin_type";
  }

  return NULL;
}

In the .inc file itself, we will do 2 things, clarify how to render and how to finish configuration (optional).

$plugin = array(
  'title' => t('TESTING'),
  'description' => t('TESTING'),
  'hook theme' => array(
      'path' => '/PATH/TO/TEMPLATE',
      'template' => 'TEMPLATE-NAME',
    ),
  ),
  'settings form' => 'CALL_BACK_FUNCTION',
);

content type plugins could also lives in features, feature recreate action will not override/ remove the registration or .inc file. And we avoid putting content type plugins in the theme level, as the pane could be used across different themes.

Here’s another sample for the layout include file. TBC

Form API

Form API is most used concept in Drupal. There are some many technique(trap) in there area. I list some of them I encounter in daily work.

  • Method GET and POST matters, as the form token is needed.
$form['#method'] = 'get'; 
 
  • drupal_render_children could help us to print the remaining items (e.g hidden fields), so we could focus on the fields we want to tailor-make.:
<div class="row collapse">
  <div class="small-10 large-11 xlarge-10 columns">
    <?php print render($form['search_keys']); ?>
  </div>
  <div class="small-2 large-1 xlarge-2 columns">
    <?php print render($form['op']); ?>
    <?php
    // Print remaining items (hidden fields).
    print drupal_render_children($form);
    ?>
  </div>
</div>
  • The class attributes should be stored in a array like this
'#attributes' => array(
        'class' => array('button', 'form-submit', 'postfix', 'icon', 'icon-search'),
        'id' => 'edit-submit',
      ),
  • How to use programatically render a form.
$form = drupal_get_form('user_register_form');
print drupal_render($form);

Block and context

Block is container to wrap the content/logic provided by a module, in this aspect, it’s like the pane created by ctools. But block could be put in the region based on the context (e.g. content type, URL). You don’t need to configure the setting for every panel/node. As all the setting is done in the module itself.

/**
 * Implements hook_block_info().
 */
function module_name_block_info() {
  $blocks = array();
  $blocks['block_name'] = array(
    'info' => t('GSA Search Form'),
  );
  return $blocks;
}

/**
 * Implements hook_block_view().
 */
function module_name_block_view($delta = '') {
  $block = array();
  switch ($delta) {
    case 'block_name':
      $block['subject'] = '';
      $block['content'] = drupal_get_form('form_id');
      break;
  }
  return $block;
}

/**
 * Implements hook_form().
 */
function module_name_form($form, &$form_state) {
  $form = array(
    'search_keys' => array(
      '#type'     => 'textfield',
      '#required' => TRUE,
      '#attributes' => array(
        'class' => array('form-text'),
        'id' => 'edit-search-keys',
        'placeholder' => array('Search'),
      ),
      '#theme_wrappers' => array(),
    ),
    'op' => array(
      '#type' => 'submit',
      '#attributes' => array(
        'class' => array('button', 'form-submit', 'postfix', 'icon', 'icon-search'),
        'id' => 'edit-submit',
      ),
      '#value' => 'I',
    ),
    '#theme' =>  'theme_name',
  );
  return $form;
}

Formatter and Widget

Drupal provide a serias field type to us, but sometimes we need some special field, like the map, icon, feature, which is combination of entity reference, existing field type, taxonomy.

In this case, we need to build a new field. And we need to answer two questions, how could we render that in page (we use formatter), and how could we update the content in admin UI (we use widget).

  • Define how to render formatter:
  function hook_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
    $element = array();
    $settings = $instance['widget']['settings'] + field_info_widget_settings($instance['widget']['type']);
    $module_path = drupal_get_path('module', 'fcl_custom_fields');
    $js_path = $module_path . '/js';
    $css_path = $module_path . '/css';
  
    switch ($display['type']) {
      case 'fcl_custom_experience':
        foreach ($items as $delta => $item) {
          // Provide the element as a themeable item.
          $element[$delta] = array(
            '#theme' => 'fcl_custom_experience',
            '#type' => $item['type'],
            '#title' => $item['title'],
            '#desc' => $item['desc'],
          );
        }
        break;
    }
  }
 
  • Define how to render widget, using hook_field_widget_form().

  • Define how to save, using hook_field_presave().

View

I touched View in the first week I start using Drupal, so it is most basic advanced concept. View is the query UI to help us retrieve data from the DB.

fields, type, filter, order reflect the select field1, field2, ... fieldn from table where criteria1 order by key1

And the view is doing more than the SQL GUI, it also provide the exposed filter, contextual filter to let user interact (AJAX). No return result message, header, footer is also very help to better the user experiences.

Preprocessor

This is another blog talking about the hook system.

Feature

This is a very powerful concept in Drupal. You can export the setting stored in DB to a file, in Drupal we call that ‘Feature’. Feature is very helpful in setup environment.

But in the meantime it generate a lot of problem

  • It’s impossible to change one feature in the same time, the merge conflict cannot be fix, as the code is generated by the machine.
  • Odd status when revert, need review, default.
  • Export feature time/ resource consuming.

Javascript API

drupal_add_js could be used to load the JS via HOOK, and weight, position is the option to control the loading position.

  • Drupal.setttings is what enable us to pass information from PHP code to JS code

  • Drupal.behaviors will get called DOM has loaded (for AJAX load as well), we can use context to know which DOM is being processed.

  • Once function is jQuery plugin, we can use once() in Drupal out of the box.

// The following will change the color of each paragraph to red, just once
// for the "changecolor" key.
$('p').once('changecolor').css('color', 'red'); 
 

How Foundation work in Drupal

Setup the font size base, use rem to setup breakpoint: small, medium, large, x-large. Build up the variables (color, font-family) We put grid in page.tpl.php and layout template (used in panel) and any template. Try to use Foundation class instead of creating customized styling. If customized styling is needed, it means either create helper class and reuse, or create a component classes and reuse.

P.S. Drupal Exmaple module is a really good template we could start working on.

drupal variable vs drupal conf

we could get and use Drupal vaiable anywhere via drupal_get() and drupal_set().

conf() will persistent the drupl variable.

js setting

Entity and Node

@todo

We could use wrapper_metadata_entity('node', $node) to simplify the access to fields’ value.

How to render a node depending on the place, like in detail pages we use ‘Full’, in listing we use ‘Teaser’. ‘Full’ and ‘Teaser’ is the default view_mode of the node. We can also create a customized one to extend the use cases.

Example code?

/**
 * Implements hook_entity_info_alter().
 */
function MYMODULE_entity_info_alter(&$entity_info) {
  $entity_info['node']['view modes']['alternate'] = array(
    'label' => t('Alternate'),
    'custom settings' => TRUE,
  );
}

/**
 * Implements template_preprocess_node()
 */
function THEME_preprocess_node(&$variables) {
  $node = $variables['node'];
  $view_mode = $variables['view_mode'];
  
  // Set up template suggestions for non-standard view modes
  if ($view_mode !== 'full') {
    $variables['theme_hook_suggestions'][] = 'node__' . $node->type . '__' . $view_mode;
  }
}

paragraph

@todo

theme setting

Sometimes we would like to put some setting only in theme level, we could use the theme setting.

  1. in the theme.info define a setting like this:
' this setting is for the theme
['setting_name'] = ''
  1. define the admin UI using form api in
form['setting_name'] = 
@todo
  1. use the theme setting in any hook like this
if @todo

drush command

Drupal provide cli utility drush to let us finish admin job efficiently and make it posible to put all the command into shell script.

drush vset theme_debug 1 this command will turn the theme debug on, the theme hook suggestion will be printed along with the html markup.

drush cc all && drush rr clean all the cache

drush sqlq 'select * from table limit 20' run sql command

drush fr revert feature

more drush command we could refer this website.