You are here

shoutbox.module in Shoutbox 5

shoutbox module displays a block for users to create short messages for thw whole site. Uses AHAH to update the database and display content.

File

shoutbox.module
View source
<?php

/**
 * @file
 * shoutbox module displays a block for users to create short
 * messages for thw whole site. Uses AHAH to update the
 * database and display content.
 *
 */

/**
 * Describe the shoutbox module.
 *
 * @param $section
 *   Which help screen is requested.
 * @return
 *   HTML fragment for help text.
 */
function shoutbox_help($section) {
  switch ($section) {
    case 'admin/build/modules#description':
      return t("This module enables you to display a shoutbox.");
  }
}

/**
 * Implementation of hook_menu().
 */
function shoutbox_menu($may_cache) {
  $items = array();
  if ($may_cache) {
    $items[] = array(
      'path' => 'shoutbox/js/view',
      'title' => t('View Shouts'),
      'callback' => 'shoutbox_js_view',
      'access' => user_access('access content'),
      'type' => MENU_CALLBACK,
    );
    $items[] = array(
      'path' => 'admin/settings/shoutbox',
      'title' => t('Shoutbox'),
      'description' => t('Settings for displaying and deleting shouts'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'shoutbox_admin_settings',
      ),
      'access' => user_access('administer site configuration'),
      'type' => MENU_NORMAL_ITEM,
    );
    $items[] = array(
      'path' => 'shoutbox',
      'title' => t('All Shouts'),
      'callback' => 'shoutbox_page_view',
      'access' => user_access('access content'),
      'type' => MENU_CALLBACK,
    );
  }
  else {
    $items[] = array(
      'path' => 'shoutbox/' . arg(1) . '/edit',
      'title' => t('Edit Shout'),
      'callback' => 'shoutbox_callback',
      'callback arguments' => array(
        arg(1),
        'edit',
      ),
      'access' => shoutbox_access_callback('edit own shouts', arg(1)),
      'type' => MENU_CALLBACK,
    );
    $items[] = array(
      'path' => 'shoutbox/' . arg(1) . '/delete',
      'title' => t('Delete Shout'),
      'callback' => 'shoutbox_callback',
      'callback arguments' => array(
        arg(1),
        'delete',
      ),
      // BUGBUG - what should the access be
      'access' => shoutbox_access_callback('delete own shouts', arg(1)),
      'type' => MENU_CALLBACK,
    );
    $items[] = array(
      'path' => 'shoutbox/' . arg(1) . '/publish',
      'title' => t('Publish Shout'),
      'callback' => 'shoutbox_callback',
      'callback arguments' => array(
        arg(1),
        'publish',
      ),
      'access' => shoutbox_access_callback('moderate shoutbox', arg(1)),
      'type' => MENU_CALLBACK,
    );
    $items[] = array(
      'path' => 'shoutbox/' . arg(1) . '/unpublish',
      'title' => t('Unpublish Shout'),
      'callback' => 'shoutbox_callback',
      'callback arguments' => array(
        arg(1),
        'unpublish',
      ),
      'access' => shoutbox_access_callback('moderate shoutbox', arg(1)),
      'type' => MENU_CALLBACK,
    );
  }
  return $items;
}

/**
 *  Make the shout box block available. (Standard Drupal hook).
 *
 * @param $op
 *   "list" to request list of blocks this module exposes;
 *   any other value to display the stock quotes block.
 * @param $delta
 *   integer block selector (only recognizes 0 = stock quotes).
 * @return
 *   (if $op == "list") array containing list of blocks.
 *   (otherwise) HTML fragment for THE block.
 */
function shoutbox_block($op = 'list', $delta = 0, $edit = array()) {
  switch ($op) {
    case 'list':
      $blocks[0]["info"] = t("Shoutbox");
      return $blocks;
      break;
    case 'view':
      $block = array();
      drupal_add_css(drupal_get_path('module', 'shoutbox') . '/shoutbox.css');
      switch ($delta) {
        case 0:
          if (user_access("access content")) {

            // BUGBUG - why are we looking at $_GET
            if (!stristr($_GET['q'], 'shoutbox')) {

              // Bind submission to submit.
              drupal_add_js(drupal_get_path('module', 'shoutbox') . '/shoutbox-form.js', 'module');
              drupal_add_js(drupal_get_path('module', 'shoutbox') . '/form.js', 'module');
              $block["subject"] = t("Shout Box");
              $block["content"] = _shoutbox_block_view();
            }
          }
      }
      return $block;
      break;
    default:
      break;
  }
  return;
}

/**
 * Implementation of hook_cron().
 */
function shoutbox_cron() {
  $expiration = variable_get('shoutbox_expire', 0);
  if ($expiration > 0) {
    db_query('DELETE FROM {shoutbox} WHERE created < %d', time() - 60 * 60 * 24 * $expiration);
  }
}

/**
 * Implementation of hook_perm().
 */
function shoutbox_perm() {
  return array(
    'post shouts',
    'administer shoutbox',
    'moderate shoutbox',
    'post shouts without approval',
    'delete own shouts',
    'edit own shouts',
  );
}

/**
 * Implementation of hook_user().
 */
