You are here

heartbeat.module in Heartbeat 6.2

To fully understand this, you have to be familiar with the rules module. Lots of documentation can be found on http://drupal.org/node/298480 for an introduction and tutorial, but http://drupal.org/node/298486 is a lot of handy info for developers.

File

heartbeat.module
View source
<?php

// by Jochen Stals - ONE-agency - www.one-agency.be

/**
 * @file 
 * 
 * To fully understand this, you have to be familiar with the rules module. 
 * Lots of documentation can be found on http://drupal.org/node/298480 for 
 * an introduction and tutorial, but http://drupal.org/node/298486 is a lot 
 * of handy info for developers.
 * 
 */

# classes

/**
 * Class to handle user activity data
 *
 */
class user_activity {

  // Private members are prefixed with m_
  private $m_uid = 0;
  private $m_uid_target = 0;
  private $m_nid_target = 0;
  private $m_hid = 0;
  private $m_karma_index = 0;
  private $m_description = '';
  private $m_message = '';
  private $m_message_concat = '';
  private $m_variables_array = array();
  private $m_variables_string = '';
  public $event = '';

  /**
   * constructor
   */
  function __construct($data = null) {
    if (isset($data)) {
      $this
        ->set_data($data);
    }
  }

  /**
   * Set data into members
   */
  public function set_data($data) {
    foreach ($data as $key => $value) {
      if (isset($this->{$key})) {
        $this->{$key} = $value;
      }
      if (isset($this->{'m_' . $key})) {
        $this->{'m_' . $key} = $value;
      }
    }

    // Data variables are more complicated
    if (isset($data['variables'])) {
      $this->m_variables_string = $data['variables'];
    }

    // if the data variables have not been included
    // as normal members, do so now to be available when asked for
    // @see __get
    if ($this->m_variables_array == array() && $this->m_variables_string != '') {
      $this
        ->variables2array();
    }
  }

  /**
   * Method gets a user_activity variable
   *
   * @desc The magic getter method fetches a variable
   *       in members. If not found there, it will always
   *       check the variables as well
   */
  public function __get($variable) {

    // a private member is asked
    $var = null;
    if (isset($this->{'m_' . $variable})) {
      $var = $this->{'m_' . $variable};
    }
    else {
      if (array_key_exists($variable, $this->m_variables_array)) {
        $var = $this->m_variables_array[$variable];
      }
    }
    return $var;
  }

  /**
   * public function to set variables into readable array
   */
  public function variables2array() {
    $this->m_variables_array = heartbeat_decode_message_variables($this->m_variables_string);
    return $this->m_variables_array;
  }

  /**
   * Public function to save activity to database
   * @param array raw argument to enforce as is (pre-renderd)
   */
  public function save($raw_args = array()) {
    if (module_exists('locale')) {
      $this
        ->save_locale($raw_args);
    }
    else {
      $this
        ->_save($raw_args);
    }
  }

  /**
   * Save activity log with multilingual content
   * and multilingual parts to pre-translate
   *
   * @param array $raw_args
   */
  private function save_locale($raw_args = array()) {
    $args = $this
      ->rebuild_arguments($raw_args, true);
    $locale = $args['locale'];
    unset($args['locale']);

    // Save activity by logging a row for each active language
    // Translations only when locale exists
    $languages = locale_language_list();
    foreach ($languages as $language => $human_language) {

      // preprocess multilingual message "parts"
      // for all flagged token replacements
      foreach ($this->m_variables_array as $key => $value) {
        if (isset($locale[$key])) {
          $amp_token = str_replace("#", "!", $key);
          $args[$amp_token] = t($locale[$key], array(), $language);
        }
      }
      $this
        ->log_message($args, $language);
    }
  }

  /**
   * Save activity log
   *
   * @param array $raw_args
   */
  private function _save($raw_args = array()) {

    // Rebuild arguments with tokens
    $args = $this
      ->rebuild_arguments($raw_args);
    $this
      ->log_message($args);
  }

