Newer
Older
<?php
// $Id$
/**
* @file
* Allows users to send private messages to other users.
*/
/**
* Status constant for read messages.
*/
define('PRIVATEMSG_READ', 0);
/**
* Status constant for unread messages.
*/
define('PRIVATEMSG_UNREAD', 1);
Sascha Grossenbacher
committed
/**
* Show unlimited messages in a thread.
*/
define('PRIVATEMSG_UNLIMITED', 'unlimited');
* Implements hook_permission().
function privatemsg_permission() {
'administer privatemsg settings' => array(
'title' => t('Administer privatemsg'),
'description' => t('Perform maintenance tasks for privatemsg'),
),
'read privatemsg' => array(
'title' => t('Read private messages'),
'description' => t('Read private messages'),
),
'read all private messages' => array(
'title' => t('Read all private messages'),
'description' => t('Includes messages of other users'),
),
'write privatemsg' => array(
'title' => t('Write new private messages'),
'description' => t('Write new private messages'),
),
'delete privatemsg' => array(
'title' => t('Delete private messages'),
'description' => t('Delete private messages'),
),
Sascha Grossenbacher
committed
'allow disabling privatemsg' => array(
Sascha Grossenbacher
committed
'title' => t('Allow disabling private messages'),
'description' => t("Allows user to disable privatemsg so that they can't recieve or send any private messages.")
),
'reply only privatemsg' => array(
'title' => t('Reply to private messages'),
'description' => t('Allows to reply to private messages but not send new ones. Note that the write new private messages permission includes replies.')
Sascha Grossenbacher
committed
),
Sascha Grossenbacher
committed
'use tokens in privatemsg' => array(
'title' => t('Use tokens in private messages'),
'description' => t("Allows user to use available tokens when sending private messages.")
),
Sascha Grossenbacher
committed
'select text format for privatemsg' => array(
'title' => t('Select text format for private messages'),
'description' => t('Allows to choose the text format when sending private messages. Otherwise, the default is used.'),
),
/**
* Generate aray of user objects based on a string.
*
*
* @param $userstring
* A string with user id, for example 1,2,4. Returned by the list query
*
* @return
* Array with user objects.
*/
Sascha Grossenbacher
committed
function _privatemsg_generate_user_array($string, $slice = NULL) {
litwol
committed
// Convert user uid list (uid1,uid2,uid3) into an array. If $slice is not NULL
// pass that as argument to array_slice(). For example, -4 will only load the
// last four users.
Sascha Grossenbacher
committed
// This is done to avoid loading user objects that are not displayed, for
// obvious performance reasons.
Sascha Grossenbacher
committed
$users = explode(',', $string);
litwol
committed
if (!is_null($slice)) {
$users = array_slice($users, $slice);
}
Sascha Grossenbacher
committed
$participants = array();
foreach ($users as $uid) {
Sascha Grossenbacher
committed
// If it is an integer, it is a user id.
if ((int)$uid > 0 && ($account = _privatemsg_user_load($uid))) {
Sascha Grossenbacher
committed
$participants[privatemsg_recipient_key($account)] = $account;
}
Sascha Grossenbacher
committed
elseif (strpos($uid, '_') !== FALSE) {
list($type, $id) = explode('_', $uid);
$type_info = privatemsg_recipient_get_type($type);
if ($type_info && isset($type_info['load']) && is_callable($type_info['load'])) {
if ($participant = reset($type_info['load'](array($id)))) {
$participants[privatemsg_recipient_key($participant)] = $participant;
}
}
}
Sascha Grossenbacher
committed
}
return $participants;
/**
* Format an array of user objects.
*
* @param $part_array
* Array with user objects, for example the one returnd by
* _privatemsg_generate_user_array.
*
* @param $limit
* Limit the number of user objects which should be displayed.
* @param $no_text
* When TRUE, don't display the Participants/From text.
* @return
* String with formated user objects, like user1, user2.
*/
Sascha Grossenbacher
committed
function _privatemsg_format_participants($part_array, $limit = NULL, $no_text = FALSE) {
Sascha Grossenbacher
committed
global $user;
$limited = FALSE;
foreach ($part_array as $account) {
Sascha Grossenbacher
committed
// Directly address the current user.
if (isset($account->type) && in_array($account->type, array('hidden', 'user')) && $account->recipient == $user->uid) {
array_unshift($to, $no_text ? t('You') : t('you'));
continue;
}
Sascha Grossenbacher
committed
// Don't display recipients with type hidden.
if (isset($account->type) && $account->type == 'hidden') {
continue;
}
Sascha Grossenbacher
committed
if (is_int($limit) && count($to) >= $limit) {
Sascha Grossenbacher
committed
$to[] = privatemsg_recipient_format($account);
}
$limit_string = '';
if ($limited) {
$limit_string = t(' and others');
}
return implode(', ', $to) . $limit_string;
}
$last = array_pop($to);
if (count($to) == 0) { // Only one participant
return t("From !last", array('!last' => $last));
}
else { // Multipe participants..
$participants = implode(', ', $to);
Sascha Grossenbacher
committed
return t('Between !participants and !last', array('!participants' => $participants, '!last' => $last));
}
}
return '';
}
$items['messages'] = array(
'title' => 'Messages',
'title callback' => 'privatemsg_title_callback',
Sascha Grossenbacher
committed
'page callback' => 'privatemsg_list_page',
'page arguments' => array('list'),
'file' => 'privatemsg.pages.inc',
'access callback' => 'privatemsg_user_access',
'menu_name' => 'user-menu',
litwol
committed
$items['messages/list'] = array(
'title' => 'Messages',
Sascha Grossenbacher
committed
'page callback' => 'privatemsg_list_page',
'page arguments' => array('list'),
'file' => 'privatemsg.pages.inc',
'access callback' => 'privatemsg_user_access',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
'menu_name' => 'user-menu',
$items['messages/view/%privatemsg_thread'] = array(
Sascha Grossenbacher
committed
// Set the third argument to TRUE so that we can show access denied instead
// of not found.
'load arguments' => array(NULL, NULL, TRUE),
'title' => 'Read message',
'page callback' => 'privatemsg_view',
'page arguments' => array(2),
'file' => 'privatemsg.pages.inc',
'access callback' => 'privatemsg_view_access',
Sascha Grossenbacher
committed
'access arguments' => array(2),
'type' => MENU_LOCAL_TASK,
Sascha Grossenbacher
committed
'weight' => -5,
'menu_name' => 'user-menu',
Sascha Grossenbacher
committed
$items['messages/delete/%privatemsg_thread/%privatemsg_message'] = array(
'title' => 'Delete message',
'page callback' => 'drupal_get_form',
Sascha Grossenbacher
committed
'page arguments' => array('privatemsg_delete', 2, 3),
'file' => 'privatemsg.pages.inc',
'access callback' => 'privatemsg_user_access',
'access arguments' => array('delete privatemsg'),
'menu_name' => 'user-menu',
);
$items['messages/new'] = array(
'title' => 'Write new message',
'page callback' => 'drupal_get_form',
litwol
committed
'page arguments' => array('privatemsg_new', 2, 3, NULL),
'file' => 'privatemsg.pages.inc',
'access callback' => 'privatemsg_user_access',
'type' => MENU_LOCAL_ACTION,
Sascha Grossenbacher
committed
'weight' => -3,
'menu_name' => 'user-menu',
);
// Auto-completes available user names & removes duplicates.
Sascha Grossenbacher
committed
$items['messages/autocomplete'] = array(
'page callback' => 'privatemsg_autocomplete',
'file' => 'privatemsg.pages.inc',
'access callback' => 'privatemsg_user_access',
$items['admin/config/messaging'] = array(
'title' => 'Messaging',
'description' => 'Messaging systems.',
'page callback' => 'system_admin_menu_block_page',
'access arguments' => array('access administration pages'),
'file' => 'system.admin.inc',
'file path' => drupal_get_path('module', 'system'),
);
$items['admin/config/messaging/privatemsg'] = array(
Sascha Grossenbacher
committed
'title' => 'Private message settings',
'description' => 'Configure private messaging settings.',
'page callback' => 'drupal_get_form',
'page arguments' => array('privatemsg_admin_settings'),
'file' => 'privatemsg.admin.inc',
'access arguments' => array('administer privatemsg settings'),
'type' => MENU_NORMAL_ITEM,
Sascha Grossenbacher
committed
$items['admin/config/messaging/privatemsg/settings'] = array(
'title' => 'Private message settings',
'description' => 'Configure private messaging settings.',
'page callback' => 'drupal_get_form',
'page arguments' => array('privatemsg_admin_settings'),
'file' => 'privatemsg.admin.inc',
'access arguments' => array('administer privatemsg settings'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['messages/undo/action'] = array(
'title' => 'Private messages',
'description' => 'Undo last thread action',
'page callback' => 'privatemsg_undo_action',
'file' => 'privatemsg.pages.inc',
'access arguments' => array('read privatemsg'),
'type' => MENU_CALLBACK,
'menu' => 'user-menu',
$items['user/%/messages'] = array(
'title' => 'Messages',
Sascha Grossenbacher
committed
'page callback' => 'privatemsg_list_page',
'page arguments' => array('list', 1),
'file' => 'privatemsg.pages.inc',
Sascha Grossenbacher
committed
'access callback' => 'privatemsg_user_access',
'access arguments' => array('read all private messages'),
'type' => MENU_LOCAL_TASK,
);
/**
* Implements hook_menu_local_tasks_alter().
*/
function privatemsg_menu_local_tasks_alter(&$data, $router_item, $root_path) {
// Add action link to 'messages/new' on 'messages' page.
Sascha Grossenbacher
committed
$add_to_array = array('messages/list', 'messages/inbox', 'messages/sent');
foreach ($add_to_array as $add_to) {
if (strpos($root_path, $add_to) !== FALSE) {
$item = menu_get_item('messages/new');
if ($item['access']) {
$data['actions']['output'][] = array(
'#theme' => 'menu_local_action',
'#link' => $item,
);
}
break;
}
}
}
/**
* Privatemsg wrapper for user_access.
*
* Never allows anonymous user access as that doesn't makes sense.
* @param $permission
* Permission string, defaults to read privatemsg
* @return
* TRUE if user has access, FALSE if not
*/
function privatemsg_user_access($permission = 'read privatemsg', $account = NULL) {
Sascha Grossenbacher
committed
static $disabled_displayed = FALSE;
if ( $account === NULL ) {
global $user;
$account = $user;
}
if (!$account->uid) { // Disallow anonymous access, regardless of permissions
return FALSE;
}
Sascha Grossenbacher
committed
if (privatemsg_is_disabled($account) && ($permission == 'write privatemsg') ) {
Sascha Grossenbacher
committed
if (arg(0) == 'messages' && variable_get('privatemsg_display_disabled_message', TRUE) && !$disabled_displayed) {
$disabled_displayed = TRUE;
drupal_set_message(t('You have disabled Privatemsg and are not allowed to write messages. Go to your <a href="@settings_url">Account settings</a> to enable it again.', array('@settings_url' => url('user/' . $account->uid . '/edit'))), 'warning');
}
Sascha Grossenbacher
committed
return FALSE;
}
if (!user_access($permission, $account)) {
return FALSE;
}
return TRUE;
}
/**
* Check access to the view messages page.
*
* Function to restrict the access of the view messages page to just the
* messages/view/% pages and not to leave tabs artifact on other lower
* level pages such as the messages/new/%.
*
Sascha Grossenbacher
committed
* @param $thread
* A array containing all information about a specific thread, generated by
* privatemsg_thread_load().
*
Sascha Grossenbacher
committed
function privatemsg_view_access($thread) {
// Do not allow access to threads without messages.
if (empty($thread['messages'])) {
// Count all messages, if there
return FALSE;
}
if (privatemsg_user_access('read privatemsg') && arg(1) == 'view') {
return TRUE;
}
return FALSE;
}
Sascha Grossenbacher
committed
/**
* Checks the status of private messaging for provided user.
*
* @param user object to check
* @return TRUE if user has disabled private messaging, FALSE otherwise
*/
function privatemsg_is_disabled($account) {
Sascha Grossenbacher
committed
if (!$account || !isset($account->uid) || !$account->uid) {
Sascha Grossenbacher
committed
return FALSE;
}
if (!isset($account->privatemsg_disabled)) {
Sascha Grossenbacher
committed
// Make sure we have a fully loaded user object and try to load it if not.
if ((!empty($account->roles) || $account = user_load($account->uid)) && user_access('allow disabling privatemsg', $account)) {
$account->privatemsg_disabled = (bool)db_query('SELECT 1 FROM {pm_disable} WHERE uid = :uid ', array(':uid' => $account->uid))->fetchField();
}
else {
$account->privatemsg_disabled = FALSE;
}
Sascha Grossenbacher
committed
}
Sascha Grossenbacher
committed
return $account->privatemsg_disabled;
Sascha Grossenbacher
committed
}
/**
* Load a thread with all the messages and participants.
*
* This function is called by the menu system through the %privatemsg_thread
* wildcard.
*
* @param $thread_id
* Thread id, pmi.thread_id or pm.mid of the first message in that thread.
* @param $account
* User object for which the thread should be loaded, defaults to
* the current user.
Sascha Grossenbacher
committed
* @param $start
* Message offset from the start of the thread.
Sascha Grossenbacher
committed
* @param $useAccessDenied
* Set to TRUE if the function should forward to the access denied page
* instead of not found. This is used by the menu system because that does
* load arguments before access checks are made. Defaults to FALSE.
* @return
* $thread object, with keys messages, participants, title and user. messages
* contains an array of messages, participants an array of user, subject the
* subject of the thread and user the user viewing the thread.
*
* If no messages are found, or the thread_id is invalid, the function returns
* FALSE.
*/
Sascha Grossenbacher
committed
function privatemsg_thread_load($thread_id, $account = NULL, $start = NULL, $useAccessDenied = FALSE) {
$threads = &drupal_static(__FUNCTION__, array());
if ((int)$thread_id > 0) {
litwol
committed
$thread = array('thread_id' => $thread_id);
if (is_null($account)) {
global $user;
$account = clone $user;
}
if (!isset($threads[$account->uid])) {
$threads[$account->uid] = array();
}
Sascha Grossenbacher
committed
if (!array_key_exists($thread_id, $threads[$account->uid])) {
// Load the list of participants.
Sascha Grossenbacher
committed
$thread['participants'] = _privatemsg_load_thread_participants($thread_id, $account, FALSE, 'view');
$thread['read_all'] = FALSE;
Sascha Grossenbacher
committed
if (empty($thread['participants']) && privatemsg_user_access('read all private messages', $account)) {
$thread['read_all'] = TRUE;
Sascha Grossenbacher
committed
// Load all participants.
$thread['participants'] = _privatemsg_load_thread_participants($thread_id, FALSE, FALSE, 'view');
Sascha Grossenbacher
committed
// Load messages returned by the messages query with privatemsg_message_load_multiple().
$query = _privatemsg_assemble_query('messages', array($thread_id), $thread['read_all'] ? NULL : $account);
// Use subquery to bypass group by since it is not possible to alter
// existing GROUP BY statements.
$countQuery = db_select($query);
$countQuery->addExpression('COUNT(*)');
Sascha Grossenbacher
committed
$thread['message_count'] = $thread['to'] = $countQuery->execute()->fetchField();
$thread['from'] = 1;
// Check if we need to limit the messages.
$max_amount = variable_get('privatemsg_view_max_amount', 20);
Sascha Grossenbacher
committed
// If there is no start value, select based on get params.
if (is_null($start)) {
if (isset($_GET['start']) && $_GET['start'] < $thread['message_count']) {
$start = $_GET['start'];
}
Sascha Grossenbacher
committed
elseif (!variable_get('privatemsg_view_use_max_as_default', FALSE) && $max_amount == PRIVATEMSG_UNLIMITED) {
$start = PRIVATEMSG_UNLIMITED;
}
Sascha Grossenbacher
committed
else {
Sascha Grossenbacher
committed
$start = $thread['message_count'] - (variable_get('privatemsg_view_use_max_as_default', FALSE) ? variable_get('privatemsg_view_default_amount', 10) : $max_amount);
Sascha Grossenbacher
committed
}
}
if ($start != PRIVATEMSG_UNLIMITED) {
Sascha Grossenbacher
committed
if ($max_amount == PRIVATEMSG_UNLIMITED) {
Sascha Grossenbacher
committed
$last_page = 0;
$max_amount = $thread['message_count'];
}
else {
// Calculate the number of messages on the "last" page to avoid
// message overlap.
// Note - the last page lists the earliest messages, not the latest.
$paging_count = variable_get('privatemsg_view_use_max_as_default', FALSE) ? $thread['message_count'] - variable_get('privatemsg_view_default_amount', 10) : $thread['message_count'];
$last_page = $paging_count % $max_amount;
}
// Sanity check - we cannot start from a negative number.
if ($start < 0) {
$start = 0;
}
$thread['start'] = $start;
//If there are newer messages on the page, show pager link allowing to go to the newer messages.
if (($start + $max_amount + 1) < $thread['message_count']) {
$thread['to'] = $start + $max_amount;
$thread['newer_start'] = $start + $max_amount;
Sascha Grossenbacher
committed
}
Sascha Grossenbacher
committed
if ($start - $max_amount >= 0) {
$thread['older_start'] = $start - $max_amount;
Sascha Grossenbacher
committed
}
Sascha Grossenbacher
committed
elseif ($start > 0) {
$thread['older_start'] = 0;
}
// Do not show messages on the last page that would show on the page
// before. This will only work when using the visual pager.
Sascha Grossenbacher
committed
if ($start < $last_page && $max_amount != PRIVATEMSG_UNLIMITED && $max_amount < $thread['message_count']) {
Sascha Grossenbacher
committed
unset($thread['older_start']);
$thread['to'] = $thread['newer_start'] = $max_amount = $last_page;
// Start from the first message - this is a specific hack to make sure
// the message display has sane paging on the last page.
$start = 0;
Sascha Grossenbacher
committed
}
Sascha Grossenbacher
committed
// Visual counts start from 1 instead of zero, so plus one.
$thread['from'] = $start + 1;
$query->range($start, $max_amount);
Sascha Grossenbacher
committed
}
Sascha Grossenbacher
committed
$conditions = array();
if (!$thread['read_all']) {
$conditions['account'] = $account;
}
$thread['messages'] = privatemsg_message_load_multiple($query->execute()->fetchCol(), $conditions);
// If there are no messages, don't allow access to the thread.
if (empty($thread['messages'])) {
Sascha Grossenbacher
committed
if ($useAccessDenied) {
// Generate new query with read all to see if the thread does exist.
$query = _privatemsg_assemble_query('messages', array($thread_id), NULL);
$exists = $query->countQuery()->execute()->fetchField();
if (!$exists) {
// Thread does not exist, display 404.
$thread = FALSE;
}
}
else {
$thread = FALSE;
}
Sascha Grossenbacher
committed
// General data, assume subject is the same for all messages of that thread.
$thread['user'] = $account;
$message = current($thread['messages']);
Sascha Grossenbacher
committed
$thread['subject'] = $thread['subject-original'] = $message->subject;
if ($message->has_tokens) {
$thread['subject'] = privatemsg_token_replace($thread['subject'], array('privatemsg_message' => $message), array('sanitize' => TRUE, 'privatemsg-show-span' => FALSE));
}
}
$threads[$account->uid][$thread_id] = $thread;
}
return $threads[$account->uid][$thread_id];
}
return FALSE;
}
* Implementation of hook_privatemsg_view_template().
*
* Allows modules to define different message view template.
*
* This hook returns information about available themes for privatemsg viewing.
*
* array(
* 'machine_template_name' => 'Human readable template name',
* 'machine_template_name_2' => 'Human readable template name 2'
* };
*/
function privatemsg_privatemsg_view_template() {
return array(
Sascha Grossenbacher
committed
/**
* Implementation of hook_cron().
*
* If the flush feature is enabled, a given amount of deleted messages that are
* old enough are flushed.
*/
function privatemsg_cron() {
if (variable_get('privatemsg_flush_enabled', FALSE)) {
$query = _privatemsg_assemble_query('deleted', variable_get('privatemsg_flush_days', 30), variable_get('privatemsg_flush_max', 200));
foreach ($query->execute()->fetchCol() as $mid) {
$message = privatemsg_message_load($mid);
Sascha Grossenbacher
committed
module_invoke_all('privatemsg_message_flush', $message);
// Delete recipients of the message.
db_delete('pm_index')
->condition('mid', $mid)
->execute();
// Delete message itself.
db_delete('pm_message')
->condition('mid', $mid)
->execute();
}
}
Sascha Grossenbacher
committed
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
$result = db_query_range("SELECT pmi.recipient, pmi.type, pmi.mid FROM {pm_index} pmi WHERE pmi.type NOT IN ('user', 'hidden') AND pmi.is_new = 1 ORDER BY mid ASC", 0, 10);
// Number of user ids to process for this cron run.
$total_remaining = variable_get('privatemgs_cron_recipient_per_run', 1000);
$current_process = variable_get('privatemsg_cron_recipient_process', array());
foreach ($result as $row) {
$type = privatemsg_recipient_get_type($row->type);
if (!$type) {
continue;
}
if (isset($type['load']) && is_callable($type['load'])) {
$loaded = $type['load'](array($row->recipient));
if (empty($loaded)) {
continue;
}
$recipient = reset($loaded);
}
// Check if we already started to process this recipient.
$offset = 0;
if (!empty($current_process) && $current_process['mid'] == $row->mid && $current_process['recipient'] == $row->recipient && $current_process['type'] == $row->type) {
$offset = $current_process['offset'];
}
$load_function = $type['generate recipients'];
$uids = $load_function($recipient, $total_remaining, $offset);
if (!empty($uids)) {
foreach ($uids as $uid) {
privatemsg_message_change_recipient($row->mid, $uid, 'hidden');
}
}
// If less than the total remaining uids were returned, we are finished.
if (count($uids) < $total_remaining) {
$total_remaining -= count($uids);
db_update('pm_index')
->fields(array('is_new' => PRIVATEMSG_READ))
->condition('mid', $row->mid)
->condition('recipient', $row->recipient)
->condition('type', $row->type)
->execute();
// Reset current process if necessary.
if ($offset > 0) {
variable_set('privatemsg_cron_recipient_process', array());
}
}
else {
// We are not yet finished, save current process and break.
$existing_offset = isset($current_process['offset']) ? $current_process['offset'] : 0;
$current_process = (array)$row;
$current_process['offset'] = $existing_offset + count($uids);
variable_set('privatemsg_cron_recipient_process', $current_process);
break;
}
}
Sascha Grossenbacher
committed
}
$templates = array(
Sascha Grossenbacher
committed
'variables' => array('message' => NULL),
'template' => variable_get('private_message_view_template', 'privatemsg-view'), // 'privatemsg',
Sascha Grossenbacher
committed
'variables' => array('author' => NULL),
litwol
committed
'privatemsg_recipients' => array(
Sascha Grossenbacher
committed
'variables' => array('message' => NULL),
Sascha Grossenbacher
committed
'variables' => array('recipients' => NULL),
// Define pattern for header/field templates. The theme system will register all
// theme functions that start with the defined pattern.
'privatemsg_list_header' => array(
'file' => 'privatemsg.theme.inc',
'path' => drupal_get_path('module', 'privatemsg'),
'pattern' => 'privatemsg_list_header__',
'variables' => array(),
),
'privatemsg_list_field' => array(
'file' => 'privatemsg.theme.inc',
'path' => drupal_get_path('module', 'privatemsg'),
'pattern' => 'privatemsg_list_field__',
'variables' => array('thread' => array()),
'privatemsg_new_block' => array(
'file' => 'privatemsg.theme.inc',
'path' => drupal_get_path('module', 'privatemsg'),
'variables' => array('count'),
),
Sascha Grossenbacher
committed
'privatemsg_username' => array(
'file' => 'privatemsg.theme.inc',
'path' => drupal_get_path('module', 'privatemsg'),
'variables' => array('recipient' => NULL, 'options' => array()),
),
// Include the theme file to load the theme suggestions.
module_load_include('inc', 'privatemsg', 'privatemsg.theme');
$templates += drupal_find_theme_functions($templates, array('theme'));
return $templates;
function template_preprocess_privatemsg_view(&$vars) {
Sascha Grossenbacher
committed
global $user;
$message = $vars['message'];
$vars['mid'] = isset($message->mid) ? $message->mid : NULL;
$vars['thread_id'] = isset($message->thread_id) ? $message->thread_id : NULL;
$vars['author_picture'] = theme('user_picture', array('account' => $message->author));
Sascha Grossenbacher
committed
// Directly address the current user if he is the author.
if ($user->uid == $message->author->uid){
$vars['author_name_link'] = t('You');
}
else {
$vars['author_name_link'] = privatemsg_recipient_format($message->author);
}
Sascha Grossenbacher
committed
$vars['message_timestamp'] = privatemsg_format_date($message->timestamp);
Sascha Grossenbacher
committed
$message->content = array(
'#view_mode' => 'message',
'body' => array(
'#markup' => check_markup($message->body, $message->format),
'#weight' => -4,
),
);
Sascha Grossenbacher
committed
if ($message->has_tokens) {
// Replace tokens including option to add a notice if the user is not a
// recipient.
$message->content['body']['#markup'] = privatemsg_token_replace($message->content['body']['#markup'], array('privatemsg_message' => $message), array('privatemsg-token-notice' => TRUE, 'sanitize' => TRUE));
}
// Build fields content.
field_attach_prepare_view('privatemsg_message', array($vars['mid'] => $message), 'message');
Sascha Grossenbacher
committed
$message->content += field_attach_view('privatemsg_message', $message, 'message');
Sascha Grossenbacher
committed
// Render message body.
$vars['message_body'] = drupal_render($message->content);
Sascha Grossenbacher
committed
if (isset($vars['mid']) && isset($vars['thread_id']) && privatemsg_user_access('delete privatemsg')) {
Sascha Grossenbacher
committed
$vars['message_actions'][] = array('title' => t('Delete'), 'href' => 'messages/delete/' . $vars['thread_id'] . '/' . $vars['mid']);
}
$vars['message_anchors'][] = 'privatemsg-mid-' . $vars['mid'];
if (!empty($message->is_new)) {
$vars['message_anchors'][] = 'new';
$vars['new'] = drupal_ucfirst(t('new'));
litwol
committed
// call hook_privatemsg_message_view_alter
drupal_alter('privatemsg_message_view', $vars);
litwol
committed
Sascha Grossenbacher
committed
$vars['message_actions'] = !empty($vars['message_actions']) ? theme('links', array('links' => $vars['message_actions'], 'attributes' => array('class' => array('privatemsg-message-actions', 'links', 'inline')))) : '';
$vars['anchors'] = '';
foreach ($vars['message_anchors'] as $anchor) {
$vars['anchors'].= '<a name="' . $anchor . '"></a>';
}
Marco Molinari
committed
}
function template_preprocess_privatemsg_recipients(&$vars) {
$vars['participants'] = ''; // assign a default empty value
Sascha Grossenbacher
committed
if (isset($vars['thread']['participants'])) {
$vars['participants'] = _privatemsg_format_participants($vars['thread']['participants']);
Marco Molinari
committed
}
}
* Changes the read/new status of a single message.
* @param $pmid
* Message id
* @param $status
* Either PRIVATEMSG_READ or PRIVATEMSG_UNREAD
* @param $account
* User object, defaults to the current user
function privatemsg_message_change_status($pmid, $status, $account = NULL) {
if (!$account) {
global $user;
$account = $user;
db_update('pm_index')
->fields(array('is_new' => $status))
->condition('mid', $pmid)
Sascha Grossenbacher
committed
->condition('recipient', $account->uid)
->condition('type', array('hidden', 'user'))
}
/**
* Return number of unread messages for an account.
*
* @param $account
* Specifiy the user for which the unread count should be loaded.
*
* @ingroup api
*/
function privatemsg_unread_count($account = NULL) {
$counts = &drupal_static(__FUNCTION__, array());
if (!$account || $account->uid == 0) {
global $user;
if (!isset($counts[$account->uid])) {
$counts[$account->uid] = _privatemsg_assemble_query('unread_count', $account)
->execute()
->fetchField();
/**
* Load all participants of a thread.
*
* @param $thread_id
* Thread ID for wich the participants should be loaded.
Sascha Grossenbacher
committed
* @param $account
* For which account should the messages be loaded. *
* @param $ignore_hidden
* Ignores hidden participants.
* @param $access
* Which access permission should be checked (write or view).
*
* @return
* Array with all visible/writable participants for that thread.
*/
Sascha Grossenbacher
committed
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
function _privatemsg_load_thread_participants($thread_id, $account, $ignore_hidden = TRUE, $access = 'write') {
$query = _privatemsg_assemble_query('participants', $thread_id, $account);
$participants = array();
$to_load = array();
foreach ($query->execute() as $participant) {
if ($ignore_hidden && $participant->type == 'hidden') {
continue;
}
if ($participant->type == 'user' || $participant->type == 'hidden') {
if ($participant = _privatemsg_user_load($participant->recipient)) {
$participants[privatemsg_recipient_key($participant)] = $participant;
}
}
elseif (privatemsg_recipient_access($participant->type, $access, $participant)) {
$to_load[$participant->type][] = $participant->recipient;
}
}
// Now, load all non-user recipients.
foreach ($to_load as $type => $ids) {
$type_info = privatemsg_recipient_get_type($type);
if (isset($type_info['load']) && is_callable($type_info['load'])) {
$loaded = $type_info['load']($ids);
if (is_array($loaded)) {
$participants += $loaded;
}
}
}
if ($access == 'write' && $account) {
// Remove author if loading participants for writing and when he is not the
// only recipient.
if (isset($participants['user_' . $account->uid]) && count($participants) > 1) {
unset($participants['user_' . $account->uid]);
}
}
return $participants;
}
/**
* Extract the valid usernames of a string and loads them.
*
* This function is used to parse a string supplied by a username autocomplete
* field and load all user objects.
*
* @param $string
* A string in the form "usernameA, usernameB, ...".
Sascha Grossenbacher
committed
* @return $type
* Array of recipient types this should be limited to.
*
* @return
* Array, first element is an array of loaded user objects, second an array
* with invalid names.
*
*/
Sascha Grossenbacher
committed
function _privatemsg_parse_userstring($input, $types_limitations = array()) {
if (is_string($input)) {
$input = explode(',', $input);
}
// Start working through the input array.
$recipients = array();
Sascha Grossenbacher
committed
$duplicates = array();
$denieds = array();
foreach ($input as $string) {
$string = trim($string);
Sascha Grossenbacher
committed
// Ignore spaces.
if (!empty($string)) {
// First, collect all matches.
$matches = array();
// Remember if a possible match denies access.
$access_denied = FALSE;
Sascha Grossenbacher
committed
// Load recipient types.
$types = privatemsg_recipient_get_types();
Sascha Grossenbacher
committed
// Collect matches from hook implementations.
foreach (module_implements('privatemsg_name_lookup') as $module) {
$function = $module . '_privatemsg_name_lookup';
Sascha Grossenbacher
committed
$return = $function($string);
if (isset($return) && is_array($return)) {
Sascha Grossenbacher
committed
foreach ($return as $recipient) {
// Save recipients under their key to merge recipients which were
// loaded multiple times.
if (empty($recipient->type)) {
$recipient->type = 'user';
$recipient->recipient = $recipient->uid;
}
$matches[privatemsg_recipient_key($recipient)] = $recipient;
}
Sascha Grossenbacher
committed
Sascha Grossenbacher
committed
foreach ($matches as $key => $recipient) {
Sascha Grossenbacher
committed
// Check permissions, remove any recipients the user doesn't have write
// access for.
Sascha Grossenbacher
committed
if (!privatemsg_recipient_access($recipient->type, 'write', $recipient)) {
unset($matches[$key]);
$access_denied = TRUE;
Sascha Grossenbacher
committed
// Appliy limitations.
if (!empty($types_limitations) && !in_array($recipient->type, $types_limitations)) {
unset($matches[$key]);
}
Sascha Grossenbacher
committed
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
// Allow modules to alter the found matches.
drupal_alter('privatemsg_name_lookup_matches', $matches, $string);
// Check if there are any matches.
$number_of_matches = count($matches);
switch ($number_of_matches) {
case 1:
// Only a single match found, add to recipients.
$recipients += $matches;
break;
case 0:
// No match found, check if access was denied.
if ($access_denied) {
// There were possible matches, but access was denied.
$denieds[$string] = $string;
}
else {
// The string does not contain any valid recipients.
$invalid[$string] = $string;
}
break;
default:
// Multiple matches were found. The user has to specify which one he
// meant.
$duplicates[$string] = $matches;
break;
}
Sascha Grossenbacher
committed
// Todo: Provide better API.
return array($recipients, $invalid, $duplicates, $denieds);
}
Sascha Grossenbacher
committed
/**
* Implements hook_privatemsg_name_lookup().
*/
function privatemsg_privatemsg_name_lookup($string) {
// Remove optonal user specifier.
$string = trim(str_replace(t('[user]'), '', $string));
// Fall back to the default username lookup.
if (!$error = module_invoke('user', 'validate_name', $string)) {
// String is a valid username, look it up.
if ($recipient = user_load_by_name($string)) {
$recipient->recipient = $recipient->uid;
$recipient->type = 'user';
return array(privatemsg_recipient_key($recipient) => $recipient);
}
}
/**
* @addtogroup sql
* @{
*/
Sascha Grossenbacher
committed
/**
* Query definition to load a list of threads.
*
* @param $account
* User object for which the messages are being loaded.
* @param $argument
* string argument which can be used in the query builder to modify the thread listing.
*/
function privatemsg_sql_list($account, $argument = 'list') {
$query = db_select('pm_message', 'pm')->extend('TableSort')->extend('PagerDefault');
$query->join('pm_index', 'pmi', 'pm.mid = pmi.mid');
// Create count query;
$count_query = db_select('pm_message', 'pm');
$count_query->addExpression('COUNT(DISTINCT pmi.thread_id)', 'count');
$count_query->join('pm_index', 'pmi', 'pm.mid = pmi.mid');
$count_query
Sascha Grossenbacher
committed
->condition('pmi.recipient', $account->uid)
->condition('pmi.type', array('hidden', 'user'))
->condition('pmi.deleted', 0);
$query->setCountQuery($count_query);