Altering a Drupal core form theme function to add additional information

This handbook page was inspired by this feature request to the Scheduler module:

Under Administer > Content Management> Content

Please provide a listing under the "STATUS" column which would indicate "Not Published/Scheduled" instead of just "Not published".

It would be even better it said "Scheduled to publish on {date and time}"

The problem here is that the form (inside a table) is generated by Core (specifically node_admin_nodes() and it's associated theme function theme_node_admin_nodes($form) in modules/node/node.admin.inc). Although hook_form_alter() would allow a module to alter a form, this page describes how it can be done within the theme layer which is more appropriate to the requested change).

The key to solving this problem in the theme layer is in theme_node_admin_nodes($form). This tells us the table form is theme-able (in fact, all forms are theme-able, you just need to find the form_id string) and this is where we can get at the status text to amend.

This first snippet (which you would place inside your theme's template.php file) is a good start and shows how you can override a form theme function:

function phptemplate_node_admin_nodes($form) {

  if (isset($form['title']) && is_array($form['title'])) {
    $nids = element_children($form['title']);
    $sql = 'SELECT nid FROM {scheduler} WHERE publish_on > %d AND nid IN (%s)';
    $r = db_query($sql, time(), implode(',', $nids));
    while ($row = db_fetch_array($r)) {
      $scheduled_nids[] = $row['nid'];
    }
    foreach ($nids as $key) {
      if (in_array($key, $scheduled_nids)) {
        $form['status'][$key]['#value'] .= ' / '. t('scheduled');
      }
    }
  }

  // Chain to Drupal's core theme function
  return theme_node_admin_nodes($form);
}

If you wanted to go that extra step and add Scheduled to publish on {date and time} then this would be what you need:-

function phptemplate_node_admin_nodes($form) {

  if (isset($form['title']) && is_array($form['title'])) {
    $nids = element_children($form['title']);
    $sql = 'SELECT nid, publish_on FROM {scheduler} WHERE publish_on > %d AND nid IN (%s)';
    $r = db_query($sql, time(), implode(',', $nids));
    while ($row = db_fetch_array($r)) {
      $scheduled_nids[$row['nid']] = $row['publish_on'];
    }
    if (is_array($scheduled_nids)) foreach ($nids as $key) {
      if (array_key_exists($key, $scheduled_nids)) {
        $form['status'][$key]['#value'] .= ' / '. 
          t('scheduled to publish on ') . format_date($scheduled_nids[$key],'small');
      }
    }
  }

  // Chain to Drupal's core theme function
  return theme_node_admin_nodes($form);
}

This third example displays the publish_on and/or unpublish_on times. To save space in the table if the item is going to be published then it will omit the current status of 'unpublished'. Similarly if the content is scheduled for unpublishing then the current state of 'published' is left out.

function phptemplate_node_admin_nodes($form) {
  if (isset($form['title']) && is_array($form['title'])) {
    $nids = element_children($form['title']);
    // select both time fields from scheduler. No need to filter on times > now because
    // rows are deleted from the scheduler table when they have been acted on.  
    $sql = 'SELECT nid, publish_on, unpublish_on FROM {scheduler} WHERE nid IN (%s)';
    $r = db_query($sql, implode(',', $nids));
    while ($row = db_fetch_array($r)) {
      $scheduled_nids[$row['nid']] = array('publish'=>$row['publish_on'],
                                           'unpublish'=>$row['unpublish_on']);
    }
    if (is_array($scheduled_nids)) foreach ($nids as $key) {
      if (array_key_exists($key, $scheduled_nids)) {
        if ($scheduled_nids[$key]['publish'] > 0) 
          $form['status'][$key]['#value'] = t('publish on ')
            .format_date($scheduled_nids[$key]['publish'],'custom','d/m/Y');
        if ($scheduled_nids[$key]['unpublish'] > 0) {
          if ($scheduled_nids[$key]['publish'] == 0) $form['status'][$key]['#value']='';
          else $form['status'][$key]['#value'].='<br>';
          $form['status'][$key]['#value'] .= t('unpublish on ')
            .format_date($scheduled_nids[$key]['unpublish'],'custom','d/m/Y');
        }
      }
    }
  }
  // Chain to Drupal's core theme function
  return theme_node_admin_nodes($form);
}

These theme override functions have been tested and they work fine. However, a minor issue/problem is that the Status column only has so much width, and these enhancements can widen the column a lot. If you can live with it, fine. Otherwise, have a play and develop your own shorthand.

Notes

These theme override functions contain db_query(). What you have to ask yourself here is "I am prepared to live with database functions inside my theme override functions?" It's normal not to do this and there is an alternative. If we did away with the SQL/database part of this snippet we could just use node_load(array('nid' => $key)) in the foreach() loop. This would make the ->publish_on variable available and we could use that instead. However, consider a content table with 30 nodes. That would mean 30 additional SQL queries to the database. The method used above uses one SQL query and makes use of the SQL IN operator. It's faster and more efficient, but to use it, you have to live with DB calls in your theme function (which isn't the end of the World, it does work just fine).

(The snippets above aren't perfect Drupal coding standards as I made sure the line lengths were not too long so they display on Drupal.org ok).

Comments

jhodgdon’s picture

The format_date() function has changed in Drupal 7 and this page may need an update. See #221006: Handbook is not up to date on format_date for details.