You are here

class HeartbeatActivity in Heartbeat 7

Same name and namespace in other branches
  1. 6.4 includes/heartbeatactivity.inc \HeartbeatActivity
  2. 6.3 includes/heartbeatactivity.inc \HeartbeatActivity

Class defines an activity message object

Hierarchy

Expanded class hierarchy of HeartbeatActivity

File

includes/heartbeatactivity.inc, line 12
HeartbeatActivity object Defines one heartbeat activity object.

View source
class HeartbeatActivity extends stdClass {

  // DB fields
  public $uaid = 0;
  public $message_id = '';
  public $uid = 0;
  public $uid_access = TRUE;
  public $uid_target = 0;
  public $nid = 0;
  public $cid = 0;
  public $nid_access = TRUE;
  public $nid_target = 0;
  public $nid_target_access = TRUE;
  public $in_group = 0;
  public $timestamp = 0;
  public $language = 'en';
  public $message = '';
  public $message_concat = '';
  public $access = HEARTBEAT_PUBLIC_TO_ALL;
  public $variables = array();
  public $attachments = array();
  public $additions = NULL;
  public $index = NULL;
  public $buttons = array();
  public $classes = '';

  // User object of the uid, being the actor of activity
  public $actor = NULL;

  // Template composite
  public $template = '';

  // Global properties on message base
  public $show_time = 0;
  public $count = 0;

  // count for same instance
  public $target_count = 0;

  // count for same instance that is merged by target
  public $show_message_times = 1;
  public $show_message_times_grouped = 1;
  public $delete_access = FALSE;
  public $actor_access = FALSE;

  // Template variables
  public $name = 'heartbeatactivity';
  public $type = 'heartbeatactivity';
  public $streamname = '';
  public $view_mode = 'full';
  public $content = array();

  //public $_vars = NULL;

  /**
   * constructor
   * @param $data Array or Object with fields that can match this object
   *   Data for the message, some are converted.
   *   It's possible to give a sql-result set as data which will
   *   adapt to a heartbeatActivity object.
   * @param $template HeartbeatMessageTemplate
   *   holds the template data.
   */
  function __construct($data, HeartbeatMessageTemplate $template) {

    //$this->_vars = new stdClass();
    $this->template = $template;
    $this->additions = new stdClass();
    $this->message_id = $this->template->message_id;
    if (is_object($data)) {
      $data = (array) $data;
    }
    if (isset($data['uaid'])) {
      $this->uaid = $data['uaid'];
    }
    if (isset($data['count'])) {
      $this->count = $data['count'];
    }
    $this->timestamp = isset($data['timestamp']) ? $data['timestamp'] : $_SERVER['REQUEST_TIME'];
    $this
      ->set_actor($data);
    if (isset($data['uid_target'])) {
      $this->uid_target = $data['uid_target'];
    }
    if (isset($data['nid'])) {
      $this->nid = $data['nid'];
    }
    if (isset($data['nid_target'])) {
      $this->nid_target = $data['nid_target'];
    }
    if (isset($data['cid'])) {
      $this->cid = $data['cid'];
    }
    if (isset($data['in_group'])) {
      $this->in_group = $data['in_group'];
    }
    if (isset($data['variables'])) {
      $this
        ->set_variables($data['variables']);
    }
    if (isset($data['extra']['duplicate'])) {
      $this->variables['duplicate'] = $data['extra']['duplicate'];
    }

    // Check access on the buttons and put their state onto the object
    $this
      ->set_delete_access();

    // Add buttons. Other modules can still add or remove buttons
    // using the available entity hooks.
    $this
      ->add_buttons();

    // Set the access for this message.
    $this
      ->setAccess($data);

    // Build the runtime activity message.
    $this
      ->rebuild_message();
    foreach ($data as $key => $value) {
      if (strpos($key, 'field_') === 0) {
        $this->{$key} = $value;
      }
    }
  }

  /**
   * Set the actor on the activity.
   */
  public function set_actor($data) {
    if (isset($data['actor'])) {
      $this->actor = $data['actor'];
    }
    elseif (!empty($data['name']) && !empty($data['created'])) {
      $this->actor = new stdClass();
      $this->actor->uid = $data['uid'];
      $this->actor->name = $data['name'];
      $this->actor->mail = $data['mail'];
      $this->actor->theme = $data['theme'];
      $this->actor->signature = $data['signature'];
      $this->actor->signature_format = $data['signature_format'];
      $this->actor->created = $data['created'];
      $this->actor->access = $data['users_access'];
      $this->actor->login = $data['login'];
      $this->actor->status = $data['status'];
      $this->actor->timezone = $data['timezone'];
      $this->actor->language = $data['language'];
      $this->actor->picture = $data['picture'];
      $this->actor->init = $data['init'];
      $this->actor->data = unserialize($data['data']);
    }
    else {
      $this->actor = user_load(isset($data['uid']) ? $data['uid'] : $GLOBALS['user']->uid);
    }
    $this->uid = $this->actor->uid;
  }

