t('Private messages'), 'page callback' => 'privatemsg_hello', 'access arguments' => array('read privatemsg'), 'type' => MENU_NORMAL_ITEM, ); //list all messages this user received $items['messages/inbox'] = array( 'title' => t('Inbox'), 'page callback' => 'privatemsg_list', 'access arguments' => array('read privatemsg'), 'type' => MENU_NORMAL_ITEM, 'weight' => -10, ); $items['messages/mark-read'] = array( 'title' => t('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, ); //list messages that this user is an author of $items['messages/sent'] = array( 'title' => t('Sent'), 'page callback' => 'privatemsg_list', 'access arguments' => array('read privatemsg'), 'type' => MENU_NORMAL_ITEM, 'weight' => -5, ); //view a single message $items['messages/view/%'] = array( 'title' => t('Read Private Message'), 'page callback' => 'privatemsg_view', 'page arguments' => array(2), 'access arguments' => array('read privatemsg'), 'type' => MENU_CALLBACK, 'weight' => -10, ); //create a new message $items['messages/new'] = array( 'title' => t('Write New'), 'page callback' => 'drupal_get_form', 'page arguments' => array('privatemsg_new'), 'access arguments' => array('write privatemsg'), 'type' => MENU_NORMAL_ITEM, 'weight' => -7, ); $items['messages/new/%message_recipient'] = array( 'page callback' => 'drupal_get_form', 'page arguments' => array('privatemsg_new', 2), 'access arguments' => array('write privatemsg'), 'type' => MENU_CALLBACK, 'weight' => -10, ); //auto completes available user names & removes duplicates $items['messages/user-name-autocomplete'] = array( 'page callback' => 'privatemsg_user_name_autocomplete', 'access arguments' => array('write privatemsg'), 'type' => MENU_CALLBACK, 'weight' => -10, ); //Admin pages $items['admin/settings/messages'] = array( 'title' => t('Private Messages'), 'page callback' => 'drupal_get_form', 'page arguments' => array('private_message_settings'), 'access arguments' => array('administer privatemsg settings'), 'type' => MENU_NORMAL_ITEM, ); return $items; } function message_recipient_load($uid) { $user = NULL; if ( is_numeric($uid)) { $user = user_load(array('uid' => $uid)); } return $user; } function private_message_view_options() { $options = module_invoke_all('privatemsg_view_template'); return $options; } /** * 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( 'privatemsg-view' => 'Default view (Facebook style)', ); } function private_message_settings() { $form = array(); $form['theming_settings'] = array( '#type' => 'fieldset', '#collapsible' => TRUE, '#collapsed' => TRUE, '#title' => t('Theming Settings'), ); $form['theming_settings']['private_message_view_template'] = array( '#type' => 'radios', '#title' => t('Private Message Display Template'), '#default_value' => variable_get('private_message_view_template', 'privatemsg-view'), '#options' => private_message_view_options(), ); $form['#submit'][] = 'private_message_settings_submit'; return system_settings_form($form); } function private_message_settings_submit() { drupal_rebuild_theme_registry(); } function privatemsg_theme() { return array( 'privatemsg_view' => array( 'arguments' => array('message' => NULL), 'template' => variable_get('private_message_view_template', 'privatemsg-view'),//'privatemsg', ), 'privatemsg_from' => array( 'arguments' => array('author' => NULL), 'template' => 'privatemsg-from', ), 'privatemsg_to' => array( 'arguments' => array('message' => NULL), 'template' => 'privatemsg-recipients', ), 'privatemsg_between' => array( 'arguments' => array('recipients' => NULL), 'template' => 'privatemsg-between', ), ); } function privatemsg_preprocess_privatemsg_view(&$vars) { $message = $vars['message']; $vars['author_picture'] = theme('user_picture', $message['author']); $vars['author_name_link'] = theme('username', $message['author']); $vars['message_timestamp'] = date("M j \a\\t g:ia", $message['timestamp']); $vars['message_body'] = check_markup($message['body']); // drupal_set_message('
'. print_r('what', 1) .'
'); // drupal_set_message('
'. print_r(func_get_args(), 1) .'
'); // drupal_set_message('
'. print_r($vars, 1) .'
'); } function privatemsg_preprocess_privatemsg_to(&$vars) { global $user; //replicates facebook display for participants of a message $to = array(); if ( isset($vars['message']['recipients'])) { foreach ($vars['message']['recipients'] as $uid => $account ) { if ($vars['message']['author']->uid == $account->uid) { continue; //skip putting author in the recipients list for now } $to[]= theme('username', $account); } } $recipients = implode(', ', $to); $you = theme('username', $vars['message']['author']); if (strlen($recipients)) { $recipients .= " and "; } $vars['recipients'] = 'Between '. $recipients . $you; } /** * Function that lists messages * * @param $uid - user id for whom to load messages */ function privatemsg_list($uid = NULL) { global $user; disallow_anon_access(); if (!$uid) { // default behavior: we are trying to view our own private message if no uid is passed $account = $user; } else { //here it means we are viewing either our own or some one else's messages if ($uid && $uid == $user->uid) { //viewing our own messagess $account = $user; } else if ($uid && $uid != $user->uid && user_access('read all private messages') ) { $account = user_load(array('uid' => $uid)); } else { //we tried viewing some one else's messages but didnt have sufficient rights drupal_set_message("You do not have sufficient rights to view some one else's messages", WATCHDOG_WARNING); $account = $user; } }//end access permissions /** * By This point we have figured out for which user we are listing messages * now it is safe to use $account->uid in listing query */ // drupal_set_message('
'. print_r($uid, 1) .'
'); if ( arg(1) == 'inbox') { $query = _privatemsg_assemble_query('privatemsg_list', $account); } else if( arg(1) == 'sent') { $query = _privatemsg_assemble_query('privatemsg_list_sent', $account); } $result = db_query($query); //debugging info // drupal_set_message('
'. print_r($query, 1) .'
'); $messages = array(); while($row = db_fetch_array($result)) { $row['subject'] = l($row['subject'], 'messages/view/'. $row['id']); $messages[] = $row; } //debugging $head = array(); if (count($messages)) { $row = $messages[0]; foreach ($row as $index => $value) { $head[] = array('data' => t($index), 'field' => $index, 'sort'=> 'desc'); } $tablesort = tablesort_sql($head); if ( arg(1) == 'inbox') { $query = _privatemsg_assemble_query('privatemsg_list', $account); } else if( arg(1) == 'sent') { $query = _privatemsg_assemble_query('privatemsg_list_sent', $account); } $query .= $tablesort; // drupal_set_message('
'. print_r($query, 1) .'
'); $result = db_query($query); $messages = array(); while($row = db_fetch_array($result)) { $row['subject'] = l($row['subject'], 'messages/view/'. $row['id']); $row['author'] = theme('username', user_load($row['author'])); $messages[] = $row; } } $content = theme('table', $head, $messages); // drupal_set_message('
'. print_r(count($messages), 1) .'
'); //begin theming process //1) expose each message to table HEADER and table row alteration //2) something else return $content; } /** * API function * * sets a single message as read for a user */ function privatemsg_mark_as_read($pmid, $account) { $query = "UPDATE {pm_index} SET 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) { if ( !$account || $account->uid == 0) { global $user; $account = $user; } $query = _privatemsg_assemble_query('privatemsg_unread_count', $account); return db_result(db_query($query)); } /** * 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} pmi SET new = %d WHERE recipient = %d AND new = %d"; $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 = 'Unread'; } else { $status = 'Read'; } $total_marked = db_affected_rows(); $msg = format_plural($total_marked, "1 message marked as !status", '@count messages marked as !status', array('!status' => $status)); drupal_set_message($msg); } else { drupal_set_message('An error has occured, please contact site administrator', 'error'); } return ''; } } function privatemsg_view($pmid) { global $user; //loads the message object $message = _privatemsg_load($pmid, $user->uid); privatemsg_set_new_status($user, $pmid); //loads the list of recipients $query = _privatemsg_assemble_query('privatemsg_recipients', $message['thread_id']); $result = db_query($query); while ($row = db_fetch_object($result)) { $message['recipients'][] = user_load($row->uid); } $message['author'] = user_load($message['author']); // $message['recipient'] = user_load($message['recipient']); // $output = '
'. print_r($message, 1) .'
'; $output = theme('privatemsg_to', $message); return $output . theme('privatemsg_view', $message); } function privatemsg_new(&$form_state, $user = null) { $recipient = ''; $subject = ''; $body = ''; if (isset($user)) { $recipient = $user->name . ', '; } if (isset($form_state['values'])) { $form_values = $form_state['values']; $recipient = $form_values['recipient']; $subject = $form_values['subject']; $body = $form_values['body']; } $form = array(); if (isset($form_state['privatemsg_preview'])) { $form['message_header'] = array( '#type' => 'fieldset', '#attributes' => array('class'=> 'preview'), ); $form['message_header']['message_preview'] = array( '#value' => $form_state['privatemsg_preview'], ); } $form['header'] = array( '#type' => 'fieldset' ); $form['header']['recipient'] = array( '#type' => 'textfield', '#title' => t('To'), '#description' => t('Separate multiple names with commas.'), '#default_value' => $recipient,//populate this later '#size' => 50, '#autocomplete_path' => 'messages/user-name-autocomplete', //do not hardcode #maxlength, make it configurable by number of recipients, not their name length ); $form['header']['subject'] = array( '#type' => 'textfield', '#title' => t('Subject'), '#size' => 50, '#maxlength' => 255, '#default_value' => $subject, ); $form['content'] = array( '#type' => 'fieldset', ); $form['content']['body'] = array( '#type' => 'textarea', '#title' => t('Message'), '#cols' => 10, '#rows' => 6, '#default_value' => $body, ); $form['controls'] = array( '#type' => 'fieldset', ); $form['controls']['preview'] = array( '#type' => 'submit', '#value' => 'Preview', '#submit' => array('pm_preview'), ); $form['controls']['submit'] = array( '#type' => 'submit', '#value' => 'Send', '#submit' => array('pm_send'), ); $form['controls']['cancel'] = array( '#type' => 'submit', '#value' => t('Cancel'), '#submit' => array('pm_cancel'), ); $form['#validate'][] = 'pm_send_validate'; return $form; } function pm_cancel($form, &$form_state) { $form_state['redirect'] = 'messages/new'; } function pm_send_validate($form, &$form_state) { global $user; $form_values = $form_state['values']; // if ($form_state['clicked_button']['#value'] == t('Cancel')) { // return ; // } if (empty($form_values['recipient'])) { form_set_error('recipient', t("You must fill out the !to field", array('!to' => $form['header']['recipient']['#title']))); } if (empty($form_values['subject'])) { form_set_error('subject', t("You must fill out the !subject field", array('!subject' => $form['header']['subject']['#title']))); } if (empty($form_values['body'])) { form_set_error('body', t("You must fill out the !body field", array('!body' => $form['content']['body']['#title']))); } // drupal_set_message('
'. print_r($form, 1). '
'); // drupal_set_message('
'. print_r($form_state, 1). '
'); //the actual message that is being sent, we create this during validation and pass to submit to send out $message = array(); $message['body'] = $form_values['body']; $message['subject'] = $form_values['subject']; $message['author'] = $user; $message['timestamp'] = time(); // drupal_set_message('
'. print_r('one', 1) .' 
'); // drupal_set_message('
'. print_r($form_state, 1) .' 
'); /** * Verify that recipients name syntax is correct */ $fragments = explode(',', $form_values['recipient']); $invalid = array(); $valid = array(); foreach ($fragments as $index => $name) { $name = trim($name); if (!empty($name)) { //we dont care about white space names if (empty($name) || $error = module_invoke('user', 'validate_name', $name)) { //these names are invalid due to incorrect user name syntax $invalid[$name] = $name; } else { $valid[$name] = $name; //these are valid only due to user name syntax. still need to check if user exist and accepts messages } } } /** * End verify user name syntax */ /** * Verify users exist and load their accounts */ foreach ($valid as $index => $name ) { if ( $account = user_load(array('name' => $name)) ) { //helps avoiding duplicates $message['recipients'][$account->uid] = $account; } else { //here we add more invalid names due to the fact that they dont exist $invalid[$name] = $name; } } //when user sends a message he also sends a message to himself // if ($user->uid) { // $message['recipients'][$user->uid] = $user; // } /** * End verify user existance */ /** * @TODO: add banned and blocked users check later */ //verify that our recipients are valid /** * VALIDATE NAMES * 1) make sure the name exist * 2) make sure he accepts private messages * 3) make sure the sender is not on block list of the recipient */ /** * BUILD VALID RECIPIENT LIST * 1) names that were not valid from previous step will be stripped out * 2) names that remain will be put into a recipients array */ $form_state['validate_built_message'] = $message; if (!empty($invalid)) { drupal_set_message('The following users will not receive private message: '. check_plain( implode(", ", $invalid)), 'error'); } } function pm_send($form, &$form_state) { // drupal_set_message('
'. print_r('two', 1) .' 
'); // drupal_set_message('
'. print_r($form_state, 1) .' 
');; $message = $form_state['validate_built_message']; // drupal_set_message('
'. print_r($message, 1) .' 
');; //1) save the message body first $args = array(); $args[] = $message['subject']; $args[] = $message['author']->uid; $args[] = $message['body']; $query = "INSERT INTO {pm_message} (mid, subject, author, body) VALUES (null, '%s', %d, '%s')"; $resuld = db_query($query, $args); $mid = db_last_insert_id('pm_message', 'mid'); $message['mid'] = $mid; //thread ID is the same as the mid if its the first message in the thread if (!isset($message['thread_id'])) { $message['thread_id'] = $mid; } //2) save message to recipients //each repicient gets a recods in the pm_index table $query = "INSERT INTO {pm_index} (mid, thread_id, recipient, author, timestamp, new) VALUES (%d, %d, %d, %d, %d, 1)"; foreach ($message['recipients'] as $recipient) { $mid = $message['mid']; $thread_id = $message['thread_id']; $timestamp = $message['timestamp']; db_query($query, $mid, $thread_id, $recipient->uid, $message['author']->uid , $timestamp); } drupal_set_message('Private message has been sent to '. $form_state['values']['recipient']); $form_state['rebuild'] = true; //rebuild this message for ease of use } function privatemsg_render_preview($message) { global $user; $from = ''; $to = array(); $subject = $message['subject']; $timestamp = $message['timestamp']; $body = $message['body']; if (isset($message['recipients'])) { foreach ($message['recipients'] as $uid => $account ) { $to[]= theme('username', $account); } } if (empty($to)) { $to[] = theme('username', $user); } $to = implode(', ', $to); $from = theme('username', $user); $output = theme('privatemsg_to', $message); return $output . theme('privatemsg_view', $message); } function pm_preview($form, &$form_state) { // drupal_set_message('
two 
'); // drupal_set_message('
'. print_r($form_state, 1) .' 
'); $form_values = $form_state['values']; $op = isset($form_values['op']) ? $form_values['op'] : ''; if ($op == t('Preview')) { drupal_validate_form($form['form_id']['#value'], $form, $form_state); if (!form_get_errors()) { // drupal_set_message('
'. print_r($form, 1) .' 
');; //TODO: generate message preview here $form_state['privatemsg_preview'] = privatemsg_render_preview($form_state['validate_built_message']); } } $form_state['rebuild'] = true; //this forces our form to be rebuild instead of being submitted } function privatemsg_privatemsg_list_sent_alter(&$fragments, $account) { $fragments['primary_table'] = '{pm_message} pm'; $fragments['select'][] = 'pm.mid as id'; $fragments['select'][] = 'pm.subject'; $fragments['select'][] = 'pmi.author'; $fragments['inner_join'][] = 'INNER JOIN {pm_index} pmi ON pm.mid = pmi.mid'; $fragments['where'][] = 'pmi.author = %d'; $fragments['query_args'][] = $account->uid; // $fragments['group_by'][] = 'pmi.mid'; } function privatemsg_privatemsg_list_alter(&$fragments, $account) { $fragments['primary_table'] = '{pm_message} pm'; $fragments['select'][] = 'pmi.mid as id'; $fragments['select'][] = 'pm.subject'; $fragments['select'][] = 'pmi.author'; $fragments['select'][] = 'pmi.*'; $fragments['inner_join'][] = 'INNER JOIN {pm_index} pmi ON pm.mid = pmi.mid'; $fragments['where'][] = 'pmi.recipient = %d'; $fragments['query_args'][] = $account->uid; /* if(THREADED) { //when dealing with a thread. message that has the lowest ID within a thread //is always the parent message of the whole thread $fragments['select'][] = 'min(pm.id) as id'; //we must group our results by gid, because it is our thread ID in this case //this is a MySQL requirement if we use min() function $fragments['group_by'][] = 'pg.gid'; } else { $fragments['select'][] = 'pm.id'; $fragments['where'][] = 'pm.author != %d'; $fragments['query_args'][] = $account->uid; } $fragments['select'][] = 'pm.*'; $fragments['select'][] = 'pg.*'; $fragments['select'][] = 'pu.*'; //this implements a check of whether we are allowed to view this message $fragments['inner_join'][] = 'INNER JOIN {privatemsg_group} pg ON pm.id = pg.pmid'; $fragments['inner_join'][] = 'INNER JOIN {privatemsg_group_user} pu ON pu.gid = pg.gid'; //this means only show me queries where i am the recipient //this is a measure of security so people who did not receive this message cannot read it $fragments['where'][] = 'pu.uid = %d'; $fragments['query_args'][] = $account->uid; */ } function privatemsg_privatemsg_load_alter(&$fragments, $pmid, $uid) { // drupal_set_message('
'. print_r(func_get_args(), 1) . '
'); $fragments['primary_table'] = '{pm_message} pm'; //our primary table $fragments['select'][] = "pm.*"; //fets all data from primary table $fragments['select'][] = "pmi.*"; $fragments['inner_join'][] = 'INNER JOIN {pm_index} pmi ON pm.mid = pmi.mid'; $fragments['where'][] = 'pmi.mid = %d'; $fragments['query_args'][] = $pmid; $fragments['where'][] = 'pmi.recipient = %d or pmi.author = %d'; $fragments['query_args'][] = $uid; $fragments['query_args'][] = $uid; } function privatemsg_privatemsg_recipients_alter(&$fragments, $thread_id) { $fragments['primary_table'] = '{pm_index} pmi'; $fragments['select'][] = 'pmi.recipient as uid'; $fragments['where'][] = 'pmi.thread_id = %d'; $fragments['query_args'][] = $thread_id; } function privatemsg_privatemsg_unread_count_alter(&$fragments, $account) { $fragments['primary_table'] = '{pm_index} pmi'; $fragments['select'][] = 'count(*) as unread_count'; $fragments['where'][] = 'pmi.new = 1'; $fragments['where'][] = 'pmi.recipient = %d'; $fragments['query_args'][] = $account->uid; } /** * Return autocomplete results for usernames. * prevents usernames from being used and/or suggested twice */ function privatemsg_user_name_autocomplete($string) { $names = array(); //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)) { //do nothing if this name does not validate } else { $names[$name] = $name; } } //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 = "SELECT name FROM {users} u WHERE name like '%s%%'"; $query.= " AND name NOT IN ('". implode("', '", $names) ."')"; //this will prevent suggesting name that is already in our list $query.= " AND status <> 0 ORDER BY name ASC"; $result = db_query_range($query, $fragment, 0, 10); $prefix = count($names) ? implode(", ", $names). ", " : ''; //3: build proper suggestions and print $matches = array(); while ($user = db_fetch_object($result)) { $matches[$prefix. $user->name. ", "] = $user->name; } print drupal_to_js($matches); exit(); } } /** * Placeholder function for the messages menu space */ function privatemsg_hello() { $output = ''; $output.= 'Welcome to your Private Messages box'; return $output; } /** * Use this function at the top of your own function to block anon user access */ function disallow_anon_access() { global $user; //access permissions if (!$user->uid) { //even though admin/user/permissions may allow anonymous users //to access privatemsg, it still doesnt make sense for that to //happen. therefore we disallow this here so there's no way to overcome it. drupal_access_denied(); exit; } } function privatemsg_user($op, &$edit, &$account, $category = NULL) { switch ($op) { case 'view': $account->content['privatemsg_send_new_message'] = array( '#type' => 'markup', '#value' => l('Send Message', 'messages/new/'.$account->uid), '#weight' => 10, ); break; } } function privatemsg_block($op = 'list', $delta = 0, $edit = array()) { if( 'list' == $op ) { $blocks = array(); $blocks['privatemsg-menu'] = array( 'info' => t('Privatemsg links'), ); return $blocks; } else if ('view' == $op ) { $block = array(); switch ($delta) { case 'privatemsg-menu': $count = privatemsg_unread_count(); $new = ''; if ( $count ) { $new = " ({$count} new)"; } $block = array( 'subject' => 'Messages', 'content' => l('Inbox'. $new, 'messages/inbox'), ); break; } return $block; } }