Skip to content
privatemsg.module 46.6 KiB
Newer Older
litwol's avatar
litwol committed
<?php
// $Id$

/**
 * @file
 * Allows users to send private messages to other users.
 */
litwol's avatar
litwol committed
define('THREADED', 0);
Marco Molinari's avatar
Marco Molinari committed

Zen's avatar
Zen committed
/**
litwol's avatar
litwol committed
 * Implementation of hook_perm().
Zen's avatar
Zen committed
 */
litwol's avatar
litwol committed
function privatemsg_perm() {
  return array(
    'read privatemsg',
    'read all private messages',
litwol's avatar
litwol committed
    'administer privatemsg settings',
litwol's avatar
litwol committed
    'write privatemsg'
function _privatemsg_generate_user_array($userstring) {
  static $user_cache = array();

  $users = array_slice(explode(',', $userstring), -4);
  $participants = array();
  foreach ($users as $user) {
    if (isset($user_cache[$user])) {
      $participants[$user] = $user_cache[$user];
    }
    else {
      $participants[$user] = $user_cache[$user] = user_load($user);
    }
  }
  return $participants;
}

function _privatemsg_format_participants($part_array, $limit = 20, $no_text = FALSE) {
  if (count($part_array) > 0) {
    $limited = FALSE;
    foreach ($part_array as $account) {
      if (count($to) >= $limit) {
        $limited = TRUE;
      $to[] = theme('username', $account);
    }

    $limit_string = '';
    if ($limited) {
      $limit_string = t(' and others');
    }


    if ($no_text) {
      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));
litwol's avatar
litwol committed
 * Implementation of hook_menu().
litwol's avatar
litwol committed
function privatemsg_menu() {
litwol's avatar
litwol committed
  $items['messages'] = array(
    'title'            => 'Messages',
    'title callback'  => 'privatemsg_title_callback',
    'page callback'    => 'privatemsg_list',
    'access callback'  => 'privatemsg_user_access',
litwol's avatar
litwol committed
    'type'             => MENU_NORMAL_ITEM,
litwol's avatar
litwol committed
  );
litwol's avatar
litwol committed
  $items['messages/inbox'] = array(
    'title'            => 'All messages',
    'page callback'    => 'privatemsg_list',
    'access callback'  => 'privatemsg_user_access',
litwol's avatar
litwol committed
    'type'             => MENU_DEFAULT_LOCAL_TASK,
    'weight'           => -10,
litwol's avatar
litwol committed
  );
litwol's avatar
litwol committed
  $items['messages/mark-read'] = array(
    'title'            => 'Mark all as read',
    'page callback'    => 'privatemsg_set_new_status',
    'page arguments'   => array(NULL, NULL, 0, TRUE),
    'access callback'  => 'privatemsg_unread_count',
    'type'             => MENU_NORMAL_ITEM,
    'weight'           => -9,
  );
  $items['messages/sent'] = array(
    'title'            => 'Sent messages',
    'page callback'    => 'privatemsg_list',
    'access callback'  => 'privatemsg_user_access',
litwol's avatar
litwol committed
    'type'             => MENU_LOCAL_TASK,
    'weight'           => -5,
litwol's avatar
litwol committed
  );
  $items['messages/view/%privatemsg_thread'] = array(
    'title'            => 'Read message',
litwol's avatar
litwol committed
    'page callback'    => 'privatemsg_view',
    'page arguments'   => array(2),
    'access callback'  => 'privatemsg_view_access',
litwol's avatar
litwol committed
    'weight'           => -10,
litwol's avatar
litwol committed
  );
litwol's avatar
litwol committed
  $items['messages/delete/%'] = array(
    'title'            => 'Delete message',
    'page callback'    => 'drupal_get_form',
    'page arguments'   => array('privatemsg_delete', 2),
    'access callback'  => 'privatemsg_user_access',
litwol's avatar
litwol committed
    'type'             => MENU_CALLBACK,
    'weight'           => -10,
  );
  $items['messages/new'] = array(
    'title'            => 'Write new message',
    'page callback'    => 'drupal_get_form',
    'page arguments'   => array('privatemsg_new', 2),
    'access callback'  => 'privatemsg_user_access',
litwol's avatar
litwol committed
    'access arguments' => array('write privatemsg'),
    'type'             => MENU_LOCAL_TASK,
    'weight'           => -7,
  );
  // Auto-completes available user names & removes duplicates.
  $items['messages/user-name-autocomplete'] = array(
    'page callback'    => 'privatemsg_user_name_autocomplete',
    'access callback'  => 'privatemsg_user_access',
litwol's avatar
litwol committed
    'access arguments' => array('write privatemsg'),
litwol's avatar
litwol committed
    'type'             => MENU_CALLBACK,
    'weight'           => -10,
  );
  $items['admin/settings/messages'] = array(
    'title'            => 'Private messages',
    'description'      => 'Configure private messaging settings.',
    'page callback'    => 'drupal_get_form',
    'page arguments'   => array('private_message_settings'),
    'access arguments' => array('administer privatemsg settings'),
    'type'             => MENU_NORMAL_ITEM,
litwol's avatar
litwol committed
  return $items;

/**
 * Privatemsg access wrapper for user_callback
 *
 * Never allows anonymous user access as that doesn't makes sense
 *
 * @param string $permission Permission string, defaults to read privatemsg
 *
 * @return boolean 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;
  }
  if (!user_access($permission, $account)) {
    return FALSE;
  }
  return TRUE;
}


/**
 * 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/%
 */
function privatemsg_view_access() {
  if (privatemsg_user_access('read privatemsg') && arg(1) == 'view') {
    return TRUE;
  }
  return FALSE;
}

/**
 * This function is called by the menu system through the %privatemsg_thread wildcard
 *
 * TODO: Really load the full thread in here
 */
function privatemsg_thread_load($thread_id) {
  if ((int)$thread_id > 0) {
    return $thread_id;
  }
  return FALSE;
}
litwol's avatar
litwol committed
function private_message_view_options() {
  $options = module_invoke_all('privatemsg_view_template');
  return $options;
chx's avatar
chx committed
}
litwol's avatar
litwol committed

litwol's avatar
litwol committed
 * Implementation of hook_privatemsg_view_template().
 *
 * Allows modules to define different message view template.
 *
 * This hook returns information about available themes for privatemsg viewing.
litwol's avatar
litwol committed
 *
 * array(
 *  'machine_template_name' => 'Human readable template name',
 *  'machine_template_name_2' => 'Human readable template name 2'
 * };
 */
function privatemsg_privatemsg_view_template() {
  return array(
litwol's avatar
litwol committed
    'privatemsg-view' => 'Default view',
litwol's avatar
litwol committed
}
litwol's avatar
litwol committed

litwol's avatar
litwol committed
function private_message_settings() {
  $form = array();
litwol's avatar
litwol committed

litwol's avatar
litwol committed
  $form['theming_settings'] = array(
litwol's avatar
litwol committed
    '#type'        => 'fieldset',
    '#collapsible' => TRUE,
    '#collapsed'   => TRUE,
    '#title'       => t('Theming settings'),
litwol's avatar
litwol committed
  );
  $form['theming_settings']['private_message_view_template'] = array(
litwol's avatar
litwol committed
    '#type'          => 'radios',
    '#title'         => t('Private message display template'),
litwol's avatar
litwol committed
    '#default_value' => variable_get('private_message_view_template', 'privatemsg-view'),
litwol's avatar
litwol committed
    '#options'       => private_message_view_options(),
  );
  $form['privatemsg_per_page'] = array(
    '#type' => 'select',
    '#title' => t('Messages per page'),
    '#default_value' => variable_get('privatemsg_per_page', 25),
    '#options' => drupal_map_assoc(array(10, 25, 50, 75, 100)),
    '#description' => t('Choose the number of conversations that should be listed per page.'),
litwol's avatar
litwol committed
  );
  $form['privatemsg_display_loginmessage'] = array(
    '#type' => 'checkbox',
    '#title' => t('Inform the user about new messages on login'),
    '#default_value' => variable_get('privatemsg_display_loginmessage', TRUE),
    '#description' => t('This option can safely be disabled if the "New message indication" block is used instead.'),
  );
litwol's avatar
litwol committed
  $form['#submit'][] = 'private_message_settings_submit';
  return system_settings_form($form);
}
litwol's avatar
litwol committed

litwol's avatar
litwol committed
function private_message_settings_submit() {
  drupal_rebuild_theme_registry();
}
litwol's avatar
litwol committed
function privatemsg_theme() {
  return array(
    'privatemsg_view'    => array(
litwol's avatar
litwol committed
      'arguments'        => array('message' => NULL),
      'template'         => variable_get('private_message_view_template', 'privatemsg-view'),//'privatemsg',
litwol's avatar
litwol committed
    ),
    'privatemsg_from'    => array(
litwol's avatar
litwol committed
      'arguments'        => array('author' => NULL),
      'template'         => 'privatemsg-from',
litwol's avatar
litwol committed
    ),
litwol's avatar
litwol committed
    'privatemsg_to'      => array(
      'arguments'        => array('message' => NULL),
      'template'         => 'privatemsg-recipients',
litwol's avatar
litwol committed
    ),
litwol's avatar
litwol committed
    'privatemsg_between' => array(
      'arguments'        => array('recipients' => NULL),
      'template'         => 'privatemsg-between',
    ),
    'privatemsg_message_row'  => array(
      'arguments'             => array('row'),
litwol's avatar
litwol committed
    ),
    'privatemsg_new_block'  => array(
      'arguments'             => array('count'),
    ),
litwol's avatar
litwol committed

litwol's avatar
litwol committed
function privatemsg_preprocess_privatemsg_view(&$vars) {
litwol's avatar
litwol committed
//  drupal_set_message('<pre>'. print_r($vars,1 ) . '</pre>');

litwol's avatar
litwol committed
  $message = $vars['message'];
litwol's avatar
litwol committed
  $vars['mid'] = isset($message['mid']) ? $message['mid']:null;
litwol's avatar
litwol committed
  $vars['author_picture'] = theme('user_picture', $message['author']);
  $vars['author_name_link'] = theme('username', $message['author']);
litwol's avatar
litwol committed
  /**
   * @todo perhaps make this timestamp configurable via admin UI?
   */
  $vars['message_timestamp'] = format_date($message['timestamp'], 'small');
litwol's avatar
litwol committed
  $vars['message_body'] = check_markup($message['body']);
    $vars['message_actions'][] = array('title' => t('Delete message'), 'href' => 'messages/delete/'. $vars['mid']);
litwol's avatar
litwol committed
  }

  // call hook_privatemsg_message_view_alter
  drupal_alter('privatemsg_message_view', $vars);
  $vars['message_actions'] = !empty($vars['message_actions']) ? theme('links', $vars['message_actions'], array('class' => 'message-actions')) : '';
litwol's avatar
litwol committed

litwol's avatar
litwol committed
function privatemsg_preprocess_privatemsg_to(&$vars) {
litwol's avatar
litwol committed
  $vars['participants'] = '';//assign a default empty value
  if (isset($vars['message']['participants'])) {
    $vars['participants'] = _privatemsg_format_participants($vars['message']['participants']);
litwol's avatar
litwol committed
 * List messages.
litwol's avatar
litwol committed
 * @param $uid - user id for whom to load messages.
litwol's avatar
litwol committed
function privatemsg_list($uid = NULL) {
  global $user;
litwol's avatar
litwol committed

  // Setting default behavior...
  $account = $user;
litwol's avatar
litwol committed
  if (is_int($uid) && $uid != $user->uid) {
    // Trying to view someone else's messages...
    if (!privatemsg_user_access('read all private messages')) {
litwol's avatar
litwol committed
      drupal_set_message(t("You do not have sufficient rights to view someone else's messages"), 'warning');
litwol's avatar
litwol committed
    elseif ($account_check = user_load(array('uid' => $uid))) {
      // Has rights and user_load return an array so user does exist
      $account = $account_check;
litwol's avatar
litwol committed
  }
  // By this point we have figured out for which user we are listing messages and now it is safe to use $account->uid in the listing query.

  $content = array();
  $content['list']['content'] = privatemsg_list_messages($account);
  $content['list']['#weight'] = 0;

  drupal_alter('privatemsg_list_messages', $content, $account);

  uasort($content, 'element_sort');

  $expand = '';
  foreach ($content as $element) {
    $expand .= $element['content'];
  }
  return $expand;
}

/**
 * Display the list of messages for a given user, called by privatemsg_privatemsg_alter
 */
function privatemsg_list_messages($account) {
  switch (arg(1)) {
    case 'sent':
      $query = _privatemsg_assemble_query('list_sent', $account);
litwol's avatar
litwol committed
      break;
    case 'inbox':
    default:
      $query = _privatemsg_assemble_query('list', $account);
litwol's avatar
litwol committed
  }
  $result = pager_query($query['query'], variable_get('privatemsg_per_page', 25), 0, $query['count']);
litwol's avatar
litwol committed
  $rows = array();
  while ($thread = db_fetch_array($result)) {
    $rows[] = theme('privatemsg_message_row', $thread);
  }
litwol's avatar
litwol committed
  $content = '';
  if (!empty($rows)) {
    $head = array();
    foreach (array_keys($rows[0]) as $index) {
      $head[$index] =  array('data' => $index);
      if ($index == t('Last updated') || $index == t('Subject')) {
        $head[$index] += array('field' => $index, 'sort' => 'desc');
litwol's avatar
litwol committed
      }
litwol's avatar
litwol committed
    $content = theme('table', $head, $rows);
    $content .= theme('pager');
    drupal_add_css(drupal_get_path('module', 'privatemsg') .'/styles/privatemsg-list.css');
litwol's avatar
litwol committed
  }
  else {
    drupal_set_message(t('No messages to display.'));
function theme_privatemsg_new_block($count) {
  $text = format_plural($count, 'You have a new message, click here to read it',
                        'You have @count new messages, click here to read them',
                        array('@count' => $count));

  return l($text, 'messages', array('attributes' => array('id' => 'privatemsg-new-link')));
}

litwol's avatar
litwol committed
/**
litwol's avatar
litwol committed
 * Theme a row of messages
litwol's avatar
litwol committed
function theme_privatemsg_message_row($row) {
  $themed_row = array();
litwol's avatar
litwol committed

  $unread = '';
  if (isset($row['is_new']) && $row['is_new'] ) {
    $unread = ' privatemsg-unread';
  }
  $themed_row[t('Subject')] = '<span class="privatemsg-list-subject'. $unread .'">'. l($row['subject'], 'messages/view/'. $row['thread_id']) .'</span>';
litwol's avatar
litwol committed
  if (isset($row['author']) && $row['author']) {
    $authors = _privatemsg_generate_user_array($row['author']);
    $themed_row[t('Authors')] = '<span class="privatemsg-list-from'.  $unread .'">'. _privatemsg_format_participants($authors, 3, TRUE) .'</span>';
litwol's avatar
litwol committed
  }
  if (array_key_exists('recipient', $row)) {
    $themed_row[t('Recipients')] = '';
    if (!empty($row['recipient'])) {
      $recipients = _privatemsg_generate_user_array($row['recipient']);
      $themed_row[t('Recipients')] = '<span class="privatemsg-list-to'.  $unread .'">'. _privatemsg_format_participants($recipients, 3, TRUE) .'</span>';
litwol's avatar
litwol committed
  }
//  drupal_set_message('<pre>'. print_r($row, 1) . '</pre>');
  if ($row['last_updated']) {
    $themed_row[t('Last updated')] = '<span class="privatemsg-list-date'.  $unread .'">'. format_date($row['last_updated'], 'small') .'</span>';
litwol's avatar
litwol committed
  }
  return $themed_row;
litwol's avatar
litwol committed
}

/**
 * API function
 *
 * Sets a single message as read for a user.
 */
function privatemsg_mark_as_read($pmid, $account) {
  $query = "UPDATE {pm_index} SET is_new = 0 WHERE mid = %d AND recipient = %d";
  db_query($query, $pmid, $account->uid);
}

/**
 * API function
 *
 * Return number of unread messages for an account.
 */
function privatemsg_unread_count($account = NULL) {
  static $counts = array();
  if (!$account || $account->uid == 0) {
    global $user;
litwol's avatar
litwol committed
    $account = $user;
litwol's avatar
litwol committed
  }
  if ( !isset($counts[$account->uid])) {
    $query = _privatemsg_assemble_query('unread_count', $account);
litwol's avatar
litwol committed
    $counts[$account->uid] = db_result(db_query($query['query']));
  }
  return $counts[$account->uid];
}

/**
 * API function
 *
 * Change status of one or ALL messages to read / unread.
 */
function privatemsg_set_new_status($account = NULL, $mid = NULL, $new = 0, $verbose = FALSE) {
  if (!$account || 0 == $account->uid) {
    global $user;
    $account = $user;
  }
  $query = "UPDATE {pm_index} SET is_new = %d WHERE uid = %d AND is_new = %d";
litwol's avatar
litwol committed
  $arg[] = $new;
  $arg[] = $account->uid;
  $arg[] = ($new == 0) ? 1 : 0;
  if ($mid) {
    $query .= " AND mid = %d";
    $arg[] = $mid;
  }

  $result = db_query($query, $arg) ;

  if ($verbose) {
    if ($result) {
      if ($new == 1) {
        $status = t('unread');
      }
      else {
        $status = t('read');
      }
      $total_marked = db_affected_rows();
      drupal_set_message(format_plural($total_marked, "1 message marked as %status", '@count messages marked as %status', array('%status' => $status)));
litwol's avatar
litwol committed
    else {
      drupal_set_message(t('An error has occured, please contact the site administrator.'), 'error');
litwol's avatar
litwol committed
    return '';
  }
}
litwol's avatar
litwol committed
function privatemsg_view($thread_id) {
  global $user;
  $output = '';
litwol's avatar
litwol committed
  // Load the list of participants.
  $query = _privatemsg_assemble_query('participants', $thread_id);
litwol's avatar
litwol committed
  $participants = db_query($query['query']);
  while ($result = db_fetch_object($participants)) {
    $message['participants'][] = user_load($result->uid);
  }
  $content['participants']['content'] = theme('privatemsg_to', $message);
  $content['participants']['#weight'] = -5;

  // Load the messages.
  $query = _privatemsg_assemble_query('messages', $thread_id, $user);
litwol's avatar
litwol committed
  $conversation = db_query($query['query']);
  $message_count = 0;
  while ($result = db_fetch_array($conversation)) {
    $pmid = $result['mid'];
    $message = _privatemsg_load($pmid, $user);
litwol's avatar
litwol committed
    privatemsg_set_new_status($user, $pmid);
    $message['author'] = user_load($message['author']);
    // Some tasks only need to be done once - on the first message of a thread.
    if ($message_count == 0) {
      // Set the page title to the message subject.
      drupal_set_title(check_plain($message['subject']));
litwol's avatar
litwol committed
    $output .= theme('privatemsg_view', $message);
litwol's avatar
litwol committed
    $message_count++;
litwol's avatar
litwol committed
  if ($message_count == 0) {
    drupal_set_message(t('There are no messages available to display.'), 'error');
  }

  $content['messages']['content'] = $output;
  $content['messages']['#weight'] = 0;

  if ($message_count > 0) {
    $content['reply']['content'] = drupal_get_form('privatemsg_new');
    $content['reply']['#weight'] = 5;
  }

  //allow other modules to hook into the $content array and alter it
  drupal_alter('privatemsg_view_messages', $content, $message_count);

  uasort($content, 'element_sort');

  $expand = '';
  foreach ($content as $element) {
    $expand .= $element['content'];
  }
  return $expand;
litwol's avatar
litwol committed

function privatemsg_new(&$form_state, $account = NULL) {
litwol's avatar
litwol committed

litwol's avatar
litwol committed
  $recipient = '';
  $subject   = '';
  $body      = '';
litwol's avatar
litwol committed

  if ((int)$account > 0 && $account = user_load($account)) {
litwol's avatar
litwol committed
    $recipient  = $account->name .', ';
  }
litwol's avatar
litwol committed
  if (isset($form_state['values'])) {
litwol's avatar
litwol committed
    $recipient = $form_state['values']['recipient'];
    $subject   = $form_state['values']['subject'];
    $body      = $form_state['values']['body'];
litwol's avatar
litwol committed
  $form = array();
  if (isset($form_state['privatemsg_preview'])) {
    $form['message_header'] = array(
      '#type' => 'fieldset',
litwol's avatar
litwol committed
      '#attributes' => array('class' => 'preview'),
litwol's avatar
litwol committed
    );
    $form['message_header']['message_preview'] = array(
      '#value'  => $form_state['privatemsg_preview'],
    );
litwol's avatar
litwol committed
  $form['privatemsg'] = array(
    '#type'               => 'fieldset',
    '#access'             => privatemsg_user_access('write privatemsg'),
litwol's avatar
litwol committed
  );
  $form['privatemsg']['author'] = array(
    '#type' => 'value',
    '#value' => $user,
litwol's avatar
litwol committed
  );
litwol's avatar
litwol committed
  $form['privatemsg']['recipient']  = array(
litwol's avatar
litwol committed
    '#type'               => 'textfield',
    '#title'              => t('To'),
    '#description'        => t('Separate multiple names with commas.'),
    '#default_value'      => $recipient,//populate this later
litwol's avatar
litwol committed
    '#required'           => TRUE,
litwol's avatar
litwol committed
    '#size'               => 50,
litwol's avatar
litwol committed
    '#autocomplete_path'  => 'messages/user-name-autocomplete',
    // Do not hardcode #maxlength, make it configurable by number of recipients, not their name length.
litwol's avatar
litwol committed
  );
litwol's avatar
litwol committed
  $form['privatemsg']['subject']    = array(
litwol's avatar
litwol committed
    '#type'               => 'textfield',
    '#title'              => t('Subject'),
    '#size'               => 50,
    '#maxlength'          => 255,
    '#default_value'      => $subject,
litwol's avatar
litwol committed
  );
litwol's avatar
litwol committed
  $form['privatemsg']['body']       = array(
litwol's avatar
litwol committed
    '#type'               => 'textarea',
    '#title'              => t('Message'),
    '#cols'               => 10,
    '#rows'               => 6,
litwol's avatar
litwol committed
    '#default_value'      => $body,
  );
litwol's avatar
litwol committed
  $form['privatemsg']['preview'] = array(
litwol's avatar
litwol committed
    '#type'               => 'submit',
litwol's avatar
litwol committed
    '#value'              => t('Preview message'),
litwol's avatar
litwol committed
    '#submit'             => array('pm_preview'),
litwol's avatar
litwol committed
  );
litwol's avatar
litwol committed
  $form['privatemsg']['submit'] = array(
litwol's avatar
litwol committed
    '#type'               => 'submit',
litwol's avatar
litwol committed
    '#value'              => t('Send message'),
litwol's avatar
litwol committed
    '#submit'             => array('pm_send'),
litwol's avatar
litwol committed
  );
  $url = 'messages';
  if (isset($_REQUEST['destination'])) {
    $url = $_REQUEST['destination'];
  }

litwol's avatar
litwol committed
  $form['privatemsg']['cancel'] = array(
    '#value'              => l(t('Cancel'), $url, array('attributes' => array('id' => 'edit-cancel'))),
litwol's avatar
litwol committed
  );
  $form['#validate'][]    = 'pm_send_validate';
litwol's avatar
litwol committed

  //modules can store data here, everything stored here will be available
  //in $message, similar to #node





litwol's avatar
litwol committed
  return $form;
litwol's avatar
litwol committed

litwol's avatar
litwol committed
function pm_send_validate($form, &$form_state) {
litwol's avatar
litwol committed
//  drupal_set_message('<pre>'. print_r($form_state['values'], 1) . '</pre>');
  // The actual message that is being sent, we create this during validation and pass to submit to send out.
  $message = array();
litwol's avatar
litwol committed
  $message['body']      = $form_state['values']['body'];
  $message['subject']   = $form_state['values']['subject'];
  $message['author']    = $form_state['values']['author'];
litwol's avatar
litwol committed
  $message['timestamp'] = time();
litwol's avatar
litwol committed
  if (isset($form_state['values']['thread_id']) && $form_state['values']['thread_id']) {
    $message['thread_id'] = $form_state['values']['thread_id'];
  }
  $trimed_body = trim(truncate_utf8(strip_tags($message['body']), 50, TRUE, TRUE));
  if (empty($message['subject']) && !empty($trimed_body)) {
    $message['subject'] = $trimed_body;
  }
litwol's avatar
litwol committed

  // Verify that recipient's name syntax is correct.
  $fragments = explode(',', $form_state['values']['recipient']);
litwol's avatar
litwol committed
  $invalid = array();
  $valid   = array();
  foreach ($fragments as $index => $name) {
    $name = trim($name);
litwol's avatar
litwol committed
    if (!empty($name)) { // We don't care about white space names.
litwol's avatar
litwol committed
      if (empty($name) || $error = module_invoke('user', 'validate_name', $name)) {
litwol's avatar
litwol committed
        // These names are invalid due to incorrect user name syntax.
litwol's avatar
litwol committed
        $invalid[$name] = $name;
      }
      else {
litwol's avatar
litwol committed
        $valid[$name] = $name;  // These are valid only due to user name syntax. We still need to check if the user exists and accepts messages.
litwol's avatar
litwol committed

  // Verify users exist and load their accounts.
  foreach ($valid as $index => $name) {
    if ($recipient = user_load(array('name' => $name))) {
      $message['recipients'][$recipient->uid] = $recipient;
litwol's avatar
litwol committed
    }
    else {
litwol's avatar
litwol committed
      // Here we add more invalid names due to the fact that they don't exist.
litwol's avatar
litwol committed
      $invalid[$name] = $name;
    }
litwol's avatar
litwol committed
  // Verify that our recipients are valid.
litwol's avatar
litwol committed
  /**
   * VALIDATE NAMES
litwol's avatar
litwol committed
   * 1) Make sure the name exists.
   * 2) Make sure he accepts private messages.
   * 3) Make sure the sender is not on block list of the recipient.
litwol's avatar
litwol committed
   */
  /**
   * BUILD VALID RECIPIENT LIST
litwol's avatar
litwol committed
   * 1) Names that were not valid from previous step will be stripped out.
   * 2) Names that remain will be put into a recipients array.
litwol's avatar
litwol committed
   */
litwol's avatar
litwol committed

  $errors = _privatemsg_validate_message($message, $message['author'], TRUE);
  if (!empty($errors)) {
      foreach ($errors as $error) {
        form_set_error('body', $error);
      }
  }

litwol's avatar
litwol committed
  $form_state['validate_built_message'] = $message;
  if (!empty($invalid)) {
litwol's avatar
litwol committed
    drupal_set_message(t('The following users will not receive this private message: @invalid', array('@invalid' => implode(", ", $invalid))), 'error');
litwol's avatar
litwol committed
  }
litwol's avatar
litwol committed
  $form_state['values']['recipient'] = implode(', ', array_diff($valid, $invalid));
litwol's avatar
litwol committed
}
litwol's avatar
litwol committed

litwol's avatar
litwol committed
function pm_send($form, &$form_state) {
  if (_privatemsg_send($form_state['validate_built_message'])) {
    drupal_set_message(t('A message has been sent to @recipients.', array('@recipients' => $form_state['values']['recipient'])));
litwol's avatar
litwol committed
function pm_preview($form, &$form_state) {
Neil Drumm's avatar
Neil Drumm committed

litwol's avatar
litwol committed
    drupal_validate_form($form['form_id']['#value'], $form, $form_state);
    if (!form_get_errors()) {
litwol's avatar
litwol committed
//  drupal_set_message('<pre>'. print_r($form, 1) .' </pre>');;
      //TODO: Generate message preview here.
      $form_state['privatemsg_preview'] = theme('privatemsg_view', $form_state['validate_built_message']);
litwol's avatar
litwol committed
    }
litwol's avatar
litwol committed

  $form_state['rebuild'] = TRUE; //this forces our form to be rebuilt instead of being submitted.
chx's avatar
chx committed
}
litwol's avatar
litwol committed

function privatemsg_sql_list_sent(&$fragments, $account) {
  $fragments['primary_table'] = '{pm_message} pm';
litwol's avatar
litwol committed

  $fragments['select'][]      = 'pmi.thread_id';
  $fragments['select'][]      = 'pm.subject';
  if ($GLOBALS['db_type'] == 'pgsql') {
    $fragments['select'][]      = "array_to_string(array(SELECT DISTINCT textin(int4out(pmia.uid))
                                                         FROM {pm_index} pmia
                                                         WHERE pmia.thread_id = pmi.thread_id AND pmia.uid <> %d), ',') AS recipient";

  }
  else {
    $fragments['select'][]      = '(SELECT GROUP_CONCAT(DISTINCT pmia.uid SEPARATOR ",")
                                                         FROM {pm_index} pmia
                                                         WHERE pmia.thread_id = pmi.thread_id AND pmia.uid <> %d) AS recipient';

  }
  $fragments['query_args'][] = $account->uid;

  $fragments['select'][]      = 'MAX(pm.timestamp) as last_updated';
  $fragments['inner_join'][]  = 'INNER JOIN {pm_index} pmi ON pm.mid = pmi.mid';
litwol's avatar
litwol committed
  $fragments['where'][]       = 'pmi.uid = %d';
  $fragments['query_args'][]  = $account->uid;
  $fragments['where'][]       = 'pm.author = %d';
  $fragments['query_args'][]  = $account->uid;
  $fragments['where'][]       = 'pmi.deleted = 0';
  $fragments['group_by'][]  = 'pmi.thread_id';
  $fragments['group_by'][]  = 'pm.subject';

  $order = 'last_updated';
litwol's avatar
litwol committed
  $sort = 'desc';
  if (isset($_GET['order'])) {
    switch ($_GET['order']) {
      case 'subject':
litwol's avatar
litwol committed
        $order = 'subject';
        break;
    }
    $sort = isset($_GET['sort']) && ($_GET['sort'] == 'asc' || $_GET['sort'] == 'desc') ? $_GET['sort'] : 'desc';
  }
  $fragments['order_by'][]  = $order .' '. $sort;
litwol's avatar
litwol committed

function privatemsg_sql_list(&$fragments, $account) {
litwol's avatar
litwol committed
  $fragments['primary_table'] = '{pm_message} pm';
litwol's avatar
litwol committed

  $fragments['select'][]      = 'pmi.thread_id';
  $fragments['select'][]      = 'MIN(pm.subject) as subject';
  $fragments['select'][]      = 'COUNT(pmi.thread_id) as count';

  if ($GLOBALS['db_type'] == 'pgsql') {
    $fragments['select'][]      = "array_to_string(array(SELECT DISTINCT textin(int4out(pma.author))
                                                         FROM {pm_message} pma
                                                         INNER JOIN pm_index pmia ON pma.mid = pmia.mid
                                                         WHERE pmia.thread_id = pmi.thread_id),',') AS author";
  }
  else {
    $fragments['select'][]      = 'GROUP_CONCAT(DISTINCT author SEPARATOR ",") as author';
  }
  $fragments['select'][]      = 'MAX(pm.timestamp) as last_updated';
  $fragments['select'][]      = 'MIN(pm.timestamp) as thread_started';
litwol's avatar
litwol committed
  $fragments['select'][]      = 'MAX(pmi.is_new) as is_new';
  // pm_index needs to be the first join.
  $fragments['inner_join'][]  = 'INNER JOIN {pm_index} pmi ON pm.mid = pmi.mid';
litwol's avatar
litwol committed
  $fragments['where'][]       = 'pmi.uid = %d';
  $fragments['query_args'][]  = $account->uid;
  $fragments['where'][]       = 'pmi.deleted = 0';
  $fragments['group_by'][]    = 'pmi.thread_id';
  $order = 'is_new';
  $sort = 'desc';
  if (isset($_GET['order'])) {
    switch ($_GET['order']) {
      case t('Subject'): // Allows translated headers to be sorted
        $order = 'subject';
        break;
      default:
    }
    $sort = isset($_GET['sort']) && ($_GET['sort'] == 'asc' || $_GET['sort'] == 'desc') ? $_GET['sort'] : 'desc';
  $fragments['order_by'][]  = $order .' '. $sort .', last_updated DESC';
litwol's avatar
litwol committed

function privatemsg_sql_load(&$fragments, $pmid, $account) {
litwol's avatar
litwol committed
//  drupal_set_message('<pre>'. print_r(func_get_args(), 1) . '</pre>');
litwol's avatar
litwol committed
  $fragments['primary_table'] = '{pm_message} pm'; // Our primary table

  $fragments['select'][]      = "pm.mid";
  $fragments['select'][]      = "pm.author";
  $fragments['select'][]      = "pm.subject";
  $fragments['select'][]      = "pm.body";
  $fragments['select'][]      = "pm.timestamp";
  $fragments['select'][]      = "pmi.is_new";

  $fragments['inner_join'][]  = 'INNER JOIN {pm_index} pmi ON pm.mid = pmi.mid';
  $fragments['where'][]       = 'pmi.mid = %d';
litwol's avatar
litwol committed
  $fragments['query_args'][]  = $pmid;
litwol's avatar
litwol committed
  $fragments['where'][]       = 'pmi.uid = %d';
  $fragments['query_args'][]  = $account->uid;
function privatemsg_sql_messages(&$fragments, $thread_id, $account) {
litwol's avatar
litwol committed
  $fragments['primary_table'] = '{pm_index} pmi';

  $fragments['select'][]      = 'DISTINCT(pmi.mid) as mid';
  $fragments['where'][]       = 'pmi.thread_id = %d';
  $fragments['query_args'][]  = $thread_id;
  $fragments['where'][]       = 'pmi.uid = %d';
  $fragments['query_args'][]  = $account->uid;
litwol's avatar
litwol committed
  $fragments['where'][]       = 'pmi.deleted = 0';
  $fragments['order_by'][]    = 'pmi.mid ASC';
Zen's avatar
Zen committed

function privatemsg_sql_participants(&$fragments, $thread_id) {
litwol's avatar
litwol committed
  $fragments['primary_table'] = '{pm_index} pmi';
litwol's avatar
litwol committed

  $fragments['select'][]      = 'DISTINCT(pmi.uid) as uid';
  $fragments['where'][]       = 'pmi.thread_id = %d';
  $fragments['query_args'][]  = $thread_id;
function privatemsg_sql_unread_count(&$fragments, $account) {
litwol's avatar
litwol committed
  $fragments['primary_table'] = '{pm_index} pmi';

  $fragments['select'][]      = 'COUNT(DISTINCT thread_id) as unread_count';
  $fragments['where'][]       = 'pmi.deleted = 0';
  $fragments['where'][]       = 'pmi.is_new = 1';
  $fragments['where'][]       = 'pmi.uid = %d';
  $fragments['query_args'][]  = $account->uid;
}

function privatemsg_sql_autocomplete(&$fragments, $search, $names) {
  $fragments['primary_table'] = '{users} u';
  $fragments['select'][] = 'u.name';
  $fragments['where'][] = "u.name LIKE '%s'";
  $fragments['query_args'][] = $search. '%%';
  if (!empty($names)) {
    $fragments['where'][] = "u.name NOT IN (". db_placeholders($names, 'text') .")";
    $fragments['query_args'] += $names;
  }
  $fragments['where'][] = 'u.status <> 0';
  $fragments['order_by'][] = 'u.name ASC';
}

/**
 * Return autocomplete results for usernames.
litwol's avatar
litwol committed
 * Prevents usernames from being used and/or suggested twice.
 */
function privatemsg_user_name_autocomplete($string) {
  $names = array();
litwol's avatar
litwol committed
  // 1: Parse $string and build list of valid user names.
  $fragments = explode(',', $string);
  foreach ($fragments as $index => $name) {
    $name = trim($name);
    if ($error = module_invoke('user', 'validate_name', $name)) {
litwol's avatar
litwol committed
      // Do nothing if this name does not validate.
    }
    else {
      $names[$name] = $name;
    }
  }
litwol's avatar
litwol committed

  // By using user_validate_user we can ensure that names included in $names are at least logisticaly possible.
  // 2: Find the next user name suggestion.
  $fragment = array_pop($names);
  if (!empty($fragment)) {
    $query = _privatemsg_assemble_query('autocomplete', $fragment, $names);
    $result = db_query_range($query['query'], $fragment, 0, 10);
litwol's avatar
litwol committed
    $prefix = count($names) ? implode(", ", $names) .", " : '';
    // 3: Build proper suggestions and print.
    $matches = array();
    while ($user = db_fetch_object($result)) {
litwol's avatar
litwol committed
      $matches[$prefix . $user->name .", "] = $user->name;
  else {
    drupal_json(array());   //prevents auto-complete js error on empty
  }
litwol's avatar
litwol committed
function privatemsg_user($op, &$edit, &$account, $category = NULL) {
  global $user;


  switch ($op) {
    case 'view':
      if ($url = privatemsg_get_link($account)) {
litwol's avatar
litwol committed
        $account->content['privatemsg_send_new_message'] = array(
          '#type'   => 'markup',
          '#value'  => l(t('Send this user a message'), $url, array('query' => drupal_get_destination())),
litwol's avatar
litwol committed
          '#weight' => 10,
        );
      }
      break;
    case 'login':
      if (variable_get('privatemsg_display_loginmessage', TRUE) && privatemsg_user_access()) {
        $count = privatemsg_unread_count();
        if ($count) {
          drupal_set_message(t('You have <a href="@inbox">%unread</a>.', array('@inbox' => url('messages/inbox'), '%unread' => format_plural($count, '1 unread message', '@count unread messages'))));
        }
litwol's avatar
litwol committed
      }
      break;
  }
}

function privatemsg_block($op = 'list', $delta = 0, $edit = array()) {
  if ('list' == $op) {
    $blocks = array();
    $blocks['privatemsg-menu'] = array(
      'info' => t('Privatemsg links'),
    );
    $blocks['privatemsg-new'] = array(
      'info' => t('New message indication'),
      'cache' => BLOCK_CACHE_PER_USER,
    );
litwol's avatar
litwol committed

    return $blocks;
  }
  else if ('view' == $op) {
    $block = array();
    switch ($delta) {
      case 'privatemsg-menu':
        $block = _privatemsg_block_menu();
        break;
      case 'privatemsg-new':
        $block = _privatemsg_block_new();
        break;
litwol's avatar
litwol committed
    }
    return $block;
  }
}

function privatemsg_title_callback($title = NULL) {
litwol's avatar
litwol committed
  $count = privatemsg_unread_count();

    return format_plural($count, 'Messages (@count new)', 'Messages (@count new)');
function _privatemsg_block_new() {
  $block = array();

  if (!privatemsg_user_access()) {
    return $block;
  }

  $count = privatemsg_unread_count();
  if ($count) {
    $block = array(
      'subject' => format_plural($count, 'New message', 'New messages'),
      'content' => theme('privatemsg_new_block', $count),
    );
    return $block;
  }
  return array();
}

litwol's avatar
litwol committed
function _privatemsg_block_menu() {
  $block = array();