  /**
   * setAccess().
   */
  public function setAccess($raw_data) {

    // By default, we always use the logged "access".
    $this->access = HEARTBEAT_PUBLIC_TO_ALL;

    // In most cases (hopefully), the property at the  time the activity
    // occurred will be set and gets priority. This value is calculated
    // from template access, user profile privacy or custom access.
    if (isset($raw_data['access'])) {
      $this->access = (int) $raw_data['access'];
    }

    // If the actor of the message has set this type of message, inherit.
    if (isset($raw_data['access_status']) && $raw_data['access_status'] <= $this->access) {
      $this->access = (int) $raw_data['access_status'];
    }

    // If for some reason what so ever, the template restriction had been changed
    // into some lower permission than calculated, lower down the access.
    if ((int) $this->template->perms < $this->access) {
      $this->access = (int) $this->template->perms;
    }
  }

  /**
   * isPrivate().
   */
  public function isPrivate() {
    return $this->access === HEARTBEAT_PRIVATE;
  }

  /**
   * hasAccess().
   *
   * @param Object $account
   *   The User object to check access for.
   * @param HeartbeatStream $heartbeatStream
   *   Optional heartbeat stream object where the account object is the viewer.
   *
   */
  public function hasAccess($account, $heartbeatStream = NULL) {

    // Case user1 is not related to userA and userB. UserA and UserB are related.
    // user1 views site activity.
    if (isset($heartbeatStream)) {
      $account = $heartbeatStream
        ->getViewer();
    }
    else {
      $heartbeatStream = heartbeat_stream('singleactivity');
    }
    $access_activity_profiles = user_access('access heartbeat activity profiles', $account);
    $access_profiles = user_access('access user profiles', $account);

    // Remove messages set private by site administrator
    // and remove messages set private by user profile setting.
    if ($this
      ->isPrivate() && $account->uid != $this->actor->uid) {
      $heartbeatStream
        ->addError('Activity message #' . $this->uaid . ' is filtered from display because it is a private message.');
      return FALSE;
    }

    // Replace user links with their name if no access.
    if ($access_activity_profiles) {
      if (!$access_profiles) {

        // TODO Think of a superb way to divide message templates into parts, bound to properties.
        // For now, let's try to keep the problem to a minimum.
        // Do some tricks to try and solve the problem of profile access from generated links.
        if (isset($this->variables['!username'])) {
          $this->message = str_replace($this->variables['!username'], $this->actor->name, $this->message);
        }
        elseif (isset($this->variables['!user'])) {
          $this->message = str_replace($this->variables['!user'], $this->actor->name, $this->message);
        }
        else {
          $link = l($this->actor->name, 'user/' . $this->actor->uid);
          $this->message = preg_replace("|{$link}|", $this->actor->name, $this->message);
        }
      }
    }
    return TRUE;
  }

  /*
   * Rebuild the message with given candidate variables
   */
  public function rebuild_message($concat = FALSE) {
    $template_message = $concat ? $this->template->message_concat : $this->template->message;

    //$this->message = strtr($template_message, $this->variables);
    $this->message = t($template_message, $this->variables);
    return $this->message;
  }