function shoutbox_user($op, &$edit, &$account, $category = NULL) {
  if ($op == "logout") {
    setcookie("shoutinfo", FALSE, time(), '/');
  }
}

/**
 * Implementation of hook_simpletest().
 */
function shoutbox_simpletest() {
  $module_name = 'shoutbox';
  $dir = drupal_get_path('module', $module_name) . '/tests';
  $tests = file_scan_directory($dir, '\\.test$');
  return array_keys($tests);
}

// CALLBACKS.

/**
 * Javascript callback.
 * Prints out shouts only.
 */
function shoutbox_js_view() {
  $show_amount = variable_get('shoutbox_showamount', '20');
  $shoutbox_posts_data = _shoutbox_display_posts($show_amount, FALSE);
  $output = $shoutbox_posts_data['output'];
  print $output;
}

/**
 * Show paged view of full list of shouts.
 */
function shoutbox_page_view() {
  drupal_add_css(drupal_get_path('module', 'shoutbox') . '/shoutbox.css');

  // Output the existing shoutbox posts.
  $shoutbox_posts_data = _shoutbox_display_posts(20, TRUE, TRUE);
  $output .= theme('table', array(), $shoutbox_posts_data['rows']);
  $output .= theme('pager', NULL, 3, 0);
  return $output;
}

/**
 * Function to handle shoutbox callbacks that
 * require a shout_id. Does some sanity checking on
 * on the id before forwarding to the actual callback.
 *
 * @param shout_id
 *   The shout id the action is being performed on.
 * @param callback
 *   The callback being called. Options are
 *      - edit: Editting the shout
 *      - delete: deleting the shout
 *      - publish: publishing/unmoderating the shout
 *      - unpublish: unpublish/moderating the shout
 * @return
 *   The callback form.
 */
function shoutbox_callback($shout_id, $callback) {
  $output = '';
  if (!is_numeric($shout_id)) {
    $output = drupal_not_found();
  }
  else {
    $shout = db_fetch_object(db_query('SELECT * FROM {shoutbox} WHERE shout_id = %d', $shout_id));
    $output = theme('shoutbox_post', $shout);
    switch ($callback) {
      case 'edit':
        $output = drupal_get_form('shoutbox_edit_form', $shout_id);
        break;
      case 'delete':
        _shoutbox_sanitize_shout($shout);
        $output .= drupal_get_form('shoutbox_delete_form', $shout_id);
        break;
      case 'publish':
        _shoutbox_sanitize_shout($shout);
        $output .= drupal_get_form('shoutbox_publish_form', $shout_id);
        break;
      case 'unpublish':
        _shoutbox_sanitize_shout($shout);
        $output .= drupal_get_form('shoutbox_unpublish_form', $shout_id);
        break;
      default:
        $output = drupal_not_found();
        break;
    }
  }
  return $output;
}

/**
 * Function to handle shoutbox access callbacks that
 * require a shout_id. Does some sanity checking on
 * on the id and loads the shout before calling
 * _shoutbox_user_access.
 *
 * @param shout_id
 *   The shout id the action is being performed on.
 * @param permission
 *   The permission that we want to check
 * @return
 *   Returns 1 if user should have accces, 0 otherwise.
 *
 */
function shoutbox_access_callback($permission, $shout_id) {
  $access = FALSE;
  if (is_numeric($shout_id)) {
    $shout = db_fetch_object(db_query('SELECT * FROM {shoutbox} WHERE shout_id = %d', $shout_id));
    $access = _shoutbox_user_access($permission, $shout);
  }
  return $access;
}

// THEMES.

/**
 * Theme function of shoutbox actions. Actions are edit, delete, promote
 * and demote. NOTE: Function does not return html but rather an array
 * with the actions as keys. See code.
 */
function theme_shoutbox_links() {
  $links['edit']['action'] = 'edit';
  $links['edit']['title'] = 'Edit';
  $links['edit']['attributes'] = array(
    "class" => "edit-shout",
  );
  $links['delete']['action'] = 'delete';
  $links['delete']['title'] = 'Delete';
  $links['delete']['attributes'] = array(
    "class" => "delete-shout",
  );
  $links['publish']['action'] = 'publish';
  $links['publish']['title'] = 'Publish';
  $links['publish']['attributes'] = array(
    "class" => "publish-shout",
  );
  $links['unpublish']['action'] = 'unpublish';
  $links['unpublish']['title'] = 'Unpublish';
  $links['unpublish']['attributes'] = array(
    "class" => "unpublish-shout",
  );
  return $links;
}

/**
 * Theme function for shoutbox posts.
 *
 * @param shout
 *   The shout to be themed.
 * @param links
 *   Links of possible actions that can be performed on this shout
 *   by the current user.
 * @param $alter_row_color
 *   Whether or not to alternate the row color for posts.  Should be set to
 *   FALSE for the page view.
 */
