uid); // Allow other modules to change the message type used for this event. $hook = 'node_insert'; $message_type = 'commons_activity_streams_node_created'; drupal_alter('commons_activity_streams_message_selection', $message_type, $hook, $node); $message = message_create($message_type, array('uid' => $account->uid, 'timestamp' => $node->created)); // Save reference to the node in the node reference field, and the $wrapper = entity_metadata_wrapper('message', $message); // We use a multiple value field in case we wish to use the same // field for grouping messages in the future // (eg http://drupal.org/node/1757060). $wrapper->field_target_nodes[] = $node; $wrapper->save(); } /** * Implements hook_comment_insert(). */ function commons_activity_streams_comment_insert($comment) { $account = user_load($comment->uid); $node = node_load($comment->nid); // Allow other modules to change the message type used for this event. $hook = 'comment_insert'; $message_type = 'commons_activity_streams_comment_created'; drupal_alter('commons_activity_streams_message_selection', $message_type, $hook, $node); $message = message_create($message_type, array('uid' => $account->uid, 'timestamp' => $comment->created)); // Save reference to the node in the node reference field, and the // "publish" state (i.e. if the node is published or unpublished). $wrapper = entity_metadata_wrapper('message', $message); $wrapper->field_target_nodes[] = $node; $wrapper->field_target_comments[] = $comment; // The message should be published only if the node and the comment are // both published. // @todo: Deal with message publishing/unpublishing. /* $published = $node->status && $comment->status; $wrapper->field_published->set($published); */ $wrapper->save(); } /** * Implements hook_comment_delete(). */ function commons_activity_streams_comment_delete($comment) { // Delete the activity stream message created when this comment // was posted. if ($mids = commons_activity_streams_existing_messages($comment->uid, array($comment->cid), 'field_target_comments', 'commons_activity_streams_comment_created')) { message_delete_multiple($mids); } } /** * Implements hook_message_access_alter(). */ function commons_activity_streams_message_access_alter(&$access, $context) { // We're only interested in the 'view' operation. if ($context['op'] != 'view') { return; } $message = $context['entity']; // Verify view access to nodes referenced in the message. if (isset($message->field_target_nodes)) { foreach ($message->field_target_nodes[LANGUAGE_NONE] as $key => $value) { $node = node_load($value['target_id']); if (!node_access('view', $node, $context['account'])) { // If the user cannot view any nodes in the message, // deny access to the entire message; $access = FALSE; return; } } } // Verify view access to comments referenced in the message. if (isset($message->field_target_comments)) { foreach ($message->field_target_comments[LANGUAGE_NONE] as $key => $value) { $comment = comment_load($value['target_id']); if (!entity_access('view', 'comment', $comment, $context['account'])) { // If the user cannot view any comments in the message, // deny access to the entire message; $access = FALSE; return; } } } } /** * Find existing messages that match certain parameters. */ function commons_activity_streams_existing_messages($acting_uid, $target_ids, $target_field, $message_type) { $query = new EntityFieldQuery(); $query->entityCondition('entity_type', 'message', '=') ->propertyCondition('uid', $acting_uid) ->propertyCondition('type', $message_type, '=') ->fieldCondition($target_field, 'target_id', $target_ids, 'IN') ->execute(); if (!empty($query->ordered_results)) { $mids = array(); foreach($query->ordered_results as $result) { $mids[] = $result->entity_id; } return $mids; } return FALSE; } /** * Create an activity stream message when a user updates her profile. */ function commons_activity_streams_user_profile_submit($form, &$form_state) { global $user; // Fields to ignore in $form_state['values'] when detecting changes. $remove_keys = array( 'uid', 'name', 'pass', 'current_pass_required_values', 'current_pass', 'status', 'roles', 'notify', 'signature', 'picture_delete', 'message_subscribe_email', 'og_user_node', 'cancel', 'metatags', 'timezone', 'signature_format', 'form_token', 'form_id', 'form_build_id', 'picture_upload', 'submit' ); $profile_values = array_diff_key($form_state['values'], array_flip($remove_keys)); ksort($profile_values); $profile_data = serialize($profile_values); $stored_profile_values = array_diff_key($form_state['storage']['values'], array_flip($remove_keys)); ksort($stored_profile_values); $stored_profile_data = serialize($stored_profile_values); // Do not generate a message if // - the user did not submit their own form // - no changes were detected // - a profile update message created within the last 15 minutes $time_ago = time() - 900; $query = new EntityFieldQuery(); $query->entityCondition('entity_type', 'message') ->propertyCondition('uid', $form_state['user']->uid) ->propertyCondition('type', 'commons_activity_streams_user_profile_updated') ->propertyCondition('timestamp', $time_ago, '>') ->count(); $count = $query->execute(); if ($user->uid != $form_state['user']->uid || $profile_data == $stored_profile_data || $count > 0) { return; } $account = $form_state['user']; // Allow other modules to change the message type used for this event. $hook = 'user_profile_update'; $message_type = 'commons_activity_streams_user_profile_updated'; drupal_alter('commons_activity_streams_message_selection', $message_type, $hook, $account); $message = message_create($message_type, array('uid' => $account->uid, 'timestamp' => REQUEST_TIME)); // Save reference to the node in the node reference field, and the $wrapper = entity_metadata_wrapper('message', $message); $wrapper->field_target_users[] = $account; $wrapper->save(); } /** * Implements hook_token_info(). */ function commons_activity_streams_token_info() { $styles = image_styles(); $tokens = array(); foreach ($styles as $style_name => $style) { $tokens['picture:' . $style_name] = array( 'name' => t('Picture: @st image style', array('@st' => $style_name)), 'description' => t('Picture: @st image style', array('@st' => $style_name)), ); } return array( 'tokens' => array( 'user' => $tokens, ), ); } /** * Implements hook_tokens(). */ function commons_activity_streams_tokens($type, $tokens, array $data = array(), array $options = array()) { $replacements = array(); if ($type == 'user' && isset($data['user'])) { $user = $data['user']; $user_wrapper = entity_metadata_wrapper('user', $user); $alt = $user->name; if (isset($user->field_name_first)) { $alt = $user_wrapper->field_name_first->value(); if (isset($user->field_name_last)) { $alt .= " " . $user_wrapper->field_name_last->value(); } } $alt = t("@name's picture", array('@name' => $alt)); foreach ($tokens as $token => $original) { if (strpos($token, 'picture:') !== FALSE) { list( , $style) = explode(':', $token); $path = variable_get('user_picture_default', '/profiles/commons/images/avatars/user-avatar.png'); if (!empty($user->picture)) { $path = image_style_url($style, $user->picture->uri); } $image_variables = array( 'path' => $path, 'alt' => $alt, 'title' => $alt, 'class' => array('image-style-none'), ); $image = theme('image', $image_variables); $user_path = user_uri($user); $link_options = array( 'attributes' => array( 'title' => t('View user profile.'), ), 'html' => TRUE, ); $replacements[$original] = "
" . l($image, $user_path['path'], $link_options) . "
"; } } } return $replacements; } /** * Implements hook_views_post_execute(). * * Emulate message_access because we don't want the row to appear at all if the * user does not have access to the node or comment. Node access is included in * the view query itself. * * Without this function, the user would see a missing rendered entity, but the * timestamp would still show. */ function commons_activity_streams_views_post_execute(&$view) { if ($view->name == 'commons_activity_streams_activity' && isset($view->result)) { foreach ($view->result AS $key => $msg) { if (isset($msg->mid)) { // We preempt the message_access on render by doing it now. Doesn't make // Two calls because we remove the result if access is false. $message = message_load($msg->mid); if (isset($message->field_target_comments)) { foreach ($message->field_target_comments[LANGUAGE_NONE] as $key => $value) { $comment = comment_load($value['target_id']); if (!entity_access('view', 'comment', $comment)) { // If the user cannot view any nodes or comments in the message, // deny access to the entire message; unset($view->result[$key]); } } } } } } }