Blog

Theme forms

I've been working on a project in Drupal 6 Commons which involves theming the user page, which is kind've a rats nest of information and options. The client wanted something with much less.

The first thing I wanted to do was remove a bunch of stuff and change the text on labels. This is a Drupal 6 project so I needed to use hook_theme() in template.php so Drupal would use my form hook.

/**
* Implementation of hook_theme.
*/
function mytheme_theme() {
  return array(
    'user_profile_form' => array(
      'arguments' => array('form' => NULL),
    ),
  );
}

The form uses the "value" of user-profile-form which I found using firebug at the bottom of the form. That's what you need to use in hook_theme() and elsewhere.

Now I could use the following form hook to unset much of the excess and change text.

/**
* Theme override for user page
*/
function mytheme_user_profile_form($form) {
    unset($form['account']['mail']['#description']);
    unset($form['account']['name']['#description']);
    unset($form['account']['pass']['#prefix']);
    $form['submit']['#value'] = 't(Save All Changes)';
    $form['account']['pass']['pass1']['#title'] = t('Reset your password');
    $form['account']['pass']['pass2']['#title'] = t('Confirm new password');
 
  //dpm($form); //uncomment to look at contents of form
  return (drupal_render($form));
}

This is a quick way of just removing elements that are already there and working with what's left. If I wanted though I could rebuild the form in a template file.

/**
* Implementation of hook_theme.
*/
function mytheme_theme() {
  return array(
    'user_profile_form' => array(
        'arguments' => array('form' => NULL),
        'path' => drupal_get_path('theme','mytheme') . '/templates',
        'template' => 'user-profile-form',
     ),
  );
}

This implementation of hook_theme() tells Drupal to look for the template file 'user-profile-form.tpl.php' inside /templates in my theme folder. From there I could use a preprocess function to rebuild the form using drupal_render().

function mytheme_preprocess_user_profile_form(&$vars) {
    $vars['name'] = drupal_render($vars['form']['account']['name']);
    $vars['mail'] = drupal_render($vars['form']['account']['mail']);
    $vars['pass'] = drupal_render($vars['form']['account']['pass']);
}

Then inside user-profile-form.tpl.php I could print the following;

     <?php
       print $name;
       print $mail;
       print $pass;
       //print drupal_render($form); uncomment to load rest of form
     ?>

If this were a Drupal 7 project I could use hook_form_alter() inside template.php.

/**
* Implementation of hook_form_FORM_ID_alter().
*/
function hook_form_user_profile_form_alter(&$form, &$form_state, $form_id) {
    //dpm($form);
}

hook_form_alter() in Drupal 6

In Drupal 6 if there are required fields you won't be able to just unset them, you'll need to use hook_form_alter() which needs to be inside a module. You would create a file like so called maybe, "themeforms.info" and put this inside it,

name = themeforms
description = themeforms implements hook_form_alter to theme tha forms
core = 6.x
package = "themeforms"

And a file called, "themeforms.module"

/**
  * Implementation of hook_form_alter() in Drupal 6
  */
 function themeforms_form_alter(&$form, $form_state, $form_id) {
 
    switch ($form_id) {
      case 'user_profile_page':
           unset($form['name']);
           unset($form['message']);
           // $output .= dsm($form);
        break;
 
          $output .= drupal_render($form);
      return $output;
    }
  }

Render arrays

Render arrays in Drupal 7 replace the template variables that were mostly globs of fully formatted HTML in prior versions. In Drupal 6 you'd see something like this to display content;

<?php print $content ?>

You often needed to break apart that variable then rebuild it in order to have full control over it's contents.

The new render function looks like this;

<?php print render($content); ?>

It has more granular control, you can look at the array contents like so (with devel module turned on);

<?php print dpm($content); ?>

The hashed values are render properties. There is a more detailed table at the bottom of this page on Drupal.org and here. Heres some common ones;

#themeThe theme hook, a function or a template which renders the element into html
#typeType of element to be rendered. A nice list of these in system element_info().
#theme_wrappersAn array with theme hooks that wrap children elements.
#weightNumber used to determine the order in which the field appears
#prefix/#suffixStuff appended or prepended
#sortedBoolean TRUE or FALSE. Used with #weight to sort blocks in a region.
#attachedSpecifies CSS or Javascript or libraries to load with the element.

You use alter hooks to generate and alter markup. Here is a hook that adds breadcrumbs to the footer through page_alter;

