Newer
Older
<?php
// $Id$
/**
* @file
* User menu callbacks for Privatemsg.
*/
/**
* List messages.
*
* @param $argument
* An argument to pass through to the query builder.
Sascha Grossenbacher
committed
* @param $account_check
* Account to check if current user has access.
Sascha Grossenbacher
committed
function privatemsg_list_page($argument = 'list', $account_check = NULL) {
global $user;
// Setting default behavior...
$account = $user;
// Because uid is submitted by the menu system, it's a string not a integer.
Sascha Grossenbacher
committed
if (is_object($account_check) && $account_check->uid != $user->uid) {
Sascha Grossenbacher
committed
if (!privatemsg_user_access('read all private messages')) {
return MENU_ACCESS_DENIED;
Sascha Grossenbacher
committed
// Has rights and user_load return an array so user does exist.
Sascha Grossenbacher
committed
$account = $account_check;
Sascha Grossenbacher
committed
return drupal_get_form('privatemsg_list', $argument, $account);
}
function privatemsg_list(&$form_state, $argument, $account) {
$query = _privatemsg_assemble_query('list', $account, $argument);
$result = pager_query($query['query'], variable_get('privatemsg_per_page', 25), 0, $query['count']);
$threads = array();
Sascha Grossenbacher
committed
$form = array('#list_argument' => $argument);
$form['#data'] = array();
while ($row = db_fetch_array($result)) {
// Store the raw row data.
$form['#data'][$row['thread_id']] = $row;
// store thread id for the checkboxes array
$threads[$row['thread_id']] = '';
}
if (!empty($form['#data'])) {
$form['actions'] = _privatemsg_action_form();
}
Sascha Grossenbacher
committed
// Save the currently active account, used for actions.
$form['account'] = array('#type' => 'value', '#value' => $account);
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// Define checkboxes, pager and theme
$form['threads'] = array('#type' => 'checkboxes', '#options' => $threads);
$form['pager'] = array('#value' => theme('pager'), '#weight' => 20);
$form['#theme'] = 'privatemsg_list';
// Store the account for which the threads are displayed.
$form['#account'] = $account;
return $form;
}
/**
* Process privatemsg_list form submissions.
*
* Execute the chosen action on the selected messages. This function is
* based on node_admin_nodes_submit().
*/
function privatemsg_list_submit($form, &$form_state) {
// Load all available operation definitions.
$operations = module_invoke_all('privatemsg_thread_operations');
// Default "default" operation, which won't do anything.
$operation = array('callback' => 0);
// Check if a valid operation has been submitted.
if (isset($form_state['values']['operation']) && isset($operations[$form_state['values']['operation']])) {
$operation = $operations[$form_state['values']['operation']];
}
// Load all keys where the value is the current op.
$keys = array_keys($form_state['values'], $form_state['values']['op']);
// The first one is op itself, we need to use the second.
if (isset($keys[1]) && isset($operations[$keys[1]])) {
$operation = $operations[$keys[1]];
}
// Only execute something if we have a valid callback and at least one checked thread.
if (!empty($operation['callback'])) {
Sascha Grossenbacher
committed
privatemsg_operation_execute($operation, $form_state['values']['threads'], $form_state['values']['account']);
}
}
/**
* Menu callback for viewing a thread.
*
* @param $thread
* A array containing all information about a specific thread, generated by
* privatemsg_thread_load().
* @return
* The page content.
* @see privatemsg_thread_load()
*/
function privatemsg_view($thread) {
drupal_set_title(check_plain($thread['subject']));
Sascha Grossenbacher
committed
if ($thread['to'] != $thread['message_count'] || !empty($thread['start'])) {
// Generate paging links.
$older = '';
if (isset($thread['older_start'])) {
$options = array(
'query' => array('start' => $thread['older_start']),
'title' => t('Display older messages'),
);
$older = l(t('<<'), privatemsg_get_dynamic_url_prefix() . '/view/' . $thread['thread_id'], $options);
}
$newer = '';
if (isset($thread['newer_start'])) {
$options = array(
'query' => array('start' => $thread['newer_start']),
'title' => t('Display newer messages'),
);
$newer = l(t('>>'), privatemsg_get_dynamic_url_prefix() . '/view/' . $thread['thread_id'], $options);
}
$substitutions = array('@from' => $thread['from'], '@to' => $thread['to'], '@total' => $thread['message_count'], '!previous_link' => $older, '!newer_link' => $newer);
$title = t('!previous_link Displaying messages @from - @to of @total !newer_link', $substitutions);
$content['pager'] = array(
'#value' => trim($title),
'#prefix' => '<div class="privatemsg-view-pager">',
'#suffix' => '</div>',
'#weight' => 3,
);
}
// Render the participants.
$content['participants']['#value'] = theme('privatemsg_recipients', $thread);
$content['participants']['#weight'] = -5;
// Render the messages.
$output = '';
foreach ($thread['messages'] as $pmid => $message) {
// Set message as read and theme it.
if (!empty($message['is_new'])) {
privatemsg_message_change_status($pmid, PRIVATEMSG_READ, $thread['user']);
}
$output .= theme('privatemsg_view', $message);
}
$content['messages']['#value'] = $output;
$content['messages']['#weight'] = 0;
// Display the reply form if user is allowed to use it.
Sascha Grossenbacher
committed
if (privatemsg_user_access('write privatemsg') || privatemsg_user_access('reply only privatemsg')) {
$content['reply']['#value'] = drupal_get_form('privatemsg_new', $thread['participants'], $thread['subject'], $thread['thread_id'], $thread['read_all']);
$content['reply']['#weight'] = 5;
}
// Check after calling the privatemsg_new form so that this message is only
// displayed when we are not sending a message.
if ($thread['read_all']) {
// User has permission to read all messages AND is not a participant of the current thread.
Sascha Grossenbacher
committed
drupal_set_message(t('This conversation is being viewed with escalated privileges and may not be the same as shown to normal users.'), 'warning');
}
// Allow other modules to hook into the $content array and alter it.
drupal_alter('privatemsg_view_messages', $content, $thread);
return drupal_render($content);
}
function privatemsg_new(&$form_state, $recipients = array(), $subject = '', $thread_id = NULL, $read_all = FALSE) {
global $user;
$recipients_string = '';
Sascha Grossenbacher
committed
$recipients_plain = '';
$body = '';
// convert recipients to array of user objects
if (!empty($recipients) && is_string($recipients) || is_int($recipients)) {
$recipients = _privatemsg_generate_user_array($recipients);
}
elseif (is_object($recipients)) {
$recipients = array($recipients);
}
elseif (empty($recipients) && is_string($recipients)) {
$recipients = array();
}
$usercount = 0;
$to = array();
Sascha Grossenbacher
committed
$to_plain = array();
$blocked = FALSE;
foreach ($recipients as $recipient) {
Sascha Grossenbacher
committed
// Allow to pass in normal user objects.
if (empty($recipient->type)) {
$recipient->type = 'user';
$recipient->recipient = $recipient->uid;
}
Sascha Grossenbacher
committed
if ($recipient->type == 'hidden') {
continue;
}
if (isset($to[privatemsg_recipient_key($recipient)])) {
// We already added the recipient to the list, skip him.
continue;
}
Sascha Grossenbacher
committed
if (!privatemsg_recipient_access($recipient->type, 'write', $recipient)) {
// User does not have access to write to this recipient, continue.
continue;
}
// Check if another module is blocking the sending of messages to the recipient by current user.
Sascha Grossenbacher
committed
$user_blocked = module_invoke_all('privatemsg_block_message', $user, array(privatemsg_recipient_key($recipient) => $recipient), array('thread_id' => $thread_id));
Sascha Grossenbacher
committed
if (!count($user_blocked) <> 0 && $recipient->recipient) {
if ($recipient->type == 'user' && $recipient->recipient == $user->uid) {
$usercount++;
// Skip putting author in the recipients list for now.
continue;
}
Sascha Grossenbacher
committed
$to[privatemsg_recipient_key($recipient)] = privatemsg_recipient_format($recipient);
Sascha Grossenbacher
committed
$to_plain[privatemsg_recipient_key($recipient)] = privatemsg_recipient_format($recipient, array('plain' => TRUE));
}
else {
// Recipient list contains blocked users.
$blocked = TRUE;
}
}
if (empty($to) && $usercount >= 1 && !$blocked) {
// Assume the user sent message to own account as if the usercount is one or less, then the user sent a message but not to self.
Sascha Grossenbacher
committed
$to['user_' . $user->uid] = privatemsg_recipient_format($user);
Sascha Grossenbacher
committed
$to_plain['user_' . $user->uid] = privatemsg_recipient_format($user, array('plain' => TRUE));
}
if (!empty($to)) {
$recipients_string = implode(', ', $to);
Sascha Grossenbacher
committed
$recipients_plain = implode(', ', $to_plain);
}
if (isset($form_state['values'])) {
if (isset($form_state['values']['recipient'])) {
Sascha Grossenbacher
committed
$recipients_plain = $form_state['values']['recipient'];
}
$subject = $form_state['values']['subject'];
$body = $form_state['values']['body'];
}
Sascha Grossenbacher
committed
if (!$thread_id && !empty($recipients_plain)) {
drupal_set_title(t('Write new message to %recipient', array('%recipient' => $recipients_plain)));
}
elseif (!$thread_id) {
drupal_set_title(t('Write new message'));
}
Sascha Grossenbacher
committed
$form = array(
'#type' => 'fieldset',
'#access' => privatemsg_user_access('write privatemsg') || privatemsg_user_access('reply only privatemsg'),
);
if (isset($form_state['privatemsg_preview'])) {
$form['message_header'] = array(
'#type' => 'fieldset',
'#title' => empty($form_state['validate_built_message']['thread_id']) ? check_plain($form_state['validate_built_message']['subject']) : t('Preview'),
'#attributes' => array('class' => 'preview'),
Sascha Grossenbacher
committed
'#weight' => -10,
);
$form['message_header']['message_preview'] = array(
'#value' => $form_state['privatemsg_preview'],
);
}
Sascha Grossenbacher
committed
$form['author'] = array(
'#type' => 'value',
'#value' => $user,
);
if (is_null($thread_id)) {
Sascha Grossenbacher
committed
$description_array = array();
foreach (privatemsg_recipient_get_types() as $name => $type) {
if (privatemsg_recipient_access($name, 'write')) {
$description_array[] = $type['description'];
}
}
$description = t('Enter the recipient, separate recipients with commas.');
$description .= theme('item_list', array('items' => $description_array));
Sascha Grossenbacher
committed
Sascha Grossenbacher
committed
$form['recipient'] = array(
'#type' => 'textfield',
'#title' => t('To'),
Sascha Grossenbacher
committed
'#description' => $description,
Sascha Grossenbacher
committed
'#default_value' => $recipients_plain,
'#required' => TRUE,
'#weight' => -10,
'#size' => 50,
Sascha Grossenbacher
committed
'#autocomplete_path' => 'messages/autocomplete',
// Do not hardcode #maxlength, make it configurable by number of recipients, not their name length.
);
}
Sascha Grossenbacher
committed
$form['subject'] = array(
'#type' => 'textfield',
'#title' => t('Subject'),
'#size' => 50,
'#maxlength' => 255,
'#default_value' => $subject,
'#weight' => -5,
);
Sascha Grossenbacher
committed
$form['body'] = array(
'#type' => 'textarea',
'#title' => t('Message'),
'#rows' => 6,
Sascha Grossenbacher
committed
'#weight' => -3,
'#default_value' => $body,
'#resizable' => TRUE,
);
$format = FILTER_FORMAT_DEFAULT;
// The input filter widget looses the format during preview, specify it
// explicitly.
if (isset($form_state['values']) && array_key_exists('format', $form_state['values'])) {
$format = $form_state['values']['format'];
}
Sascha Grossenbacher
committed
$form['format'] = filter_form($format);
Sascha Grossenbacher
committed
$form['format']['#access'] = privatemsg_user_access('select text format for privatemsg');
if (variable_get('privatemsg_display_preview_button', FALSE)) {
$form['preview'] = array(
'#type' => 'submit',
'#value' => t('Preview message'),
'#submit' => array('privatemsg_new_preview'),
'#weight' => 10,
);
}
Sascha Grossenbacher
committed
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Send message'),
'#weight' => 15,
);
Sascha Grossenbacher
committed
$url = privatemsg_get_dynamic_url_prefix();
$title = t('Cancel');
if (isset($_REQUEST['destination'])) {
$url = $_REQUEST['destination'];
}
elseif (!is_null($thread_id)) {
$url = $_GET['q'];
$title = t('Clear');
}
Sascha Grossenbacher
committed
$form['cancel'] = array(
'#value' => l($title, $url, array('attributes' => array('id' => 'edit-cancel'))),
'#weight' => 20,
);
if (!is_null($thread_id)) {
Sascha Grossenbacher
committed
$form['thread_id'] = array(
'#type' => 'value',
'#value' => $thread_id,
);
Sascha Grossenbacher
committed
$form['subject'] = array(
'#type' => 'value',
'#default_value' => $subject,
);
Sascha Grossenbacher
committed
$form['reply'] = array(
'#value' => '<h2 class="privatemsg-reply">' . t('Reply') . '</h2>',
'#weight' => -10,
);
if (empty($recipients_string)) {
// If there are no valid recipients, unset the message reply form.
Sascha Grossenbacher
committed
$form['#access'] = FALSE;
Sascha Grossenbacher
committed
// Only set read all if it is a boolean TRUE. It might also be an integer set
// through the URL.
Sascha Grossenbacher
committed
$form['read_all'] = array(
'#type' => 'value',
Sascha Grossenbacher
committed
'#value' => $read_all === TRUE,
);
return $form;
}
function privatemsg_new_validate($form, &$form_state) {
// The actual message that is being sent, we create this during validation and pass to submit to send out.
$message = $form_state['values'];
$message['timestamp'] = time();
$trimed_body = trim(truncate_utf8(strip_tags($message['body']), 50, TRUE, TRUE));
if (empty($message['subject']) && !empty($trimed_body)) {
$message['subject'] = $trimed_body;
}
// Only parse the user string for a new thread.
if (!isset($message['thread_id'])) {
Sascha Grossenbacher
committed
list($message['recipients'], $invalid, $duplicates, $denieds) = _privatemsg_parse_userstring($message['recipient']);
}
else {
Sascha Grossenbacher
committed
// Load participants. Limit recipients to visible unless read_all is TRUE.
$message['recipients'] = _privatemsg_load_thread_participants($message['thread_id'], $message['read_all'] ? FALSE : $message['author']);
Sascha Grossenbacher
committed
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
if (!empty($invalid)) {
// Display information about invalid recipients.
drupal_set_message(t('The following users will not receive this private message: @invalid.', array('@invalid' => implode(", ", $invalid))), 'error');
}
if (!empty($denieds)) {
// Display information about denied recipients.
drupal_set_message(t('You do not have access to write these recipients: @denieds.', array('@denieds' => implode(", ", $denieds))), 'error');
}
if (!empty($duplicates)) {
// Add JS and CSS to allow choosing the recipient.
drupal_add_js(drupal_get_path('module', 'privatemsg') . '/privatemsg-alternatives.js');
// Display information about recipients that couldn't be identified
// uniquely.
$js_duplicates = array();
foreach ($duplicates as $string => $duplicate) {
$alternatives = array();
foreach ($duplicate as $match) {
$formatted_match = privatemsg_recipient_format($match, array('plain' => TRUE, 'unique' => TRUE));
$js_duplicates[$formatted_match] = $string;
$alternatives[] = '<span class="privatemsg-recipient-alternative">' . $formatted_match . '</span>';
}
// Build a formatted list of possible recipients.
$alternatives = theme('item_list', $alternatives, NULL, 'ul', array('class' => 'action-links'));
form_set_error('recipient', '<div class="privatemsg-alternative-description">' . t('The site has multiple recipients named %string. Please choose your intended recipient: !list', array('%string' => $string, '!list' => $alternatives)) . '</div>');
}
// Also make that information available to the javascript replacement code.
drupal_add_js(array('privatemsg_duplicates' => $js_duplicates), 'setting');
}
Sascha Grossenbacher
committed
$validated = _privatemsg_validate_message($message, TRUE);
foreach ($validated['messages'] as $type => $text) {
drupal_set_message($text, $type);
}
$form_state['validate_built_message'] = $message;
}
/**
* Submit callback for the privatemsg_new form.
*/
function privatemsg_new_submit($form, &$form_state) {
Sascha Grossenbacher
committed
// Clear form_state storage so that it does not rebuild.
$form_state['storage'] = NULL;
$status = _privatemsg_send($form_state['validate_built_message']);
Sascha Grossenbacher
committed
// Format each recipient.
$recipient_names = array();
foreach ($form_state['validate_built_message']['recipients'] as $recipient) {
Sascha Grossenbacher
committed
$recipient_names[] = privatemsg_recipient_format($recipient);
}
if ($status !== FALSE ) {
Sascha Grossenbacher
committed
_privatemsg_handle_recipients($status['mid'], $status['recipients']);
drupal_set_message(t('A message has been sent to !recipients.', array('!recipients' => implode(', ', $recipient_names))));
Sascha Grossenbacher
committed
// Only redirect on new threads.
if ($status['mid'] == $status['thread_id'] || variable_get('privatemsg_default_redirect_reply', FALSE)) {
$redirect = variable_get('privatemsg_default_redirect', '<new-message>');
if ($redirect == '<new-message>') {
// Forward to the new message in the thread.
Sascha Grossenbacher
committed
$form_state['redirect'] = array(privatemsg_get_dynamic_url_prefix() . '/view/' . $status['thread_id'], NULL, 'privatemsg-mid-' . $status['mid']);
Sascha Grossenbacher
committed
}
elseif (!empty($redirect)) {
$form_state['redirect'] = $redirect;
}
}
Sascha Grossenbacher
committed
// Replace [new-message] placeholder with actual destination.
if (!empty($_REQUEST['destination']) && $_REQUEST['destination'] == '[new-message]') {
// url() can not be used because it does create an path with base path and
// prefix.
Sascha Grossenbacher
committed
$_REQUEST['destination'] = urlencode((privatemsg_get_dynamic_url_prefix() . '/view/' . $status['thread_id'] . '#' . 'privatemsg-mid-' . $status['mid']));
Sascha Grossenbacher
committed
}
}
else {
drupal_set_message(t('An attempt to send a message <em>may have failed</em> when sending to !recipients.', array('!recipients' => implode(', ', $recipient_names))), 'error');
}
}
function privatemsg_new_preview($form, &$form_state) {
drupal_validate_form($form['form_id']['#value'], $form, $form_state);
if (!form_get_errors()) {
$form_state['privatemsg_preview'] = theme('privatemsg_view', $form_state['validate_built_message']);
}
$form_state['rebuild'] = TRUE; // this forces our form to be rebuilt instead of being submitted.
}
function privatemsg_delete($form_state, $thread, $message) {
Sascha Grossenbacher
committed
$form['mid'] = array(
'#type' => 'value',
'#value' => $message['mid'],
);
$form['delete_destination'] = array(
'#type' => 'value',
Sascha Grossenbacher
committed
'#value' => count($thread['messages']) > 1 ? privatemsg_get_dynamic_url_prefix() . '/view/' . $message['thread_id'] : privatemsg_get_dynamic_url_prefix(),
);
if (privatemsg_user_access('read all private messages')) {
$form['delete_options'] = array(
'#type' => 'checkbox',
'#title' => t('Delete this message for all users?'),
'#description' => t('Tick the box to delete the message for all users.'),
'#default_value' => FALSE,
);
}
return confirm_form($form,
t('Are you sure you want to delete this message?'),
Sascha Grossenbacher
committed
isset($_GET['destination']) ? $_GET['destination'] : privatemsg_get_dynamic_url_prefix() . '/view/'. $message['thread_id'],
t('This action cannot be undone.'),
t('Delete'),
t('Cancel')
);
}
function privatemsg_delete_submit($form, &$form_state) {
global $user;
$account = drupal_clone($user);
if ($form_state['values']['confirm']) {
if (isset($form_state['values']['delete_options']) && $form_state['values']['delete_options']) {
Sascha Grossenbacher
committed
privatemsg_message_change_delete($form_state['values']['mid'], 1);
drupal_set_message(t('Message has been deleted for all users.'));
}
else {
Sascha Grossenbacher
committed
privatemsg_message_change_delete($form_state['values']['mid'], 1, $account);
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
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
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
drupal_set_message(t('Message has been deleted.'));
}
}
$form_state['redirect'] = $form_state['values']['delete_destination'];
}
/**
* Returns a form which handles and displays thread actions.
*
* Additional actions can be added with the privatemsg_thread_operations hook.
* It is also possible to extend this form with additional buttons or other
* elements, in that case, the definitions in the above hook need no label tag,
* instead, the submit button key needs to match with the key of the operation.
*
* @see hook_privatemsg_thread_operations()
*
* @return
* The FAPI definitions for the thread action form.
*/
function _privatemsg_action_form() {
$form = array(
'#type' => 'fieldset',
'#title' => t('Actions'),
'#prefix' => '<div class="container-inline">',
'#suffix' => '</div>',
'#collapsible' => TRUE,
'#collapsed' => FALSE,
'#weight' => 15,
);
if (privatemsg_user_access('delete privatemsg')) {
$form['delete'] = array(
'#type' => 'submit',
'#value' => t('Delete'),
);
}
// Display all operations which have a label.
$options = array(0 => t('More actions...'));
foreach (module_invoke_all('privatemsg_thread_operations') as $operation => $array) {
if (isset($array['label'])) {
$options[$operation] = $array['label'];
}
}
$form['operation'] = array(
'#type' => 'select',
'#options' => $options,
'#default_value' => 0,
);
$form['submit'] = array(
'#prefix' => '<div class="privatemsg-op-button">',
'#suffix' => '</div>',
'#type' => 'submit',
'#value' => t('Execute'),
'#submit' => array('privatemsg_list_submit'),
'#attributes' => array('class' => 'privatemsg-action-button'),
);
// JS for hiding the execute button.
drupal_add_js(drupal_get_path('module', 'privatemsg') .'/privatemsg-list.js');
return $form;
}
/**
* Returns a table header definition based on the submitted keys.
*
* Uses @link theming theme patterns @endlink to theme single headers.
*
* @param $has_posts
* TRUE when there is at least one row. Decides if the select all checkbox
* should be displayed.
* @param $keys
* Array with the keys which are present in the query/should be displayed.
* @return
* Array with header defintions for tablesort_sql and theme('table').
*/
function _privatemsg_list_headers($has_posts, $keys) {
$select_header = $has_posts ? theme('table_select_header_cell') : '';
$select_header['#weight'] = -50;
// theme() doesn't include the theme file for patterns, we need to do it manually.
include_once drupal_get_path('module', 'privatemsg') .'/privatemsg.theme.inc';
$header = array($select_header);
foreach ($keys as $key) {
// First, try to load a specific theme for that header, if not present, use the default.
if ($return = theme(array('privatemsg_list_header__'. $key, 'privatemsg_list_header'))) {
// The default theme returns nothing, only store the value if we have something.
$header[$key] = $return;
}
}
if (count($header) == 1) {
// No header definition returned, fallback to the default.
$header += _privatemsg_list_headers_fallback($keys);
}
return $header;
}
/**
* Table header definition for themes that don't support theme patterns.
*
* @return
* Array with the correct headers.
*/
function _privatemsg_list_headers_fallback($keys) {
$header = array();
foreach ($keys as $key) {
$theme_function = 'phptemplate_privatemsg_list_header__' . $key;
if (function_exists($theme_function)) {
$header[$key] = $theme_function();
}
}
return $header;
}
/**
* Formats a row in the message list.
*
* Uses @link theming theme patterns @endlink to theme single fields.
*
* @param $thread
* Array with the row data returned by the database.
* @return
* Row definition for use with theme('table')
*/
function _privatemsg_list_thread($thread) {
$row = array('data' => array());
if (!empty($thread['is_new'])) {
// Set the css class in the tr tag.
$row['class'] = 'privatemsg-unread';
}
foreach ($thread as $key => $data) {
// First, try to load a specific theme for that field, if not present, use the default.
if ($return = theme(array('privatemsg_list_field__'. $key, 'privatemsg_list_field'), $thread)) {
// The default theme returns nothing, only store the value if we have something.
$row['data'][$key] = $return;
}
}
if (empty($row['data'])) {
$row['data'] = _privatemsg_list_thread_fallback($thread);
}
return $row;
}
/**
* Table row definition for themes that don't support theme patterns.
*
* @return
* Array with row data.
*/
function _privatemsg_list_thread_fallback($thread) {
$row_data = array();
foreach ($thread as $key => $data) {
$theme_function = 'phptemplate_privatemsg_list_field__' . $key;
if (function_exists($theme_function)) {
$row_data[$key] = $theme_function($thread);
}
}
return $row_data;
}
/**
* Menu callback for messages/undo/action.
*
* This function will test if an undo callback is stored in SESSION and
* execute it.
*/
function privatemsg_undo_action() {
// Check if a undo callback for that user exists.
if (isset($_SESSION['privatemsg']['undo callback']) && is_array($_SESSION['privatemsg']['undo callback'])) {
$undo = $_SESSION['privatemsg']['undo callback'];
// If the defined undo callback exists, execute it
if (isset($undo['function']) && isset($undo['args'])) {
Sascha Grossenbacher
committed
// Load the user object.
if (isset($undo['args']['account']) && $undo['args']['account'] > 0) {
$undo['args']['account'] = user_load((int)$undo['args']['account']);
}
call_user_func_array($undo['function'], $undo['args']);
}
// Return back to the site defined by the destination GET param.
drupal_goto();
}
}
/**
* Return autocomplete results for usernames.
*
* Prevents usernames from being used and/or suggested twice.
*/
Sascha Grossenbacher
committed
function privatemsg_autocomplete($string) {
$names = array();
// 1: Parse $string and build list of valid user names.
$fragments = explode(',', $string);
foreach ($fragments as $index => $name) {
if ($name = trim($name)) {
$names[$name] = $name;
}
}
// 2: Find the next user name suggestion.
$fragment = array_pop($names);
$matches = array();
if (!empty($fragment)) {
Sascha Grossenbacher
committed
$remaining = 10;
$types = privatemsg_recipient_get_types();
foreach ($types as $name => $type) {
if (isset($type['autocomplete']) && is_callable($type['autocomplete']) && privatemsg_recipient_access($name, 'write')) {
$function = $type['autocomplete'];
$return = $function($fragment, $names, $remaining);
if (is_array($return) && !empty($return)) {
$matches = array_merge($matches, $return);
}
Sascha Grossenbacher
committed
$remaining = 10 - count($matches);
if ($remaining <= 0) {
break;
}
}
Sascha Grossenbacher
committed
// Format the suggestions.
$themed_matches = array();
foreach ($matches as $key => $match) {
$themed_matches[$key] = privatemsg_recipient_format($match, array('plain' => TRUE));
}
// Check if there are any duplicates.
if (count(array_unique($themed_matches)) != count($themed_matches)) {
// Loop over matches, look for duplicates of each one.
foreach ($themed_matches as $key => $themed_match) {
$duplicate_keys = array_keys($themed_matches, $themed_match);
if (count($duplicate_keys) > 1) {
// There are duplicates, make them unique.
foreach ($duplicate_keys as $duplicate_key) {
// Reformat them with unique argument.
$themed_matches[$duplicate_key] = privatemsg_recipient_format($matches[$duplicate_key], array('plain' => TRUE, 'unique' => TRUE));
}
}
}
}
Sascha Grossenbacher
committed
// Prefix the matches and convert them to the correct form for the
// autocomplete.
$prefix = count($names) ? implode(", ", $names) .", " : '';
$suggestions = array();
Sascha Grossenbacher
committed
foreach ($themed_matches as $match) {
Sascha Grossenbacher
committed
$suggestions[$prefix . $match . ', '] = $match;
}
// convert to object to prevent drupal bug, see http://drupal.org/node/175361
Sascha Grossenbacher
committed
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
drupal_json((object)$suggestions);
}
/**
* Batch processing function for rebuilding the index.
*/
function privatemsg_load_recipients($mid, $recipient, &$context) {
// Get type information.
$type = privatemsg_recipient_get_type($recipient->type);
// First run, initialize sandbox.
if (!isset($context['sandbox']['current_offset'])) {
$context['sandbox']['current_offset'] = 0;
$count_function = $type['count'];
$context['sandbox']['count'] = $count_function($recipient);
}
// Fetch the 10 next recipients.
$load_function = $type['generate recipients'];
$uids = $load_function($recipient, 10, $context['sandbox']['current_offset']);
if (!empty($uids)) {
foreach ($uids as $uid) {
privatemsg_message_change_recipient($mid, $uid, 'hidden');
}
$context['sandbox']['current_offset'] += 10;
// Set finished based on sandbox.
$context['finished'] = empty($context['sandbox']['count']) ? 1 : ($context['sandbox']['current_offset'] / $context['sandbox']['count']);
}
else {
// If no recipients were returned, mark as finished too.
$context['sandbox']['finished'] = 1;
}
// If we are finished, mark the recipient as read.
if ($context['finished'] >= 1) {
db_query("UPDATE {pm_index} SET is_new = %d WHERE mid = %d AND recipient = %d AND type = '%s'", PRIVATEMSG_READ, $mid, $recipient->recipient, $recipient->type);
}