function theme_shoutbox_post($shout, $links = array(), $alter_row_color = TRUE) {

  // Get the registered username of the person who posted the shout.
  if ($shout->uid > 0) {
    $user = user_load(array(
      "uid" => $shout->uid,
    ));
    $shout->username = $user->name;
  }
  else {
    $shout->username = 'an anonymous user';
  }

  // BUGBUG strstr returns from http:// till end
  // we should use that instead of full url.
  if (strstr($shout->url, "http://")) {
    $shout->url = '<a href="' . $shout->url . '" target="_blank">' . $shout->nick . '</a>';
  }
  else {
    $shout->url = $shout->nick;
  }
  if ($links) {
    foreach ($links as $link) {
      $linkattributes = $link['attributes'];
      $link_html = $link['title'];
      $link_url = 'shoutbox/' . $shout->shout_id . '/' . $link['action'];
      $action_links = l($link_html, $link_url, $linkattributes, NULL, NULL, FALSE, TRUE) . $action_links;
    }
  }
  $title = 'Posted ' . format_date($shout->created, 'custom', 'm/d/y') . ' at ' . format_date($shout->created, 'custom', 'h:ia') . ' by ' . $shout->username;
  $shout_classes = "shoutbox-msg ";
  if ($alter_row_color) {
    $shout_classes .= $shout->color ? "shoutbox-odd " : "shoutbox-even ";
  }
  if ($shout->moderate == 1) {
    $shout_classes .= "shoutbox-unpublished ";
  }
  return "<div class=\" {$shout_classes} \" title=\"{$title}\"><span class=\"actions\">{$action_links}</span> <span class=\"shout\"><b>{$shout->url}</b>: {$shout->shout}</span></div>\n";
}

/**
 * Theme function for displaying the shoutbox page.
 *
 * @param $content
 *   The page content.
 * @param $title
 *   The page title, defaults to 'Shoutbox'.
 * @return
 *   String containing HTML formatted page.
 */
function theme_shoutbox_page($content, $title = 'Shoutbox') {
  $output .= $content;
  $output = "<div id=\"shoutbox-body\">\n" . $output . "</div>\n";
  return $output;
}

/**
 * Theme function for displaying the access denied message.
 *
 * @return
 *   String containing HTML formatted access denied message.
 */
function theme_shoutbox_post_forbidden() {
  global $user;
  if ($user->uid) {
    return '<div class="shoutbox-msg">' . t("Your account does not have the permissions to post shouts") . "</div>\n";
  }
  else {
    return '<div class="shoutbox-msg">' . t('<a href="!login" target="_top">Login</a> or <a href="!register" target="_top">register</a> to post shouts', array(
      '!login' => url('user/login'),
      '!register' => url('user/register'),
    )) . "</div>\n";
  }
}

// FORMS.

/**
 * Form for admin/settings/shoutox page.
 */
function shoutbox_admin_settings() {
  $form['display_settings'] = array(
    '#type' => 'fieldset',
    '#title' => t('Display Settings'),
    '#collapsible' => true,
  );
  $form['display_settings']['shoutbox_showamount'] = array(
    '#type' => 'textfield',
    '#title' => t('Number of posts to show'),
    '#default_value' => variable_get('shoutbox_showamount', 20),
    '#size' => 4,
    '#maxlength' => 4,
    '#description' => t("Set the number of shoutbox posts to show."),
  );
  $form['display_settings']['shoutbox_ascending'] = array(
    '#type' => 'checkbox',
    '#title' => t('Post newest shouts on top'),
    '#default_value' => variable_get('shoutbox_ascending', 0),
    '#description' => t('When checked, new shouts will appear on the top instead of the bottom.'),
  );
  $form['display_settings']['shoutbox_defaultname'] = array(
    '#type' => 'checkbox',
    '#title' => t('Default the name field to the logged in user name'),
    '#default_value' => variable_get('shoutbox_defaultname', 1),
    '#description' => t('When checked, "Your name/nick" will be replaced by the logged in user name'),
  );
  $form['display_settings']['shoutbox_shownamefield'] = array(
    '#type' => 'checkbox',
    '#title' => t('Show the name field for logged in users'),
    '#default_value' => variable_get('shoutbox_shownamefield', 1),
    '#description' => t('Uncheck to hide the name field for logged in users. Name will then be the user name, so previous option will be useless.'),
  );
  $form['display_settings']['shoutbox_showurlfield'] = array(
    '#type' => 'checkbox',
    '#title' => t('Show the url field'),
    '#default_value' => variable_get('shoutbox_showurlfield', 1),
    '#description' => t('Check to allow users to enter an url.'),
  );
  $form['shoutbox_filter_format'] = _shoutbox_filter_form();
  $form['time_settings'] = array(
    '#type' => 'fieldset',
    '#title' => t('Time Settings'),
    '#collapsible' => true,
    '#collapsed' => false,
  );
  $form['time_settings']['shoutbox_refresh'] = array(
    '#type' => 'textfield',
    '#title' => t('Auto refresh (in seconds)'),
    '#default_value' => variable_get('shoutbox_refresh', 0),
    '#size' => 4,
    '#maxlength' => 4,
    '#description' => t("Shoutbox can be set to automatically refresh every x number of seconds.  Set to 0 to turn off the auto refresh (default)."),
  );
  $form['time_settings']['shoutbox_anonymous_timeout'] = array(
    '#type' => 'textfield',
    '#title' => t('Number of minutes for which anonymous users may edit or delete their own posts'),
    '#default_value' => variable_get('shoutbox_anonymous_timeout', 20),
    '#size' => 4,
    '#maxlength' => 4,
    '#description' => t("Anonymous users can edit or delete their post within this amount of time from it being posted, as long as they have the same IP address as when they posted it.  If you don't want shout editing and/or deleting, remove these permissions from Drupal's anonymous users role."),
  );
  $form['time_settings']['shoutbox_registered_timeout'] = array(
    '#type' => 'textfield',
    '#title' => t('Number of minutes for which registered users may edit or delete their own posts'),
    '#default_value' => variable_get('shoutbox_registered_timeout', 1440),
    '#size' => 4,
    '#maxlength' => 4,
    '#description' => t("Registered users can edit or delete their post within this amount of time from it being posted.  If you don't want editing and/or deleting, remove these permissions from Drupal's registered users role."),
  );
  $form['time_settings']['shoutbox_expire'] = array(
    '#type' => 'textfield',
    '#title' => t('Number of days after which shouts will be purged from the database'),
    '#default_value' => variable_get('shoutbox_expire', 0),
    '#size' => 4,
    '#maxlength' => 4,
    '#description' => t("Shouts will be permanently deleted after the number of days specified.  Shouts will never expire when this is set to zero."),
  );
  return system_settings_form($form);
}

