Index: invite.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/invite/invite.module,v
retrieving revision 1.10.2.66
diff -u -r1.10.2.66 invite.module
--- invite.module	1 Sep 2007 02:33:06 -0000	1.10.2.66
+++ invite.module	1 Sep 2007 18:57:48 -0000
@@ -284,7 +284,7 @@
       'path' => 'invite',
       'title' => variable_get('invite_page_title', t('Invite your friends and colleagues')),
       'callback' => 'drupal_get_form',
-      'callback arguments' => array('invite_form', 'page'),
+      'callback arguments' => array('invite_form', 'page', array(), FALSE),
       'access' => $send_access,
       'type' => MENU_NORMAL_ITEM,
     );
@@ -301,6 +301,7 @@
       'callback' => 'invite_overview',
       'access' => $send_access,
       'type' => MENU_LOCAL_TASK,
+      'weight' => 5,
     );
     $items[] = array(
       'path' => 'invite/withdraw',
@@ -309,9 +310,22 @@
       'type' => MENU_CALLBACK,
     );
   }
-  else if ($user->uid && $send_access) {
+  else {
     // Check for newly registered users
-    _invite_check_messages($user->uid);
+    if ($user->uid && $send_access) {
+      _invite_check_messages($user->uid);
+    }
+
+    if (arg(0) == 'invite' && arg(1) == 'remind') {
+      $items[] = array(
+        'path' => 'invite/remind/'. arg(2),
+        'title' => t('Send reminder'),
+        'callback' => 'invite_remind',
+        'callback arguments' => arg(2),
+        'access' => $send_access,
+        'type' => MENU_LOCAL_TASK,
+      );
+    }
   }
 
   return $items;
@@ -541,6 +555,7 @@
       $row[]  = check_plain($invite->email);
       $row[] = $expired ? t('Expired') : t('Pending');
       $row[] = $delete_link;
+      $row[] = $expired ? l(t('Remind'), 'invite/remind/'. $invite->reg_code) : '';
     }
     else {
       $account_exists = !empty($invite->uid);
@@ -553,6 +568,7 @@
       }
       $row[] = $account_exists ? t('Accepted') : t('Deleted');
       $row[] = $allow_delete ? $delete_link : '';
+      $row[] = '';
     }
 
     $rows[] = $row;
