You are here

message.module in Message 6

Same filename and directory in other branches
  1. 8 message.module
  2. 7 message.module

API functions to manipulate messages.

File

message.module
View source
<?php

// $Id: message.module,v 1.12 2010/06/07 10:54:11 amitaibu Exp $

/**
 * @file
 * API functions to manipulate messages.
 */

/**
 * Minimum CTools version needed.
 */
define('MESSAGE_REQUIRED_CTOOLS_API', '1.7');

/**
 * Implementation of hook_ctools_plugin_api().
 */
function message_ctools_plugin_api($module, $api) {
  if ($module == 'message' && $api == 'plugins') {
    return array(
      'version' => 1,
    );
  }
}

/**
 * Implementation of hook_ctools_plugin_directory().
 */
function message_ctools_plugin_directory($module, $type) {

  // Safety: go away if CTools is not at an appropriate version.
  if (!module_invoke('ctools', 'api_version', MESSAGE_REQUIRED_CTOOLS_API)) {
    return;
  }
  if ($module == 'message' && $type == 'plugins') {
    return 'plugins/message';
  }
  elseif ($type == 'export_ui') {
    return 'plugins/export_ui';
  }
}

/**
 * Implementation of hook_views_api().
 */
function message_views_api() {
  return array(
    'api' => 2.0,
    'path' => drupal_get_path('module', 'message') . '/includes',
  );
}

/**
 * Implementation of hook_features_api().
 */
function message_features_api() {
  return array(
    'message' => array(
      'name' => 'Messages',
      'api' => 'message',
      'default_hook' => 'message_default_messages',
      'current_version' => 1,
      'module' => 'message',
      'feature_source' => TRUE,
    ),
  );
}

/**
 * Implementation of hook_theme().
 */
function message_theme() {

  // Safety: go away if CTools is not at an appropriate version.
  if (!module_invoke('ctools', 'api_version', MESSAGE_REQUIRED_CTOOLS_API)) {
    return;
  }
  $items = array();
  $items['message'] = array(
    'arguments' => array(
      'message' => array(),
    ),
  );
  return $items;
}

/**
 * Implementation of hook_views_pre_render().
 *
 * In order to reduce the database hits, we take advantage of the fact that we
 * know the message instances IDs before they are rendered. We load them all,
 * and they are saved in a static cache. So later when they are rendered, the
 * message instances are already cached.
 *
 * @see message_instance_load_multiple().
 * @see message_show_message().
 */
function message_views_pre_render(&$view) {

  // Define if this is a view that involves message instance, and thus should be
  // processed.
  $process = FALSE;
  $ids = array();

  // Look for a field that belong to the {message_instance} table.
  foreach ($view->field as $field) {
    if ($field->definition['handler'] == 'message_handler_field_message_render') {
      $field_alias = $field->field_alias;
      $process = TRUE;
      break;
    }
  }
  if ($process && !empty($view->result)) {
    foreach ($view->result as &$row) {
      $ids[] = $row->{$field_alias};
    }
  }

  // Load the message instances, so they will be statically cached.
  if ($ids && ($message_instances = message_instance_load_multiple($ids))) {
    $remove = array();
    foreach ($message_instances as $key => $value) {

      // Check if messages should be hidden.
      if (!empty($value->hide)) {
        $remove[$key] = $key;
      }
    }

    // Remove hidden results from the view.
    foreach ($view->result as $key => $value) {

      // TODO: Get the properly the alias of iid.
      if (in_array($value->iid, $remove)) {
        unset($view->result[$key]);

        // Unset the key from the remove list, to make next checks a bit faster.
        unset($remove[$key]);
      }
      if (empty($remove)) {
        break;
      }
    }
  }
}

/**
 * Implementation of hook_ctools_plugin_*.
 *
 * Give information to CTools about the message plugin.
 */
function message_ctools_plugin_plugins() {
  return array(
    'process' => 'message_message_process',
  );
}

/**
 * Provide defaults for a message plugin.
 */
function message_message_process(&$plugin, $info) {
  $plugin += array(
    'realm' => FALSE,
    'title' => $plugin['name'],
    'description' => '',
    'views' => array(
      // Optional callback for views handlers.
      'handlers callback' => function_exists("{$plugin['name']}_views_handlers") ? "{$plugin['name']}_views_handlers" : FALSE,
      // Optional callback for views data.
      'data callback' => function_exists("{$plugin['name']}_views_data") ? "{$plugin['name']}_views_data" : FALSE,
    ),
    'access' => array(
      'access callback' => function_exists("{$plugin['name']}_access") ? "{$plugin['name']}_access" : 'message_plugin_access',
      'accessible ids callback' => function_exists("{$plugin['name']}_accessible_ids") ? "{$plugin['name']}_accessible_ids" : 'message_plugin_accessible_ids',
    ),
  );
}