/**
 * Generates form for adding shouts.
 */
function shoutbox_add_form() {
  global $user;
  if (isset($_COOKIE['shoutinfo'])) {
    $info = explode("|", $_COOKIE['shoutinfo']);
    $last_nick = $info[0];
    $last_url = $info[1];
  }
  if (variable_get('shoutbox_defaultname', 1) && $user->uid) {
    $default_nick = $user->name;
  }
  else {
    $default_nick = t('Your Name/Nick');
  }
  $default_msg = t('Enter Message');
  $default_url = t('Your Website URL');
  $form = '';
  if (!variable_get('shoutbox_shownamefield', 1) && $user->uid) {
    $form['nick'] = array(
      '#type' => 'hidden',
      '#value' => $user->name,
    );
  }
  else {
    $form['nick'] = array(
      '#type' => 'textfield',
      '#default_value' => $last_nick ? $last_nick : $default_nick,
      '#size' => 15,
      '#maxlength' => 30,
    );
  }
  $form['message'] = array(
    '#type' => 'textfield',
    '#default_value' => $default_msg,
    '#size' => 15,
  );
  if (variable_get('shoutbox_showurlfield', 1)) {
    $form['url'] = array(
      '#type' => 'textfield',
      '#default_value' => $last_url ? $last_url : $default_url,
      '#size' => 15,
      '#maxlength' => 255,
    );
  }
  $form['#attributes'] = array(
    'name' => 'shoutbox_add',
  );
  $form['#prefix'] = '<div class="shoutbox-add-form">';
  $form['#suffix'] = '</div>';
  $form['ajax'] = array(
    '#type' => 'hidden',
    '#default_value' => 0,
  );
  $form['nextcolor'] = array(
    '#type' => 'hidden',
    '#default_value' => $color,
  );
  $form[] = array(
    '#type' => 'submit',
    '#value' => t('Shout'),
  );
  return $form;
}

/**
 * Form for editing shouts.
 *
 * @param shout_id
 *   The shout id of the shout being edited.
 */
function shoutbox_edit_form($shout_id) {
  global $user;
  $shout = db_fetch_object(db_query('SELECT * FROM {shoutbox} WHERE shout_id = %d', $shout_id));
  if (_shoutbox_user_access('administer shouts')) {
    $form[] = array(
      '#type' => 'item',
      '#title' => t('Created'),
      '#value' => date('m/d/y h:i:sa', $shout->created),
    );
    $form[] = array(
      '#type' => 'item',
      '#title' => t('Changed'),
      '#value' => date('m/d/y h:i:sa', $shout->changed),
    );
    $form['moderate'] = array(
      '#type' => 'radios',
      '#title' => t('Moderation Status'),
      '#default_value' => $shout->moderate,
      '#options' => array(
        'published',
        'not published',
      ),
    );
    $users[0] = variable_get('anonymous', 'Anonymous');
    $result = db_query("SELECT uid, name FROM {users} WHERE name <> '' ORDER BY name");
    while ($usr = db_fetch_object($result)) {
      $users[$usr->uid] = $usr->name;
    }
    $form['uid'] = array(
      '#type' => 'select',
      '#title' => t('Author'),
      '#default_value' => $shout->uid,
      '#options' => $users,
    );
  }
  if (_shoutbox_user_access('edit own shouts', $shout)) {
    if (!variable_get('shoutbox_shownamefield', 1) && $user->uid) {
      $form['nick'] = array(
        '#type' => 'hidden',
        '#value' => $shout->nick,
      );
    }
    else {
      $form['nick'] = array(
        '#type' => 'textfield',
        '#title' => t('Name/Nick'),
        '#default_value' => $shout->nick,
        '#size' => 16,
        '#maxlength' => 55,
      );
    }
    $form['shout'] = array(
      '#type' => 'textarea',
      '#title' => t('Shout'),
      '#default_value' => $shout->shout,
      '#cols' => 13,
      '#rows' => 7,
    );
    if (variable_get('shoutbox_showurlfield', 1)) {
      $form['url'] = array(
        '#type' => 'textfield',
        '#title' => t('URL'),
        '#default_value' => $shout->url,
        '#size' => 16,
        '#maxlength' => 55,
      );
    }
    $form['shout_id'] = array(
      '#type' => 'hidden',
      '#value' => $shout->shout_id,
    );
  }
  $form = confirm_form($form, t(''), t(''), t(''), t('Update'), t('Cancel'));
  return $form;
}

