? .svn
? images/.svn
? includes/.svn
? modules/.svn
? modules/og_access/.svn
? modules/og_access/translations/.svn
? modules/og_actions/.svn
? modules/og_actions/translations/.svn
? modules/og_notifications/.svn
? modules/og_notifications/translations/.svn
? modules/og_views/.svn
? modules/og_views/includes/.svn
? modules/og_views/translations/.svn
? modules/og_views/views/.svn
? tests/.svn
? theme/.svn
? translations/.svn
Index: og.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/og/Attic/og.install,v
retrieving revision 1.71.4.5
diff -u -p -w -r1.71.4.5 og.install
--- og.install	15 May 2009 17:28:02 -0000	1.71.4.5
+++ og.install	12 Aug 2009 20:21:18 -0000
@@ -68,6 +68,13 @@ function og_schema() {
         'not null'        => TRUE,
         'default'         => 0,
       ),
+      'og_private_preview'=> array(
+        'description'     => 'Give invited users preview access to an otherwise private group?',
+        'type'            => 'int',
+        'size'            => 'tiny',
+        'not null'        => TRUE,
+        'default'         => 0,
+      ),
     ),
     'primary key'       => array('nid'),
   );
@@ -143,6 +150,24 @@ function og_schema() {
     ),
     'primary key'       => array('nid', 'group_nid'),
   );
+  $schema['og_invite'] = array(
+    'description'     => 'Pending OG Invitations',
+    'fields'          => array(
+        'mail' => array(
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+        'description' => "Invited email address.",
+      ),
+      'group_nid'       => array(
+        'description'     => "The group's {node}.nid.",
+        'type'            => 'int',
+        'size'            => 'normal',
+        'not null'        => TRUE,
+      ),
+    ),
+    'primary key'       => array('mail', 'group_nid'),
+  );
 
   return $schema;
 }
@@ -432,6 +457,48 @@ function og_update_6203() {
   return $ret;
 }
 
