Presented by: James Walker

This session will be an in depth look at one of the most legendary modules in drupal history. A good overview of the various module development APIs available, as well as best practices discussion. Oh, and we're gonna have some fun too.

Please note that pants are strictly optional for this talk.

Suggested audience: Beginners, those interested in learning to do module development.

Source code for the 4.7 version of this module (doesn't match this presentation but is close) is available here: http://term.ie/data/pants.module, archived below for reference:

<?php
// $Id$

/**
 * Implementation of hook_menu.
 */
function pants_menu($may_cache) {
  $items = array();
  if ($may_cache) {
    $items[] = array('title' => t('Change Pants'),
                     'path' => 'pants/change',
                     'callback' => 'pants_change',
                     'type' => MENU_CALLBACK,
                     'access' => user_access('change pants status')
      );
  }
  return $items;
}

/**
 * Implementation of hook_perm
 */
function pants_perm() {
  return array('change pants status');
}

/**
 * Implementation of hook_block
 */
function pants_block($op = 'list', $delta = 0) {
  global $user;
  
  switch ($op) {
    case 'list':
      $blocks[0]['info'] = t('Your Pants');
      $blocks[1]['info'] = t('Recent Pants');
      return $blocks;
    case 'view':
      switch ($delta) {
        case 0:
          $output .= '<div class="pants-status" style="font-weight: bold;">'. _pants_status(_pants_get($user->uid)) . "</div>\n";
          $output .= l(t('change'), 'pants/change', array('id' => 'pants'));
          drupal_add_js(array('pants' => array('basePath' => base_path())), 'setting');
          drupal_add_js(drupal_get_path('module', 'pants') . '/pants.js');
          $block['subject'] = t('Your Pants are:');
          $block['content'] = $output;
          return $block;
        case 1:
          $block['subject'] = t('Recent Pants');
          $block['content'] = theme('pants_block');
          return $block;          
      }
  }
}

/**
 * Implementation of hook_user
 */
function pants_user($op, $edit, &$account, $category = NULL) {
  switch ($op) {
    case 'form':
      $form['pants'] =
        array('#type' => 'radios',
              '#title' => t('Pants'),
              '#default_value' => $account->pants,
              '#options' => array(0 => t('Off'), 1 => t('On')),
              '#description' => t('Your current pants status'),
          );
      return $form;
    case 'load':
      $account->pants = _pants_get($account->uid);
      break;
    case 'insert':
    case 'update':
      _pants_set($account->uid, $edit['pants']);
      unset ($edit['pants']);
      break;
    case 'view' :
      $items[] = array('title' => t('Pants status'),
                       'value' => _pants_status(_pants_get($account->uid)),
                       'class' => 'user');
      return array('' => $items);
  }
}

/**
 * Implementation of hook_xmlrpc
 */
function pants_xmlrpc() {
  return array(
    array(
      'drupal.changePants',
      'pants_xmlrpc_change',
      array('array', 'string', 'string'),
      t('Changes pants status for the given user')),
    array(
      'drupal.getPants',
      'pants_xmlrpc_get',
      array('array', 'string', 'string'),
      t('Changes pants status for the given user')));
}

/**
 * Callbacks
 */
function pants_change() {
  global $user;
  $status = _pants_change($user->uid);
  if ($_GET['destination']) {
    drupal_goto($_GET['destination']);
  }
  else {
    print _pants_status($status);
  }
  return;
}

function pants_xmlrpc_change($username, $password) {
  global $user;

  $user = user_authenticate($username, $password);
  if (!$user->uid || !user_access('change pants status', $user)) {
    return xmlrpc_error(1, t('Wrong username or password.'));
  }

  return _pants_change($user->uid);
  //return _pants_change(1);
}

function pants_xmlrpc_get($username, $password) {
  global $user;

  $user = user_authenticate($username, $password);
  if (!$user->uid || !user_access('change pants status', $user)) {
    return xmlrpc_error(1, t('Wrong username or password.'));
  }

  return _pants_get($user->uid);  
  //return _pants_get(1);  
}

/**
 * Theme Functions
 */
function theme_pants_block() {
  $result = db_query_range(db_rewrite_sql('SELECT u.uid, u.name, p.status, p.changed FROM {users} u INNER JOIN {pants_history} p ON p.uid = u.uid WHERE u.status = 1 ORDER BY p.changed DESC', 'u'), 0, 10);
  $items = array();
  while ($pants = db_fetch_object($result)) {
    $label = ($pants->status) ? t('put pants on') :t('took pants off');
    $items[] = theme('username', $pants) .' '. $label .'<br />'. t('@time ago', array('@time' => format_interval(time() - $pants->changed)));
  }
  return theme('item_list', $items);
}

/**
 * Helper / "Private" Functions
 */
function _pants_change($uid) {
  $status = 1 - _pants_get($uid);
  _pants_set($uid, $status);
  return $status;
}

function _pants_get($uid, $readable = FALSE) {
  $status = db_result(db_query("SELECT status FROM {pants_history} WHERE uid = %d", $uid));
  return $status;
}

function _pants_set($uid, $status) {
  db_query('DELETE FROM {pants_history} WHERE uid = %d', $uid);
  db_query('INSERT INTO {pants_history} (uid, status) VALUES (%d, %d)', $uid, $status);
  db_query('INSERT INTO {pants_history} (uid, status, changed) VALUES (%d, %d, %d)', $uid, $status, time());
}

function _pants_status($status) {
  return ($status) ? t('On') : t('Off');
}

AttachmentSize
pants-module.pdf803.39 KB