/**
 * Displays a "Are you sure message ?" with a Yes and Cancel
 * option.
 *
 * @param shout_id
 *   The shout id of the shout being edited.
 */
function shoutbox_delete_form($shout_id) {
  $form['shout_id'] = array(
    '#type' => 'value',
    '#value' => $shout_id,
  );
  return confirm_form($form, t('Are you sure you want to delete this shout?'), '');
}

/**
 * Displays a "Are you sure message ?" with a Yes and Cancel
 * option.
 *
 * @param shout_id
 *   The shout id of the shout being published.
 */
function shoutbox_publish_form($shout_id) {
  $form['shout_id'] = array(
    '#type' => 'value',
    '#value' => $shout_id,
  );
  $form = confirm_form($form, t('Are you sure you want to publish this shout?'), '', t(''));
  return $form;
}

/**
 * Displays a "Are you sure message ?" with a Yes and Cancel
 * option.
 *
 * @param shout_id
 *   The shout id of the shout being unpublished.
 */
function shoutbox_unpublish_form($shout_id) {
  $form['shout_id'] = array(
    '#type' => 'value',
    '#value' => $shout_id,
  );
  $form = confirm_form($form, t('Are you sure you want to unpublish this shout?'), '', t(''));
  return $form;
}

// FORM SUBMITS.

/**
 * Handles submission of a shout.
 *
 * Handles both ajax submission and regular form submission.
 *
 * @param $form_id
 *   The form ID of the form.
 * @param $form_values
 *   Form values.
 */
function shoutbox_add_form_submit($form_id, $form_values) {
  global $user;

  // Save the user's nick and url in a cookie for next time (expires in 30 days)
  setcookie("shoutinfo", "{$form_values['nick']}|{$form_values['url']}", time() + 60 * 60 * 24 * 30, '/');
  $moderate = 0;

  // Check user's permission and set shout visibility status accordingly.
  if (!_shoutbox_user_access('post shouts without approval')) {
    $moderate = 1;
  }
  $created = time();

  // Add shout to the database.
  db_query("INSERT INTO {shoutbox} (uid, nick, shout, url, moderate, created, changed, sid) VALUES (%d, '%s', '%s', '%s', %d, %d, %d, '%s')", $user->uid, $form_values['nick'], $form_values['message'], $form_values['url'], $moderate, $created, $created, session_id());

  // If form was not submitted via javascript
  // set a display message and redirect the user back to the form.
  if ($form_values['ajax'] == '0') {
    if ($moderate == 1) {
      drupal_set_message(t('Your shout has been submitted for approval by a moderator. Others will not see this shout until it is approved.'));
    }
    else {
      drupal_set_message(t('Your shout has been submitted.'));
    }
    drupal_goto('');
  }
  else {

    // Pull shout out of db and display.
    // We are pulling it out because thats the only way to get th shout_id
    // which is need for edit, etc.
    $shout = db_fetch_object(db_query("SELECT * FROM {shoutbox} WHERE nick = '%s' AND shout = '%s' AND created = %d AND sid = '%s'", $form_values['nick'], $form_values['message'], $created, session_id()));

    // Add shout color.
    $shout->color = $form_values['nextcolor'];
    _shoutbox_sanitize_shout($shout);

    // Add edit/delete links depending on user's permissions.
    $shoutlinks = _shoutbox_get_links($shout);
    $ajax_output = theme('shoutbox_post', $shout, $shoutlinks);
    print $ajax_output;

    // Exit required to stop drupal from redirecting page.
    exit;
  }
}

/**
 * Handle the edit form submission.
 *
 * @param $form_id
 *   The form ID of the form.
 * @param $form_values
 *   Form values.
 */
function shoutbox_edit_form_submit($form_id, $form_values) {
  if (is_numeric($form_values['shout_id'])) {

    // Get existing shout object.
    $result = db_query("SELECT * FROM {shoutbox} WHERE shout_id = %d", $form_values['shout_id']);
    $existing_shout = db_fetch_object($result);

    // If the user is a shoutbox admin they can edit any shout.
    if (_shoutbox_user_access('administer shoutbox')) {
      db_query("UPDATE {shoutbox} SET uid=%d, nick='%s', shout='%s', url='%s', moderate=%d, changed=%d WHERE shout_id=%d", $form_values['uid'], $form_values['nick'], $form_values['shout'], $form_values['url'], $form_values['moderate'], time(), $form_values['shout_id']);
      drupal_set_message(t('The shout has been saved.'));
    }
    else {
      if (_shoutbox_user_access('edit own shouts', $existing_shout)) {
        db_query("UPDATE {shoutbox} SET nick='%s', shout='%s', url='%s', changed=%d WHERE shout_id=%d", $form_values['nick'], $form_values['shout'], $form_values['url'], time(), $form_values['shout_id']);
        drupal_set_message(t('Your shout has been saved.'));
      }
      else {
        shoutbox_set_message(t('You do not have permission to edit this shout.'));
      }
    }
  }
  else {
    return drupal_not_found();
  }
  drupal_goto('');
}

