? .cvsignore ? privatemsg_readall_4.patch ? privatemsg_readall_5.patch ? privatemsg_readall_5.patch_broken Index: privatemsg.api.php =================================================================== RCS file: /cvs/drupal/contributions/modules/privatemsg/Attic/privatemsg.api.php,v retrieving revision 1.1.2.5 diff -u -p -r1.1.2.5 privatemsg.api.php --- privatemsg.api.php 8 Jun 2009 13:38:41 -0000 1.1.2.5 +++ privatemsg.api.php 12 Aug 2009 09:05:18 -0000 @@ -150,43 +150,36 @@ function hook_privatemsg_sql_autocomplet function hook_privatemsg_sql_list_alter(&$fragment, $account) { } -/** - * Display a list of sent messages. - * - * @param $fragments - * Query fragments - * @param $account - * User object - */ -function hook_privatemsg_sql_list_sent_alter(&$fragment, $account) { - -} /** - * Load a single message. + * Query definition to load a message. * * @param $fragments - * Query fragments + * Query fragments array. * @param $pmid - * message id, pm.mid - * @param $account - * User object + * the id of the message. + * @param $account + * User object of account for which to load the message. */ -function hook_privatemsg_sql_load_alter(&$fragment, $pmid, $account) { +function privatemsg_sql_load(&$fragments, $pmid, $account) { } + /** - * Load all message id's of a thread. + * Query definition to load messages of one or multiple threads. * * @param $fragments - * Query fragments - * @param $thread_id - * Thread id, pmi.thread_id is the same as the mid of the first - * message of that thread + * Query fragments array. + * @param $threads + * Array with one or multiple thread id's. * @param $account - * User object + * User object for which the messages are being loaded. + * @param $load_all + * Deleted messages are only loaded if this is set to TRUE. + * @param $read_all + * Messages are not filtered to the user if this is set to TRUE. */ -function hook_privatemsg_sql_messages_alter(&$fragment, $thread_id, $account) { +function privatemsg_sql_messages(&$fragments, $threads, $account, $load_all = FALSE, $read_all = FALSE) { } @@ -294,10 +287,12 @@ function hook_privatemsg_message_load($m * user. * @todo There is no "undelete" hook * - * @param $message - * Message array + * @param $pmid + * ID of the message that has been deleted + * @param $deleted_by_all + * Boolean to show whether the message has been deleted by all users or not */ -function hook_privatemsg_message_delete($message) { +function hook_privatemsg_message_delete($pmid, $deleted_by_all) { } Index: privatemsg.module =================================================================== RCS file: /cvs/drupal/contributions/modules/privatemsg/privatemsg.module,v retrieving revision 1.70.2.30.2.91.2.64 diff -u -p -r1.70.2.30.2.91.2.64 privatemsg.module --- privatemsg.module 30 Jul 2009 20:02:05 -0000 1.70.2.30.2.91.2.64 +++ privatemsg.module 12 Aug 2009 09:05:20 -0000 @@ -184,6 +184,15 @@ function privatemsg_menu() { 'access arguments' => array('read privatemsg'), 'type' => MENU_CALLBACK, ); + $items['user/%/messages'] = array( + 'title' => 'Messages', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('privatemsg_list', 'list', 1), + 'access callback' => 'privatemsg_user_tab_access', + 'access arguments' => array(1), + 'type' => MENU_LOCAL_TASK, + ); + return $items; } @@ -214,6 +223,30 @@ function privatemsg_user_access($permiss return TRUE; } +/** + * Function to check if a user can access the messages tab on a user page. + * + * @param $uid + * the user's id for whom the permission is being checked. + */ +function privatemsg_user_tab_access($uid) { + global $user; + $account = $user; + + // Disallow anonymous access, regardless of permissions. + if (!$account->uid) { + return FALSE; + } + // If a user is viewing own account, allow access. + if ($account->uid == $uid && privatemsg_user_access('read privatemsg', $account)) { + return TRUE; + } + // If a user has permission to view other user's messages, then allow access. + if (privatemsg_user_access('read all private messages', $account)) { + return TRUE; + } + return FALSE; +} /** * Check access to the view messages page. @@ -261,11 +294,23 @@ function privatemsg_thread_load($thread_ global $user; $account = drupal_clone($user); } + + // Load the list of participants. + $query = _privatemsg_assemble_query('participants', $thread_id); + $participants = db_query($query['query']); + while ($participant = db_fetch_object($participants)) { + $thread['participants'][$participant->uid] = $participant; + } + $thread['read_all'] = FALSE; + if (!array_key_exists($account->uid, $thread['participants']) && privatemsg_user_access('read all private messages', $account)) { + $thread['read_all'] = TRUE; + } + // load messages returned by the messages query with _privatemsg_load(). - $query = _privatemsg_assemble_query('messages', array($thread_id), $account); + $query = _privatemsg_assemble_query('messages', array($thread_id), $thread['read_all'] ? NULL : $account); $conversation = db_query($query['query']); while ($result = db_fetch_array($conversation)) { - if ($message = _privatemsg_load($result['mid'], $account)) { + if ($message = _privatemsg_load($result['mid'], $thread['read_all'] ? NULL : $account)) { $thread['messages'][$result['mid']] = $message; } } @@ -279,12 +324,6 @@ function privatemsg_thread_load($thread_ $message = current($thread['messages']); $thread['subject'] = $message['subject']; - // Load the list of participants. - $query = _privatemsg_assemble_query('participants', $thread_id); - $participants = db_query($query['query']); - while ($participant = db_fetch_object($participants)) { - $thread['participants'][$participant->uid] = $participant; - } return $thread; } return FALSE; @@ -570,6 +609,11 @@ function privatemsg_unread_count($accoun function privatemsg_view($thread) { drupal_set_title(check_plain($thread['subject'])); + if ($thread['read_all']) { + // User has permission to read all messages AND is not a participant of the current thread. + drupal_set_message(t('This conversation is being viewed with escalated priviledges and may not be the same as shown to normal users.'), 'warning'); + } + // Render the participants. $content['participants']['#value'] = theme('privatemsg_recipients', $thread); $content['participants']['#weight'] = -5; @@ -588,7 +632,7 @@ function privatemsg_view($thread) { // Display the reply form if user is allowed to use it. if (privatemsg_user_access('write privatemsg')) { - $content['reply']['#value'] = drupal_get_form('privatemsg_new', $thread['participants'], $thread['subject'], $thread['thread_id']); + $content['reply']['#value'] = drupal_get_form('privatemsg_new', $thread['participants'], $thread['subject'], $thread['thread_id'], $thread['read_all']); $content['reply']['#weight'] = 5; } @@ -598,7 +642,7 @@ function privatemsg_view($thread) { } -function privatemsg_new(&$form_state, $recipients = array(), $subject = '', $thread_id = NULL) { +function privatemsg_new(&$form_state, $recipients = array(), $subject = '', $thread_id = NULL, $read_all = FALSE) { global $user; $recipients_string = ''; @@ -748,6 +792,10 @@ function privatemsg_new(&$form_state, $r // If there are no valid recipients, unset the message reply form. $form['privatemsg']['#access'] = FALSE; } + $form['privatemsg']['read_all'] = array( + '#type' => 'value', + '#value' => $read_all, + ); } return $form; } @@ -759,6 +807,7 @@ function pm_send_validate($form, &$form_ $message['body'] = $form_state['values']['body']; $message['subject'] = $form_state['values']['subject']; $message['author'] = $form_state['values']['author']; + $message['read_all'] = $form_state['values']['read_all']; $message['timestamp'] = time(); if (isset($form_state['values']['thread_id']) && $form_state['values']['thread_id']) { $message['thread_id'] = $form_state['values']['thread_id']; @@ -904,10 +953,16 @@ function privatemsg_sql_list(&$fragments */ /** - * Query function for load. + * Query definition to load a message. + * + * @param $fragments + * Query fragments array. + * @param $pmid + * the id of the message. + * @param $account + * User object of account for which to load the message. */ -function privatemsg_sql_load(&$fragments, $pmid, $account) { -// drupal_set_message('
'. print_r(func_get_args(), 1) . '
'); +function privatemsg_sql_load(&$fragments, $pmid, $account = NULL) { $fragments['primary_table'] = '{pm_message} pm'; // Our primary table $fragments['select'][] = "pm.mid"; @@ -920,29 +975,37 @@ function privatemsg_sql_load(&$fragments $fragments['inner_join'][] = 'INNER JOIN {pm_index} pmi ON pm.mid = pmi.mid'; $fragments['where'][] = 'pmi.mid = %d'; $fragments['query_args']['where'][] = $pmid; - $fragments['where'][] = 'pmi.uid = %d'; - $fragments['query_args']['where'][] = $account->uid; + if ($account) { + $fragments['where'][] = 'pmi.uid = %d'; + $fragments['query_args']['where'][] = $account->uid; + } } + /** * Query definition to load messages of one or multiple threads. * * @param $fragments - * Query fragments array. + * Query fragments array. * @param $threads - * Array with one or multiple thread id's. + * Array with one or multiple thread id's. * @param $account - * User object for which the messages are being loaded. + * User object for which the messages are being loaded. * @param $load_all - * Deleted messages are only loaded if this is set to TRUE. + * Deleted messages are only loaded if this is set to TRUE. + * @param $read_all + * Messages are not filtered to the user if this is set to TRUE. */ -function privatemsg_sql_messages(&$fragments, $threads, $account, $load_all = FALSE) { +function privatemsg_sql_messages(&$fragments, $threads, $account = NULL, $load_all = FALSE) { $fragments['primary_table'] = '{pm_index} pmi'; $fragments['select'][] = 'DISTINCT(pmi.mid) as mid'; $fragments['where'][] = 'pmi.thread_id IN ('. db_placeholders($threads) .')'; $fragments['query_args']['where'] += $threads; - $fragments['where'][] = 'pmi.uid = %d'; - $fragments['query_args']['where'][] = $account->uid; + if ($account) { + // Only load the user's messages. + $fragments['where'][] = 'pmi.uid = %d'; + $fragments['query_args']['where'][] = $account->uid; + } if (!$load_all) { $fragments['where'][] = 'pmi.deleted = 0'; } @@ -1133,14 +1196,21 @@ function _privatemsg_block_menu() { } function privatemsg_delete($form_state, $pmid) { - global $user; $form['pmid'] = array( '#type' => 'value', '#value' => $pmid, ); + if (privatemsg_user_access('read all private messages')) { + $form['delete_options'] = array( + '#type' => 'checkbox', + '#title' => 'Delete this message for all users?', + '#description' => '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 the message?'), + t('Are you sure you want to delete this message?'), isset($_GET['destination']) ? $_GET['destination'] : 'messages/view/'. $pmid, t('This action cannot be undone.'), t('Delete'), @@ -1148,6 +1218,23 @@ function privatemsg_delete($form_state, ); } +function privatemsg_delete_submit($form, &$form_state) { + global $user; + $account = drupal_clone($user); + + if ($form_state['values']['confirm']) { + if ($form_state['values']['delete_options']) { + privatemsg_message_change_delete($form_state['values']['pmid'], 1); + drupal_set_message(t('Message has been deleted for all users')); + } + else { + privatemsg_message_change_delete($form_state['values']['pmid'], 1, $account); + drupal_set_message(t('Message has been deleted')); + } + } + $form_state['redirect'] = 'messages'; +} + /** * Delete or restore a message. * @@ -1156,36 +1243,28 @@ function privatemsg_delete($form_state, * @param $delete * Either deletes or restores the thread (1 => delete, 0 => restore) * @param $account - * User acccount for which the message should be deleted. + * User acccount for which the delete action should be carried out - Set to NULL to delete for all users. * * @ingroup api */ function privatemsg_message_change_delete($pmid, $delete, $account = NULL) { - if (is_null($account)) { - global $user; - $account = drupal_clone($user); + if ($account){ + db_query('UPDATE {pm_index} SET deleted = %d WHERE mid = %d AND uid = %d', $delete, $pmid, $account->uid); + } + else { + // Mark deleted for all users. + db_query('UPDATE {pm_index} SET deleted = %d WHERE mid = %d', $delete, $pmid); } - $message = _privatemsg_load($pmid, $account); - - db_query('UPDATE {pm_index} SET deleted = %d WHERE mid = %d AND uid = %d', $delete, $pmid, $account->uid); $result = db_query("SELECT MIN(deleted) AS deleted_by_all FROM {pm_index} WHERE mid = %d", $pmid); $deleted = db_fetch_array($result); $deleted_by_all = FALSE; - if ($deleted['deleted_by_all'] == 0) { + if ($deleted['deleted_by_all'] > 0) { $deleted_by_all = TRUE; } - module_invoke_all('privatemsg_message_delete', $message, $deleted_by_all); -} - -function privatemsg_delete_submit($form, &$form_state) { - if ($form_state['values']['confirm']) { - privatemsg_message_change_delete($form_state['values']['pmid'], 1); - drupal_set_message(t('Message has been deleted')); - } - $form_state['redirect'] = 'messages'; + module_invoke_all('privatemsg_message_delete', $pmid, $deleted_by_all); } /** @@ -1431,22 +1510,38 @@ function _privatemsg_send($message) { // Each recipient gets a record in the pm_index table. $query = "INSERT INTO {pm_index} (mid, thread_id, uid, is_new, deleted) VALUES (%d, %d, %d, %d, 0)"; foreach ($message['recipients'] as $recipient) { - if ( !db_query($query, $mid, $message['thread_id'], $recipient->uid, 1) ) { - return FALSE; // We assume if one insert failed then the rest may fail too against the same table + if (!db_query($query, $mid, $message['thread_id'], $recipient->uid, 1) ) { + // We assume if one insert failed then the rest may fail too against the + // same table. + return FALSE; + } + } + + if (isset($message['read_all'])) { + // The message was sent in read all mode, add the author as recipient to all + // existing messages. + $query_messages = _privatemsg_assemble_query('messages', array($message['thread_id']), NULL); + $conversation = db_query($query_messages['query']); + while ($result = db_fetch_array($conversation)) { + if (!db_query($query, $result['mid'], $message['thread_id'], $message['author']->uid, 0)) { + return FALSE; + } } } - // When author is also the recipient, we want to set message to UNREAD. all other times his message is set to READ. + // When author is also the recipient, we want to set message to UNREAD. + // all other times his message is set to READ. $is_new = isset($message['recipients'][$message['author']->uid]) ? 1 : 0; // Also add a record for the author to the pm_index table. - if ( !db_query($query, $mid, $message['thread_id'], $message['author']->uid, $is_new)) { - return FALSE; // same as above + if (!db_query($query, $mid, $message['thread_id'], $message['author']->uid, $is_new)) { + return FALSE; } module_invoke_all('privatemsg_message_insert', $message); - return $message; // if we reached here that means we were successful at writing all messages to db + // If we reached here that means we were successful at writing all messages to db. + return $message; } /** @@ -1511,23 +1606,19 @@ function privatemsg_get_link($recipients * @ingroup api */ function _privatemsg_load($pmid, $account = NULL) { - if (empty($account)) { - global $user; - $account = drupal_clone($user); - } - $query = _privatemsg_assemble_query('load', $pmid, $account); $result = db_query($query['query']); - $message = db_fetch_array($result); - $message['user'] = $account; - // Load author of message. - $message['author'] = user_load($message['author']); - $returned = module_invoke_all('privatemsg_message_load', $message); - if (!empty($returned)) { - $message = array_merge_recursive($returned, $message); + if ($message = db_fetch_array($result)) { + // Load author of message. + $message['author'] = user_load($message['author']); + $returned = module_invoke_all('privatemsg_message_load', $message); + if (!empty($returned)) { + $message = array_merge_recursive($returned, $message); + } + return $message; } - return $message; + return FALSE; } /**