/**
 * Implementation of hook_nodeapi().
 */
function message_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {

  // Advanced implementation may want to disable the cleanup and do it
  // themselves.
  if (!variable_get('message_entity_delete_cleanup', TRUE)) {
    return;
  }

  // Preform basic clean up when a node is deleted.
  if ($op == 'delete') {
    message_deleted_entity_cleanup('node', $node->nid);
  }
}

/**
 * Implementation of hook_comment().
 */
function message_comment(&$a1, $op) {

  // Advanced implementation may want to disable the cleanup and do it
  // themselves.
  if (!variable_get('message_entity_delete_cleanup', TRUE)) {
    return;
  }

  // Preform basic clean up when a comment is deleted.
  if ($op == 'delete') {
  }
}

/**
 * Implementation of hook_user().
 */
function message_user($op, &$edit, &$account, $category = NULL) {

  // Advanced implementation may want to disable the cleanup and do it
  // themselves.
  if (!variable_get('message_entity_delete_cleanup', TRUE)) {
    return;
  }
  if ($op == 'delete') {

    // Preform basic clean up when a user is deleted.
    message_deleted_entity_cleanup('user', $account->uid);
  }
}

/**
 * API function; Save a message instance and save it to realms.
 *
 * @param $message_name
 *   The message name.
 * @param $entity_type
 *   The entity type.
 * @param $etid
 *   The entity ID.
 * @param $arguments
 *   Optional; Array of arguments to pass to the callback function.
 * @param $realms
 *   Optioanl; An array of arrays keyed with the realm ID. If no realms are
 *   provided then a the message instance will be assigned by default to the
 *   user realm, with the user ID as the realm ID.
 * @param $account
 *   Optional; The user object, if empty the logged in user will be used.
 * @param $extra_identifier
 *   Optioanl; The string value to save as the extra identfier.
 */
function message_save_message_to_realms($message_name, $entity_type, $eid, $arguments = array(), $realms = array(), $account = NULL, $extra_identifier = '') {
  if (empty($account)) {
    global $user;
    $account = drupal_clone($user);
  }
  $return = array();

  // Create message instance.
  $message_instance = new stdClass();
  $message_instance->name = $message_name;
  $message_instance->uid = $account->uid;
  $message_instance->entity_type = $entity_type;
  $message_instance->eid = $eid;
  $message_instance->arguments = $arguments;
  $message_instance->extra_identifier = $extra_identifier;
  $message_instance = message_instance_save($message_instance);
  $return['message instance'] = $message_instance;
  if (empty($realms)) {

    // Set default realm.
    $realms[] = array(
      'realm' => 'user',
      'realm id' => $account->uid,
    );
  }

  // Save to the realms.
  foreach ($realms as $value) {
    $realm = new stdClass();
    $realm->iid = $message_instance->iid;
    $realm->realm = $value['realm'];
    $realm->realm_id = $value['realm id'];
    $return['message realm'][] = message_realm_save($realm);
  }
  return $return;
}

/**
 * Load & crud functions ==============================================
 */

/**
 * Message loader.
 *
 * @param $name
 *   The name for this message object.
 *
 * @return
 *   Returns a fully-loaded message definition.
 */
function message_load($name = NULL, $reset = FALSE) {
  ctools_include('export');
  if ($reset) {
    ctools_export_load_object_reset('message');
  }
  $messages = ctools_export_load_object('message', 'all');

  // We cache the i18n strings, so we don't re-create them every time. We don't
  // save the strings with variable_set() as it won't scale well with many
  // messages.
  if ($messages && module_exists('i18nstrings')) {

    // Check if translation was registered.
    $cache = cache_get('message_i18nstrings');
    $i18nstrings = array();
    $set = FALSE;
    foreach ($messages as $message) {
      if (empty($cache->data[$message->name])) {

        // Create string.
        i18nstrings_update("messages:message:{$message->name}:message", $message->message);
        $i18nstrings[$message->name] = TRUE;
        $set = TRUE;
      }
    }
    if ($set) {

      // Cache all the strings that were translated.
      cache_set('message_i18nstrings', $i18nstrings);
    }
  }
  if (isset($name)) {
    return isset($messages[$name]) ? $messages[$name] : FALSE;
  }
  return $messages;
}

