You are here

heartbeatparser.inc in Heartbeat 6.3

Same filename and directory in other branches
  1. 6.4 includes/heartbeatparser.inc
  2. 7 includes/heartbeatparser.inc

File

includes/heartbeatparser.inc
View source
<?php

class heartbeatParser {
  private $_info = null;
  private $_candidates = array();
  private $_messages = array();
  private $_sets = array();
  private $_timespan_gap = 0;
  private $_allowed_tags = array(
    'a',
    'span',
    'em',
    'strong',
    'ul',
    'li',
    'p',
    'img',
  );
  private function __construct() {
  }

  /**
   * Set the first time , start time of the gap
   */
  public function set_timespan_gap($gap) {
    $this->_timespan_gap = $gap;
  }

  /**
   * build sets of messages
   */
  public function build_sets($messages_raw) {
    $renewal_time = $_SERVER['REQUEST_TIME'] - $this->_timespan_gap;
    $raw_messages = array();
    foreach ($messages_raw as $key => $message) {

      // if the time with the gap exceeds the starttime
      if ($renewal_time >= $message->timestamp) {

        // reset the start time of the set
        $renewal_time = $message->timestamp - $this->_timespan_gap;
      }
      $this->_sets[$renewal_time][$key] = new HeartbeatActivity($message);
    }
  }

  /**
   * Sets information on heartbeat messages in total
   */
  public function set_info(HeartbeatInfo $info) {
    $this->_info = $info;
  }
  public function get_info() {
    return $this->_info;
  }

  /**
   * Merges sets of messages to fully formatted messages
   * regenerated at runtime to group settings
   */
  public function merge_sets() {
    $set_count = 0;
    foreach ($this->_sets as $message_set) {
      $this
        ->prepare_candidates($message_set, $set_count);
      $set_count++;
    }
    $this
      ->remove_variables_duplicates();
    $this
      ->rebuild_in_groups();
    return $count;
  }

  /**
   * Gets the messages as they exist for the time asked
   */
  public function get_messages($limit = 25) {
    if ($limit > 0) {
      return array_slice($this->_messages, 0, $limit);
    }
    return $this->_messages;
  }

  /**
   * Function to remove duplicate messages
   * meaning remove exactly the same already build messages,
   * incrementing the count of the messages
   * User-user relation always have duplicates that need to be removed.
   * This is handled here as well. Take care: the counter cannot increment!
   * @TODO Refactor this crappy shit
   */
  private function remove_message_duplicates($message_set) {
    global $user;
    $ow_relations = array();
    foreach ($message_set as $key => $message) {
      if ($message->concat_args['group_by'] == 'user-user') {
        if (!isset($ow_relations[$message->uid_target])) {
          $ow_relations[$message->uid_target] = array();
        }

        // Find bad duplicates in two way relations (5-2 and 2-5)
        if (!in_array($message->uid, $ow_relations[$message->uid_target])) {
          $ow_relations[$message->uid][] = $message->uid_target;
        }
      }
    }
    foreach ($message_set as $key => $message) {
      if ($message->concat_args['group_by'] == 'user-user') {

        // If i am not the actor, but one of my relations is
        // and i am the target for the relation, then
        // it is a duplicate message

        /*if($this->_info->access == HEARTBEAT_PUBLIC_TO_CONNECTED) {
            if($message->uid_target == $user->uid && in_array($message->uid, $user->heartbeat_relations)) {
              unset($message_set[$key]);
            }
          }
          if($this->_info->access == HEARTBEAT_PUBLIC_TO_ALL) {
            if(!in_array($message->uid, $ow_relations[$message->uid_target])) {
             unset($message_set[$key]);
            }
          }*/
        if (!in_array($message->uid, $ow_relations[$message->uid_target])) {
          unset($message_set[$key]);
        }
      }
    }

    // hash holding all unique values
    // if this would be static ...you could do it for all timespan-sets
    $holder = array();
    foreach ($message_set as $key => $message) {

      // if an exact identical message occurs contextual wise,
      // then skip it with an incrementation of the frequency
      // of the message
      $id = $message->message_id . '-' . $message->uid . '-' . $message->uid_target . '-' . $message->nid_target;
      if (in_array($id, $holder)) {
        $message_set[array_search($id, $holder)]->count++;
        unset($message_set[$key]);
      }
      else {
        $holder[$key] = $id;

        //$message->message;
      }
    }
    return $message_set;
  }