/**
 * Handle the delete form submission.
 *
 * @param $form_id
 *   The form ID of the form.
 * @param $form_values
 *   Form values.
 */
function shoutbox_delete_form_submit($form_id, $form_values) {
  if (is_numeric($form_values['shout_id'])) {
    $result = db_query("SELECT * FROM {shoutbox} WHERE shout_id = %d", $form_values['shout_id']);
    if ($shout = db_fetch_object($result)) {
      if (_shoutbox_user_access('delete own shouts', $shout)) {
        db_query("DELETE FROM {shoutbox} WHERE shout_id =%d", $form_values['shout_id']);
        drupal_set_message(t('Your shout was deleted.'));
      }
      else {
        drupal_set_message(t('You do not have permission to delete this post.'));
      }
    }
    else {
      $message = t('Invalid shout ID: %shout', array(
        '%shout' => $form_values['shout_id'],
      ));
      drupal_set_message($message);
    }
  }
  else {
    return drupal_not_found();
  }
  drupal_goto('');
}

/**
 * Handle the publish form submission.
 */
function shoutbox_publish_form_submit($form_id, $form_values) {
  return _shoutbox_moderate_shout($form_values['shout_id'], 0);
}

/**
 * Handle the unpublish form submission.
 */
function shoutbox_unpublish_form_submit($form_id, $form_values) {
  return _shoutbox_moderate_shout($form_values['shout_id'], 1);
}

// FORM VALIDATE.

/**
 * Makes sure uses don't submit default values.
 *
 * @param $form_id
 *   The form ID of the form.
 * @param $form_values
 *   Form values.
 */
function shoutbox_add_form_validate($form_id, $form_values) {
  if ($form_values['nick'] == t('Your Name/Nick') || $form_values['message'] == t('Enter Message')) {
    form_set_error('', t('Default values are not acceptable'));
  }
  if ($form_values['nick'] == '' || $form_values['message'] == '') {
    form_set_error('', t('You must enter a nick and a message.'));
  }

  // URL is optional.
  if ($form_values['url'] == t('Your Website URL')) {
    $form_values['url'] = '';
  }
}

// INTERNAL FUNCTIONS.

/**
 * Returns the themed HTML to be displayed in the block.
 *
 * @return
 *   Themed HTML content.
 */
function _shoutbox_block_view() {

  // Output the existing shoutbox posts.
  $show_amount = variable_get('shoutbox_showamount', '20');
  $shoutbox_ascending = variable_get('shoutbox_ascending', FALSE);
  $shoutbox_posts_data = _shoutbox_display_posts($show_amount);
  $output = $shoutbox_posts_data['output'];

  // Output the shoutbox form.
  if (_shoutbox_user_access('post shouts') || _shoutbox_user_access('post shouts without approval')) {
    $output .= drupal_get_form('shoutbox_add_form');
  }
  else {
    $output .= theme('shoutbox_post_forbidden');
  }
  $default_nick = t('Your Name/Nick');
  $default_msg = t('Enter Message');
  $default_url = t('Your Website URL');
  $base_path = base_path();

  // Variable needed by javascript code.
  $js_settings = array(
    'showAmount' => $show_amount,
    // Convert to milliseconds.
    'refreshDelay' => 1000 * variable_get('shoutbox_refresh', 0),
    'ascending' => $shoutbox_ascending,
    'shownAmount' => $shoutbox_posts_data['count'],
    'defaultNick' => $default_nick,
    'defaultMsg' => $default_msg,
    'defaultUrl' => $default_url,
    'refreshPath' => url('shoutbox/js/view'),
  );
  drupal_add_js(array(
    'shoutbox' => $js_settings,
  ), 'setting');
  $output .= l(t('All Shouts'), 'shoutbox');
  return theme('shoutbox_page', $output, $title);
}

/**
 * Output existing shoutbox posts as html.
 * Used by shoutbox_block_view.
 *
 * @param $show_amount
 *   The number of posts to show.
 * @param $wrap
 *   Whether or not to wrap posts in <div id="shoutbox-posts">
 * @param $pager
 *   Whether or not to use pager_query() instead of db_query_range(), defaults
 *   to FALSE.
 * @return
 *   HTML for show_amount number of posts.
 */