/**
 * Inserts or updates a message object into the database.
 *
 * @param $message
 *   The message object to be inserted.
 *
 * @return
 *   Failure to write a record will return FALSE. Otherwise SAVED_NEW or
 *   SAVED_UPDATED is returned depending on the operation performed.
 */
function message_save($message) {
  ctools_include('export');
  return ctools_export_crud_save('message', $message);
}

/**
 * Deletes an existing message.
 *
 * @param $message
 *   The message object to be deleted.
 */
function message_delete($message) {
  ctools_include('export');
  ctools_export_crud_delete('message', $message);
}

/**
 * Load multiple message instances.
 *
 * @param $iids
 *   Array with message instances ids.
 * @return
 *   Array with the message instances object.
 */
function message_instance_load_multiple($ids = array()) {
  $message_instances =& ctools_static(__FUNCTION__, array());

  // The ids we need to query, as some might have been cached already.
  $query_ids = array_diff($ids, array_keys($message_instances));
  if (!empty($query_ids)) {
    $placeholders = db_placeholders($query_ids);
    $result = db_query("SELECT * FROM {message_instance} WHERE iid IN ({$placeholders})", $query_ids);
    while ($row = db_fetch_object($result)) {

      // Unserialize the arguments.
      if (!empty($row->arguments)) {
        $row->arguments = unserialize($row->arguments);
      }
      $message_instances[$row->iid] = $row;
    }

    // Allow other modules to alter the message instances.
    drupal_alter('message_instances', $message_instances);
  }
  return $message_instances;
}

/**
 * Message instance load.
 *
 * @see message_instance_load_multiple.
 *
 * @param $iid
 *   The message instance ID.
 * @return
 *   A message instance object.
 */
function message_instance_load($iid) {
  $messages = message_instance_load_multiple(array(
    $iid,
  ));
  return !empty($messages[$iid]) ? $messages[$iid] : FALSE;
}

/**
 * Message instance save.
 *
 * @param $message_instance
 *   A message instance object.
 * @return
 *   The saved message instance object.
 */
function message_instance_save($message_instance) {
  if ($existing = message_instance_load($message_instance->iid)) {
    drupal_write_record('message_instance', $message_instance, 'iid');
  }
  else {
    $message_instance->timestamp = !empty($message_instance->timestamp) ? $message_instance->timestamp : time();
    drupal_write_record('message_instance', $message_instance);
  }
  return $message_instance;
}

/**
 * Message instance delete.
 *
 * @param $iid
 *   Message instance IDs array.
 */
function message_instance_delete($iids = array()) {
  $placeholders = db_placeholders($iids);

  // Delete all message instances.
  db_query("DELETE FROM {message_instance} WHERE iid IN({$placeholders})", explode(',', $iids));
  message_realm_delete_by_message_instance($iids);
}

/**
 * Get realm messages by their ID.
 *
 * @param $message_id
 *   The message ID.
 * @return
 *   Array keyed by the message realm and the realm Id as the value.
 */
function message_realm_load_multiple_by_message($message_id) {
  static $messages = array();
  if (!isset($messages[$message_id])) {
    $result = db_query("SELECT * FROM {message_realm} WHERE iid = %d", $message_id);
    while ($row = db_fetch_object($result)) {
      $messages[$message_id][$row->realm][$row->realm_id] = $row->realm_id;
    }
  }
  return !empty($messages[$message_id]) ? $messages[$message_id] : array();
}

/**
 * Message realm load.
 *
 * @param $iid
 * @return unknown_type
 */
function message_realm_load($rid) {
  return db_fetch_object(db_query("SELECT * FROM {message_realm} where rid = %d", $rid));
}

/**
 * Message realm save.
 *
 * @param $message_instance
 *
 * @return
 *   The message realm object.
 */
function message_realm_save($message_realm) {
  if ($existing = message_realm_load($message_realm->rid)) {
    drupal_write_record('message_realm', $message_realm, 'rid');
  }
  else {
    drupal_write_record('message_realm', $message_realm);
  }
  return $message_realm;
}

/**
 * Message realm delete.
 *
 * @param $rid
 *   Message realm IDs array.
 */
function message_realm_delete($rids = array()) {
  $placeholders = db_placeholders($rids);
  db_query("DELETE FROM {message_realm} WHERE rid IN ({$placeholders})", explode(',', $rids));
}

/**
 * Message realm delete by message instance ID.
 *
 * @param $iids
 *   Message instance IDs array.
 */
