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'),
),
/**
* 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.
*/
litwol
committed
function _privatemsg_generate_user_array($userstring, $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.
litwol
committed
$users = explode(',', $userstring);
if (!is_null($slice)) {
$users = array_slice($users, $slice);
}
return user_load_multiple($users);
/**
* 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) {
$limited = FALSE;
foreach ($part_array as $account) {
Sascha Grossenbacher
committed
if (is_int($limit) && count($to) >= $limit) {
Sascha Grossenbacher
committed
$to[] = theme('username', array('account' => $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);
return t('Participants: !participants and !last', array('!participants' => $participants, '!last' => $last));
}
}
return '';
}
$items['messages'] = array(
'title' => 'Messages',
'title callback' => 'privatemsg_title_callback',
'page callback' => 'drupal_get_form',
litwol
committed
'page arguments' => array('privatemsg_list', 'list'),
'file' => 'privatemsg.pages.inc',
'access callback' => 'privatemsg_user_access',
'menu_name' => 'user-menu',
litwol
committed
$items['messages/list'] = array(
'title' => 'Messages',
'page callback' => 'drupal_get_form',
litwol
committed
'page arguments' => array('privatemsg_list', '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.
$items['messages/user-name-autocomplete'] = array(
'page callback' => 'privatemsg_user_name_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(
'title' => 'Private messages',
'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,
$items['admin/config/messaging/privatemsg/default'] = array(
'title' => 'Private messages',
'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',
'page callback' => 'drupal_get_form',
'page arguments' => array('privatemsg_list', '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,
);
/**
* 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) {
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') ) {
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) {
if (!$account || !$account->uid) {
return FALSE;
}
if (!isset($account->privatemsg_disabled)) {
$account->privatemsg_disabled = (bool)db_query('SELECT 1 FROM {pm_disable} WHERE uid = :uid ', array(':uid' => $account->uid))->fetchField();
}
return ($account->privatemsg_disabled);
}
/**
* 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();
}
if (!array_key_exists($thread_id, $threads[$account->uid])) {
// Load the list of participants.
$thread['participants'] = _privatemsg_assemble_query('participants', $thread_id)->execute()->fetchAllAssoc('uid');
$thread['read_all'] = FALSE;
if (!array_key_exists($account->uid, $thread['participants']) && privatemsg_user_access('read all private messages', $account)) {
$thread['read_all'] = TRUE;
}
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
}
$thread['messages'] = privatemsg_message_load_multiple($query->execute()->fetchCol(), $thread['read_all'] ? NULL : $account);
// 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']);
$thread['subject'] = $message['subject'];
}
$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();
}
}
}
function privatemsg_theme() {
return array(
'privatemsg_view' => 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__',
'arguments' => array(),
),
'privatemsg_list_field' => array(
'file' => 'privatemsg.theme.inc',
'path' => drupal_get_path('module', 'privatemsg'),
'pattern' => 'privatemsg_list_field__',
'arguments' => array('thread'),
'privatemsg_new_block' => array(
'file' => 'privatemsg.theme.inc',
'path' => drupal_get_path('module', 'privatemsg'),
'arguments' => array('count'),
),
function privatemsg_preprocess_privatemsg_view(&$vars) {
// drupal_set_message('<pre>'. print_r($vars,1 ) . '</pre>');
$message = $vars['message'];
Sascha Grossenbacher
committed
$vars['mid'] = isset($message['mid']) ? $message['mid'] : NULL;
$vars['thread_id'] = isset($message['thread_id']) ? $message['thread_id'] : NULL;
Sascha Grossenbacher
committed
$vars['author_picture'] = theme('user_picture', array('account' => $message['author']));
$vars['author_name_link'] = theme('username', array('account' => $message['author']));
/**
* @todo perhaps make this timestamp configurable via admin UI?
*/
$vars['message_timestamp'] = format_date($message['timestamp'], 'small');
Sascha Grossenbacher
committed
$vars['message_body'] = check_markup($message['body'], $message['format']);
Sascha Grossenbacher
committed
if (isset($vars['mid']) && isset($vars['thread_id']) && privatemsg_user_access('delete privatemsg')) {
$vars['message_actions'][] = array('title' => t('Delete message'), '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('message-actions')))) : '';
$vars['anchors'] = '';
foreach ($vars['message_anchors'] as $anchor) {
$vars['anchors'].= '<a name="' . $anchor . '"></a>';
}
Marco Molinari
committed
}
litwol
committed
function privatemsg_preprocess_privatemsg_recipients(&$vars) {
$vars['participants'] = ''; // assign a default empty value
if (isset($vars['message']['participants'])) {
$vars['participants'] = _privatemsg_format_participants($vars['message']['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)
->condition('uid', $account->uid)
->execute();
}
/**
* 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.
*/
function _privatemsg_load_thread_participants($thread_id) {
$query = _privatemsg_assemble_query('participants', $thread_id);
return user_load_multiple($query->execute()->fetchCol());
}
/**
* 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, ...".
* @return
* Array, first element is an array of loaded user objects, second an array
* with invalid names.
*
*/
function _privatemsg_parse_userstring($input) {
if (is_string($input)) {
$input = explode(',', $input);
}
// Start working through the input array.
$recipients = array();
foreach ($input as $string) {
$string = trim($string);
if (!empty($string)) { // We don't care about white space names.
// First, check if another module is able to resolve the string into an
// user object.
foreach (module_implements('privatemsg_name_lookup') as $module) {
$function = $module . '_privatemsg_name_lookup';
if (($recipient = $function($string)) && is_object($recipient)) {
// If there is a match, continue with the next input string.
$recipients[$recipient->uid] = $recipient;
continue 2;
}
// 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)) {
$recipients[$recipient->uid] = $recipient;
continue;
}
$invalid[$string] = $string;
return array($recipients, $invalid);
/**
* @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
->condition('pmi.uid', $account->uid)
->condition('pmi.deleted', 0);
$query->setCountQuery($count_query);
$query->addField('pmi', 'thread_id');
$query->addExpression('MIN(pm.subject)', 'subject');
$query->addExpression('MAX(pm.timestamp)', 'last_updated');
$query->addExpression('SUM(pmi.is_new)', 'is_new');
// Load enabled columns
$fields = array_filter(variable_get('privatemsg_display_fields', array('participants')));
if (in_array('count', $fields)) {
// We only want the distinct number of messages in this thread.
$query->addExpression('COUNT(distinct pmi.mid)', 'count');
litwol
committed
if (in_array('participants', $fields)) {
// Query for a string with uid's, for example "1,6,7". This needs a subquery on PostgreSQL.
if (db_driver() == 'pgsql') {
$query->addExpression("array_to_string(array(SELECT DISTINCT pmia.uid
FROM {pm_index} pmia
WHERE pmia.thread_id = pmi.thread_id), ',')", 'participants');
$query->addExpression("(SELECT GROUP_CONCAT(DISTINCT pmia.uid SEPARATOR ',')
FROM {pm_index} pmia
WHERE pmia.thread_id = pmi.thread_id)", 'participants');
}
}
if (in_array('thread_started', $fields)) {
$query->addExpression('MIN(pm.timestamp)', 'thread_started');
}
return $query
->condition('pmi.uid', $account->uid)
->condition('pmi.deleted', 0)
->groupBy('pmi.thread_id')
Sascha Grossenbacher
committed
->orderBy('MAX(pmi.is_new)', 'DESC')
Sascha Grossenbacher
committed
->orderByHeader(_privatemsg_list_headers(array_merge(array('subject', 'last_updated'), $fields)))
->limit(variable_get('privatemsg_per_page', 25));
* Query function for loading a single or multiple messages.
* @param $pmids
* Array of pmids.
* Account for which the messages should be loaded.
function privatemsg_sql_load($pmids, $account = NULL) {
$query = db_select('pm_message', 'pm')
Sascha Grossenbacher
committed
->fields('pm', array('mid', 'author', 'subject', 'body', 'timestamp', 'format'))
Sascha Grossenbacher
committed
->fields('pmi', array('is_new', 'thread_id'))
Sascha Grossenbacher
committed
->condition('pmi.mid', $pmids)
->orderBy('pm.timestamp', 'ASC');
if($account) {
$query->condition('pmi.uid', $account->uid);
}
$query->join('pm_index', 'pmi', 'pm.mid = pmi.mid');
return $query;
/**
* Query definition to load messages of one or multiple threads.
*
* @param $threads
* Array with one or multiple thread id's.
* User object for which the messages are being loaded.
* Deleted messages are only loaded if this is set to TRUE.
function privatemsg_sql_messages($threads, $account = NULL, $load_all = FALSE) {
$query = db_select('pm_index', 'pmi');
$query->addField('pmi', 'mid');
Sascha Grossenbacher
committed
$query->join('pm_message', 'pm', 'pm.mid = pmi.mid');
$query->condition('pmi.deleted', 0);
Sascha Grossenbacher
committed
// If there are multiple inserts during the same second (tests, for example)
// sort by mid second to have them in the same order as they were saved.
->condition('pmi.thread_id', $threads)
->groupBy('pm.timestamp')
->groupBy('pmi.mid')
Sascha Grossenbacher
committed
// Order by timestamp first.
->orderBy('pm.timestamp', 'ASC')
// If there are multiple inserts during the same second (tests, for example)
// sort by mid second to have them in the same order as they were saved.
->orderBy('pmi.mid', 'ASC');
if($account) {
$query->condition('pmi.uid', $account->uid);
}
return $query;
/**
* Load all participants of a thread.
*
* @param $thread_id
* Thread id from which the participants should be loaded.
*/
function privatemsg_sql_participants($thread_id) {
$query = db_select('pm_index', 'pmi');
$query->join('users', 'u', 'u.uid = pmi.uid');
return $query
->distinct()
->fields('pmi', array('uid'))
->fields('u', array('name'))
->condition('pmi.thread_id', $thread_id);
function privatemsg_sql_unread_count($account) {
$query = db_select('pm_index', 'pmi');
$query->addExpression('COUNT(DISTINCT thread_id)', 'unread_count');
return $query
->condition('pmi.deleted', 0)
->condition('pmi.is_new', 1)
->condition('pmi.uid', $account->uid);
function privatemsg_sql_autocomplete($search, $names) {
$query = db_select('users', 'u')
->fields('u', array('name'))
->condition('u.name', $search . '%', 'LIKE')
->condition('u.status', 0, '<>')
Sascha Grossenbacher
committed
->where('NOT EXISTS (SELECT 1 FROM {pm_disable} pd WHERE pd.uid=u.uid)')
->orderBy('u.name', 'ASC')
->range(0, 10);
if (!empty($names)) {
$query->condition('u.name', $names, 'NOT IN');
Sascha Grossenbacher
committed
/**
* Query Builder function to load all messages that should be flushed.
*
* @param $days
* Select messages older than x days.
* @param $max
* Select no more than $max messages.
*/
function privatemsg_sql_deleted($days, $max) {
$query = db_select('pm_message', 'pm');
$query->addField('pm', 'mid');
$query->join('pm_index', 'pmi', 'pmi.mid = pm.mid');
return $query
->groupBy('pm.mid')
->havingCondition('MIN(pmi.deleted)', 0, '>')
->havingCondition('MAX(pmi.deleted)', time() - $days * 86400, '<')
->range(0, $max);
}
/**
* Implements hook_user_view().
*/
function privatemsg_user_view(&$account) {
Sascha Grossenbacher
committed
if (($url = privatemsg_get_link(array($account))) && variable_get('privatemsg_display_profile_links', 1)) {
$account->content['privatemsg_send_new_message'] = array(
'#markup' => l(t('Send this user a message'), $url, array('query' => drupal_get_destination())),
'#weight' => 10,
);
}
}
/**
* Implements hook_user_login().
*/
function privatemsg_user_login(&$edit, &$account) {
if (variable_get('privatemsg_display_loginmessage', TRUE) && privatemsg_user_access()) {
$count = privatemsg_unread_count();
if ($count) {
drupal_set_message(t('You have <a href="@messages">%unread</a>.', array('@messages' => url('messages'), '%unread' => format_plural($count, '1 unread message', '@count unread messages'))));
}
Sascha Grossenbacher
committed
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
/**
* Implements hook_user_cancel().
*/
function privatemsg_user_cancel($edit, $account, $method) {
switch ($method) {
case 'user_cancel_reassign':
db_update('pm_message')
->condition('author', $account->uid)
->fields(array('author' => 0))
->execute();
break;
case 'user_cancel_delete':
case 'user_cancel_block_unpublish':
$mids = db_select('pm_message', 'pm')
->fields('pm', array('mid'))
->condition('author', $account->uid)
->execute()
->fetchCol();
if (!empty($mids)) {
// Delete recipient entries in {pm_index} of the messages the user wrote.
db_delete('pm_index')
->condition('mid', $mids)
->execute();
}
// Delete messages the user wrote.
db_delete('pm_message')
->condition('author', $account->uid)
->execute();
// Delete recipient entries of that user.
db_delete('pm_index')
->condition('uid', $account->uid)
->execute();
break;
}
Sascha Grossenbacher
committed
// Always delete user settings.
db_delete('pm_disable')
->condition('uid', $account->uid)
->execute();
}
/**
* Implements hook_form_alter().
*/
function privatemsg_form_alter(&$form, &$form_state, $form_id) {
Sascha Grossenbacher
committed
// We have to use user_acces() because privatemsg_user_access() would
// return FALSE when privatemsg is disabled.
if (($form_id == 'user_register_form' || $form_id == 'user_profile_form') && $form['#user_category'] == 'account' && user_access('write privatemsg')) {
Sascha Grossenbacher
committed
$form['privatemsg'] = array(
'#type' => 'fieldset',
'#title' => t('Privatemsg settings'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
'#weight' => 10,
);
$form['privatemsg']['pm_enable'] = array(
'#type' => 'checkbox',
'#title' => t('Enable Private Messaging'),
'#default_value' => !privatemsg_is_disabled($form['#user']),
'#description' => t('Disabling private messages prevents you from sending or receiving messages from other users.'),
);
}
}
/**
* Implements hook_user_insert().
*/
function privatemsg_user_update(&$edit, $account, $category) {
Sascha Grossenbacher
committed
if ($category == 'account' && isset($edit['pm_enable']) && user_access('write privatemsg')) {
Sascha Grossenbacher
committed
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
$current = privatemsg_is_disabled($account);
$disabled = (!$edit['pm_enable']);
$edit['pm_enable'] = NULL;
$account->privatemsg_disabled = $disabled;
// only perform the save if the value has changed
if ($current != $disabled) {
if ($disabled) {
db_insert('pm_disable')
->fields(array('uid' => $account->uid))
->execute();
}
else {
db_delete('pm_disable')
->condition('uid', $account->uid)
->execute();
}
}
}
}
/**
* Implementation of hook_privatemsg_block_message.
*/
function privatemsg_privatemsg_block_message($author, $recipients) {
$blocked = array();
if (privatemsg_is_disabled($author)) {
$blocked[] = array('uid' => $author->uid,
'message' => t('You have disabled private message sending and receiving.'),
);