@@ -569,7 +585,7 @@
   // Display table of invites
   $output = '<div><fieldset><legend>'. t('Your invitations') .'</legend>';
   if (count($items) > 0) {
-    $headers = array(t('E-mail'), t('Status'), '');
+    $headers = array(t('E-mail'), t('Status'), array('data' => '', 'colspan' => 2));
     $output .= theme('table', $headers, $items, array('id' => 'invite_table'));
   }
   else {
@@ -585,10 +601,18 @@
  *
  * @param $op
  *   The type of form to generate, 'page' or 'block'.
+ * @param $edit
+ *   Previous values when sending a reminder.
+ * @param $is_reminder
+ *   Whether or not sending a reminder (doesn't count towards limit).
  */
-function invite_form($op = 'page') {
+function invite_form($op = 'page', $edit = array(), $is_reminder = FALSE) {
   global $user;
 
+  if (!is_array($edit)) {
+    $edit = (array)$edit;
+  }
+
   $maximum_invites = _invite_max_invites_by_role();
   $maximum_per_turn = _invite_max_invites_per_turn();
 
@@ -603,14 +627,22 @@
         $form['#access'] = FALSE;
         return $form;
       }
-      else {
-        // Redirect to invites list
+      else if (!$is_reminder) {
+        // Redirect to invites list, except when sending a reminder
         drupal_set_message(t('Sorry, you reached the maximum number (@max) of invitations.', array('@max' => $maximum_invites)), 'error');
         drupal_goto('invite/list');
       }
     }
   }
 
+  $form['is_reminder'] = array(
+    '#type' => 'value',
+    '#value' => $is_reminder,
+  );
+  $form['reg_code'] = array(
+    '#type' => 'value',
+    '#value' => $is_reminder ? $edit['reg_code'] : NULL,
+  );
   $form['remaining_invites'] = array(
     '#type' => 'value',
     '#value' => $invites_left,
@@ -673,34 +705,63 @@
       );
 
       // Recipient email address
-      $failed_emails = '';
-
-      if (isset($_SESSION['invite_failed_emails'])) {
-        $failed_emails = implode("\n", (array)unserialize($_SESSION['invite_failed_emails']));
-        unset($_SESSION['invite_failed_emails']);
-      }
-      $form['email'] = array(
-        '#title' => t('To'),
-        '#default_value' => $failed_emails,
-        '#description' => format_plural($maximum_per_turn, 'Type the e-mail address of the person you would like to invite.', 'Type the e-mail address(es) of the person(s) you would like to invite. Addresses should be separated by newlines or commas.'),
-        '#required' => TRUE,
-      );
-      if ($maximum_per_turn != 1) {
-        $form['email']['#type'] = 'textarea';
-        $form['email']['#rows'] = 2;
+      if (!$is_reminder) {
+        $failed_emails = '';
+        if (isset($_SESSION['invite_failed_emails'])) {
+          $failed_emails = implode("\n", (array)unserialize($_SESSION['invite_failed_emails']));
+          unset($_SESSION['invite_failed_emails']);
+        }
+        $form['email'] = array(
+          '#title' => t('To'),
+          '#default_value' => $failed_emails,
+          '#description' => format_plural($maximum_per_turn, 'Type the e-mail address of the person you would like to invite.', 'Type the e-mail address(es) of the person(s) you would like to invite. Addresses should be separated by newlines or commas.'),
+          '#required' => TRUE,
+        );
+        if ($maximum_per_turn != 1) {
+          $form['email']['#type'] = 'textarea';
+          $form['email']['#rows'] = 2;
+        }
+        else {
+          $form['email']['#type'] = 'textfield';
+          $form['email']['#maxlength'] = 64;
+        }
+        if ($failed_emails) {
+          $form['email']['#attributes']['class'] = 'error';
+        }
       }
       else {
-        $form['email']['#type'] = 'textfield';
-        $form['email']['#maxlength'] = 64;
-      }
-      if ($failed_emails) {
-        $form['email']['#attributes']['class'] = 'error';
+        // When sending a reminder the email is not editable
+        $maximum_per_turn = 1;
+        $form['email_markup'] = array(
+          '#type' => 'item',
+          '#title' => t('To'),
+          '#value' => check_plain($edit['email']),
+        );
+        $form['email'] = array(
+          '#type' => 'value',
+          '#value' => $edit['email'],
+        );
       }
 
       // Message subject
-      $subject = invite_get_subject();
+      $subject_editable = variable_get('invite_subject_editable', FALSE);
 
-      if (variable_get('invite_subject_editable', FALSE)) {
+      // Perform token replacement on the default email subject
+      $default_subject = invite_get_subject();
+      if ($is_reminder) {
+        $default_subject = t('Re:') .' '. $default_subject;
+      }
+      $form['default_subject'] = array(
+        '#type' => 'value',
+        '#value' => $default_subject,
+      );
+      if (!$is_reminder) {
+        $subject = $default_subject;
+      }
+      else if ($subject_editable && !empty($edit['data']['subject'])) {
+        $subject = t('Re:') .' '. $edit['data']['subject'];
+      }
+      if ($subject_editable) {
         $form['subject'] = array(
           '#type' => 'textfield',
           '#title' => t('Subject'),
@@ -711,11 +772,15 @@
         );
       }
       else {
-        $form['subject'] = array(
+        $form['subject_markup'] = array(
           '#type' => 'item',
           '#title' => t('Subject'),
           '#value' => check_plain($subject),
         );
+        $form['subject'] = array(
+          '#type' => 'value',
+          '#value' => $subject,
+        );
       }
 
       // Message body
@@ -725,6 +790,7 @@
       );
       $form['message'] = array(
         '#type' => 'textarea',
+        '#default_value' => ($is_reminder && !empty($edit['data']['message'])) ? $edit['data']['message'] : '',
         '#description' => format_plural($maximum_per_turn, 'This message will be added to the mail sent to the person you are inviting.', 'This message will be added to the mail sent to the person(s) you are inviting.'),
       );
 
@@ -750,7 +816,13 @@
     $output .= drupal_render($form['remaining_invites_markup']);
     $output .= drupal_render($form['remaining_invites']);
     $output .= drupal_render($form['from']);
+    if (isset($form['email_markup'])) {
+      $output .= drupal_render($form['email_markup']);
+    }
     $output .= drupal_render($form['email']);
+    if (isset($form['subject_markup'])) {
+      $output .= drupal_render($form['subject_markup']);
+    }
     $output .= drupal_render($form['subject']);
 
     // Show complete invitation message
@@ -817,45 +889,47 @@
 
   $emails = _invite_get_emails($edit['email']);
 
-  if (count($emails) > 0) {
-    // Filter out already registered users, but pass validation
-    $failed_emails = _invite_validate_emails("SELECT mail AS email FROM {users} WHERE mail IN (%s)", $emails);
-    if (count($failed_emails)) {
-      $error = format_plural(count($failed_emails), 'The following recipient is already a member:', 'The following recipients are already members:') .'<br />';
-      foreach ($failed_emails as $key => $email) {
-        $account = user_load(array('mail' => $email));
-        $failed_emails[$key] = theme('username', $account) .' ('. check_plain($email) .')';
-      }
-      $error .= implode(', ', $failed_emails);
-      drupal_set_message($error, 'error');
-    }
-
-    // Filter out already invited users, but pass validation
-    $failed_emails = _invite_validate_emails("SELECT email FROM {invite} WHERE email IN (%s)", $emails);
-    if (count($failed_emails)) {
-      $error = format_plural(count($failed_emails), 'The following recipient has already been invited:', 'The following recipients have already been invited:') .'<br />';
-      $error .= implode(', ', array_map('check_plain', $failed_emails));
-      drupal_set_message($error, 'error');
-    }
-
-    // Check that there is at least one valid e-mail remaining after filtering
-    // out dupes
-    if (count($emails) == 0) {
-      form_set_error('email');
-      return;
-    }
-
-    // Check invite limit, fail to let the user choose which ones to send
-    if ($edit['remaining_invites'] != INVITE_UNLIMITED_INVITES && count($emails) > $edit['remaining_invites']) {
-      form_set_error('email', format_plural($edit['remaining_invites'], 'You have only 1 invite left.', 'You have only @count invites left.'));
-      return;
-    }
-
-    // Check limit per turn
-    $maximum_per_turn = _invite_max_invites_per_turn();
-    if ($maximum_per_turn != INVITE_UNLIMITED_INVITES && count($emails) > $maximum_per_turn) {
-      form_set_error('email', format_plural($maximum_per_turn, 'You cannot send more than one invitation per turn.', 'You cannot send more than @count invitations per turn.'));
-      return;
+  if (!$edit['is_reminder']) {
+    if (count($emails) > 0) {
+      // Filter out already registered users, but pass validation
+      $failed_emails = _invite_validate_emails("SELECT mail AS email FROM {users} WHERE mail IN (%s)", $emails);
+      if (count($failed_emails)) {
+        $error = format_plural(count($failed_emails), 'The following recipient is already a member:', 'The following recipients are already members:') .'<br />';
+        foreach ($failed_emails as $key => $email) {
+          $account = user_load(array('mail' => $email));
+          $failed_emails[$key] = theme('username', $account) .' ('. check_plain($email) .')';
+        }
+        $error .= implode(', ', $failed_emails);
+        drupal_set_message($error, 'error');
+      }
+  
+      // Filter out already invited users, but pass validation
+      $failed_emails = _invite_validate_emails("SELECT email FROM {invite} WHERE email IN (%s)", $emails);
+      if (count($failed_emails)) {
+        $error = format_plural(count($failed_emails), 'The following recipient has already been invited:', 'The following recipients have already been invited:') .'<br />';
+        $error .= implode(', ', array_map('check_plain', $failed_emails));
+        drupal_set_message($error, 'error');
+      }
+  
+      // Check that there is at least one valid email remaining after filtering
+      // out dupes
+      if (count($emails) == 0) {
+        form_set_error('email');
+        return;
+      }
+  
+      // Check invite limit, fail to let the user choose which ones to send
+      if ($edit['remaining_invites'] != INVITE_UNLIMITED_INVITES && count($emails) > $edit['remaining_invites']) {
+        form_set_error('email', format_plural($edit['remaining_invites'], 'You have only 1 invite left.', 'You have only @count invites left.'));
+        return;
+      }
+
+      // Check limit per turn
+      $maximum_per_turn = _invite_max_invites_per_turn();
+      if ($maximum_per_turn != INVITE_UNLIMITED_INVITES && count($emails) > $maximum_per_turn) {
+        form_set_error('email', format_plural($maximum_per_turn, 'You cannot send more than one invitation per turn.', 'You cannot send more than @count invitations per turn.'));
+        return;
+      }
     }
   }
 
@@ -882,9 +956,10 @@
 
   foreach ($edit['valid_emails'] as $email) {
     // Create the invite object
+    $code = isset($edit['reg_code']) ? $edit['reg_code'] : _invite_create_regcode();
     $invite = _invite_substitutions(array(
       'email'   => $email,
-      'code'    => _invite_create_regcode(),
+      'code'    => $code,
       'data'    => array('subject' => $subject, 'message' => $message),
     ));
 
@@ -897,8 +972,10 @@
       invite_save($invite);
 
       // Notify other modules
-      $args = array('inviter' => $invite->inviter);
-      module_invoke_all('invite', 'invite', $args);
+      if (!$edit['is_reminder']) {
+        $args = array('inviter' => $invite->inviter);
+        module_invoke_all('invite', 'invite', $args);
+      }
 
       $count_success++;
     }
@@ -948,7 +1025,12 @@
   $expiry = time() + (variable_get('invite_expiry', 30)*60*60*24);
   $data = serialize($edit['data']);
 
-  return db_query("INSERT INTO {invite} (email, reg_code, uid, expiry, data) VALUES ('%s', '%s', %d, %d, '%s')", $edit['email'], $edit['code'], $edit['inviter']->uid, $expiry, $data);
+  $result = db_query("UPDATE {invite} SET expiry = %d, data = '%s' WHERE email = '%s'", $expiry, $data, $edit['email']);
+  if (!db_affected_rows()) {
+    $result = db_query("INSERT INTO {invite} (email, reg_code, uid, expiry, data) VALUES ('%s', '%s', %d, %d, '%s')", $edit['email'], $edit['code'], $edit['inviter']->uid, $expiry, $data);
+  }
+
+  return $result;
 }
 
 /**
@@ -1015,6 +1097,26 @@
 }
 
 /**
+ * Menu callback; send a reminder for an expired invitation.
+ *
+ * @param $code
+ *   Registration code of invitation to send reminder to.
+ */
+function invite_remind($code) {
+  global $user;
+
+  $invite = _invite_fetch_invite($code);
+
+  // Inviter must match current user and invitation must have expired
+  if ($invite->uid == $user->uid && $invite->expiry < time()) {
+    return drupal_get_form('invite_form', 'page', $invite, TRUE);
+  }
+  else {
+    drupal_access_denied();
+  }
+}
+
+/**
  * Calculate the number of successful/pending/unsuccessful invitations.
  *
  * @param $uid
@@ -1313,6 +1415,7 @@
     $reg_code = user_password(8);
     $result = db_query("SELECT COUNT(*) FROM {invite} WHERE reg_code = '%s'", $reg_code);
   } while (db_result($result));
+
   return $reg_code;
 }
 
@@ -1399,7 +1502,7 @@
 <ul>
   <li><strong>Accepted</strong>: Shows that the person you have invited has used your invitation to join the site. Click on the e-mail address to take you to his/her profile page.</li>
   <li><strong>Pending</strong>: The invitation has been sent, but your friend has still not accepted the invitation.</li>
-  <li><strong>Expired</strong>: The invitation has not been used to register on the site within the expiration period.</li>
+  <li><strong>Expired</strong>: The invitation has not been used to register on the site within the expiration period. You may resend the invitation by clicking on the <em>Remind</em> link.</li>
   <li><strong>Deleted</strong>: The user account has been terminated.</li>
 </ul>
 </p>

