Index: og.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/og/og.module,v
retrieving revision 1.628
diff -u -p -r1.628 og.module
--- og.module	31 Mar 2009 13:30:54 -0000	1.628
+++ og.module	6 Apr 2009 11:15:25 -0000
@@ -862,6 +862,14 @@ function og_invite_form($form_state, $no
     '#title' => t('Personal message'), 
     '#description' => t('Optional. Enter a message which will become part of the invitation email.')
   );
+  
+  if($node->og_selective == OG_MODERATED) {
+    $form['jointoken'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Create unique subscription urls to automatically approve invitees'),
+    );
+  }
+  
   $form['op'] = array('#type' => 'submit', '#value' => t('Send invitation'));
   $form['gid'] = array ('#type' => 'value', '#value' => $node->nid);
   return $form;
@@ -927,13 +935,37 @@ function og_invite_form_submit($form, &$
   
   global $user;
   $from = $user->mail;
+  
+  $add_token = $form_state['values']['jointoken'] == 1;
+  
   foreach ($emails as $mail) {
+    $variables['!group_url'] = _og_invite_url($mail, $node->nid, $add_token);
     drupal_mail('og', 'invite_user', $mail, $GLOBALS['language'], $variables, $from);
   }
   drupal_set_message(format_plural(count($emails), '1 invitation sent.', '@count invitations sent.'));
 }
 
 /**
+ * construct the invitation url - adding timestamp and hash query params if requested
+ */
+function _og_invite_url($email, $gid, $add_token = FALSE) {
+  $options = array('absolute' => TRUE);
+  if($add_token) {
+    $timestamp = time();
+    $invite_hash = _og_invite_rehash($email, $timestamp, $gid);
+    $options['query'] = "t=$timestamp&h=$invite_hash";
+  }
+  return url("og/subscribe/$gid", $options);
+}
+
+/**
+ * make a hash of email, timestamp, and gid
+ */
+function _og_invite_rehash($email, $timestamp, $gid) {
+  return md5($timestamp . $email . $gid);
+}
+
+/**
  * Implementation of hook_mail().
  */
 function og_mail($key, &$message, $params) {
@@ -944,13 +976,18 @@ function og_mail($key, &$message, $param
 
 function og_subscribe($node, $uid = NULL) {
   global $user;
+  
+  if(isset($_GET['h'])) {
+    $_SESSION['og_join_params'] = $_GET;    
+  }
+
   if (is_null($uid)) {
     if ($user->uid) {
       $account = $user;
     }
     else {
-      drupal_set_message(t('In order to join this group, you must login or register a new account. After you have successfully done so, you will need to request membership again.'));
-      drupal_goto('user');
+      drupal_set_message(t('In order to join this group, you must login or register a new account.'));
+      drupal_goto('user', 'destination='.$_GET['q']);
     }
   }
   else {
@@ -978,12 +1015,60 @@ function og_subscribe($node, $uid = NULL
 }
 
 /**
+ * gets the group join information from the session if available and passes basic validity tests
+ */
+function _og_get_join_token() {
+  static $join_token;
+  
+  if(!isset($join_token)) {
+    $join_token = FALSE;
+    if(isset($_SESSION['og_join_params'])) {
+      $timestamp = $_SESSION['og_join_params']['t'];
+      $hash = $_SESSION['og_join_params']['h'];
+      $path = $_SESSION['og_join_params']['q'];
+      
+      if(strpos($path, 'og/subscribe/') !== 0) {
+        return $join_token;
+      }
+
+      $path_args = explode('/', $path);
+      
+      // is session state for this group
+      if(!isset($path_args[2]) || !is_numeric($path_args[2])) {
+        return $join_token;
+      }
+      
+      $join_token = array('hash' => $hash, 'timestamp' => $timestamp, 'gid' => $path_args[2]);
+    }
+  }
+  
+  return $join_token;
+}
+
+/**
+ * returns TRUE if the hash matches the group and account email
+ */
+function _og_check_join_token($join_token, $gid, $account) {
+  return $join_token['gid'] == $gid && $join_token['hash'] == _og_invite_rehash($account->mail, $join_token['timestamp'], $gid);
+}
+
+/**
+ * remove the join token from the session
+ */
+function _og_use_join_token($join_token) {
+  unset($_SESSION['og_join_params']);
+}
+
+/**
  * Confirm og membership form
  */
 function og_confirm_subscribe($form_state, $gid, $node, $account) {
   $form['gid'] = array('#type' => 'value', '#value' => $gid);
   $form['account'] = array('#type' => 'value', '#value' => $account);
-  if ($node->og_selective == OG_MODERATED) {
+  
+  $join_token = _og_get_join_token();
+  
+  if ($node->og_selective == OG_MODERATED && !($join_token !== FALSE && _og_check_join_token($join_token, $gid, $account))) {
     $form['request'] = array(
       '#type' => 'textarea', 
       '#title' => t('Additional details'), 
@@ -1023,8 +1108,22 @@ function og_confirm_subscribe_submit($fo
 function og_subscribe_user($gid, $account, $request = NULL) {
   // moderated groups must approve all members (selective=1)
   $node = node_load($gid);
+  
   switch ($node->og_selective) {
     case OG_MODERATED:
+      $join_token = _og_get_join_token();
+
+      // if join token in the session matches this subscription then bypass approval - invalid token silently defaults to usual workflow
+      if($join_token !== FALSE && _og_check_join_token($join_token, $gid, $account)) {
+        og_save_subscription($gid, $account->uid, array('is_active' => 1)); // as per http://drupal.org/node/156224
+        $return_value = array('type' => 'subscribed',
+                              'message' => t('You are now a member of the %group.', array('%group' => $node->title)));
+        // remove the session variable
+        _og_use_join_token($join_token);
+        break;
+      }
+
+    
       $admins = array();
       og_save_subscription($gid, $account->uid, array('is_active' => 0));
       $sql = og_list_users_sql(1, 1, NULL);
@@ -2210,9 +2309,11 @@ function og_get_sql_args() {
 
 function og_user($op, $edit, &$account, $category = NULL) {
   global $user;
-
   switch ($op) {
     case 'register':
+      // set gids from session if user has followed og invite url
+      _og_join_set_gids();
+      
       $options = array();
       list($types, $in) = og_get_sql_args();
       
@@ -2283,9 +2384,34 @@ function og_user($op, $edit, &$account, 
         );
       }
       break;
+    case 'login':
+      _og_join_set_destination();
+      break;
   }
 }
 
+/**
+ * Streamlined invitation workflow - populate request destination paramater from url stored in session variable
+ */
+function _og_join_set_destination() {
+  // ensure that subscribe page is the destination while join session variable present
+  if(!isset($_REQUEST['destination']) && isset($_SESSION['og_join_params'])) {
+    $_REQUEST['destination'] = $_SESSION['og_join_params']['q'];
+  }  
+}
+
+/**
+ * Streamlined invitation workflow - populate request gids paramater from gid derived from session variable
+ */
+function _og_join_set_gids() {
+  // get gids from session
+  if(!isset($_REQUEST['gids']) && isset($_SESSION['og_join_params'])) {
+    $join_token = _og_get_join_token();
+    
+    $_REQUEST['gids'] = array($join_token['gid']);
+  }  
+}
+
 function og_save_ancestry($node) {
   if (og_is_group_post_type($node->type)) {
     $sql = "DELETE FROM {og_ancestry} WHERE nid = %d";
@@ -2711,4 +2837,4 @@ function og_check_token($token, $seed) {
  */
 function og_broadcast_access($node) {
   return og_is_group_admin($node) && module_exists('og_notifications');
-}
+}
\ No newline at end of file