function message_realm_delete_by_message_instance($iids = array()) {
  $placeholders = db_placeholders($iids);
  db_query("DELETE FROM {message_realm} WHERE iid IN ({$placeholders})", implode(',', $iids));
}

/**
 * Message realm delete by realm type and realm ID.
 *
 * @param $realm
 *   The realm name.
 * @param $realm_id
 *   The realm ID.
 */
function message_realm_delete_by_realm($realm, $realm_id) {
  db_query("DELETE FROM {message_realm} WHERE realm = '%s' AND eid = '%s'", $realm, $realm_id);
}
function message_get_plugin_message($plugin_name) {
  ctools_include('plugins');
  return ctools_get_plugins('message', 'plugins', $plugin_name);
}
function message_get_plugin_messages() {
  ctools_include('plugins');
  return ctools_get_plugins('message', 'plugins');
}

/**
 * Show a message by the message instance ID.
 *
 * @param $message_instance
 *   The message instance object.
 * @param $account
 *   Optional; The account that access should be checked for.
 *
 * @return
 *   HTML with the message.
 */
function message_show_message($message_instance, $skip_access = FALSE, $account = NULL) {
  $output = '';
  $access = FALSE;
  if (empty($account)) {
    global $user;
    $account = drupal_clone($user);
  }
  if ($skip_access) {
    $access = TRUE;
  }
  elseif ($realms = message_realm_load_multiple_by_message($message_instance->iid)) {
    foreach (message_get_plugin_messages() as $plugin) {
      if (in_array($plugin['realm'], array_keys($realms)) && message_show_message_access($plugin, $message_instance, $realms[$plugin['realm']])) {
        $access = TRUE;
        break;
      }
    }
  }
  if ($access) {
    $output = theme('message', array(
      'message' => $message_instance,
      'output' => message_t($message_instance),
    ));
  }
  return $output;
}
function message_show_message_access($plugin, $message_instance, $realm_ids = array(), $account = NULL) {
  $return = FALSE;
  if (!empty($plugin['realm'])) {
    if (empty($realm_ids)) {
      $realms = message_realm_load_multiple_by_message($message_instance->iid);
      $realm_ids = !empty($realms[$plugin['realm']]) ? $realms[$plugin['realm']] : array();
    }
    if ($realm_ids) {
      if (empty($account)) {
        global $user;
        $account = drupal_clone($user);
      }
      $return = call_user_func($plugin['access']['access callback'], $plugin, $message_instance, $realm_ids, $account);
    }
  }
  return $return;
}
function message_get_accessiable_ids_by($plugin, $account = NULL) {
  if (empty($account)) {
    global $user;
    $account = drupal_clone($user);
  }
  return call_user_func($plugin['access']['accessible ids callback'], $account);
}

/**
 * Replace arguments with their placeholders.
 *
 * @param $message
 *   The message instance object.
 *
 * @see t().
 */
function message_t($message_instance) {
  if (!empty($message_instance->hide)) {
    return '';
  }
  $string = message_get_string_i18n($message_instance);
  if (empty($message_instance->arguments)) {
    return $string;
  }
  else {

    // Transform arguments before inserting them.
    foreach ($message_instance->arguments as $key => $value) {
      if (is_array($value) && !empty($value['callback']) && function_exists($value['callback'])) {

        // A replacement via callback function.
        $value = call_user_func_array($value['callback'], $value['callback arguments']);
      }
      switch ($key[0]) {
        case '@':

          // Escaped only.
          $args[$key] = check_plain($value);
          break;
        case '%':
        default:

          // Escaped and placeholder.
          $args[$key] = theme('placeholder', $value);
          break;
        case '!':

          // Pass-through.
          $args[$key] = $value;
      }
    }
    return strtr($string, $args);
  }
}

/**
 * Get the string of the message.
 *
 * @param $message
 *   The message object,
 *
 * @return
 *   This function checkes if the i18n module is enabled and provides the
 *   translated string if needed. It also checkes if the message was overriden
 *   and if so, return the overriden string.
 */
function message_get_string_i18n($message_instance) {
  $message = message_load($message_instance->name);
  if (!empty($message_instance->override)) {
    $name_property = $message_instance->override_name;
    $message_property = $message_instance->message;
  }
  else {
    $name_property = $message->name;
    $message_property = $message->message;
  }
  if (module_exists(i18nstrings)) {

    // Get the translation of the original or overriden message.
    $string = i18nstrings('messages:message:' . $name_property . ':message', $message_property);
  }
  else {
    $string = $message_property;
  }
  return $string;
}

