Theming

Contextual blocks

Sometimes you want to display content from a node in a separate region, like a sidebar. A nice way of doing that is to add an additional field to your content type. Call it something like, "sidebar body". Hide it's display under "manage display". Then create a new view block that uses a 'contextual filter' of type 'Content: Nid'. Check off "provide a default value" and select "Content ID from URL" in the dropdown. Add your 'sidebar body' field into the view fields then put your block in the sidebar. The contents of that field will now display in the block on the page where the rest of the node content is displayed.

What about the title

Instead of hard setting the title for that block, make it dynamic. Add an additional field to your content type called, "sidebar body title". Set the title for that block to your new field through hook_block_view_alter(). Here's an example from a project I did recently,

/**
 * Implementation of hook_block_view_alter()
 */
function awesometheme_block_view_alter(&$data, $block) {
 
   //We grab the field 'sidebarbody title' (in 'basic_page' content type) from the node and set it equal to the title
   //of the block that spits out 'sidebar body'
    if (isset($block->info)) {
         if ( $block->info == 'View: Basic Node Content Revisions: Sidebar' ) {
               if ($node = menu_get_object()) {
                      if (isset($node->field_sidebar_title[$node->language]['0']['value'])) {
                            $block->title = $node->field_sidebar_title[$node->language]['0']['value'];
                       }
                 }
            }
      }
}

Using the devel module (include kpr($data) to look inside data, kpr($block) to look inside block) I looked for my block and something unique about it. In this case I grabbed the contents in block->info because it was unique about the block. menu_get_object() is another way of looking at the node instead of using node_load(), the node is often stored in the menu_router on node pages. Looking at the contents of the node (using kpr($node) inside the menu_get_object() statement), I set the sidebar_title field equal to the title of the block.

*update: This can be also done through Display Suite region to block functionality, described in the seventh screencast - link on the module page.

 

∗ Permalink

Calendar

The above image is a partial screenshot for a calendar in Drupal 7 I've been working on. Working with the calendar and better exposed filters modules - I added a taxonomy vocabulary for the colored categories.

Full page layout

The first thing I wanted to do was remove sidebars on the calendar page and prevent any from being added because they would mess with my layout. This is a 'fusion theme so I needed to remove helper classes which determine the layout.

/**
 * Implementation of preprocess_page in template.php
 */
function awesometheme_preprocess_page(&$variables) {
 
    //Unset sidebar on calendar page, they get in the way of the calendar
   if (arg(0) == 'calendar') {
       unset($variables['page']['sidebar_second']);
   }
}
 
/**
 * Implementation of preprocess_html
 */
function awesometheme_preprocess_html(&$variables) {
 
  //We unset sidebar in preprocess_page(), now we remove helper classes
  //and add one new one ('no-sidebars')
   if (arg(0) == 'calendar') {
      foreach ( $variables['classes_array'] as $key => $item ) {
          if ( ($item == 'one-sidebar sidebar-second')) {
              unset($variables['classes_array'][$key]);
            }
        }
      $variables['classes_array'][] = 'no-sidebars';
    }
}

Popup tooltip

For the tooltip I used the beautytips module. In the readme.txt is some nice api stuff. I knew I wanted to load the full node (the page linked to in the calendar item) so I could create the content for the tooltip. I knew I'd use node_load() which requires the node_id as a parameter. I added the node_id as a field in the calendar to do this. Now I needed to write some logic that made all this happen so I turned on the theme developer module which told me all the calendar date items load inside the template "calendar-item.tpl.php". I copy / pasted it into my theme then opened up template.php to create my variables.

/**
 * Implementation of preprocess_calendar_item
 */