+// Add og_invite table to store emails for inviting non-users to groups.
+function og_update_6204() {
+  $ret = array();
+  $schema['og_invite'] = array(
+    'description'     => 'Pending OG Invitations',
+    'fields'          => array(
+        'mail' => array(
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+        'description' => "Invited email address.",
+      ),
+      'group_nid'       => array(
+        'description'     => "The group's {node}.nid.",
+        'type'            => 'int',
+        'size'            => 'normal',
+        'not null'        => TRUE,
+      ),
+    ),
+    'primary key'       => array('mail', 'group_nid'),
+  );
+  db_create_table($ret, 'og_invite', $schema['og_invite']);
+  return $ret;
+}
+
+// Add private_preview field to allow invited users to preview a private group before joining
+function og_update_6205() {
+    $ret = array();
+    
+    $schema = array(
+        'description'     => 'Give invited users preview access to an otherwise private group?',
+        'type'            => 'int',
+        'size'            => 'tiny',
+        'not null'        => TRUE,
+        'default'         => 0,
+      );
+    
+    
+    db_add_field($ret, 'og', 'og_private_preview', $schema);
+    return $ret;
+}
+
 // end updates //
 
 function og_uninstall() {
Index: og.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/og/Attic/og.module,v
retrieving revision 1.628.4.22
diff -u -p -w -r1.628.4.22 og.module
--- og.module	10 Aug 2009 20:39:11 -0000	1.628.4.22
+++ og.module	12 Aug 2009 20:21:18 -0000
@@ -45,8 +45,8 @@ function og_menu() {
     'file' => 'og.pages.inc',
     'page callback' => 'og_subscribe',
     'page arguments' => array(2), 
-    'access callback' => 'node_access',
-    'access arguments' => array('view', 2),
+    'access callback' => 'og_menu_access_subscribe',
+    'access arguments' => array(2),
     'title' => 'Join group'
   );
   
@@ -258,6 +258,29 @@ function og_menu_access_invite($node) {
   return og_is_group_member($node) && ($node->og_selective < OG_INVITE_ONLY || og_is_group_admin($node));
 }
 
+function og_menu_access_subscribe($group_node) {
+    if($group_node->og_private) {
+        if(og_is_invited($group_node)) return TRUE;
+        else return FALSE;
+    }
+    else return node_access('view', $group_node);
+}
+
+function og_is_invited($group_node) {
+    global $user;
+
+    if (!isset($user->og_groups)) {
+      $user = user_load(array('uid' => $user->uid));  
+    }
+
+    $sql = "SELECT COUNT(*) FROM {og_invite} WHERE group_nid = %d and mail = '%s'";
+    $query = db_query($sql, $group_node->nid, $user->uid);
+    $cnt = db_result($query);
+
+    if($cnt == 0) return FALSE;
+    else return TRUE;
+}
+
 /**
  * Check a user's membership in a group. 
  * 
@@ -727,8 +750,12 @@ function og_subscribe_user($gid, $accoun
   else {
     // Moderated groups must approve all members (selective=1).
     $node = node_load($gid);
+    // this switch statement may take some careful reading
     switch ($node->og_selective) {
       case OG_MODERATED:
+        $sql = "SELECT COUNT(*) FROM {og_invite} WHERE mail = '%d' AND group_nid = %d";
+        $cnt = db_result(db_query($sql, $account->uid, $node->nid));
+        if (!$cnt) {
         $admins = array();
         og_save_subscription($gid, $account->uid, array('is_active' => 0));
         $sql = og_list_users_sql(1, 1, NULL);
@@ -757,15 +784,16 @@ function og_subscribe_user($gid, $accoun
         }
         $return_value = array('type' => 'approval',
                               'message' => t('Membership request to the %group group awaits approval by an administrator.', array('%group' => $node->title)));
-
-        break;
-      case OG_OPEN:
-        og_save_subscription($gid, $account->uid, array('is_active' => 1));
-        $return_value = array('type' => 'subscribed',
-                              'message' => t('You are now a member of %group.', array('%group' => $node->title)));
         break;
-      case OG_CLOSED:
+        }
       case OG_INVITE_ONLY:
+        if (!$cnt) {
+          $sql = "SELECT COUNT(*) FROM {og_invite} WHERE mail = '%d' AND group_nid = %d";
+          $cnt = db_result(db_query($sql, $account->uid, $node->nid));
+        }
+      case OG_CLOSED:
+        if (!$cnt) {
+          // no invitations
         // admins can add members to these groups, but others can't.
         if (og_is_group_admin($node)) {
           og_save_subscription($gid, $account->uid, array('is_active' => 1));
@@ -774,6 +802,17 @@ function og_subscribe_user($gid, $accoun
           $return_value = array('type' => 'rejected',
                               'message' => t('Membership request to the %group group was rejected, only group administrators can add users to this group.', array('%group' => $node->title)));
         }
+          break;
+        }
+        $sql = "DELETE FROM {og_invite} WHERE mail = '%d'";
+        $result = db_query($sql, $account->uid);
+        // fall through if we are already invited
+      case OG_OPEN:
+        og_save_subscription($gid, $account->uid, array('is_active' => 1));
+        $return_value = array('type' => 'subscribed',
+                              'message' => t('You are now a member of the %group.', array('%group' => $node->title)));
+        break;
+
     }
   }
                       
@@ -1787,6 +1826,14 @@ function _og_mail_text($messageid, $vari
         return t("Invitation to join the group '@group' at @site", $variables, $langcode);
       case 'og_invite_user_body':
         return t("Hi. I'm a member of '@group' and I welcome you to join this group as well. Please see the link and message below.\n\n@group\n@description\nJoin: !group_url\n@body", $variables, $langcode);
+      case 'og_invite_nonuser_subject':
+        return t("Invitation to join the group '@group' at @site", $variables, $langcode);
+      case 'og_invite_nonuser_body':
+        return t("Hi. I'm a member of '@group' and I welcome you to join this group as well. Please see the link and message below.\n\n@group\n@description\nJoin: !group_url\n@body", $variables, $langcode);
+      case 'og_invite_user_privatepreview_subject':
+        return t("Invitation to join the group '@group' at @site", $variables, $langcode);
+      case 'og_invite_user_privatepreview_body':
+        return t("Hi. I'm a member of '@group' and I welcome you to join this group as well. Please see the link and message below.\n\n@group\n@description\nJoin: !group_url\n@body", $variables, $langcode);
       case 'og_request_user_subject':
         return t("Membership request for '@group' from '@username'", $variables, $langcode);        
       case 'og_request_user_body':
@@ -1850,6 +1897,7 @@ function og_user($op, $edit, &$account, 
         );
         return $form;
       }
+      break;
     case 'insert':
       if (isset($edit['og_register'])) {
         foreach (array_keys(array_filter($edit['og_register'])) as $gid) {
@@ -1859,6 +1907,16 @@ function og_user($op, $edit, &$account, 
           }
         }  
       }
+      $sql = "SELECT group_nid FROM {og_invite} WHERE mail = '%s'";
+      $result = db_query($sql, $account->mail);
+      while ($group = db_fetch_array($result)) {
+        $gid = $group['group_nid'];
+        $groupnode = node_load($gid);
+        og_save_subscription($gid, $account->uid, array('is_active' => 1));
+        drupal_set_message(t('You are now a member of the %group.', array('%group' => $groupnode->title))); 
+      }
+      $sql = "DELETE FROM {og_invite} WHERE mail = '%s'";
+      $result = db_query($sql, $account->mail);
       break;
     case 'delete':
       $sql = 'DELETE FROM {og_uid} WHERE uid=%d';
@@ -2104,7 +2162,7 @@ function og_block_details() {
     if ($subscription == 'active' || user_access('administer nodes')) {
       $links = module_invoke_all('og_create_links', $node);
       // We want to open this up for OG_INVITE_ONLY but we need to handle invitation workflow much better. See http://drupal.org/node/170332
-      if ($node->og_selective < OG_INVITE_ONLY) {
+      if ($node->og_selective < OG_INVITE_ONLY  || og_is_group_admin($node)) {
         $links['invite'] = l(t('Invite friend'), "og/invite/$node->nid");
       }
       $links['subscribers'] = $txt;
@@ -2180,11 +2238,14 @@ function og_block_details() {
 }
 
 function og_subscribe_link($node) {
-  if ($node->og_selective == OG_MODERATED) {
-    $txt = t('Request membership');
-  }
-  elseif ($node->og_selective == OG_OPEN) {
+  global $user;
+  $sql = "SELECT COUNT(*) FROM {og_invite} WHERE mail = '%d' AND group_nid = %d";
+  $cnt = db_result(db_query($sql, $user->uid, $node->nid));
+  if ($cnt || $node->og_selective == OG_OPEN) {
+    // already invited or open group
     $txt = t('Join');
+  } elseif ($node->og_selective == OG_MODERATED) {
+    $txt = t('Request membership');
   }
   if(isset($txt))
     return l($txt, "og/subscribe/$node->nid", array('attributes' => array('rel' => 'nofollow'), 'query' => drupal_get_destination()));
Index: og.pages.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/og/Attic/og.pages.inc,v
retrieving revision 1.1.2.3
diff -u -p -w -r1.1.2.3 og.pages.inc
--- og.pages.inc	8 Aug 2009 17:37:37 -0000	1.1.2.3
+++ og.pages.inc	12 Aug 2009 20:21:19 -0000
@@ -214,6 +214,9 @@ function og_invite_form_validate($form, 
   elseif (in_array($user->mail, $emails)) {
     form_set_error('mails', t('You may not invite yourself - @self.', array('@self' => $user->mail)));
   }
+  elseif (in_array($user->name, $emails)) {
+    form_set_error('mails', t('You may not invite yourself - @self.', array('@self' => $user->name)));
+  }
   else {
     $valid_emails = array();
     $bad = array();
@@ -253,16 +256,79 @@ function og_invite_form_submit($form, &$
     '@group' => $node->title,
     '@description' => $node->og_description,
     '@site' => variable_get('site_name', 'drupal'),
-    '!group_url' => url("og/subscribe/$node->nid", array('absolute' => TRUE)),
     '@body' => $form_state['values']['pmessage'],
   );
   
   global $user;
   $from = $user->mail;
+  $sent = 0;
   foreach ($emails as $mail) {
+    $variables['!group_url'] = url("og/subscribe/$node->nid", array('absolute' => TRUE));
+    if($node->og_private and $node->og_private_preview)
+      $variables['!preview_url'] = url("node/$node->nid", array('absolute' => TRUE));
+
+    $account = user_load(array('mail' => $mail));
+    $wasinactive = FALSE;
+    if ($account) {
+      $sql = "SELECT is_active,uid FROM {og_uid} WHERE uid = %d AND nid = %d";
+      $oguid = db_fetch_array(db_query($sql, $account->uid, $node->nid));
+      if ($oguid['uid']) {
+        // user already active/pending
+        if($oguid['is_active']) {
+          drupal_set_message(t("@username is already a member of the group", array('@username' => $account->name)));
+          continue;
+        } else {
+          $wasinactive = TRUE;
+          drupal_set_message(t('@username already requested group membership', array('@username' => $account->name)));
+        }
+      }
+    }
+      
+    if (og_is_group_admin($node)) {
+      if ($account) {
+        if ($wasinactive) {
+          // user wanted to be a member, we want them to be a member--oh happy day
+          db_query("UPDATE {og_uid} SET is_active = 1 WHERE uid = %d and nid = %d", $account->uid, $node->nid);
+          drupal_set_message(t('@username has now been approved as a member', array('@username' => $account->name)));
+        } else {
+          $sql = "SELECT COUNT(*) FROM {og_invite} WHERE mail = '%d' AND group_nid = %d";
+          $cnt = db_result(db_query($sql, $account->uid, $node->nid));
+          if ($cnt) {
+            drupal_set_message(t('@username was already on the invitation list for this group, but will be re-invited.', array('@username' => $account->name)));
+          }
+          else {
+            $record = array('mail' => $account->uid, 'group_nid' => $node->nid);
+            $result = drupal_write_record('og_invite', $record);
+          }
+        }
+      } else {
+        $sql = "SELECT COUNT(*) FROM {og_invite} WHERE mail = '%s' AND group_nid = %d";
+        $cnt = db_result(db_query($sql, $mail, $node->nid));
+        if ($cnt) {
+          drupal_set_message(t('@email was already on the invitation list for this group, but will be re-invited.', array('@email' => $mail)));
+        }
+        else {
+          $record = array('mail' => $mail, 'group_nid' => $node->nid);
+          $result = drupal_write_record('og_invite', $record);
+        }
+        $variables['!group_url'] = url('user/register', array('absolute' => TRUE));
+      }
+    }
+    // could still be here if user in there but is_active = 0: don't send another invitation if they don't need one
+    if ($wasinactive) {
+      continue;
+    }
+    $sent ++;
+
+    // Send different emails based on whether the invitee has an account yet or not and whether the group is private w/preview enabled
+    if($account) {
+      if($node->og_private and $node->og_private_preview)
     drupal_mail('og', 'invite_user', $mail, $GLOBALS['language'], $variables, $from);
+      else drupal_mail('og', 'invite_user_privatepreview', $mail, $GLOBALS['language'], $variables, $from);
+    }
+    else drupal_mail('og', 'invite_nonuser', $mail, $GLOBALS['language'], $variables, $from);
   }
-  drupal_set_message(format_plural(count($emails), '1 invitation sent.', '@count invitations sent.'));
+  drupal_set_message(format_plural($sent, '1 invitation sent.', '@count invitations sent.'));
 }
 
 function og_subscribe($node, $uid = NULL) {
@@ -286,7 +352,9 @@ function og_subscribe($node, $uid = NULL
     $account = user_load(array('uid' => $uid));
   }
 
-  if ($node->og_selective >= OG_INVITE_ONLY || $node->status == 0 || !og_is_group_type($node->type)) {
+  $sql = "SELECT COUNT(*) FROM {og_invite} WHERE mail = '%d' AND group_nid = %d";
+  $cnt = db_result(db_query($sql, $account->uid, $node->nid));
+  if (($node->og_selective >= OG_INVITE_ONLY && !$cnt) || $node->og_selective >= OG_CLOSED || $node->status == 0 || !og_is_group_type($node->type)) {
     drupal_access_denied();
     exit();
   }
@@ -312,7 +380,9 @@ function og_subscribe($node, $uid = NULL
 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) {
+  $sql = "SELECT COUNT(*) FROM {og_invite} WHERE mail = '%d' AND group_nid = %d";
+  $cnt = db_result(db_query($sql, $account->uid, $node->nid));
+  if ($node->og_selective == OG_MODERATED && !$cnt) {
     $form['request'] = array(
       '#type' => 'textarea', 
       '#title' => t('Additional details'), 
@@ -384,6 +454,7 @@ function og_add_users($form_state, $grou
     '#description' => t('Add one or more usernames in order to associate users with this group. Multiple usernames should be separated by a comma.'),
     '#element_validate' => array('og_add_users_og_names_validate'),
   );
+  $form['og_names']['#description'] .= '<br />' . t('If you wish to use emails in addition to usernames, <a href="@url">invite</a> will accept a mix of both.', array( '@url' => url('og/invite/'. $group_node->nid)));
   $form['op'] = array('#type' => 'submit', '#value' => t('Add users'));
   $form['gid'] = array('#type' => 'value', '#value' => $group_node->nid);
   return $form;
Index: includes/og.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/og/includes/Attic/og.admin.inc,v
retrieving revision 1.15.4.2
diff -u -p -w -r1.15.4.2 og.admin.inc
--- includes/og.admin.inc	22 Jun 2009 15:59:27 -0000	1.15.4.2
+++ includes/og.admin.inc	12 Aug 2009 20:21:19 -0000
@@ -156,9 +156,35 @@ function og_admin_settings() {
     '#type' => 'textarea',
     '#title' => t('Invite user notification body'),
     '#rows' => 10,
-    '#description' => t('The body of the message sent to users invited to join a group. Available variables: @group, @site, @description, !group_url, @body'),
+    '#description' => t('The body of the message sent to users invited to join a group. Available variables: @group, @site, @description, !group_url, !preview_url, @body'),
     '#default_value' => _og_mail_text('og_invite_user_body')
   );   
+  $form['og_settings']['notifications']['og_invite_nonuser_subject'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Invite non-user notification subject'),
+    '#description' => t('The subject of the message sent to people without accounts invited to join a group. Available variables: @group, @site, @description, !group_url, @body'),
+    '#default_value' => _og_mail_text('og_invite_nonuser_subject')
+  );
+  $form['og_settings']['notifications']['og_invite_nonuser_body'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Invite non-user notification body'),
+    '#rows' => 10,
+    '#description' => t('The body of the message sent to people without accounts invited to join a group. Available variables: @group, @site, @description, !group_url, !preview_url, @body'),
+    '#default_value' => _og_mail_text('og_invite_nonuser_body')
+  );
+  $form['og_settings']['notifications']['og_invite_user_privatepreview_subject'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Invite user notification subject (for private groups with private preview enabled)'),
+    '#description' => t('The subject of the message sent to users invited to join a private group with private preview enabled. Available variables: @group, @site, @description, !group_url, @body'),
+    '#default_value' => _og_mail_text('og_invite_nonuser_subject')
+  );
+  $form['og_settings']['notifications']['og_invite_user_privatepreview_body'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Invite user notification body (for private groups with private preview enabled)'),
+    '#rows' => 10,
+    '#description' => t('The body of the message sent to people without accounts invited to join a group. Available variables: @group, @site, @description, !group_url, !preview_url, @body'),
+    '#default_value' => _og_mail_text('og_invite_nonuser_body')
+  );
   $form['og_settings']['notifications']['og_request_user_subject'] = array(
     '#type' => 'textfield',
     '#title' => t('Request user notification subject'),
Index: modules/og_access/og_access.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/og/modules/og_access/Attic/og_access.module,v
retrieving revision 1.32.4.2
diff -u -p -w -r1.32.4.2 og_access.module
--- modules/og_access/og_access.module	8 Aug 2009 18:58:30 -0000	1.32.4.2
+++ modules/og_access/og_access.module	12 Aug 2009 20:21:19 -0000
@@ -106,6 +106,11 @@ function og_access_content_extra_fields(
       'description' => t('Checkbox for visibility of group home page to non-members.'),
       'weight' => 0,
     );
+    $extra['og_private_preview'] = array(
+        'label' => t('Private group preview'),
+        'description' => t('If this box is checked, users invited to private groups will be able to view the group in order to decide whether or not they want to join.'),
+        'weight' => 0,
+    );
   }
   return $extra;
 }
@@ -133,6 +138,14 @@ function og_access_alter_group_form(&$fo
         '#type' => 'value',
         '#value' => 1
       );
+
+      $form['og_private_preview'] = array(
+        '#type' => 'checkbox',
+        '#title' => t('Private group preview'),
+        '#default_value' => isset($node->nid) ? $node->og_private_preview : 0,
+        '#weight' => module_exists('content') ? content_extra_field_weight($node->type, 'og_private_preview') : 0,
+        '#description' => t('If this box is checked, users invited to private groups will be able to view the group in order to decide whether or not they want to join.'),
+      );
       break;
 
     case OG_PRIVATE_GROUPS_CHOOSE_TRUE :
@@ -147,6 +160,14 @@ function og_access_alter_group_form(&$fo
         '#weight' => module_exists('content') ? content_extra_field_weight($node->type, 'og_private') : 0,        
         '#description' => t('Should this group be visible only to its members? Disabled if the group is set to <em>List in Directory</em> or <em>Membership requests: open</em>.')
       );
+
+      $form['og_private_preview'] = array(
+        '#type' => 'checkbox',
+        '#title' => t('Private group preview'),
+        '#default_value' => isset($node->nid) ? $node->og_private_preview : 0,
+        '#weight' => module_exists('content') ? content_extra_field_weight($node->type, 'og_private_preview') : 0,
+        '#description' => t('If this box is checked, users invited to private groups will be able to view the group in order to decide whether or not they want to join.'),
+      );
       break;
   }
 }
@@ -254,6 +275,11 @@ function og_access_form_alter(&$form, &$
 function og_access_node_grants($account, $op) { 
   if ($op == 'view') {
     $grants['og_public'][] = 0; // everyone can see a public node
+
+    $sql = "SELECT group_nid FROM og_invite WHERE mail='%s'";
+	$result = db_query($sql, $account->uid);
+    while($gid = db_result($result))
+      $grants['og_private_preview'][] = $gid;
   }
 
   // Subscribers get an admin or non-admin grant for each subscription
@@ -298,7 +324,7 @@ function og_access_node_access_records($
       );
     }
     else {
-      // If the group private, let subscribers view the group homepage.
+      // If the group is private, let subscribers view the group homepage.
       $grants[] = array (
         'realm' => 'og_subscriber',
         'gid' => $node->nid,
@@ -308,6 +334,18 @@ function og_access_node_access_records($
         'priority' => 0,
       );
 
+      if($node->og_private_preview) {
+        // If the private group allows previews for invited users, let those users view the group homepage as well.
+          $grants[] = array (
+            'realm' => 'og_private_preview',
+            'gid' => $node->nid,
+            'grant_view' => 1,
+            'grant_update' => 0,
+            'grant_delete' => 0,
+            'priority' => 0,
+          );
+      }
+
     }
   }
   elseif (!empty($node->og_groups)) {
Index: modules/og_actions/og_actions.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/og/modules/og_actions/Attic/og_actions.module,v
retrieving revision 1.9.4.2
diff -u -p -w -r1.9.4.2 og_actions.module
--- modules/og_actions/og_actions.module	9 Jun 2009 01:04:44 -0000	1.9.4.2
+++ modules/og_actions/og_actions.module	12 Aug 2009 20:21:19 -0000
@@ -33,6 +33,12 @@ function og_actions_action_info() {
         'nodeapi' => array('insert', 'update'),
       ),
     ),
+    'og_invite_user_action' => array(
+      'type' => 'user',
+      'description' => t('Invite user to the specified groups'),
+      'configurable' => TRUE,
+      'hooks' => array(),
+    ),
     'og_subscribe_user_action' => array(
       'type' => 'user',
       'description' => t('Subscribe user to the specified groups'),
@@ -274,6 +280,63 @@ function og_subscribe_user_action_submit
 }
 
 /**
+ * A configurable action to subscribe a user to specific groups.
+ */
+function og_invite_user_action($account, $context) {
+  if (isset($context['groups'])) {
+    require_once drupal_get_path('module', 'og') ."/og.pages.inc";
+    foreach ($context['groups'] as $gid) {
+      $form_state = array('values' => array('mails' => $account->name, 'gid' => $gid, 'pmessage' => $context['pmessage']));
+      drupal_execute('og_invite_form', $form_state, node_load($gid));
+    }
+    watchdog('action', 'Invited user %name to groups %groups.', array('%name' => $account->name, '%groups' => implode(',', $context['groups'])));
+  }
+}
+
+/**
+ * Configuration form for Invite User action.
+ */
+function og_invite_user_action_form($context) {
+  $form = array();
+
+  if (!isset($context['groups'])) {
+    $context['groups'] = array();
+  }
+
+  $groups = og_all_groups_options();
+  if (count($groups)) {
+    $form['groups'] = array(
+      '#type' => 'select',
+      '#title' => t('Groups'),
+      '#options' => $groups,
+      '#description' => t('Select the groups to which this user should be invited.'),
+      '#default_value' => $context['groups'],
+      '#required' => TRUE,
+      '#multiple' => TRUE,
+    );
+    $form['pmessage'] = array(
+      '#type' => 'textarea', 
+      '#title' => t('Personal message'), 
+      '#description' => t('Optional. Enter a message which will become part of the invitation email.')
+    );
+  }
+  else {
+    drupal_set_message(t('Please <a href="!url">create</a> a group first.', array('!url' => url('admin/content'))));
+  }
+  return $form;
+}
+
+/**
+ * Submission handler for Invite User action configuration form.
+ */
+function og_invite_user_action_submit($form, &$form_state) {
+  return array(
+    'groups' => $form_state['values']['groups'],
+    'pmessage' => $form_state['values']['pmessage'],
+  );
+}
+
+/**
  * A configurable action to unsubscribe a user from specific groups.
  */
 function og_unsubscribe_user_action($account, $context) {