  /**
   * Logs a heartbeat message
   * @param string language optional
   *
   */
  private function log_message($args, $lang = '') {
    if ($lang == '') {
      global $language;
      $lang = $language->language;
    }

    // Log relational message to user activity
    db_query("INSERT INTO user_activity SET uid=%d, uid_target=%d, nid_target=%d, hid=%d,  language='%s', \n    message='%s', timestamp=%d, event='%s', variables='%s'", $this->m_uid, $this->m_uid_target, $this->m_nid_target, $this->m_hid, $lang, t($this->m_message, $args, $lang), $_SERVER['REQUEST_TIME'], $this->event, $this->m_variables_string);
  }

  /**
   * Rebuild the arguments for variables
   * to share within this object
   *
   * @param array $raw_input of arguments
   */
  private function rebuild_arguments($raw_input, $locale = false) {
    $args = array();
    if ($locale) {

      // Variables that need to be pre-translated go here
      $args['locale'] = array();
    }

    // Rebuild arguments with language tokens
    foreach ($this->m_variables_array as $key => $value) {

      // Leave $key[0] == "!"  asis
      if ($key[0] != "@" && $key[0] != "#") {
        continue;

        // bad argument
      }
      $oldkey = $key;

      // Reset the key of the arguments to ! to parse the next
      // tokenization asis.
      if ($key[0] == "@") {
        $key[0] = "!";
      }

      // # and @ token replacement prefixes are kept,
      // but set a flag for it in the raw_arguments
      if ($key[0] == "#") {
        dsm($key . ' ' . $value);

        // if it has to be translated ...
        if ($locale) {
          $args['locale'][$key] = $value;
        }

        // Now reset the key
        $key[0] = "!";
      }

      // if argument is prefilled, override
      if (isset($raw_args[$oldkey])) {
        $args[$key] = $raw_args[$oldkey];
        continue;
      }

      // Argument gets the value as in variables
      $args[$key] = $value;
    }
    return $args;
  }

}

// eof class user_activity

# core hooks

/**
 * Implementation of hook_perm().
 */
function heartbeat_perm() {
  return array(
    'configure heartbeat',
    'configure heartbeat messages',
    'heartbeat view messages',
  );
}

/**
 *  Implementation of hook_menu().
 */
function heartbeat_menu() {
  $items = array();

  // Administer list
  $items['admin/settings/heartbeat'] = array(
    'title' => t('heartbeat settings'),
    'description' => t('Administer settings for heartbeat.'),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'heartbeat_admin_settings',
    ),
    'access arguments' => array(
      'configure heartbeat',
    ),
    'file' => 'heartbeat.admin.inc',
  );

  // Tabs
  $items['admin/settings/heartbeat/settings'] = array(
    'title' => t('Heartbeat settings'),
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => 0,
  );
  $items['admin/settings/heartbeat/messages'] = array(
    'title' => t('Heartbeat messages'),
    'description' => t('Administer messages for heartbeat.'),
    'type' => MENU_LOCAL_TASK,
    'weight' => 1,
    'page callback' => 'heartbeat_messages_overview',
    'access arguments' => array(
      'configure heartbeat messages',
    ),
    'file' => 'heartbeat.admin.inc',
  );
  $items['admin/settings/heartbeat/messages/%'] = array(
    'title' => t('Heartbeat message editer'),
    'description' => t('Administer message for heartbeat.'),
    'weight' => 1,
    'type' => MENU_LOCAL_TASK,
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'heartbeat_messages_edit',
      4,
    ),
    'access arguments' => array(
      'configure heartbeat messages',
    ),
    'file' => 'heartbeat.admin.inc',
  );
  return $items;
}

/**
 * Implementation of hook_theme().
 */
function heartbeat_theme() {
  return array(
    'heartbeat_block' => array(
      'arguments' => array(
        'messages' => array(),
      ),
    ),
  );
}