  /**
   * Prepare message candidates
   * This is an important method that handles several things
   * - Build an id for each time-gap limiting groupable messages
   * - Hold the candidates for each id (and count of messages)
   * - Logic to handle the group_by node or user setting
   */
  private function prepare_candidates($message_set, $set_count = 0) {
    static $singles = 0;

    // Remove duplicate messages in the same set,
    // incrementing the counter on the grouped message
    $message_set = $this
      ->remove_message_duplicates($message_set);
    foreach ($message_set as $key => $message) {
      $type = $message->concat_args['type'];

      // Variable to hold the unique grouping gap id.
      // Extending the gap id will result in summaries
      // and groups of identical and related messages.
      $gap_id = 'BEAT_' . $set_count . '_' . $message->message_id;

      // Summaries have to be evaluated for merging
      if ($type == 'summary' && $this
        ->extend_gap_id($gap_id, $message, $type, $set_count)) {

        // Add a candidates if this one does not exist yet
        if (!isset($this->_candidates[$gap_id])) {
          $this->_candidates[$gap_id] = array(
            'count' => 0,
            'group_target' => $message->concat_args['group_target'],
            'variables' => array(),
          );

          // Add the message
          $this->_messages[$gap_id] = $message;
        }

        // Add the counters and variables needed for merging in groups
        $this->_messages[$gap_id]->target_count++;

        // Message occurrency, first time is 1
        $this->_candidates[$gap_id]['variables'][] = $message->variables_array;
        $this->_candidates[$gap_id]['count']++;

        // variable count, NOT the same as target_count
      }
      elseif ($type == 'count') {

        //$gap_id = $gap_id . '_'.$message->uid;
        $gap_id = $gap_id . '_count';
        $this->_messages[$gap_id] = $message;
        $this->_messages[$gap_id]->target_count++;
      }
      else {

        // Autoincrement the singles to make the gap_id unique
        $gap_id .= '_single_' . $singles;
        $this->_messages[$gap_id] = $message;
        $singles++;
      }
    }
  }

  /**
   * Remove broken messages
   * @Todo build this function
   */
  public function remove_broken_messages($messages = array()) {
    if ($messages == array()) {
      $messages = $this->_messages;
    }
    return $messages;
  }

  /**
   * Function to remove duplicate messages valuating the
   * variables and count properties
   */
  private function remove_variables_duplicates() {
    $uniques = array();
    foreach ($this->_candidates as $single_id => $info) {
      $uniques[$single_id] = array();

      // Loop through variables to check if there are identical
      foreach ($info['variables'] as $rid => $value) {
        if (!in_array($value, $uniques[$single_id])) {
          $uniques[$single_id][] = $value;
        }
        else {
          unset($this->_candidates[$single_id]['variables'][$rid]);
        }
      }
    }

    // Re-evaluate the counts
    foreach ($this->_candidates as $single_id => $info) {
      $this->_candidates[$single_id]['count'] = count($this->_candidates[$single_id]['variables']);
    }
    return $uniques;
  }

