name == 'friend') { switch ($event) { case 'flag': // See the status of the friendship. $status = flag_friend_determine_friend_status($flag, $account->uid, $content_id); // If both are now flagged, we record the relationship and remove the flags. if ($status == FLAG_FRIEND_BOTH) { // Remove any message entries for either user. flag_friend_message('unflag', $flag, $account->uid, $content_id); flag_friend_message('unflag', $flag, $content_id, $account->uid); // Since these users have flagged eachother, we create the relationship in the flag_friend table. db_query("INSERT INTO {flag_friend} VALUES(%d, %d, %d)", $account->uid, $content_id, $_SERVER['REQUEST_TIME']); // Then remove the flags. $flag->flag('unflag', $content_id, $account); $flag->flag('unflag', $account->uid, user_load(array('uid' => $content_id))); } break; case 'unflag': // Remove message. flag_friend_message($event, $flag, $content_id, $account->uid); break; } } } /** * Implementation of hook_preprocess_flag(). */ function flag_friend_preprocess_flag(&$vars) { // this hook preprocesses ALL flag links, so make sure we have ours if ($vars['flag']->name == 'friend') { global $user; // Determine what the status in the friend process is. $status = flag_friend_determine_friend_status($vars['flag'], $user->uid, $vars['content_id']); // Depending on the status, we need to manipulate the vars. if ($status == FLAG_FRIEND_PENDING) { $vars['link_text'] = t('Pending - Cancel?'); $vars['flag_name_css'] = 'pending friend'; $vars['link_href'] = str_replace('flag/confirm', 'flag-friend/confirm', $vars['link_href']); // TODO: some js to detect pending links and handle accordingly } if ($status == FLAG_FRIEND_FLAGGED) { // Make this link into a remove link with $vars['action'] = 'unflag'; $vars['link_href'] = str_replace('flag/confirm/flag', 'flag-friend/confirm/unfriend', $vars['link_href']); $vars['link_text'] = $vars['flag']->unflag_short; } if ($status == FLAG_FRIEND_APPROVAL) { $vars['link_text'] = t('Approve'); } } } /** * Implementation of hook_perm(). */ function flag_friend_perm() { return array('receive friend email notification'); } /** * Implementation of hook_menu(). */ function flag_friend_menu($may_cache) { global $user; $items = array(); if (!$may_cache) { if ($user->uid) { if (arg(0) == 'user' && is_numeric(arg(1))) { // menu item to see message and respond $items[] = array( 'path' => 'user/'. arg(1) .'/friends', 'callback' => 'flag_friend_page', 'callback arguments' => array(user_load(array('uid' => arg(1)))), 'access' => user_access('access content'), 'type' => MENU_CALLBACK, ); } } $items[] = array( 'path' => 'flag-friend/confirm', 'callback' => 'drupal_get_form', 'callback arguments' => array('flag_friend_unfriend_confirm'), 'access' => user_access('access content'), 'type' => MENU_CALLBACK, ); // post means that there isn't a friend // message already $post = TRUE; if (isset($_SESSION['messages']['notice'])) { foreach ($_SESSION['messages']['notice'] as $message) { if (strpos($message, 'pending friend') !== FALSE) { $post = FALSE; } } } if ($post) { // grab the count of the pending friends and output them $flag = flag_get_flag('friend'); $flags = flag_friend_get_flags($flag, $user->uid); if ($count = count($flags)) { drupal_set_message(l(t('You have !count.', array('!count' => format_plural($count, '1 pending friend request', '@count pending friend requests'))), 'user/' . $user->uid . '/friends'), 'notice'); } } } return $items; } /** * Menu callback for displaying friends */ function flag_friend_page($account) { global $user; $output = ''; // make sure that only you can see your own pending friends if ($user->uid == $account->uid) { drupal_set_title(t('My friends')); $flag = flag_get_flag('friend'); // display a list of any pending friend requests $flags = flag_friend_get_flags($flag, $account->uid); if (!empty($flags)) { $output .= theme('flag_friend_pending_flags', $account, $flags); } } else { drupal_set_title(t("@name's friends", array('@name' => $account->name))); } // display a list of this user's friends $friends = flag_friend_get_friends($account->uid); $output .= theme('flag_friend_friend_list', $account, $friends); return $output; } /** * Callback function to retrieve pending friend flags for theming. */ function flag_friend_get_flags($flag, $content_id, $reset = NULL) { static $flagged_content; $uid = $content_id; $content_type = $flag->content_type; if (!isset($flagged_content[$uid][$content_type][$content_id]) || $reset) { $flags = flag_get_flags($flag->content_type); $flagged_content[$uid][$content_type][$content_id] = array(); // get flags with messages $result = db_query("SELECT fc.*, ffm.message FROM {flag_content} fc LEFT JOIN {flag_friend_message} ffm ON ffm.fcid = fc.fcid WHERE content_type = '%s' AND content_id = %d", $content_type, $content_id); while ($new_flag = db_fetch_object($result)) { $fcid = flag_friend_get_fcid($flag, $content_id, $new_flag->uid); $flagged_content[$uid][$content_type][$content_id][$fcid] = $new_flag; $flagged_content[$uid][$content_type][$content_id][$fcid]->user = user_load(array('uid' => $new_flag->uid)); } } return $flagged_content[$uid][$content_type][$content_id]; } /** * Callback function to retrieve a list of friends for the given user. */ function flag_friend_get_friends($uid, $reset = NULL) { static $friends; if (!isset($friends[$uid]) || $reset) { $result = db_query("SELECT * FROM {flag_friend} WHERE uid = %d OR friend_uid = %d", $uid, $uid); while ($friend = db_fetch_object($result)) { // if the current user is in the uid column if ($friend->uid == $uid) { // load the friend_uid $friends[$uid][$friend->friend_uid] = user_load(array('uid' => $friend->friend_uid)); } else { // the current user is the friend_uid // load the uid column as the friend $friends[$uid][$friend->uid] = user_load(array('uid' => $friend->uid)); } } } return $friends[$uid]; } /** * Theme function for the list of pending flags. */ function theme_flag_friend_pending_flags($account, $flags) { // $account is the user of the page we're looking at // $flag->user is the user requesting the relationship $output = ''; $output .= t('

You have !friend_request.

', array('!friend_request' => format_plural(count($flags), 'a friend request', count($flags) .' friend requests'))); $output .= '
'; $count = count($flags); $output .= '
'. $count .' Pending Friend'. format_plural($count, '', 's') .'
'; $output .= '
'; return $output; } /** * Form for confirming the (un)flagging of a piece of content. * NOTE: this duplicates flag.module's flag/confirm but not sure * what else to do in order to circumvent the flagging * process in the case of a friend removal where no flag * is actually set. */ function flag_friend_unfriend_confirm($action = 'flag', $flag_name, $content_id = '') { global $user; $flag = flag_get_flag($flag_name); $status = flag_friend_determine_friend_status($flag, $user->uid, $content_id); $form = array(); $form['action'] = array( '#type' => 'value', '#value' => $action, ); $form['flag_name'] = array( '#type' => 'value', '#value' => $flag_name, ); $form['content_id'] = array( '#type' => 'value', '#value' => $content_id, ); $form['status'] = array( '#type' => 'value', '#value' => $status, ); $form['#theme'] = 'flag_friend_unfriend_form'; switch ($status) { case FLAG_FRIEND_FLAGGED: $question = t('Are you sure you want to remove this user from your list of friends?'); break; case FLAG_FRIEND_APPROVAL: $question = t('Are you sure you don\'t want to be friends with this user?'); break; default: $question = $flag->get_label('unflag_confirmation', $content_id); break; } $path = isset($_GET['destination']) ? $_GET['destination'] : ''; $yes = $flag->get_label('unflag_short', $content_id); return confirm_form($form, $question, $path, '', $yes); } function flag_friend_unfriend_confirm_submit($form_id, $form_values) { $action = $form_values['action']; $flag_name = $form_values['flag_name']; $content_id = $form_values['content_id']; $status = $form_values['status']; $flag = flag_get_flag($flag_name); flag_friend_unfriend($action, $flag_name, $content_id, $status); drupal_set_message($flag->get_label($action . '_message', $content_id)); } /** * Menu callback to either unflag yourself, or remove the relationship record. */ function flag_friend_unfriend($event, $flag_name, $content_id, $status) { global $user; // 'Denial' and 'Pending - Cancel?' if ($event == 'unflag') { $flag = flag_get_flag($flag_name); if ($status == FLAG_FRIEND_APPROVAL) { // Denial // the content_id is actually the account param in this case $account = user_load(array('uid' => $content_id)); // and the $user->uid is actually the content(_id) we're unflagging $content_id = $user->uid; } // else we're cancelling out own flag $flag->flag($event, $content_id, $account); } else { // event = unfriend // remove the friend relationship db_query('DELETE FROM {flag_friend} WHERE (uid = %d AND friend_uid = %d) OR (uid = %d AND friend_uid = %d)', $user->uid, $content_id, $content_id, $user->uid); } drupal_goto(); } /** * Implementation of hook_user(). */ function flag_friend_user($op, &$edit, &$account, $category = NULL) { switch ($op) { case 'form': // The user account edit form is about to be displayed. The module should present the form elements it wishes to inject into the form. $form = array(); $form['friend_notification'] = array( '#type' => 'select', '#title' => t('I would like to be notified when someone wants to be friends with me'), '#multiple' => FALSE, '#options' => array(0 => 'Yes', -1 => 'No'), '#default_value' => isset($account->friend_notification) ? $account->friend_notification : FLAG_FRIEND_NOTIFICATION, '#weight' => -10, ); return $form; break; } } /** * Implementation of hook_user_link(). */ function flag_friend_user_link($account) { global $user; // do not supply a link if the account and user are the same if ($user->uid != $account->uid) { $links = array(); $links['flag-friend'] = array( 'title' => flag_create_link('friend', $account->uid), 'html' => TRUE, ); return $links; } } /** * Create a denial link. */ function flag_friend_create_link($type, $uid) { if ($type == 'unfriend') { $flag = flag_get_flag('friend'); $link = str_replace('Approve', 'Deny', str_replace('/flag/confirm', '/flag-friend/confirm', $flag->theme('unflag', $uid))); return $link; } } /** * Theme function for the list of friends for the given user. */ function theme_flag_friend_friend_list($account, $friends) { $output = ''; if (!empty($friends)) { $output .= ''; } else { $output .= 'You have no friends, loser.'; } return $output; } /** * Determines the status of the friendship by testing various conditions. * * @param object $flag * The flag object. * * @param int $uid1 * The account id of one of the users. * * @param int $uid2 * The account id of the other user. * * @return * A string describing the status of the relationship. * * NOTE: this could possibly go into hook_flag_access? once available. */ function flag_friend_determine_friend_status($flag, $uid1, $uid2) { static $status_cache = array(); // always keep these in the same order if ($uid1 > $uid2) { $key1 = $uid1; $key2 = $uid2; } else { $key1 = $uid2; $key2 = $uid1; } if (!isset($status_cache[$key1][$key2])) { $you_are_flagged = $flag->is_flagged($uid1, $uid2); $they_are_flagged = $flag->is_flagged($uid2, $uid1); $friends = db_result(db_query("SELECT * FROM {flag_friend} WHERE (uid = %d AND friend_uid = %d) OR (uid = %d AND friend_uid = %d)", $uid1, $uid2, $uid2, $uid1)); // see if these users have flagged eachother if ($you_are_flagged && $they_are_flagged) { $status_cache[$key1][$key2] = FLAG_FRIEND_BOTH; } else if ($friends) { $status_cache[$key1][$key2] = FLAG_FRIEND_FLAGGED; } else if (!$you_are_flagged && !$they_are_flagged) { $status_cache[$key1][$key2] = FLAG_FRIEND_UNFLAGGED; } else if ($you_are_flagged && !$they_are_flagged) { $status_cache[$key1][$key2] = FLAG_FRIEND_APPROVAL; } else if (!$you_are_flagged && $they_are_flagged) { $status_cache[$key1][$key2] = FLAG_FRIEND_PENDING; } } return $status_cache[$key1][$key2]; } function flag_friend_form_alter($form_id, &$form) { if ($form_id == 'flag_confirm' && $form['flag_name']['#value'] == 'friend') { $action = $form['action']['#value']; $flag = flag_get_flag('friend'); $content_id = $form['content_id']['#value']; $token = $_REQUEST['token']; switch ($action) { case 'flag': $flag_form = flag_friend_message_form($action, $flag, $content_id, $token); $form = array_merge($flag_form, $form); unset($form['actions']['submit']); unset($form['actions']['cancel']); $form['#submit']['flag_friend_form_submit'] = array(); break; case 'unflag': $unflag_form = flag_friend_unfriend_form($action, $flag, $content_id, $token); $form = array_merge($form, $unflag_form); $form['#submit']['flag_friend_form_submit'] = array(); break; } } } /** * Form to send a message to a user before friend flagging. */ function flag_friend_message_form($action, $flag, $content_id, $token) { $form['current'] = array('#type' => 'value', '#value' => func_get_args()); $form['flag_friend_message'] = array( '#type' => 'textarea', '#title' => t('Friend message (optional)'), '#description' => t('Enter a message to send to this user.'), '#cols' => 60, '#rows' => 5, ); $form['flag_friend_submit'] = array( '#type' => 'submit', '#value' => t('Send'), '#suffix' => l('Cancel', $_GET['destination']), ); $form['#theme'] = 'flag_friend_message_form'; return $form; } /** * Form to confirm an unfriend flagging. */ function flag_friend_unfriend_form($action, $flag, $content_id, $token) { $form['current'] = array('#type' => 'value', '#value' => func_get_args()); $question = t('Are you sure you want to !action?', array('!action' => $action)); $path = $_REQUEST['destination']; $form = confirm_form($form, $question, $path); $form['#redirect'] = 'flag/confirm/'. $action .'/'. $flag->name .'/'. $content_id .'/'. $token .'?'. drupal_get_destination(); $form['#theme'] = 'flag_friend_unfriend_form'; return $form; } /** * Submit handler for message_form() and unfriend_form(). */ function flag_friend_form_submit($form_id, $form_values) { global $user; $action = $form_values['current'][0]; $flag = $form_values['current'][1]; $content_id = $form_values['current'][2]; $account = $user; $token = $form_values['current'][3]; if ($form_values['flag_friend_message']) { $flag->friend_message = $form_values['flag_friend_message']; } flag_friend_message($action, $flag, $content_id, $account->uid); $status = flag_friend_determine_friend_status($flag, $account->uid, $content_id); flag_friend_message_email($status, $flag, $content_id, $account); } /** * API callback function to update our new field. */ function flag_friend_message($action, $flag, $content_id, $account_uid) { // see if the flag has an fcid if (!isset($flag->fcid)) { $flag->fcid = flag_friend_get_fcid($flag, $content_id, $account_uid); } if ($action == 'flag' && $flag->friend_message) { db_query("INSERT INTO {flag_friend_message} VALUES(%d, '%s')", $flag->fcid, $flag->friend_message); } else if ($action == 'unflag') { db_query("DELETE FROM {flag_friend_message} WHERE fcid = %d", $flag->fcid); } } function flag_friend_message_email($status, $flag, $recipient_uid, $sender) { $recipient = user_load(array('uid' => $recipient_uid)); // if the user can receive notifications if (user_access('receive friend email notification')) { // and they've expressed they want them if ((isset($recipient->friend_notification) && $recipient->friend_notification !== -1) || !isset($recipient->friend_notification)) { $email = theme('flag_friend_message_email', $status, $flag, $recipient, $sender); if (isset($email['body'])) { if (function_exists('messaging_message_send_user')) { messaging_message_send_user($recipient, $email, NULL, 1); } else { drupal_mail($email['type'], $recipient->mail, $email['subject'], $email['body']); } } } } } /** * Theme the outgoing email message. * * @param string $status * Status of the friendship. * * @param object $flag * The flag object. * * @param object $recipient * The user object of the person receiving the email. * * @param object $sender * The user object of the person sending the email. * * @return * An array containing the email [type] (mailkey), [subject] and [body]. */ function theme_flag_friend_message_email($status, $flag, $recipient, $sender) { $email = array(); $email['type'] = 'flag-friend'; switch ($status) { case FLAG_FRIEND_BOTH: // sender confirmed you as a friend $email['subject'] = t('!username confirmed you as a friend !site', array( '!username' => $sender->name, '!site' => 'on '. variable_get('site_name', ''), )); $email['body'] = t('!firstname confirmed you as a friend on !site. To view !firstname\'s profile, follow this link, !link !message Thanks, The !site Team', array( '!firstname' => $sender->firstname ? $sender->firstname : $sender->name, '!site' => variable_get('site_name', ''), '!message' => $flag->friend_message ? 'Message: '. $flag->friend_message : '', '!link' => url('user/'. $sender->uid, NULL, NULL, TRUE), )); break; case FLAG_FRIEND_PENDING: // sender added you as a friend $email['subject'] = t('!username added you as a friend !site', array('!username' => $sender->name, '!site' => 'on '. variable_get('site_name', ''))); $email['body'] = t('!firstname added you as a friend on !site. We need to confirm that you know !firstname in order for you to be friends on !site. To confirm this friend request, follow the link below: !link !message Thanks, The !site Team', array( '!firstname' => $sender->firstname ? $sender->firstname : $sender->name, '!site' => variable_get('site_name', ''), '!message' => $flag->friend_message ? 'Message: '. $flag->friend_message : '', '!link' => url('user/'. $recipient->uid .'/friends', NULL, NULL, TRUE), )); break; } return $email; } /** * Retrieves the fcid of a flag. * * NOTE: hopefully fcid will be passed into the hook_flag() at some point * at which time will render this function unnecessary */ function flag_friend_get_fcid($flag, $content_id, $account_uid) { return db_result(db_query("SELECT fcid FROM {flag_content} WHERE fid = %d AND content_type = '%s' AND content_id = %d AND uid = %d", $flag->fid, $flag->content_type, $content_id, $account_uid)); } /** * Retrieve our flag's message. */ function flag_friend_get_message($fcid) { $flag_friend = FALSE; $result = db_result(db_query("SELECT message FROM {flag_friend_message} WHERE fcid = %d", $fcid)); if ($result) { $flag_friend = $result; } return $flag_friend; } /** * Theme function for the message form. */ function theme_flag_friend_message_form($form) { drupal_set_title('Send a friend request.'); return drupal_render($form); } /** * Theme function for the unfriending action. */ function theme_flag_friend_unfriend_form($form) { return drupal_render($form); } /** * Implementation of hook_flag_default_flags(). */ function flag_friend_flag_default_flags() { $flags = array(); $flags[] = array( 'content_type' => 'user', 'name' => 'friend', 'title' => 'Friend', 'roles' => array( 0 => '2', ), 'global' => FALSE, 'flag_short' => 'Add friend', 'flag_long' => 'Add this user to your list of friends.', 'flag_confirmation' => 'Are you sure you want to add this user to your list of friends?', 'unflag_short' => 'Remove friend', 'unflag_long' => 'Remove this user from your list of friends.', 'unflag_confirmation' => 'Are you sure you want to cancel your pending friend request?', 'status' => FALSE, 'link_type' => 'confirm', 'locked' => array('name', 'global', 'link_type'), ); return $flags; }