/**
 * Implementation of hook_blocks()
 *
 */
function heartbeat_block($op = 'list', $delta = 0) {
  if (user_access('heartbeat view messages')) {
    switch ($op) {
      case 'list':
        $blocks[0]['info'] = t('Personal heartbeat');
        $blocks[0]['cache'] = BLOCK_NO_CACHE;
        return $blocks;
      case 'view':
        if (variable_get('heartbeat_enabled', 1)) {
          global $user, $language;
          $max_gap = variable_get('user_activity_grouping_seconds', 3600);
          $end_time = $_SERVER['REQUEST_TIME'] - (int) $max_gap;

          // Retrieve the most receent heartbeat.
          $messages = array();

          // We really need every field
          $sql = "SELECT * FROM {user_activity} WHERE \n\t        (user_activity.uid = %d) AND (user_activity.language = '%s') AND timestamp > %d ORDER BY timestamp DESC";
          $result = db_query($sql, $user->uid, $language->language, $end_time);
          while ($heartbeat = db_fetch_object($result)) {
            $messages[] = $heartbeat;
          }
          $block['subject'] = t('Recent heartbeat');
          $messages = heartbeat_group_messages($messages);
          $block['content'] = theme('heartbeat_block', $messages);
          return $block;
        }
      default:
    }
  }
}

# heartbeat api functions

/**
 * Function that gathers all messages from all modules
 * New ones and existing ones
 *
 */
function heartbeat_gather_messages() {
  $info_default = module_invoke_all('heartbeat_message_info');

  // dsm($info);
  $info_cache = heartbeat_messages('all', false, false);
  $info = array_diff($info_default, $info_cache);

  //dsm($info);
  $info = heartbeat_messages_install($info);
  return $info;
}

# custom functions

/**
 * Theme function for blocks
 */
function theme_heartbeat_block($messages) {
  if (empty($messages)) {
    return t('<p>No activity yet</p>');
  }
  $content = '';
  foreach ($messages as $key => $message) {
    $content .= '<span class="heartbeat-message-block ' . (($key + 1) % 2 ? 'odd' : 'even') . '">' . $message->message . '</span>';
  }
  return $content;
}

/**
 * Function to group messages,
 * remove duplicates, check timespan, permissions, interests, etc ..;
 *
 * @param array $messages
 */
function heartbeat_group_messages($messages) {

  //$messages = heartbeat_group_remove_duplicates();
  return $messages;
}

/**
 * Theme function to help
 *
 * @param string $module
 * @param boolean $reset
 * @param boolean $objects
 * @return array messages
 */
function heartbeat_messages($module = 'all', $reset = false, $objects = true) {
  static $messages;
  if (empty($messages) || $reset == true) {
    $messages = array();
    if ($module == 'all') {
      $result = db_query("SELECT * FROM {heartbeat_messages}");
    }
    else {
      $result = db_query("SELECT * FROM {heartbeat_messages} WHERE module = '%s'", $module);
    }
    while ($row = db_fetch_array($result)) {
      if ($objects) {
        $messages[] = new user_activity($row);
      }
      else {
        $messages[] = $row;
      }
    }
  }
  return $messages;
}

/**
 * Fetches the translatable message for corresponding action
 *
 * @param string $event
 */
function heartbeat_event_message($event) {
  $result = db_query("SELECT message from {heartbeat_messages} WHERE event = '%s' LIMIT 1", $event);
  $message = db_fetch_object($result);
  return $message->message;
}

/**
 * Fetches the translatable message for corresponding action
 *
 * @param string $event
 */