function mytheme_page_alter(&$page) {
  // dpm($page);
   $page['footer']['breadcrumbs'] = array(
      '#type' => 'container',
      '#attributes' => array('class' => array('wrap-breadcrumbs', 'clearfix')),
     );
   $page['footer']['breadcrumbs']['breadcrumb'] = array(
      '#theme' => 'breadcrumb',
      '#prefix' => '<h3> Breadcrumbs </h3>',
      '#breadcrumb' => drupal_get_breadcrumb(),
      );
}

The ['breadcrumbs'] are a new element that get tacked onto $page['footer'] and it uses the render properties to add markup and theme functions (breadcrumbs is a theme function in "include/theme.inc" in drupal core). You can get a feel for how the render properties are used in different blocks of content by looking at the dpm() (or kpr()) output of blocks / pages. This hook uses hook_html_head_alter() to change the meta content type in the doctype to match HTML5's shorter version.

function mytheme_html_head_alter(&$head_elements) {
   $head_elements['system_meta_content_type']['#attributes'] = array(
        'charset' => 'utf-8'
    );
}

There are quite a few alter hooks you can use to generate or alter content, many listed here in modules/system/system.api.php in Drupal core. Here's a list of some as they relate to themable templates.

 VariableTemplateAlter HookDescription
$pagepage.tpl.phphook_page_alter() Contains entire page, all fields.
$contentnode.tpl.php, comment.tpl.php, taxonomy-term.tpl.phphook_node_view_alter() , hook_comment_view_alter() , hook_taxonomy_term_view_alter() Contents of entities. Uses hook_entity_view_alter()
$tabspage.tpl.phphook_menu_local_tasks_alter() Primary and secondary tabs.
$action_linkspage.tpl.phphook_menu_local_tasks_alter() Action links.
$itemfield.tpl.phphook_field_display_alter() Display settings for fields.

I recommend this DrupalCon video by Franz Heinzmann for understanding the render api.

Preprocess functions

Preprocess functions let you make Drupal themes work the way you want them to. They go in the file 'template.php' that comes with all themes and they let you manipulate the variables and html that come out in the other templates.

Syntax

function themename_preprocess_themefunction(&$variables) {
     // Do stuff
}

_themefunction is the template you want to add or manipulate variables inside such as node.tpl.php, page... , breadcrumbs... , html.... , region... , user_picture... , search_results... , views_viewname... , etc..

Code is gobbledegook unless you know what it's doing and why. Drupal uses mostly PHP arrays and objects to do things. There is a couple ways of looking inside an array so you can see what to do. My favorite in Drupal is the devel module. Once installed you can pass in an object to one of it's functions and get a detailed look into it's innards.

function mytheme_preprocess_node(&$variables) {
   dpm($variables);      // Tell me what's inside $variables
}

You get a nice list of the variables and arrays inside the object $variables which you can click to open and drill down further. In PHP syntax you can grab stuff in an array like this;

    $hidetitle = $variables['field_hide_title']['und']['0']['value'];

For this blog, I added a field of type 'boolean' called "Hide Title" (machine name: "field_hide_title") which I can check off if I want to hide the title - like Tumblr and stuff.

/**
 * See if hide_title is checked off in the node
 */
function mytheme_preprocess_node(&$variables) {
  if ( ($variables['type'] == 'blog') && ($variables['field_hide_title']['und']['0']['value'] == '1') ) {
        $variables['title'] = '';
     }
}

Pretty straightforward. If my "field_hide_title" is 1 for true, set the title to nothing. Not done yet though. There is a '$title' variable set in node.tpl.php for teasers and such but there is also one inside page.tpl.php for full page views. So need to write a preprocess function for page. Page.tpl.php has a whole different set of variables though. Which you can see here. The field_hide_title I added to blog entries is not one of them, so I need to pull in the node to check what's inside field_hide_title. Page uses menu_get_object() to grab the node. Menu_get_object() is a Drupal API function that grabs what's in the menu router, usually a node. In PHP syntax, you move into an object like so;

   $vars['array']['object']->property['another_element']
/**
 * See if hide_title is checked off in the node, within the page
 */
function mytheme_preprocess_page(&$variables) {
   // check if node is in menu_get_object()
   if ( $variables['node'] = menu_get_object() ) {
       // dpm($variables['node']   // Uncomment to look inside the node
       if ( ($variables['node']->type =='blog') && ($vars['node']->field_hide_title['und']['0']['value'] == '1') ) {
         $variables['title'] = '';
             }
         }
     }