  /*
   * Rebuild the message with given candidate variables
   */
  public function create_grouped_message($candidates, $max_items_to_group = 5) {
    global $base_url;
    $message_template = $this
      ->rebuild_message(TRUE);
    $message_extension = '';
    $message_template = str_replace("%times%", $this->target_count, $message_template);
    $message_template = str_replace("%count%", $this->target_count, $message_template);

    // Prepare the merged factors from the stored messages
    $merged_string = '';
    $remains = array();
    $target = $this->template->concat_args['group_target'];
    $beat = 'beat-item-' . $this->uaid;
    $unique = $candidates['variables'];
    $count = $candidates['count'];

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

    // Limit the number of displayed (grouped) messages by message
    // or left as default global configuration
    if (!empty($this->template->concat_args['group_num_max'])) {
      $max_items_to_group = $this->template->concat_args['group_num_max'];
    }

    // Reduce the variables for substitution to the maximum
    // Add the remains by keeping the others plus the last one,
    // in case the total exceeds the max group setting.
    if ($count > $max_items_to_group) {
      $count = $max_items_to_group;
      $unique = array_slice($unique, 0, $count);
      $remains = array_slice($candidates['variables'], $count - 1);
    }

    // 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)) {
      if (count($remains) > 0) {
        $hidden_remains = '<ul id="' . $beat . '_remaining_wrapper" style="display: none;">';
        foreach ($remains as $remain) {
          $key = isset($remain["@" . $target]) ? $remain["@" . $target] : (isset($remain["!" . $target]) ? $remain["!" . $target] : $remain["%" . $target]);
          $hidden_remains .= '<li>' . $key . '</li>';
        }
        $hidden_remains .= '</ul>';
      }
      $placeholder = $matches[1][0];
      $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];
          }
          elseif ($i < $count && $count > 2) {
            $merged_string .= $this->template->concat_args['merge_separator'];
            $merged_string .= $stored_variables["!" . $target];
          }
          elseif ($i == $count || $count == 2) {
            $merged_string .= ' ' . $this->template->concat_args['merge_end_separator'];
            if (count($remains) >= 1 && $hidden_remains != '') {
              $attributes = array(
                'attributes' => array(
                  'id' => $beat . '_remaining',
                  'class' => array(
                    'beat-remaining',
                  ),
                ),
                'absolute' => TRUE,
                'fragment' => $beat,
              );
              $merged_string .= l(t('@num more', array(
                '@num' => count($remains),
              )), $base_url . request_uri(), $attributes);
              $message_extension .= $hidden_remains;
            }
            else {
              $merged_string .= $stored_variables["!" . $target];
            }
          }
        }
        $i++;
      }
      $message_template = str_replace("%" . $placeholder . "%", $merged_string, $message_template);
      if (isset($message_extension)) {
        $message_template .= $message_extension;
      }
    }
    $this->message = $message_template;
  }

  /**
   * Public function to save activity to database
   * @param array raw argument to enforce as is (pre-renderd)
   */
  public function save($raw_args = array()) {
    if (!empty($this->nid)) {

      // if a user selected a language, then we follow i19n
      $node = node_load($this->nid);
      if (!empty($node->language)) {
        return $this
          ->_save($raw_args, $node->language);
      }
    }
    return $this
      ->_save($raw_args);
  }

  /**
   * Sets the variables array
   * Data variables are stored in an array to use them to build real variables.
   * this means that when activity message objects get merged, the variables
   * will be filled with variables components from all instances.
   */
  public function set_variables($variables = NULL) {
    if (!empty($variables)) {
      if (is_string($variables)) {
        $this->variables = heartbeat_decode_message_variables($variables);
      }
      elseif (is_array($variables)) {
        $this->variables = $variables;
      }
    }
  }

  /**
   * Add buttons to the activity message.
   */
  protected function add_buttons() {

    // Internal button.
    if ($this->delete_access || $this->actor_access) {
      heartbeat_ctools_modal_prepare();
      drupal_add_library('system', 'drupal.ajax');
      $link = l(t('Delete'), 'heartbeat/nojs/activity/delete/' . $this->uaid, array(
        'query' => array(
          'destination' => $_GET['q'],
        ),
        'html' => TRUE,
        'attributes' => array(
          'class' => array(
            'use-ajax',
            'ctools-modal-ctools-heartbeat-style',
          ),
          'title' => t('Delete'),
        ),
      ));
      $button = '<span class="hover-delete">' . $link . '</span>';

      //$button = '<span class="hover-delete">' . ctools_modal_text_button(t('Delete'), 'heartbeat/nojs/activity/delete/' . $this->uaid, t('Delete'),  'ctools-modal-ctools-heartbeat-style') . '</span>';
      $this
        ->add_button($button);
    }
  }

  /**
   * Add button to the activity message.
   */
  public function add_button($button) {
    $this->buttons[] = $button;
  }

  /**
   * add_attachment().
   */
  public function add_attachment($attachment) {
    $this->attachments[] = $attachment;
  }

  /**
   * add_plugin().
   */
  public function add_plugin($pluginId, iHeartbeatPlugin $plugin) {
    if (!isset($this->additions->plugins)) {
      $this->additions->plugins = array();
    }
    $this->additions->plugins[$pluginId] = $plugin;
  }

  /**
   * remove_plugin().
   */
  public function remove_plugin($pluginId) {
    unset($this->additions->plugins[$pluginId]);
  }

  /**
   * Save activity log
   *
   * @param array $raw_args
   *   All variables as key => value.
   * @param Object $lang
   *   Optional lang_code.
   */
  private function _save($raw_args = array(), $lang = '') {

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

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

    // Checks if there should be logging what so ever.
    if (empty($this->message)) {
      watchdog('heartbeat', 'Error in logging user activity: it is not possible to log empty message', array(), WATCHDOG_ERROR);
      return FALSE;
    }

    // Rewrite access if the user has configured the access state of this message type.
    $heartbeat_user_templates = heartbeat_user_templates_load($this->uid);
    if (!empty($heartbeat_user_templates) && isset($heartbeat_user_templates[$this->message_id])) {
      $this->access = $heartbeat_user_templates[$this->message_id]->status;
    }

    // Prepare extra variables (hardcoded for node for now)
    // TODO think of a cleaner sollution.
    if ($this->nid > 0 && ($node = node_load($this->nid))) {
      $this->variables['node_type'] = $node->type;
      $this->variables['node_status'] = $node->status;
      $this->variables['node_uid'] = $node->uid;
    }
    if ($this->nid_target > 0 && ($node = node_load($this->nid_target))) {
      $this->variables['node_target_type'] = $node->type;
      $this->variables['node_target_status'] = $node->status;
      $this->variables['node_target_uid'] = $node->uid;
    }

    // Log relational message to user activity
    $last_id = db_insert('heartbeat_activity')
      ->fields(array(
      'message_id',
      'nid',
      'uid',
      'nid_target',
      'uid_target',
      'cid',
      'access',
      'timestamp',
      'language',
      'variables',
      'in_group',
    ))
      ->values(array(
      'message_id' => $this->message_id,
      'nid' => $this->nid,
      'uid' => $this->uid,
      'nid_target' => $this->nid_target,
      'uid_target' => $this->uid_target,
      'cid' => $this->cid,
      'access' => $this->access,
      'timestamp' => $this->timestamp,
      'language' => $lang,
      'in_group' => $this->in_group,
      'variables' => heartbeat_encode_message_variables($this->variables),
    ))
      ->execute();

    // Add the uaid.
    $this->uaid = $last_id;

    // Allow modules to respond to the save of a heartbeat activity message.
    module_invoke_all('heartbeat_activity_save', $this);
    return $last_id ? $last_id : 0;
  }

  /**
   * protected function to set the delete access for the message.
   */
  protected function set_delete_access() {
    $this->delete_access = user_access('admin heartbeat delete all');
    $this->actor_access = $this->delete_access || $this->uid == $GLOBALS['user']->uid && user_access('admin heartbeat delete own');
  }

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

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

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

        // bad argument
      }
      $oldkey = $key;

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

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

}