function heartbeat_event_messages($event, $field) {
  static $messages;

  // If the one asked does not exist, fetch them all
  if (!isset($messages[$event])) {

    // They were fetched, but this seems to be a new one
    if (count($messages) > 0) {
      $result = db_query("SELECT event, message, message_concat, variables from {heartbeat_messages} WHERE event = '%s' LIMIT 1", $event);
    }
    else {
      $result = db_query("SELECT event, message, message_concat, variables from {heartbeat_messages}");
    }
    while ($message = db_fetch_object($result)) {
      $messages[$message->event] = $message;
    }
  }
  return $messages[$event]->{$field};
}

/**
 * Fetches the id for corresponding action
 *
 * @param string $action
 */
function heartbeat_event_id($action) {
  $result = db_query("SELECT hid from {heartbeat_messages} WHERE event = '%s' LIMIT 1", $action);
  $res = db_fetch_object($result);
  return $res->hid;
}

/**
 * Function to install default records
 *
 * @param string $module to conventionally look 
 *        for defined record objects in heartbeat_messages
 */
function heartbeat_messages_install($objects) {
  foreach ($objects as $record) {
    $record = (object) $record;
    if (isset($record->concat_args) && is_array($record->concat_args)) {
      $record->concat_args = heartbeat_encode_message_variables($record->concat_args);
    }
    if (isset($record->variables) && is_array($record->variables)) {
      $record->variables = heartbeat_encode_message_variables($record->variables);
    }

    //watchdog('heartbeat', $module.' is writing a message to heartbeat_messages.', ((array)$record) );

    // drupal_write record does not work when installing all modules together
    // reason is the schema static cannot rebuilt without messing in the core.
    // drupal_write_record('heartbeat_messages', $record);
    db_query("INSERT INTO {heartbeat_messages} (message, message_concat, concat_args, event, karma_index, description, module, variables) \n    VALUES ('%s', '%s','%s','%s',%d,'%s','%s','%s') ", $record->message, $record->message_concat, $record->concat_args, $record->event, $record->karma_index, $record->description, $record->module, $record->variables);
  }
  return $objects;
}

/**
 * Function to uninstall default records
 */
function heartbeat_messages_uninstall($module) {
  db_query("DELETE FROM {heartbeat_messages} WHERE module = '%s'", $module);
}

/**
 * Decode heartbeat message variables
 */
function heartbeat_decode_message_variables($string, $object = false) {

  // Variable string need to be cleared from spaces to decode properly
  $array = explode("-|-", $string);
  $variables = array();
  if (!empty($array)) {
    foreach ($array as $varvalue) {
      $parts = explode("=|=", $varvalue);
      if (isset($parts[0]) && !empty($parts[0])) {
        $variables[$parts[0]] = (string) $parts[1];
      }
    }
  }

  //$variables = unserialize($string);
  return $object ? (object) $variables : (array) $variables;
}

/**
 * Encode heartbeat message variables
 */
function heartbeat_encode_message_variables($array) {
  $string = '';
  foreach ($array as $key => $value) {
    $string .= $key . '=|=' . $value . '-|-';
  }

  //$string = serialize((object)$array);
  return $string;
}

Functions

Namesort descending Description
heartbeat_block Implementation of hook_blocks()
heartbeat_decode_message_variables Decode heartbeat message variables
heartbeat_encode_message_variables Encode heartbeat message variables
heartbeat_event_id Fetches the id for corresponding action
heartbeat_event_message Fetches the translatable message for corresponding action
heartbeat_event_messages Fetches the translatable message for corresponding action
heartbeat_gather_messages Function that gathers all messages from all modules New ones and existing ones
heartbeat_group_messages Function to group messages, remove duplicates, check timespan, permissions, interests, etc ..;
heartbeat_menu Implementation of hook_menu().
heartbeat_messages Theme function to help
heartbeat_messages_install Function to install default records
heartbeat_messages_uninstall Function to uninstall default records
heartbeat_perm Implementation of hook_perm().
heartbeat_theme Implementation of hook_theme().
theme_heartbeat_block Theme function for blocks

Classes

Namesort descending Description
user_activity Class to handle user activity data