function _shoutbox_display_posts($show_amount, $wrap = TRUE, $pager = FALSE) {
  $color = 0;
  $count = 0;
  $output = '';
  $rows = array();

  // Figure out if we should display it in ascending or descending order.
  $ascending = variable_get('shoutbox_ascending', FALSE);

  // Get the shouts from the database.
  if (!$pager) {
    $result = db_query_range("SELECT * FROM {shoutbox} ORDER BY created DESC", 0, $show_amount);
  }
  else {
    $result = pager_query("SELECT * FROM {shoutbox} ORDER BY created DESC", $show_amount);
  }
  while ($shout = db_fetch_object($result)) {
    if ($shout->moderate == 0 || _shoutbox_user_access('moderate shoutbox') || _shoutbox_is_user_owned($shout)) {
      _shoutbox_sanitize_shout($shout);

      // Add edit/delete links depending on user's permissions.
      $shoutlinks = _shoutbox_get_links($shout);

      // Alternate colors for each post (row of the shoutbox).
      if ($color == 0) {
        $color = 1;
      }
      else {
        $color = 0;
      }
      $shout->color = $color;

      // Theme the shoutbox post.
      if ($ascending) {
        $output .= theme('shoutbox_post', $shout, $shoutlinks);
      }
      else {
        $output = theme('shoutbox_post', $shout, $shoutlinks) . $output;
      }
      $rows[] = array(
        theme('shoutbox_post', $shout, $shoutlinks, FALSE),
      );
      ++$count;
    }
  }
  if (!$ascending) {
    $rows = array_reverse($rows);
  }
  if (!$count) {
    $output .= '<div class="shoutbox-even" title="no shouts">' . t("There are no shouts to view.") . "</div>\n";
  }
  if ($wrap) {

    // Wrap shout box messages.
    $output = "<div id=\"shoutbox-posts\">\n" . $output . "</div>\n";
  }
  $output_data['count'] = $count;
  $output_data['output'] = $output;
  $output_data['rows'] = $rows;
  return $output_data;
}

/**
 * Returns an array containing the possible actions for the current user based
 * on permissions and shout. The actions are edit, delete, moderate.
 *
 * @param shout
 *   The shout for which we are testing permissions.
 * @return
 *   Array of themed actions.
 */
function _shoutbox_get_links($shout) {
  $links = theme('shoutbox_links');

  // Get array of links.
  if (_shoutbox_user_access('edit own shouts', $shout)) {
    $shoutlinks[] = $links['edit'];
  }
  if (_shoutbox_user_access('delete own shouts', $shout)) {
    $shoutlinks[] = $links['delete'];
  }
  if (_shoutbox_user_access('moderate shoutbox', $shout)) {
    if ($shout->moderate == 0) {
      $shoutlinks[] = $links['unpublish'];
    }
    else {
      $shoutlinks[] = $links['publish'];
    }
  }
  return $shoutlinks;
}

/**
 * This function is necessary because even if a user has permission
 * (according to the user_access function), they still should not have
 * some permissions, such as moderating their own posts, etc.
 *
 * @param $permission
 *   The user's permissions.
 * @param $shout
 *   The shout post object.
 * @return
 *   Returns 1 if user should have accces, 0 otherwise.
 */
function _shoutbox_user_access($permission, $shout = NULL) {
  global $user;

  // Handles both user uid = 1 and
  // administer case
  if (user_access('administer shoutbox')) {
    return TRUE;
  }
  $user_timeout = FALSE;
  $user_owned = FALSE;
  $access_granted = user_access($permission);

  // If user_access says no, it's definitely no.
  if ($access_granted && ($permission == 'edit own shouts' || $permission == 'delete own shouts')) {
    if (_shoutbox_is_user_owned($shout)) {

      // A registered user's own post.
      if ($shout->uid > 0 && $shout->created < time() - 60 * variable_get('shoutbox_registered_timeout', 1440)) {
        $user_timeout = TRUE;
      }
      else {
        if ($shout->uid == 0 && $shout->created < time() - 60 * variable_get('shoutbox_anonymous_timeout', 20)) {
          $user_timeout = TRUE;
        }
      }
      $user_owned = TRUE;
    }

    // If not user owned the post or editing priviledges have timed out ...
    $access_granted = $user_owned && !$user_timeout;
  }
  return $access_granted;
}

/**
 * This function cleans the shout object before it is used.
 *
 * @param &$shout
 *   The shout post object.
 */
function _shoutbox_sanitize_shout(&$shout) {

  // All filtering (including urls, email addresses, censored words, and
  // emoticons) is handled by the drupal filter system.
  $shout->nick = check_plain($shout->nick);
  $format = variable_get('shoutbox_filter_format', 'PLAIN');
  if ($format == 'PLAIN') {
    $shout->shout = check_plain($shout->shout);
  }
  else {

    // ADD HUGE WARNING ABOUT DANGERS OF NOT FILTERING HTML
    $shout->shout = check_markup($shout->shout, $format, FALSE);
  }
  $shout->url = check_url($shout->url);
  $shout->color = check_plain($shout->color);
}
function _shoutbox_filter_form() {
  $form = filter_form(variable_get('shoutbox_filter_format', 'FILTER_FORMAT_DEFAULT'), NULL, array(
    'shoutbox_filter_format',
  ));
  $formats = filter_formats();
  if (count($formats) > 1) {
    $form['#title'] = t('Shoutbox Input Format');
  }
  return $form;
}

/**
 * Update the moderate bit in the database.
 *
 * @param $shout_id
 *   The shout id of the shout being moderated.
 *
 * @param $moderate
 *   The vale to set the moderate bit to. (0 or 1)
 *
 * @return
 *    N/A
 */