Some more preprocess examples

/**
 * Add helpful body classes for CSS styling
 */
function mytheme_preprocess_html(&$variables) {
    // arg() grabs url segments, if page is at node/edit then add helpful 'edit-page' class to body "classes_array"
    if ( arg(0) == 'node' && arg(1) == 'edit' ) {
           $variables['classes_array'][] = 'edit-page';
     }
}
 
/**
 * Change the search form to use the HTML5 "search" input attribute 
 */
function mytheme_preprocess_search_block_form(&variables) {
   $variables['search_form'] = str_replace('type="text"', 'type="search"', $variables['search_form']);
}

PHP functions

Some common PHP functions used in Drupal;

  • isset()    //Check if a variable is or is not set and is not NULL, e.g. isset( $variables['body']['0']['value'] );
  • empty()     //Check if a variable is empty, e.g. empty( $variables['body']['0']['value'] );
  • unset()     //Destroy a variable, e.g. unset( $variables['title'] );
  • in_array()      //Check if something exists in an array, e.g. in_array( 'page-front', $variables['body_classes'] );
  • foreach()     //Iterate through arrays and objects, e.g;
   foreach ( $variables['classes_array'] as $key => $item ) {
          if ( $item == 'node-blog' ) {
                $variables['classes_array'][$key] = 'boing-boing';  //Change 'node-blog' in classes_array to 'boing-boing'
         }
    }

Drupal API functions

Drupal writes a lot of it's own functions to do "Drupally" type things. Their documented at api.drupal.org. Here's a few common ones.

  • arg()    //Return url segments
  • t()   //Proper method for printing text, handles language translation, e.g. t('Apples and oranges');
  • l()     //The proper method for writing links in PHP inside Drupal, handles aliased path's and add's "active" classes for themin e.g. l( t('Create a new blog entry'), 'node/add/blog') );
  • drupal_get_title()    //Returns the title of the current page
  • base_path()     //Returns the path of the Drupal install
  • path_to_theme()     //Return path to themed elements, goes nice with base_path() e.g. <?php print base_path() . path_to_theme() . '/' ?>images/my_image.jpg
  • node_load()      //Returns full node elements, requires the node id as a parameter, e.g. node_load( $variables['nid'] );

 

GeSHi filter

Geshi filter allows for adding code with syntax highlighting in blog posts. It's a bit of a pain to set up in Drupal 7 properly with WYSIWYG buttons so I'm documenting the process here.

How to install

Download and turn on these Drupal 7 modules;

Download 1.0.8x version of Geshi and put it in sites/all/libraries;

Setup the wysiwyg. Go to "configuration -> wysiwyg profiles" to set up text formats. You may need to install an editor. I am using the ckeditor for Drupal 7 that should be installed in sites/all/libraries. You would then select the editor you're using, edit each text format and check off the buttons you want.

Configure GeSHi

Configure GeSHi at admin/config/content/formats/geshifilter (you will get WSOD if you installed the libraries API module prior to the 7.x-2.x branch). Under "Generic syntax highlighting tags" add "pre" after blockcode. Select the languages tab and check off the code languages you want to include along with the tags (<jquery> etc..).

Go to "configuration -> text formats". Select "Add text format". Check off "GeSHi filter" and uncheck everything else -- or else be careful for conflicts and make sure GeSHi Filter is listed before other filters in the "filter processing order".

Go to "configuration -> wysiwyg profiles" and select an editor for GeSHi Filter. Edit the filter and under "buttons and plugins" check off the Geshi buttons you want to use (GeSHi: PHP, GeSHi: CSS etc..).

Include an image uploader

These two modules go great with wysiwyg for uploading images;

Setup the profiles #overlay=admin/config/media/imce and configure permissions. Turn on "image" and "IMCE" in the buttons section under configuration -> wysiwyg profiles -> edit a filter.

I format images under the advanced tab in the Image Properties box. Include some CSS classes in your theme.

img.left {
  margin: 20px 20px 12px -250px;
  float: left;
}
 
img.right {
  margin: 20px -250px 20px 20px;
  float: right;
}
 
img.center {
  margin: 20px 0 20px -3em;
}
 
img.border {
  border: 1px solid rgba(255,255,255,0.25);
}

Pages