' . t('Node Invite provides a means to invite people to nodes. Customized email message is token enabled.') . '

'; $output .= '

' . t('Here are tokens you can use:') . '

'; $output .= theme('token_help', 'node'); break; case 'admin/settings/node_invite': $output .= '

' . t('Node invite allows you to invite people to nodes. The email they receive is token-enabled.') . '

'; break; } return $output; } /** * Implementation of hook_perm(). */ function node_invite_perm() { return array( 'invite users to invite-enabled nodes', 'admin node invite settings', 'respond to invites', 'manage all invites', 'manage own invites', 'view invitee lists', ); } /** * Implementation of hook_theme(). */ function node_invite_theme() { return array( 'node_invite_view_field_invitees' => array( 'arguments' => array('nid' => NULL, 'invites' => array()), ), 'node_invite_list_invites' => array( 'arguments' => array('sql' => NULL, 'values' => NULL, 'fields' => NULL, 'limit' => NULL), ), 'node_invite_list_invites_empty' => array( 'arguments' => array(), ), 'node_invite_view' => array( 'arguments' => array('node_invite' => NULL), ), ); } /** * Implementation hook_views_api */ function node_invite_views_api() { return array( 'api' => 2, ); } /** * Implementation of hook_menu(). */ function node_invite_menu() { $items = array(); $items['admin/settings/node_invite'] = array( 'title' => 'Node invite', 'description' => 'Node Invite module is similar to RSVP. Invite people to nodes, manage your invites, etc.', 'page callback' => 'drupal_get_form', 'page arguments' => array('node_invite_admin_settings_form'), 'access arguments' => array('admin node invite settings'), 'file' => 'node_invite.admin.inc', ); $items['admin/settings/node_invite/settings'] = array( 'title' => 'Settings', 'weight' => 0, 'type' => MENU_DEFAULT_LOCAL_TASK, ); $items['admin/settings/node_invite/notifications'] = array( 'title' => 'Notifications', 'page callback' => 'drupal_get_form', 'page arguments' => array('node_invite_notifications_form'), 'access arguments' => array('admin node invite settings'), 'file' => 'node_invite.admin.inc', 'weight' => 1, 'type' => MENU_LOCAL_TASK, ); $items['admin/settings/node_invite/manage'] = array( 'title' => 'Manage invites', 'page callback' => 'node_invite_manage_page', 'access arguments' => array('manage all invites'), 'type' => MENU_LOCAL_TASK, 'file' => 'node_invite.admin.inc', 'weight' => 2, ); if (variable_get('node_invite_display', 'link') == 'tab') { $items['node/%node/invite'] = array( 'title' => 'Invite people', 'page callback' => 'drupal_get_form', 'page arguments' => array('node_invite_send', 1), 'access callback' => 'node_invite_invites_access', 'access arguments' => array(1, 'tab'), 'file' => 'node_invite.send.inc', 'weight' => 2, 'type' => MENU_LOCAL_TASK, ); } else { $items['node_invite/invite/%node'] = array( 'title' => 'Invite', 'page callback' => 'drupal_get_form', 'page arguments' => array('node_invite_send', 2), 'access callback' => 'node_invite_invites_access', 'access arguments' => array(2, 'link'), 'file' => 'node_invite.send.inc', 'type' => MENU_CALLBACK, ); } $items['node_invite/find_invite/%node'] = array( 'title' => 'Find invite', 'page callback' => 'node_invite_find_invite', 'page arguments' => array(2), 'access callback' => 'node_invite_rsvp_access', 'access arguments' => array(2), 'file' => 'node_invite.rsvp.inc', 'type' => MENU_CALLBACK, ); $items['node_invite/clear_rsvp_session/%node'] = array( 'title' => 'Clear RSVP session', 'page callback' => 'node_invite_clear_rsvp_session', 'page arguments' => array(2), 'access callback' => TRUE, 'file' => 'node_invite.rsvp.inc', 'type' => MENU_CALLBACK, ); $items['node_invite/rsvp/%node/%node_invite'] = array( 'title arguments' => array(2, 3), 'title callback' => 'node_invite_rsvp_title', 'page callback' => 'drupal_get_form', 'page arguments' => array('node_invite_rsvp', 2, 3), 'access arguments' => array('respond to invites'), 'file' => 'node_invite.rsvp.inc', 'type' => MENU_CALLBACK, ); $items['node_invite/rsvp_new/%node'] = array( 'title' => 'Your RSVP', 'page callback' => 'drupal_get_form', 'page arguments' => array('node_invite_rsvp_new', 2), 'access callback' => 'node_invite_rsvp_access', 'access arguments' => array(2), 'file' => 'node_invite.rsvp.inc', 'type' => MENU_CALLBACK, ); $items['node_invite/revoke/%node_invite'] = array( 'title' => 'Revoke invite', 'page callback' => 'drupal_get_form', 'page arguments' => array('node_invite_revoke', 2), 'access callback' => 'node_invite_can_manage_invite', 'access arguments' => array(2), 'file' => 'node_invite.admin.inc', 'type' => MENU_CALLBACK, ); $items['node_invite/resend/%node_invite'] = array( 'title' => 'Resend invite', 'page callback' => 'drupal_get_form', 'page arguments' => array('node_invite_resend', 2), 'access callback' => 'node_invite_can_manage_invite', 'access arguments' => array(2), 'file' => 'node_invite.admin.inc', 'type' => MENU_CALLBACK, ); $items['node_invite/reinstate/%node_invite'] = array( 'title' => 'Reinstate invitation', 'page callback' => 'node_invite_reinstate', 'page arguments' => array(2), 'file' => 'node_invite.admin.inc', 'access callback' => 'node_invite_can_manage_invite', 'access arguments' => array(2), 'type' => MENU_CALLBACK, ); $items['node_invite/view/%node_invite'] = array( 'title' => 'View invitation', 'page callback' => 'theme', 'page arguments' => array('node_invite_view', 2), 'file' => 'node_invite.admin.inc', 'access callback' => 'node_invite_can_manage_invite', 'access arguments' => array(2), 'type' => MENU_CALLBACK, ); $items['user/%user/invites'] = array( 'title' => 'My invites', 'page callback' => 'node_invite_list_invites_callback', 'page arguments' => array(1), 'access arguments' => array('manage own invites'), 'weight' => 2, 'type' => MENU_LOCAL_TASK, ); $items['node/%node/manage_invites'] = array( 'title' => 'Manage invites', 'page callback' => 'node_invite_list_invites_callback', 'page arguments' => array(NULL, 1), 'access callback' => 'node_invite_manage_invites_access', 'access arguments' => array(1), 'weight' => 3, 'type' => MENU_LOCAL_TASK, ); $items['node_invite/user_autocomplete'] = array( 'title' => 'User autocomplete', 'page callback' => '_node_invite_user_autocomplete', 'access arguments' => array('access user profiles'), 'type' => MENU_CALLBACK, ); return $items; } /** * The title callback for the rsvp page */ function node_invite_rsvp_title($node, $invite) { node_invite_specific_info($invite, $node); $page_title = variable_get('node_invite_rsvp_page_title', NODE_INVITE_RSVP_PAGE_TITLE); // There appears to be an issue when the [title] token is used that it can't // be properly replaced with token_replace in the title menu callback. $page_title = str_replace('[title]', $node->title, $page_title); $page_title = token_replace($page_title, 'node', $node); return t($page_title); } /** * Menu access callback for determining if the current user can manage * the given invite. */ function node_invite_can_manage_invite($invite) { global $user; if (user_access('manage all invites') || ($invite->uid_inviter == $user->uid && user_access('manage own invites'))) { return TRUE; } return FALSE; } /** * Menu access callback for the manage invites tab. */ function node_invite_manage_invites_access($node) { if (user_access('manage own invites') && _ni_node_is_enabled($node)) { return TRUE; } return FALSE; } /** * Menu access callback for the manage invites tab. */ function node_invite_invites_access($node, $type = NULL) { if (user_access('invite users to invite-enabled nodes') && _node_invite_type_is_allowed($node->type) && _ni_node_is_enabled($node)) { if (!isset($type) || ($type == variable_get('node_invite_display', 'link'))) { return TRUE; } } return FALSE; } /** * Menu access callback for the rsvp link on invite node. */ function node_invite_rsvp_access($node) { global $user; if (user_access('respond to invites') && _node_invite_type_is_allowed($node->type) && _ni_node_is_enabled($node)) { if ($node->node_invite_settings['open']) { return TRUE; } else { // Try and find an invite for this user. Note that people who were // invited by email only will not be able to see this link. $invites = node_invite_user_invites($user->uid); foreach ($invites as $invite) { if ($invite->nid == $node->nid) { return TRUE; } } } } return FALSE; } /** * Load an invite. */ function node_invite_load($invite_id, $reset = NULL) { static $invite_cache = array(); if ($reset) { $invite_cache = array(); } if (!isset($invite_cache[$invite_id])) { // The id can be either a hash or an iid... figure it out if (is_numeric($invite_id)) { $where = "ni.iid = %d"; } else { $where = "ni.hash = '%s'"; } $invite = db_fetch_object(db_query("SELECT ni.*, u.mail, u.name FROM {node_invites} AS ni JOIN {users} AS u ON ni.uid_inviter = u.uid WHERE $where", $invite_id)); // Load the settings for this invite $invite->settings = db_fetch_array(db_query("SELECT * FROM {node_invite_settings} WHERE nid = %d", $invite->nid)); $invite_cache[$invite_id] = $invite; } return $invite_cache[$invite_id]; } /** * Implementation of hook_link(). */ function node_invite_link($type, $node = NULL, $teaser = FALSE) { global $user; $links = array(); if ($type == 'node' && _node_invite_type_is_allowed($node->type) && _ni_node_is_enabled($node)) { if (user_access('invite users to invite-enabled nodes') && variable_get('node_invite_display', 'link') == 'link') { $links['node_invite'] = array( 'title' => t('Invite people to this @type', array('@type' => $node->type)), 'href' => "node_invite/invite/$node->nid", ); } if ($node->node_invite_settings['open'] && variable_get('node_invite_rsvp_display', 'link') == 'link') { $links['node_invite_rsvp'] = array( 'title' => t('RSVP to this @type', array('@type' => $node->type)), 'href' => "node_invite/find_invite/$node->nid", ); } } return $links; } /** * Implementation of hook_user(). * * Update invite email addresses if the user changes their own email address. */ function node_invite_user($op, &$edit, &$account, $category = NULL) { if ($op == 'update') { if (variable_get('node_invite_sync_users', 0) && $edit['mail'] != $account->mail) { db_query("UPDATE {node_invites} SET email_invitee = '%s' WHERE uid_invitee = %d", $edit['mail'], $account->uid); } } } /** * Implementation of hook_mail(). */ function node_invite_mail($key, &$message, $params) { $message['headers']['Content-Type'] = 'text/html; charset=UTF-8; format=flowed'; $message['subject'] = $params['subject']; $message['body'] = $params['body']; // Replace from address if a replacement is set. if ($params['from'] != $message['from']) { $message['headers']['Reply-To'] = $message['from']; $message['headers']['From'] = $params['from']; $message['from'] = $params['from']; } } /** * Implementation of hook_token_list(). */ function node_invite_token_list($type = 'all') { $tokens = array(); if (in_array($type, array('all', 'node_invite', 'node'))) { $tokens['node invite']['inviter-name'] = t('The username of the person currently logged in (and thus sending the invite). This will not work as a page-title token since the person rendering the page title will be logged in as someone different (or anonymous).'); $tokens['node invite']['inviter-mail'] = t('The email address of the person currently logged in (and thus sending the invite). This will not work as a page-title token since the person rendering the page title will be logged in as someone different (or anonymous).'); $tokens['node invite']['inviter-rsvp-url'] = t('This is the URL of the RSVP form. Send users here to confirm or deny attendance.'); $tokens['node invite']['inviter-node-url'] = t('This is the URL of the node to which they are being invited. Send them here "for more details."'); $tokens['node invite']['node-invite-recip-mail'] = t('The email of the person receiving the invite'); $tokens['node invite']['node-invite-recip-name'] = t('The name of the person receiving the invite'); $tokens['node invite']['node-invite-iid'] = t('The invite_id (unique identifier) of the invite being sent'); $tokens['node invite']['node-invite-personal-message'] = t('The personal message added to the invite being sent'); } return $tokens; } /** * Implementation of hook_token_values(). */ function node_invite_token_values($type, $object = NULL) { $values = array(); if (in_array($type, array('all', 'node invite', 'node'))) { global $user; $values['inviter-name'] = $user->name; $values['inviter-mail'] = $user->mail; $values['inviter-rsvp-url'] = url('node_invite/invite/' . $object->nid, array('absolute' => TRUE)); $values['inviter-node-url'] = url('node/' . $object->nid, array('absolute' => TRUE)); // this is hacked in for each invite sent. Won't exist other times if (isset($object->invite_specific_info)) { $values['node-invite-iid'] = $object->invite_specific_info['node-invite-iid']; $values['node-invite-recip-name'] = $object->invite_specific_info['node-invite-recip-name']; $values['node-invite-recip-mail'] = $object->invite_specific_info['node-invite-recip-mail']; $values['node-invite-personal-message'] = str_replace("\n", "
\n", $object->invite_specific_info['node-invite-personal-message']); // the above are defaults... this comes in if alternatives are available $values['inviter-name'] = $object->invite_specific_info['inviter-name']; $values['inviter-mail'] = $object->invite_specific_info['inviter-mail']; } } return $values; } /** * Implementation of hook_content_extra_fields(). */ function node_invite_content_extra_fields($type_name) { $extra = array(); if (_node_invite_type_is_allowed($type_name)) { $extra['node_invite_group'] = array( 'label' => t('Node invite settings'), 'description' => t('Node invite module form.'), 'weight' => 10, ); } return $extra; } /** * Implementation of hook_block(). */ function node_invite_block($op = 'list', $delta = 0, $edit = array()) { // The blocks here can only be used on node invite enabled nodes and // node_invite pages. if ($op == 'list') { $blocks = array(); $blocks[0] = array( 'info' => t('Node invite - invite list'), ); $blocks[1] = array( 'info' => t('Node invite - attending'), ); $blocks[2] = array( 'info' => t('Node invite - maybe attending'), ); $blocks[3] = array( 'info' => t('Node invite - not attending'), ); $blocks[4] = array( 'info' => t('Node invite - no response'), ); return $blocks; } elseif ($op == 'view') { // Figure out which node we're viewing. $path = $_GET['q']; $matches = array(); if (preg_match('/node\/([0-9]+)/', $path, $matches)) { $nid = $matches[1]; } elseif (preg_match('/node_invite\/[a-zA-Z]+\/([0-9]+)/', $path, $matches)) { $nid = $matches[1]; } else { return; } // Ensure that the permissions are adequate to display a block on // the specified node. $node = node_load($nid); if (!(_ni_node_is_enabled($node) && _node_invite_type_is_allowed($node->type) && user_access('view invitee lists'))) { return; } switch ($delta) { case 0: $invites = node_invite_load_invites_by_nid($nid); $subject = t('Invite List'); break; case 1: $invites = node_invite_load_invites_by_nid($nid, NODE_INVITE_RESPONSE_YES); $subject = t('Attending'); break; case 2: $invites = node_invite_load_invites_by_nid($nid, NODE_INVITE_RESPONSE_MAYBE); $subject = t('Maybe Attending'); break; case 3: $invites = node_invite_load_invites_by_nid($nid, NODE_INVITE_RESPONSE_NO); $subject = t('Not Attending'); break; case 4: $invites = node_invite_load_invites_by_nid($nid, NODE_INVITE_NEW); $subject = t('No Response'); break; } $block = array( 'subject' => $subject, 'content' => theme('node_invite_view_field_invitees', $nid, $invites), ); return $block; } } /** * Menu callback for the user and node invite lists. */ function node_invite_list_invites_callback($account = NULL, $node = NULL) { $filters = array(); $fields = NULL; if (isset($account)) { $filters['node_invites.uid_inviter'] = $account->uid; } if (isset($node)) { $filters['node.nid'] = $node->nid; $fields = array( 'email_invitee' => t('Sent to'), 'users.name' => t('Sent by'), 'node_invites.sent' => t('Invite Sent'), 'node_invites.status' => t('Status'), 'acted_upon' => t('Acted Upon'), ); } return node_invite_list_invites($filters, $fields); } /** * Return a list of invites in a themed table * * @param $filters - An array of filters to apply to the query. For example: * array('node_invites.uid_inviter' => 1, 'node.nid' => 13); * The filters can be any of the fields in the node_invites table, the node * table or the user table. * @param $fields - An array of fields and column titles to display * @return the themed table of invites */ function node_invite_list_invites($filters = NULL, $fields = NULL, $limit = 50) { // TODO: // 4. Provide ajax(?) text links to alter status of invites // 5. Provide csv export(?) if (!isset($fields) || empty($fields)) { $fields = array( 'node.title' => t('What'), 'email_invitee' => t('Sent to'), 'users.name' => t('Sent by'), 'node_invites.sent' => t('Invite Sent'), 'node_invites.status' => t('Status'), 'acted_upon' => t('Acted Upon'), ); } // TODO: Do we need fields besides iid here? $default_fields = array('iid', 'node_invites.nid', 'uid_inviter', 'notes_invitee', 'notes_inviter'); // Build the WHERE $where = array(); $values = array(); if (isset($filters) && is_array($filters)) { foreach ($filters as $field => $value) { if (is_int($value)) { $where[] = "$field = %d"; } else { $where[] = "$field = '%s'"; } // Don't display fields that are being filtered on unset($fields[$field]); $values[] = $value; } $where = implode(" AND ", $where); } // Build the SELECT part of the SQL Query $select = "SELECT " . implode(', ', $default_fields); $select .= ', ' . implode(', ', array_keys($fields)); // Build the FROM/JOIN component $from = "FROM {node_invites} AS node_invites JOIN {node} AS node ON node_invites.nid = node.nid JOIN {users} AS users ON users.uid = node_invites.uid_inviter"; $sql = $select . ' ' . $from; if ($where) { $sql .= ' AND ' . $where; } return theme('node_invite_list_invites', $sql, $values, $fields, $limit); } /** * Implementation of hook_nodeapi(). */ function node_invite_nodeapi(&$node, $op, $teaser = NULL, $a4 = NULL) { // decides whether or not to enable disable invites for this node. if ($op == 'load') { // We need node_invites_enabled here as well as settings because sometimes // the node will be set has enabled, but that node type won't be available // for using with node_invite. $node->node_invites_enabled = _ni_node_is_enabled($node); $settings = db_fetch_array(db_query("SELECT * FROM {node_invite_settings} WHERE nid = %d", $node->nid)); $node->node_invite_settings = $settings; } elseif ($op == 'update' || $op == 'insert') { if (!isset($node->node_invites_enabled)) { $node->node_invites_enabled = _ni_node_is_enabled($node); } // Replace any existing data with the content of the submitted node $sth = db_query("DELETE FROM {node_invite_settings} WHERE nid = %d", $node->nid); $sth = db_query("INSERT INTO {node_invite_settings} (nid, invites_enabled, open, subject, message) VALUES (%d, %d, '%s', '%s', '%s')", $node->nid, $node->node_invites_enabled, $node->node_invites_open, $node->node_invite_subject_override, $node->node_invite_message_override); } elseif ($op == 'delete') { db_query("DELETE FROM {node_invite_settings} WHERE nid = %d", $node->nid); } elseif ($op == 'view' && (!$teaser || variable_get('node_invite_rsvp_form_on_teasers', FALSE))) { // @TODO: The variable node_invite_form_on_teasers is not yet available // on the admin interface and needs to be set manually because there's no // particular reason to not allow the form on the teaser view. Note that // this variable is only for the RSVP form, not the invite form. // What would be ideal is if node_invite acted as almost a cck field // that could be attached to any node type. D7 potential? if (node_invite_invites_access($node) && variable_get('node_invite_display', 'link') == 'form' && !$teaser) { module_load_include('inc', 'node_invite', 'node_invite.send'); $invite_form = drupal_get_form('node_invite_send', $node, TRUE); } if (variable_get('node_invite_rsvp_display', 'link') == 'form') { module_load_include('inc', 'node_invite', 'node_invite.rsvp'); $invite = node_invite_find_invite($node, FALSE); if ($invite) { $rsvp_form = drupal_get_form('node_invite_rsvp', $node, $invite, TRUE); } elseif ($node->node_invite_settings['open']) { $rsvp_form = drupal_get_form('node_invite_rsvp_new', $node, TRUE); } } if ($invite_form || $rsvp_form) { $node->content['node_invite'] = array('#weight' => 10); $node->content['node_invite']['invite'] = array('#value' => $invite_form); $node->content['node_invite']['rsvp'] = array('#value' => $rsvp_form); } } } /** * Implementation of hook_form_alter(). * * Inserts the node_invite form element into node add/edit forms */ function node_invite_form_alter(&$form, &$form_state, $form_id) { $matches = array(); if (preg_match("/(.+)_node_form$/", $form_id, $matches) && _node_invite_type_is_allowed($matches[1])) { $node = $form['#node']; $enabled = _ni_node_is_enabled($node); // grab the default message/subject (if they exist) // only if node isn't new (DUH) $node_invite_subject = $node_invite_message = ''; if (isset($node->nid)) { $query = db_query("SELECT * FROM {node_invite_settings} WHERE nid = %d", $node->nid); if ($row = db_fetch_object($query)) { $node_invite_subject = $row->subject; $node_invite_message = $row->message; $node_invite_open = $row->open; } } // add a form element to override $form['node_invite_group'] = array( '#type' => 'fieldset', '#title' => t('Node Invite'), '#collapsed' => TRUE, '#collapsible' => TRUE, '#description' => t('Set the invite settings for this node.'), ); $form['node_invite_group']['node_invites_enabled'] = array( '#type' => 'radios', '#title' => t('Enable invites on this node'), '#options' => array( '1' => t('Yes'), '0' => t('No'), ), '#default_value' => $enabled, ); $form['node_invite_group']['node_invites_open'] = array( '#type' => 'checkbox', '#title' => t('Allow anyone with permission to RSVP'), '#description' => t('Leaving this unchecked will mean that only users who have been explicitly invited will be able to rsvp.'), '#default_value' => $node_invite_open, ); $form['node_invite_group']['node_invite_subject_override'] = array( '#type' => 'textfield', '#title' => t('Override default subject?'), '#description' => t('Leave this textarea BLANK to use the site-default invite subject.'), '#default_value' => $node_invite_subject, ); $form['node_invite_group']['node_invite_message_override'] = array( '#type' => 'textarea', '#title' => t('Override default message?'), '#description' => t('Leave this textarea BLANK to use the site-default invite message.'), '#default_value' => $node_invite_message, ); $form['node_invite_group']['node_invite_token_wrapper'] = array( '#type' => 'fieldset', '#description' => theme('token_help', 'node'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#title' => t('Available Tokens'), ); } } /** * Save or update the given invite. * * @TODO: We need a hook here for 3rd party module integration */ function node_invite_save($invite, $params = array()) { $ni_fields = array('iid', 'nid', 'uid_inviter', 'inviter_custom_name', 'inviter_custom_email', 'email_invitee', 'name_invitee', 'uid_invitee', 'status', 'sent', 'acted_upon', 'notes_invitee', 'notes_inviter', 'hash', 'personal_message'); if (is_object($invite) && $invite->iid) { // Updating an invite $query = array(); $values = array(); foreach ($params as $key => $value) { if (in_array($key, $ni_fields)) { $query[] = "$key = '%s'"; $values[] = $value; } } // The status was changed, so acted_upon needs to be set to now() $keys = array_keys($params); if (!in_array('acted_upon', $keys) && in_array('status', $keys)) { $query[] = "acted_upon = %d"; $values[] = time(); } $success = db_query('UPDATE {node_invites} SET ' . implode(', ', $query) . ' WHERE iid = %d', array_merge($values, array($invite->iid))); if (!$success) { return FALSE; } // Refresh the invite object $invite = node_invite_load($invite->iid, TRUE); } else { // Creating a new invite $fields = array(); $values = array(); $s = array(); foreach ($params as $key => $value) { if (in_array($key, $ni_fields)) { $fields[] = $key; $values[] = $value; $s[] = is_numeric($value) ? "%d" : "'%s'"; } } $sent = time(); $fields[] = 'sent'; $values[] = time(); $s[] = "%d"; $hash = md5($params['email_invitee'] . $sent); $fields[] = 'hash'; $values[] = $hash; $s[] = "'%s'"; $success = db_query('INSERT INTO {node_invites} (' . implode(', ', $fields) . ') VALUES ('. implode(', ', $s) . ')', $values); if (!$success) { return FALSE; } $iid = db_last_insert_id('node_invite', 'iid'); $invite = node_invite_load($iid); } return $invite; } /** * Return a list of all invites associated with the given node. */ function node_invite_load_invites_by_nid($nid, $status = NULL) { $status = $status == NODE_INVITE_ANY ? NULL : $status; $query_args = array($nid); $status_qry = ''; if ($status && is_array($status)) { $status_qry = "AND ni.status IN ('" . implode("', '", array_fill(0, count($status), '%s')) . "')"; $query_args = array_merge($query_args, $status); } elseif ($status) { $status_qry = "AND ni.status = '%s'"; $query_args[] = $status; } $sql = db_query("SELECT ni.*, u.mail FROM {node_invites} AS ni JOIN {users} AS u ON ni.uid_inviter = u.uid WHERE ni.nid = %d $status_qry", $query_args); $invites = array(); while ($invite = db_fetch_object($sql)) { $invites[$invite->iid] = $invite; } return $invites; } /** * Attaches invite specific info onto a node. */ function node_invite_specific_info($invite, &$node) { $account = user_load($invite->uid_inviter); $name = $invite->name_invitee ? $invite->name_invitee : $invitee->email_invitee; $node->invite_specific_info = array( 'node-invite-iid' => $invite->iid, 'node-invite-hash' => $invite->hash, 'node-invite-recip-name' => $name, 'node-invite-recip-mail' => $invite->email_invitee, 'node-invite-recip-uid' => $invite->uid_invitee, 'inviter-name' => $account->name, 'inviter-mail' => $account->mail, 'node-invite-personal-message' => $invite->personal_message, ); } /** * Mail out the invite. * * @param $invite - The invite to send out. * @param $node - The node object with node_invite_specific_info() already * having been executed on it. * @param $token_flush - This is needed if sending multiple invites in a * single page load (i.e. on the invite send page) as the token's can't * be cached in that case or everyone's emails will say the same thing * even if a name or email token is used. * * @return the value of drupal_mail(). */ function node_invite_send_mail($invite, $node, $token_flush = FALSE) { $message = variable_get('node_invite_message_default', NODE_INVITE_MESSAGE_DEFAULT); if (isset($invite->settings['message']) && !empty($invite->settings['message'])) { $message = $invite->settings['message']; } $rsvp_url = url(node_invite_rsvp_path($invite), array('absolute' => TRUE)); $message = str_replace("[inviter-rsvp-url]", $rsvp_url, $message); if ($token_flush) { $message = token_replace($message, 'node', $node, '[', ']', array(), TRUE); } else { $message = token_replace($message, 'node', $node); } // Since the parsed message can contain the "personal message" which can also // contain tokens, so we need to run a token_replace twice. $message = token_replace($message, 'node', $node); $subject = variable_get('node_invite_subject_default', NODE_INVITE_SUBJECT_DEFAULT); if (isset($invite->settings['subject']) && !empty($invite->settings['subject'])) { $subject = $invite->settings['subject']; } $subject = token_replace($subject, 'node', $node); $from_string = _ni_format_from_address($invite); $params = array( 'subject' => $subject, 'body' => $message, 'from' => _ni_format_from_address($invite, TRUE), ); $msg = drupal_mail( 'node_invite', // module 'invite', // key $invite->email_invitee, // to language_default(), // language $params, // params $from_string, // from TRUE // send ); if ($msg) { watchdog('node_invite', 'Invite sent to: !email', array('!email' => check_plain($invite->email_invitee))); } else { watchdog('node_invite', 'Unable to send invite to: !email', array('!email' => check_plain($invite->email_invitee)), WATCHDOG_ERROR); } return $msg; } /** * Returns a formatted email address to send the message from. */ function _ni_format_from_address($invite, $default = FALSE) { // Format the from address $from_name = $invite->name; if ($invite->inviter_custom_name) { $from_name = $invite->inviter_custom_name; } // Use the default from address? if ($default && $from = variable_get('node_invite_default_from', FALSE)) { $from_email = $from; } else { $from_email = $invite->mail; if ($invite->inviter_custom_email) { $from_email = $invite->inviter_custom_email; } } $from_string = $from_email; if (!empty($from_name)) { $from_string = '"' . check_plain($from_name) . '" <' . $from_email . '>'; } return $from_string; } function _ni_format_to_address($invite) { if ($invite->uid_invitee) { $user = user_load($invite->uid_invitee); $to_email = $user->mail; $to_name = $user->name; } else { $to_email = $invite->email_invitee; $to_name = $invite->name_invitee; } $to_string = $to_email; if (!empty($to_name) && $to_name != $to_email) { $to_string = check_plain($to_name) . ' <' . $to_email . '>'; } return $to_string; } /*** Theme functions ***/ function theme_node_invite_view_field_invitees($nid, $invites) { $items = array(); foreach ($invites as $iid => $invite) { if ($invite->uid_invitee) { $account = user_load($invite->uid_invitee); $items[] = $account->mail; } else { $items[] = $invite->email_invitee; } } if (empty($invites)) { return t('No one has been invited to this event'); } else { return theme('item_list', $items); } } function theme_node_invite_list_invites($sql, $values, $fields, $limit = 50) { global $user; // Setup the table headers $header = array(); foreach ($fields as $field => $title) { $header[] = array('data' => $title, 'field' => $field); } $header[] = array('data' => t('Actions')); if (isset($account)) { // Remove the 'Sent by' field, since it's redundant in this case unset($header[2]); $header = array_filter($header); } if (isset($node)) { // Remove the 'What' field, since it's redundant in this case unset($header[0]); $header = array_filter($header); } // Execute the query and build the table row data $sql .= tablesort_sql($header); $result = pager_query($sql, $limit, 0, NULL, $values); // Create a list of display fields without the table part $display_fields = array(); foreach ($fields as $field => $value) { if (($index = strrpos($field, '.')) !== FALSE) { $display_fields[] = substr($field, strrpos($field, '.') + 1); } else { $display_fields[] = $field; } } $destination = drupal_get_destination(); $rows = array(); while ($row = db_fetch_object($result)) { $actions = array(); if (user_access('manage all invites') || (user_access('manage own invites') && $row->uid_inviter == $user->uid)) { if (!in_array($row->status, array(NODE_INVITE_RESPONSE_YES, NODE_INVITE_REVOKED))) { $actions[] = l(t('Revoke'), 'node_invite/revoke/' . $row->iid, array('query' => $destination)); $actions[] = l(t('Resend'), 'node_invite/resend/' . $row->iid, array('query' => $destination)); } if ($row->status == NODE_INVITE_REVOKED) { $actions[] = l(t('Reinstate'), 'node_invite/reinstate/' . $row->iid, array('query' => $destination)); } if (!in_array($row->status, array(NODE_INVITE_NEW, NODE_INVITE_REVOKED))) { $title = $row->notes_invitee ? t('View (comment)') : t('View'); $actions[] = l($title, 'node_invite/view/' . $row->iid, array('query' => $destination)); } $url = node_invite_rsvp_path($row->iid); $actions[] = l(t('Edit'), $url, array('query' => $destination)); } $actions = implode(' ', $actions); $actions = empty($actions) ? t('N/A') : $actions; $current_row = array(); foreach ((array)$row as $field => $value) { // Don't display non-display fields if (!in_array($field, $display_fields)) continue; // Format the date fields if (in_array($field, array('sent', 'acted_upon'))) { if (!$value) { $value = t('Never'); } else { $value = format_date($value, 'small'); } } $current_row[] = $value; } $current_row[] = $actions; $rows[] = $current_row; } if (!$rows) { return theme('node_invite_list_invites_empty'); } return theme('table', $header, $rows, array('class' => 'manage-invites')) . theme('pager'); } function theme_node_invite_list_invites_empty() { return '
' . t('No invites have been sent yet') . '
'; } function theme_node_invite_view($invite) { if ($invite->uid_invitee) { $user = user_load($invite->uid_invitee); $name = theme('username', $user); } elseif (!empty($invite->name_invitee)) { $name = $invite->name_invitee; } else { $name = $invite->email_invitee; } drupal_set_title(t('Viewing invitation for @name', array('@name' => $name))); $output = array(); $output[] = t('Response: !response', array('!response' => $invite->status)); $output[] = t('Sent: !date', array('!date' => format_date($invite->sent))); $output[] = t('Updated: !date', array('!date' => format_date($invite->acted_upon))); $output[] = t('Comment: !notes', array('!notes' => $invite->notes_invitee)); $output = theme_item_list($output); $link = !empty($_GET['destination']) ? $_GET['destination'] : 'node/' . $invite->nid . '/manage_invites'; $output .= l(t('Back'), $link); return $output; } /*** Node Invite Helper Functions ***/ /** * Return TRUE if node_invites is enabled on the given nid. * @param $node - The node to check whether invites are enabled */ function _ni_node_is_enabled($node) { // First confirm that invites are allowed on this node type if (!_node_invite_type_is_allowed($node->type)) { return FALSE; } $enabled = variable_get('node_invite_default', NODE_INVITE_DEFAULT_FOR_ENABLED_NODES); // Test to see if the invite status has been explicitly set on this node. $sth = db_query("SELECT invites_enabled FROM {node_invite_settings} WHERE nid = %d", $node->nid); if ($row = db_fetch_array($sth)) { $enabled = $row['invites_enabled']; } return $enabled; } /** * Whether node_invite can be used with the specified node type. * * @param $type - The node type in question * @return TRUE if node_invite can be used with the given type. */ function _node_invite_type_is_allowed($type) { return in_array($type, variable_get('node_invite_node_types', array()), TRUE); } /** * Return the path for the RSVP page for the specified invite. */ function node_invite_rsvp_path($invite) { if (!is_object($invite)) { $invite = node_invite_load($invite); } $url = 'node_invite/rsvp/' . $invite->nid . '/'; $url .= variable_get('node_invite_hash_urls', FALSE) ? $invite->hash : $invite->iid; return $url; } /** * Function to retrieve a list of users based on the given string. * This is a slight modification of the user_autocomplete() function as we * want to allow the user to enter a comma separated list of users instead of * just a single user. * * @param $string - A list of usernames separated by comma's * @return an array containing a list of matching users. */ function _node_invite_user_autocomplete($string = '') { $usernames = preg_split('/ *, */', $string); // Only try and match on the last username in the list $username = array_pop($usernames); $string = implode(', ', $usernames); $limit = variable_get('node_invite_user_limit', 10); $matches = array(); if ($username && variable_get('node_invite_userreference', FALSE) && module_exists('userreference')) { $settings = array( 'referenceable_roles' => variable_get('node_invite_referenceable_roles', array()), 'referenceable_status' => variable_get('node_invite_referenceable_status', ''), 'advanced_view' => variable_get('node_invite_advanced_view', NULL), 'advanced_view_args' => variable_get('node_invite_advanced_view_args', ''), ); $results = FALSE; if (!empty($settings['advanced_view'])) { $results = _userreference_potential_references_views($settings, $username, 'contains', array(), $limit); } if (!$results) { $results = _userreference_potential_references_standard($settings, $username, 'contains', array(), $limit); } foreach ($results as $result) { $key = $string ? $string . ', ' . $result['title'] : $result['title']; $matches[$key] = '
' . $result['rendered'] . '
'; } } elseif ($username) { // Userreference selection is not enabled, so fall back to the default if (count($usernames)) { // Filter out all users who have already been selected $not_in = "AND LOWER(name) NOT IN (" . implode(', ', array_fill(0, count($usernames), "LOWER('%s')")) . ")"; } $values = array_merge(array($username), $usernames); $result = db_query_range("SELECT name FROM {users} WHERE LOWER(name) LIKE LOWER('%s%%') $not_in", $values, 0, $limit); while ($user = db_fetch_object($result)) { $key = $string ? $string . ', ' . $user->name : $user->name; $matches[$key] = check_plain($user->name); } } drupal_json($matches); } /** * Return a list of invites for the specified uid or email address. If the uid * is set then the email address parameter will be ignored. */ function node_invite_user_invites($uid = NULL, $email = NULL) { $invites = array(); if ($uid) { $query = "SELECT * FROM node_invites WHERE uid_invitee = %d"; $param = $uid; } elseif ($email) { $query = "SELECT * FROM node_invites WHERE email_invitee = '%s'"; $param = $email; } if ($query) { $result = db_query($query, $param); while ($invite = db_fetch_object($result)) { $invites[$invite->iid] = $invite; } } return $invites; }