function awesometheme_preprocess_calendar_item(&$variables) {
 
   //Create tooltip variables for calendar-item.tpl.php
   if (arg(0) == 'calendar') {
      $nid = $variables['rendered_fields'][1];
      $fullnode = node_load($nid, NULL, TRUE);
 
      $variables['term'] = $fullnode->field_eventgenre[$fullnode->language][0]['tid'];
      $variables['body'] = $fullnode->body[$fullnode->language][0]['value'];
      $variables['title'] = check_plain($fullnode->title);
 
  if ( $fullnode->type == 'event' ) {
      $unixtimestamp = strtotime($fullnode->field_event_date[$fullnode->language][0]['value']);
      $drupaldate = format_date($unixtimestamp,'custom','M, j D - g:i a');
      $variables['public_date'] = $drupaldate;
      $variables['location'] = check_plain($fullnode->locations[0]['name']);
      $variables['address'] = check_plain($fullnode->locations[0]['street']);
      $variables['address'] .= ' ' .check_plain($fullnode->locations[0]['additional']);
      $variables['city'] = check_plain($fullnode->locations[0]['city']);
      $variables['state'] = check_plain($fullnode->locations[0]['province']);
   }
 
      //Remove node_id view field since we're done with it
      unset($variables['rendered_fields'][1]);
      //kpr($fullnode);
   }
}

Now that my variables were created I could build the tooltip in calendar-item.tpl.php where I also included the beautytips api so it would load into a nice html5 canvas popup.

  <!-- BeautyTips module API garnered from readme.txt -->
  <?php
    if (arg(0) == 'calendar') {
       $options['calendar_tooltip'] = array(
         'cssSelect' => '.view-item .calendar a',
         'contentSelector' => '$(this).next()',
         'trigger' => array('mouseover', 'click'),
         'width' => 350,
       );
      beautytips_add_beautytips($options);
   }
  ?>
 
   <!-- Tooltip popup for calendar, variables created in preprocess_calendar_item in template.php -->
   <?php if (arg(0) == 'calendar') { ?>
      <div class="tooltip" style="display:none;">
         //Should include 'isset' statements, removed for readability
         <h1 class="title"> <?php print $title ?> </h1>
         <h2 class="date-time"> <label>Date/Time:</label> <?php print $public_date; ?></h2>
         <h2 class="location"> <label> Location: </label> <?php print $location; ?>; <?php print $address; ?>, <?php print $city; ?>, <?php print $state; ?></h2>
         <span class="body"> <?php print $body; ?></span>
      </div><!-- /tooltip -->
    <?php } ?>

Anonymous create event form

The client also wanted anyone visiting the site to be able to submit calendar events. I created a new content type called "public event" because they had certain requirements for the fields and such. I needed to open a node/add form for this content type for anonymous users inside the same theme. I decided to create a module and load the form inside a function callback using hook_menu(). Which I cleaned up with hook_form_alter().

<?php
 
/**
 * @file
 * Create Event module file
 *
 * This module allows anonymous users to create calendar events.
 */
 
 
/**
 *  Implementation of hook_menu()
 */
function public_calendar_events_menu() {
    $items = array();
    $items['create-calendar-event'] = array(
      'type' => MENU_CALLBACK,
      'page callback' => 'public_calendar_events_page',
      'access arguments' => array('access content'),
    );
 
  return $items;
}
 
/**
 * Page callback to display the calendar public_event node creation form
 */
function public_calendar_events_page() {
 
    global $user;
    $type = 'public_event';
    $node = (object) array(
      'uid' => $user->uid,
      'name' => (isset($user->name) ? $user->name : ''),
      'type' => $type,
      'language' => LANGUAGE_NONE,
    );
 
    $form_state['build_info']['args'] = array($node);
    form_load_include($form_state, 'inc', 'node', 'node.pages');
    $form = drupal_build_form($type . '_node_form', $form_state);
 
    return $form;
}
 
 
/**
 * Alter public_event creation form
 */
 
function public_calendar_events_form_alter(&$form, $form_state, $form_id)  {
 
  if ( ($form_id == 'public_event_node_form') && (arg(0) == 'create-calendar-event') )  {
 
        unset($form['author']);
        unset($form['comment_settings']);
        unset($form['menu']);
        unset($form['path']);
        $form['locations']['#collapsed'] = 0;
        $form['locations']['#weight'] = 3;
        $form['locations']['#title'] = t('Location or venue');
        $form['actions']['preview']['#value'] = t('Preview form before submitting');
        $form['actions']['submit']['#value'] = t('Submit your event for review');
    }
}
∗ Permalink

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;
    }
  }
∗ Permalink
Subscribe to Theming