  /**
   * Private helper function to build the gap_id
   */
  private function extend_gap_id(&$gap_id, $message, $type, $set_count) {

    // Extend the gap id with the node_type if available
    if (!empty($message->variables_array['@node_type'])) {
      $gap_id .= '_' . $message->variables_array['@node_type'];
    }

    // group by node will result in a summary of users
    // performing the activity
    if ($message->concat_args['group_by'] == 'node') {
      $gap_id .= '_' . $message->concat_args['group_target'] . '_node_' . $message->nid_target;
    }
    else {
      if ($message->concat_args['group_by'] == 'user') {
        $gap_id .= '_' . $message->concat_args['group_target'] . '_user_' . $message->uid;
      }
      else {
        if ($message->concat_args['group_by'] == 'user-user') {
          $gap_id .= '_user_relation';
          $gap_id .= '_' . $message->uid;
        }
        else {

          // Handle the fall-backs as unique messages
          $gap_id .= '_' . $message->uid . '_' . $message->uid_target;
        }
      }
    }
    return TRUE;
  }

  /**
   * Function to rebuild the messages in groups
   * all sets are handled already
   */
  private function rebuild_in_groups() {

    // Check the candidates and make the rebuild array
    foreach ($this->_candidates as $single_id => $info) {
      $this->_messages[$single_id]->message = filter_xss($this->_messages[$single_id]->message, $this->_allowed_tags);
      $this->_messages[$single_id]->message_concat = filter_xss($this->_messages[$single_id]->message_concat, $this->_allowed_tags);
      $message = $this->_messages[$single_id];

      // if a candidate exists for this message and
      // it has more than one count
      // Take care!! not message->count because this could be set with the sql as well
      // The purpose would be to fill %times% or its alias %count%
      if ($this->_candidates[$single_id]['count'] > 1) {
        $message_template = $this->_messages[$single_id]->message_concat;
        $message_template = str_replace("%times%", $message->target_count, $message_template);
        $message_template = str_replace("%count%", $message->target_count, $message_template);

        // Prepare the merged factors from the stored messages
        $merged_string = '';
        $unique = $info['variables'];
        $count = $info['count'];

        //count($info['variables']);

        /**
         * TODO Make a message property to handle max_messages_count
         **/
        if ($count > variable_get('heartbeat_activity_grouping_how_many', 6)) {
          $count = variable_get('heartbeat_activity_grouping_how_many', 6);
          $unique = array_slice($unique, 0, $count);
        }

        // Replacement of placeholder with group targets
        // If there is a match for %variable%
        // Try to replace the variable with the group_target variables
        if (preg_match_all("|\\%(.*)\\%(.*)|U", $message_template, $matches)) {

          /**
           * TODO Make a decision on how to handle the target on merge groups
           */

          // This code in comment is/was a attempt to have more grouping words
          // The difficult and almost impossible part is the fact that you always
          // have to target something to group on (for the same node or same user)

          //foreach($matches[1] as $target) {
          $placeholder = $matches[1][0];
          $target = $message->concat_args['group_target'];

          //dsm($target.$placeholder);
          $i = 1;
          foreach ($unique as $stored_variables) {

            // limit them to the value given by the group variable setting
            if (isset($stored_variables["@" . $target])) {
              if ($i == 1) {
                $merged_string .= ' ' . $stored_variables["@" . $target];
              }
              else {
                if ($i < $count && $count > 2) {
                  $merged_string .= ' ' . $message->concat_args['merge_separator'];
                  $merged_string .= ' ' . $stored_variables["@" . $target];
                }
                else {
                  if ($i == $count || $count == 2) {
                    $merged_string .= ' ' . $message->concat_args['merge_end_separator'];
                    $merged_string .= ' ' . $stored_variables["@" . $target];
                  }
                }
              }
            }
            $i++;
          }
          $message_template = str_replace("%" . $placeholder . "%", $merged_string, $message_template);

          //}
        }
        $this->_messages[$single_id]->message = $message_template;
      }
    }
  }

  /**
   * Creates an instance of heartbeat as singleton
   */
  public static function instantiate($type = 'cached') {
    static $instances;
    if (!$instances) {
      $instances = array();
    }
    if (!isset($instances[$type])) {
      $instances[$type] = new HeartbeatParser();
    }
    return $instances[$type];
  }

}

// eof

Classes

Namesort descending Description
heartbeatParser