/**
 * Override the message, and if i18n module is enabled, register the string.
 *
 * @param $message
 *   The message object passed by reference.
 * @param $override_name
 *   The name of the overriden message, to be appended to the original name.
 * @param $override_message
 *   The overriden message.
 */
function message_override_message_instance(&$message_instance, $override_name, $override_message) {
  $message = message_load($message_instance->name);
  $message_instance->override_name = $message->name . '_' . $override_name;
  $message_instance->message = $override_message;

  // Set a flag.
  $message_instance->override = TRUE;
  if (module_exists('i18nstrings')) {

    // Register the string.
    $cache = cache_get('message_i18nstrings');
    $i18nstrings = array();
    if (!empty($cache->data)) {
      $i18nstrings = $cache->data;
    }
    $set = FALSE;
    if (empty($i18nstrings[$message_instance->override_name])) {

      // Create string.
      i18nstrings_update("messages:message:{$message_instance->name}:message", $message_instance->message);
      $i18nstrings[$message_instance->override_name] = TRUE;
      $set = TRUE;
    }
    if ($set) {

      // Cache all the strings that were translated.
      cache_set('message_i18nstrings', $i18nstrings);
    }
  }
}

/**
 * Theme the message.
 */
function theme_message($message = array()) {
  return '<span class="message message-instance-' . $message['message']->iid . '">' . $message['output'] . '</span>';
}

/**
 * Return an array of realms keyed by the plugin name and the realm value.
 */
function message_get_realm_types() {
  $options =& ctools_static(__FUNCTION__, array());
  if (empty($options)) {
    foreach (message_get_plugin_messages() as $plugin) {
      if (!empty($plugin['realm'])) {
        $options[$plugin['name']] = check_plain($plugin['title']);
      }
    }
  }
  return $options;
}

/**
 * Delete message instances that are related to a a deleted entity.
 *
 * For example, if a node is deleted then all the message instances that are
 * related to that node will be deleted.
 *
 * @param $entity_type
 *   The entity type.
 * @param $eid
 *   The entity ID.
 */
function message_deleted_entity_cleanup($entity_type, $eid) {

  // Get all the message instanced.
  $result = db_query("SELECT iid FROM {message_instance} WHERE entity_type = '%s' AND eid = %d", $entity_type, $eid);
  $iids = array();
  while ($row = db_fetch_object($result)) {
    $iids[] = $row->iid;
  }

  // Instances were found and need to be deleted.
  if ($iids) {
    message_instance_delete($iids);
    message_realm_delete($entity_type, $eid);
  }
}

Functions

Namesort descending Description
message_comment Implementation of hook_comment().
message_ctools_plugin_api Implementation of hook_ctools_plugin_api().
message_ctools_plugin_directory Implementation of hook_ctools_plugin_directory().
message_ctools_plugin_plugins Implementation of hook_ctools_plugin_*.
message_delete Deletes an existing message.
message_deleted_entity_cleanup Delete message instances that are related to a a deleted entity.
message_features_api Implementation of hook_features_api().
message_get_accessiable_ids_by
message_get_plugin_message
message_get_plugin_messages
message_get_realm_types Return an array of realms keyed by the plugin name and the realm value.
message_get_string_i18n Get the string of the message.
message_instance_delete Message instance delete.
message_instance_load Message instance load.
message_instance_load_multiple Load multiple message instances.
message_instance_save Message instance save.
message_load Message loader.
message_message_process Provide defaults for a message plugin.
message_nodeapi Implementation of hook_nodeapi().
message_override_message_instance Override the message, and if i18n module is enabled, register the string.
message_realm_delete Message realm delete.
message_realm_delete_by_message_instance Message realm delete by message instance ID.
message_realm_delete_by_realm Message realm delete by realm type and realm ID.
message_realm_load Message realm load.
message_realm_load_multiple_by_message Get realm messages by their ID.
message_realm_save Message realm save.
message_save Inserts or updates a message object into the database.
message_save_message_to_realms API function; Save a message instance and save it to realms.
message_show_message Show a message by the message instance ID.
message_show_message_access
message_t Replace arguments with their placeholders.
message_theme Implementation of hook_theme().
message_user Implementation of hook_user().
message_views_api Implementation of hook_views_api().
message_views_pre_render Implementation of hook_views_pre_render().
theme_message Theme the message.

Constants

Namesort descending Description
MESSAGE_REQUIRED_CTOOLS_API Minimum CTools version needed.