Members

Namesort descending Modifiers Type Description Overrides
HeartbeatActivity::$access public property
HeartbeatActivity::$actor public property
HeartbeatActivity::$actor_access public property
HeartbeatActivity::$additions public property
HeartbeatActivity::$attachments public property
HeartbeatActivity::$buttons public property
HeartbeatActivity::$cid public property
HeartbeatActivity::$classes public property
HeartbeatActivity::$content public property
HeartbeatActivity::$count public property
HeartbeatActivity::$delete_access public property
HeartbeatActivity::$index public property
HeartbeatActivity::$in_group public property
HeartbeatActivity::$language public property
HeartbeatActivity::$message public property
HeartbeatActivity::$message_concat public property
HeartbeatActivity::$message_id public property
HeartbeatActivity::$name public property
HeartbeatActivity::$nid public property
HeartbeatActivity::$nid_access public property
HeartbeatActivity::$nid_target public property
HeartbeatActivity::$nid_target_access public property
HeartbeatActivity::$show_message_times public property
HeartbeatActivity::$show_message_times_grouped public property
HeartbeatActivity::$show_time public property
HeartbeatActivity::$streamname public property
HeartbeatActivity::$target_count public property
HeartbeatActivity::$template public property
HeartbeatActivity::$timestamp public property
HeartbeatActivity::$type public property
HeartbeatActivity::$uaid public property
HeartbeatActivity::$uid public property
HeartbeatActivity::$uid_access public property
HeartbeatActivity::$uid_target public property
HeartbeatActivity::$variables public property
HeartbeatActivity::$view_mode public property
HeartbeatActivity::add_attachment public function add_attachment().
HeartbeatActivity::add_button public function Add button to the activity message.
HeartbeatActivity::add_buttons protected function Add buttons to the activity message.
HeartbeatActivity::add_plugin public function add_plugin().
HeartbeatActivity::create_grouped_message public function
HeartbeatActivity::hasAccess public function hasAccess().
HeartbeatActivity::isPrivate public function isPrivate().
HeartbeatActivity::log_message private function Logs a heartbeat message
HeartbeatActivity::rebuild_arguments private function Rebuild the arguments for variables to share within this object
HeartbeatActivity::rebuild_message public function
HeartbeatActivity::remove_plugin public function remove_plugin().
HeartbeatActivity::save public function Public function to save activity to database
HeartbeatActivity::setAccess public function setAccess().
HeartbeatActivity::set_actor public function Set the actor on the activity.
HeartbeatActivity::set_delete_access protected function protected function to set the delete access for the message.
HeartbeatActivity::set_variables public function Sets the variables array Data variables are stored in an array to use them to build real variables. this means that when activity message objects get merged, the variables will be filled with variables components from all instances.
HeartbeatActivity::_save private function Save activity log
HeartbeatActivity::__construct function constructor