function _shoutbox_moderate_shout($shout_id, $moderate) {
  $output = '';
  if (is_numeric($shout_id) && ($moderate == 0 || $moderate == 1)) {
    if (_shoutbox_user_access('moderate shoutbox')) {
      db_query("UPDATE {shoutbox} set moderate='%d' WHERE shout_id =%d", $moderate, $shout_id);
      if ($moderate == 0) {
        drupal_set_message(t('The shout was published.'));
      }
      else {
        drupal_set_message(t('The shout was unpublished.'));
      }
    }
    else {
      drupal_set_message(t('You do not have permission to moderate this shout.'));
    }
  }
  else {
    $output = drupal_not_found();
  }
  return $output;
}

/**
 * Determine if the current user owns the
 * $shout.
 *
 * @param $shout
 *   The shout object that we want to check.
 *
 * @return
 *   True if the shout is owned by the current user
 *
 */
function _shoutbox_is_user_owned($shout) {
  global $user;
  $user_owned = FALSE;
  if ($shout->uid > 0 && $shout->uid == $user->uid) {
    $user_owned = TRUE;
  }
  else {
    if ($shout->uid == 0 && $user->uid == 0) {
      if ($shout->sid == session_id()) {
        $user_owned = TRUE;
      }
      else {
        if (empty($shout->sid) && !empty($shout->hostname)) {
          $user_owned = $shout->hostname == _shoutbox_ip_address();
        }
      }
    }
  }
  return $user_owned;
}

/**
 * Backport of ip_address function from Drupal 6.
 *
 *
 */
function _shoutbox_ip_address() {
  static $ip_address = NULL;
  if (!isset($ip_address)) {
    $ip_address = $_SERVER['REMOTE_ADDR'];
    if (variable_get('reverse_proxy', 0) && array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)) {

      // If an array of known reverse proxy IPs is provided, then trust
      // the XFF header if request really comes from one of them.
      $reverse_proxy_addresses = variable_get('reverse_proxy_addresses', array());
      if (!empty($reverse_proxy_addresses) && in_array($ip_address, $reverse_proxy_addresses, TRUE)) {

        // If there are several arguments, we need to check the most
        // recently added one, i.e. the last one.
        $ip_address = array_pop(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']));
      }
    }
  }
  return $ip_address;
}

Functions

Namesort descending Description
shoutbox_access_callback Function to handle shoutbox access callbacks that require a shout_id. Does some sanity checking on on the id and loads the shout before calling _shoutbox_user_access.
shoutbox_add_form Generates form for adding shouts.
shoutbox_add_form_submit Handles submission of a shout.
shoutbox_add_form_validate Makes sure uses don't submit default values.
shoutbox_admin_settings Form for admin/settings/shoutox page.
shoutbox_block Make the shout box block available. (Standard Drupal hook).
shoutbox_callback Function to handle shoutbox callbacks that require a shout_id. Does some sanity checking on on the id before forwarding to the actual callback.
shoutbox_cron Implementation of hook_cron().
shoutbox_delete_form Displays a "Are you sure message ?" with a Yes and Cancel option.
shoutbox_delete_form_submit Handle the delete form submission.
shoutbox_edit_form Form for editing shouts.
shoutbox_edit_form_submit Handle the edit form submission.
shoutbox_help Describe the shoutbox module.
shoutbox_js_view Javascript callback. Prints out shouts only.
shoutbox_menu Implementation of hook_menu().
shoutbox_page_view Show paged view of full list of shouts.
shoutbox_perm Implementation of hook_perm().
shoutbox_publish_form Displays a "Are you sure message ?" with a Yes and Cancel option.
shoutbox_publish_form_submit Handle the publish form submission.
shoutbox_simpletest Implementation of hook_simpletest().
shoutbox_unpublish_form Displays a "Are you sure message ?" with a Yes and Cancel option.
shoutbox_unpublish_form_submit Handle the unpublish form submission.
shoutbox_user Implementation of hook_user().
theme_shoutbox_links Theme function of shoutbox actions. Actions are edit, delete, promote and demote. NOTE: Function does not return html but rather an array with the actions as keys. See code.
theme_shoutbox_page Theme function for displaying the shoutbox page.
theme_shoutbox_post Theme function for shoutbox posts.
theme_shoutbox_post_forbidden Theme function for displaying the access denied message.
_shoutbox_block_view Returns the themed HTML to be displayed in the block.
_shoutbox_display_posts Output existing shoutbox posts as html. Used by shoutbox_block_view.
_shoutbox_filter_form
_shoutbox_get_links Returns an array containing the possible actions for the current user based on permissions and shout. The actions are edit, delete, moderate.
_shoutbox_ip_address Backport of ip_address function from Drupal 6.
_shoutbox_is_user_owned Determine if the current user owns the $shout.
_shoutbox_moderate_shout Update the moderate bit in the database.
_shoutbox_sanitize_shout This function cleans the shout object before it is used.
_shoutbox_user_access This function is necessary because even if a user has permission (according to the user_access function), they still should not have some permissions, such as moderating their own posts, etc.