diff --git src/bp-activity/bp-activity-screens.php src/bp-activity/bp-activity-screens.php
index a131548..668b1c1 100644
--- src/bp-activity/bp-activity-screens.php
+++ src/bp-activity/bp-activity-screens.php
@@ -234,17 +234,10 @@ function bp_activity_screen_single_activity_permalink() {
 			return;
 		}
 
-		// Check to see if the group is not public, if so, check the
-		// user has access to see this activity.
-		if ( $group = groups_get_group( array( 'group_id' => $activity->item_id ) ) ) {
-
-			// Group is not public.
-			if ( 'public' != $group->status ) {
-
-				// User is not a member of group.
-				if ( !groups_is_user_member( bp_loggedin_user_id(), $group->id ) ) {
-					$has_access = false;
-				}
+		// Check to see if the user has access to see this activity.
+		if ( $group = groups_get_group( array( 'group_id' => $activity->item_id, 'populate_extras' => true ) ) ) {
+			if ( ! $group->user_has_access ) {
+				$has_access = false;
 			}
 		}
 	}
diff --git src/bp-core/bp-core-functions.php src/bp-core/bp-core-functions.php
index 6acfb73..056a1ff 100644
--- src/bp-core/bp-core-functions.php
+++ src/bp-core/bp-core-functions.php
@@ -109,13 +109,21 @@ function bp_core_get_table_prefix() {
  *
  * @since 2.2.0
  *
- * @param array      $items The items to be sorted. Its constituent items can be either associative arrays or objects.
- * @param string|int $key   The array index or property name to sort by.
- * @param string     $type  Sort type. 'alpha' for alphabetical, 'num' for numeric. Default: 'alpha'.
+ * @param array      $items         The items to be sorted. Its constituent items
+ *                                  can be either associative arrays or objects.
+ * @param string|int $key           The array index or property name to sort by.
+ * @param string     $type          Sort type. 'alpha' for alphabetical, 'num'
+ *                                  for numeric. Default: 'alpha'.
+ * @param bool       $preserve_keys Whether to keep the keys or not.
+ *
  * @return array $items The sorted array.
  */
-function bp_sort_by_key( $items, $key, $type = 'alpha' ) {
-	usort( $items, array( new BP_Core_Sort_By_Key_Callback( $key, $type ), 'sort_callback' ) );
+function bp_sort_by_key( $items, $key, $type = 'alpha', $preserve_keys = false ) {
+	if ( true === $preserve_keys ) {
+		uasort( $items, array( new BP_Core_Sort_By_Key_Callback( $key, $type ), 'sort_callback' ) );
+	} else {
+		usort( $items, array( new BP_Core_Sort_By_Key_Callback( $key, $type ), 'sort_callback' ) );
+	}
 
 	return $items;
 }
diff --git src/bp-forums/bp-forums-screens.php src/bp-forums/bp-forums-screens.php
index 7b8a435..76c452c 100644
--- src/bp-forums/bp-forums-screens.php
+++ src/bp-forums/bp-forums-screens.php
@@ -45,7 +45,7 @@ function bp_forums_directory_forums_setup() {
 			$bp->groups->current_group = groups_get_group( array( 'group_id' => $_POST['topic_group_id'] ) );
 			if ( !empty( $bp->groups->current_group->id ) ) {
 				// Auto join this user if they are not yet a member of this group.
-				if ( !bp_current_user_can( 'bp_moderate' ) && 'public' == $bp->groups->current_group->status && !groups_is_user_member( bp_loggedin_user_id(), $bp->groups->current_group->id ) )
+				if ( ! bp_current_user_can( 'bp_moderate' ) && 'anyone_can_join' == bp_groups_group_has_cap( $bp->groups->current_group, 'join_method' ) && ! groups_is_user_member( bp_loggedin_user_id(), $bp->groups->current_group->id ) )
 					groups_join_group( $bp->groups->current_group->id );
 
 				$error_message = '';
diff --git src/bp-groups/bp-groups-actions.php src/bp-groups/bp-groups-actions.php
index 4a3c510..7d71738 100644
--- src/bp-groups/bp-groups-actions.php
+++ src/bp-groups/bp-groups-actions.php
@@ -41,9 +41,10 @@ function bp_groups_group_access_protection() {
 
 	$current_group   = groups_get_current_group();
 	$user_has_access = $current_group->user_has_access;
+	$is_visible      = $current_group->is_visible;
 	$no_access_args  = array();
 
-	if ( ! $user_has_access && 'hidden' !== $current_group->status ) {
+	if ( ! $user_has_access && $is_visible ) {
 		// Always allow access to home and request-membership.
 		if ( bp_is_current_action( 'home' ) || bp_is_current_action( 'request-membership' ) ) {
 			$user_has_access = true;
@@ -93,7 +94,7 @@ function bp_groups_group_access_protection() {
 	// Hidden groups should return a 404 for non-members.
 	// Unset the current group so that you're not redirected
 	// to the default group tab.
-	if ( 'hidden' == $current_group->status ) {
+	if ( ! $is_visible ) {
 		buddypress()->groups->current_group = 0;
 		buddypress()->is_single_item        = false;
 		bp_do_404();
@@ -200,10 +201,9 @@ function groups_action_create_group() {
 				}
 			}
 
-			if ( 'private' == $_POST['group-status'] )
-				$group_status = 'private';
-			elseif ( 'hidden' == $_POST['group-status'] )
-				$group_status = 'hidden';
+			if ( in_array( $_POST['group-status'], bp_groups_get_group_statuses( array(), 'names' ) ) ) {
+				$group_status = sanitize_key( $_POST['group-status'] );
+			}
 
 			if ( !$bp->groups->new_group_id = groups_create_group( array( 'group_id' => $bp->groups->new_group_id, 'status' => $group_status, 'enable_forum' => $group_enable_forum ) ) ) {
 				bp_core_add_message( __( 'There was an error saving group details. Please try again.', 'buddypress' ), 'error' );
@@ -395,7 +395,7 @@ function groups_action_join_group() {
 	if ( !groups_is_user_member( bp_loggedin_user_id(), $bp->groups->current_group->id ) && !groups_is_user_banned( bp_loggedin_user_id(), $bp->groups->current_group->id ) ) {
 
 		// User wants to join a group that is not public.
-		if ( $bp->groups->current_group->status != 'public' ) {
+		if ( 'anyone_can_join' != bp_groups_group_has_cap( $bp->groups->current_group, 'join_method' ) ) {
 			if ( !groups_check_user_has_invite( bp_loggedin_user_id(), $bp->groups->current_group->id ) ) {
 				bp_core_add_message( __( 'There was an error joining the group.', 'buddypress' ), 'error' );
 				bp_core_redirect( bp_get_group_permalink( $bp->groups->current_group ) );
@@ -462,7 +462,7 @@ function groups_action_leave_group() {
 
 		$redirect = bp_get_group_permalink( groups_get_current_group() );
 
-		if( 'hidden' == $bp->groups->current_group->status ) {
+		if ( ! bp_groups_group_has_cap( $bp->groups->current_group, 'show_group' ) ) {
 			$redirect = trailingslashit( bp_loggedin_user_domain() . bp_get_groups_slug() );
 		}
 
@@ -566,3 +566,18 @@ function groups_action_group_feed() {
 	) );
 }
 add_action( 'bp_actions', 'groups_action_group_feed' );
+
+/**
+ * Fire the 'bp_groups_register_group_statuses' action.
+ *
+ * @since 2.7.0
+ */
+function bp_groups_register_group_statuses() {
+	/**
+	 * Fires when it's appropriate to register group statuses.
+	 *
+	 * @since 2.7.0
+	 */
+	do_action( 'bp_groups_register_group_statuses' );
+}
+add_action( 'bp_loaded', 'bp_groups_register_group_statuses' );
diff --git src/bp-groups/bp-groups-activity.php src/bp-groups/bp-groups-activity.php
index 0bf4f29..e99f75a 100644
--- src/bp-groups/bp-groups-activity.php
+++ src/bp-groups/bp-groups-activity.php
@@ -363,7 +363,7 @@ function groups_record_activity( $args = '' ) {
 			$group = groups_get_group( array( 'group_id' => $args['item_id'] ) );
 		}
 
-		if ( isset( $group->status ) && 'public' != $group->status ) {
+		if ( 'anyone' != bp_groups_group_has_cap( $group, 'access_group' ) ) {
 			$hide_sitewide = true;
 		}
 	}
diff --git src/bp-groups/bp-groups-admin.php src/bp-groups/bp-groups-admin.php
index 5a2a0bf..eae2119 100644
--- src/bp-groups/bp-groups-admin.php
+++ src/bp-groups/bp-groups-admin.php
@@ -265,7 +265,7 @@ function bp_groups_admin_load() {
 		 *
 		 * @param array $value Array of allowed group statuses.
 		 */
-		$allowed_status = apply_filters( 'groups_allowed_status', array( 'public', 'private', 'hidden' ) );
+		$allowed_status = apply_filters( 'groups_allowed_status', bp_groups_get_group_statuses( array(), 'names' ) );
 		$status         = ( in_array( $_POST['group-status'], (array) $allowed_status ) ) ? $_POST['group-status'] : 'public';
 
 		/**
@@ -794,7 +794,9 @@ function bp_groups_admin_index() {
  */
 function bp_groups_admin_edit_metabox_settings( $item ) {
 
-	$invite_status = groups_get_groupmeta( $item->id, 'invite_status' ); ?>
+	$invite_status    = groups_get_groupmeta( $item->id, 'invite_status' );
+	$privacy_statuses = bp_groups_get_group_statuses( array(), 'objects' );
+	?>
 
 	<?php if ( bp_is_active( 'forums' ) ) : ?>
 		<div class="bp-groups-settings-section" id="bp-groups-settings-section-forum">
@@ -807,9 +809,9 @@ function bp_groups_admin_edit_metabox_settings( $item ) {
 			<legend><?php _e( 'Privacy', 'buddypress' ); ?></legend>
 
 			<ul>
-				<li><input type="radio" name="group-status" id="bp-group-status-public" value="public" <?php checked( $item->status, 'public' ) ?> /><label for="bp-group-status-public"><?php _e( 'Public', 'buddypress' ) ?></label></li>
-				<li><input type="radio" name="group-status" id="bp-group-status-private" value="private" <?php checked( $item->status, 'private' ) ?> /><label for="bp-group-status-private"><?php _e( 'Private', 'buddypress' ) ?></label></li>
-				<li><input type="radio" name="group-status" id="bp-group-status-hidden" value="hidden" <?php checked( $item->status, 'hidden' ) ?> /><label for="bp-group-status-hidden"><?php _e( 'Hidden', 'buddypress' ) ?></label></li>
+			<?php foreach ( $privacy_statuses as $status) : ?>
+				<li><input type="radio" name="group-status" id="bp-group-status-<?php echo $status->name; ?>" value="<?php echo $status->name; ?>" <?php checked( $item->status, $status->name ); ?> /><label for="bp-group-status-<?php echo $status->name; ?>"><?php echo $status->display_name; ?></label></li>
+			<?php endforeach; ?>
 			</ul>
 		</fieldset>
 	</div>
diff --git src/bp-groups/bp-groups-capabilities.php src/bp-groups/bp-groups-capabilities.php
new file mode 100644
index 0000000..869a996
--- /dev/null
+++ src/bp-groups/bp-groups-capabilities.php
@@ -0,0 +1,578 @@
+<?php
+/**
+ * BuddyPress Groups Capabilities.
+ *
+ * @package BuddyPress
+ * @subpackage GroupsCapabilities
+ * @since 2.7.0
+ */
+
+/** Group Statuses and Capabilities *******************************************/
+
+/**
+ * Set up base group statuses.
+ *
+ * @since 2.7.0
+ */
+function bp_groups_register_base_group_statuses() {
+
+	$public_group_caps = array(
+		'join_method'    => 'anyone_can_join',
+		'show_group'     => 'anyone',
+		'access_group'	 => 'anyone',
+		'post_in_forum'  => 'anyone',
+	);
+	/**
+	 * Filters the basic capabilities of the "public" group status.
+	 *
+	 * @since 2.7.0
+	 *
+	 * @param array $public_group_caps Array of capabilities.
+	 */
+	$public_group_caps = apply_filters( 'bp_groups_public_group_status_caps', $public_group_caps );
+
+	bp_groups_register_group_status( 'public', array(
+		'display_name'    => _x( 'Public', 'Group status name', 'buddypress' ),
+		'capabilities'    => $public_group_caps,
+		'fallback_status' => 'none',
+		'priority'        => 10,
+	) );
+
+	$private_group_caps = array(
+		'join_method'   => 'accepts_membership_requests',
+		'show_group'    => 'anyone',
+		'access_group'  => 'member',
+		'post_in_forum' => 'member',
+	);
+	/**
+	 * Filters the basic capabilities of the "public" group status.
+	 *
+	 * @since 2.7.0
+	 *
+	 * @param array $private_group_caps Array of capabilities.
+	 */
+	$private_group_caps = apply_filters( 'bp_groups_private_group_status_caps', $private_group_caps );
+
+	bp_groups_register_group_status( 'private', array(
+		'display_name'    => _x( 'Private', 'Group status name', 'buddypress' ),
+		'capabilities'    => $private_group_caps,
+		'fallback_status' => 'none',
+		'priority'        => 50,
+	) );
+
+	$hidden_group_caps = array(
+		'join_method'   => 'invitation_only',
+		'show_group'    => array( 'member', 'invited' ), // Invitees must be able to know about hidden groups.
+		'access_group'	=> 'member',
+		'post_in_forum' => 'member',
+	);
+	/**
+	 * Filters the basic capabilities of the "public" group status.
+	 *
+	 * @since 2.7.0
+	 *
+	 * @param array $private_group_caps Array of capabilities.
+	 */
+	$hidden_group_caps = apply_filters( 'bp_groups_hidden_group_status_caps', $hidden_group_caps );
+
+	bp_groups_register_group_status( 'hidden', array(
+		'display_name'    => _x( 'Hidden', 'Group status name', 'buddypress' ),
+		'capabilities'    => $hidden_group_caps,
+		'fallback_status' => 'none',
+		'priority'        => 90,
+	) );
+}
+add_action( 'bp_groups_register_group_statuses', 'bp_groups_register_base_group_statuses', 8 );
+
+/**
+ * Register a group status.
+ *
+ * @since 2.7.0
+ *
+ * @param string $group_status Unique string identifier for the group status.
+ * @param array  $args {
+ *     Array of arguments describing the group type.
+ *
+ *         @type string $name            Displayed name.
+ *         @type array  $capabilities    Array of capabilities. See
+ *                                       `bp_groups_register_base_group_statuses()`
+ *                                       for commons capability sets.
+ *         @type string $fallback_status If a capability isn't set, which typical
+ *                                       status is most similar to the new status?
+ *                                       Specify 'public', 'private', or 'hidden'.
+ *         @type int    $priority        Order the capability among the default
+ *                                       statuses: 'public' has a priority of 10,
+ *                                       'private' has a priority of 50, and
+ *                                       'hidden' has a priority of 90.
+ *
+ * }
+ * @return object|WP_Error Group type object on success, WP_Error object on failure.
+ */
+function bp_groups_register_group_status( $group_status, $args = array() ) {
+	$bp = buddypress();
+
+	if ( isset( $bp->groups->statuses[ $group_status ] ) ) {
+		return new WP_Error( 'bp_group_status_exists', __( 'Group status already exists.', 'buddypress' ), $group_status );
+	}
+
+	$r = bp_parse_args( $args, array(
+		'name'            => $group_status,
+		'display_name'    => ucfirst( $group_status ),
+		'capabilities'    => array(),
+		'fallback_status' => 'public',
+	), 'register_group_status' );
+
+	$group_status = sanitize_key( $group_status );
+
+	/**
+	 * Filters the list of illegal group status names.
+	 *
+	 * - 'any' is a special pseudo-type.
+	 *
+	 * @since 2.7.0
+	 *
+	 * @param array $illegal_names Array of illegal names.
+	 */
+	$illegal_names = apply_filters( 'bp_group_status_illegal_names', array( 'any' ) );
+	if ( in_array( $group_status, $illegal_names, true ) ) {
+		return new WP_Error( 'bp_group_status_illegal_name', __( 'You may not register a group status with this name.', 'buddypress' ), $group_status );
+	}
+
+	// Use the fallback status to fill out the status.
+	if ( 'none' != $r['fallback_status'] ) {
+		$fallback_caps = bp_groups_get_group_status_capabilities( $r['fallback_status'] );
+
+		if ( $fallback_caps ) {
+			$r['capabilities'] = bp_parse_args( $r['capabilities'], $fallback_caps, 'register_group_status_parse_caps' );
+		}
+	}
+
+	$bp->groups->statuses[ $group_status ] = $status = (object) $r;
+
+	/**
+	 * Fires after a group status is registered.
+	 *
+	 * @since 2.7.0
+	 *
+	 * @param string $group_status Group status identifier.
+	 * @param object $status       Group status object.
+	 */
+	do_action( 'bp_groups_register_group_status', $group_status, $status );
+
+	return $status;
+}
+
+/**
+ * Deregister a group status.
+ *
+ * @since 2.7.0
+ *
+ * @param string $group_status Unique string identifier for the group status.
+ *
+ * @return bool|WP_Error if the group status isn't registered.
+ */
+function bp_groups_deregister_group_status( $group_status ) {
+	$bp = buddypress();
+	$retval = false;
+
+	if ( ! isset( $bp->groups->statuses[ $group_status ] ) ) {
+		return new WP_Error( 'bp_group_status_does_not_exist', __( 'Group status does not exist.', 'buddypress' ), $group_status );
+	} else {
+		$old_status_object = $bp->groups->statuses[ $group_status ];
+		unset( $bp->groups->statuses[ $group_status ] );
+		$retval = true;
+	}
+
+	/**
+	 * Fires after a group status is deregistered.
+	 *
+	 * @since 2.7.0
+	 *
+	 * @param string $group_status Group status identifier.
+	 * @param object $status       Removed group status object.
+	 */
+	do_action( 'bp_groups_deregister_group_status', $group_status, $old_status_object );
+
+	return $retval;
+}
+
+/**
+ * Add a group capability to an existing status.
+ *
+ * @since 2.7.0
+ *
+ * @param string $status The name of the status to edit.
+ * @param string $cap    Capability to add.
+ * @param string $value  New value of the capability.
+ *
+ * @return bool True if set, false otherwise.
+ */
+function bp_groups_add_group_status_capability( $status, $cap, $value = true ) {
+	$bp = buddypress();
+	if ( empty( $status ) || empty( $cap ) || ! isset( $bp->groups->statuses[$status] ) ) {
+		return false;
+	}
+	$cap_name = sanitize_key( $cap );
+	$bp->groups->statuses[$status]->capabilities[$cap_name] = $value;
+	return true;
+}
+
+/**
+ * Edit a group capability for a specific status.
+ *
+ * Edit an existing group capability by changing the value.
+ *
+ * @since 2.7.0
+ *
+ * @param string $status The name of the status to edit.
+ * @param string $cap    Capability to edit.
+ * @param string $value  New value of the capability.
+ *
+ * @return bool True if set, false otherwise.
+ */
+function bp_groups_edit_group_status_capability( $status, $cap, $value = true ) {
+	$bp = buddypress();
+	if ( empty( $status )
+		|| empty( $cap )
+		|| ! isset( $bp->groups->statuses[$status] )
+		|| ! isset( $bp->groups->statuses[$status]->capabilities[$cap] ) ) {
+		return false;
+	}
+	$bp->groups->statuses[$status]->capabilities[$cap] = $value;
+	return true;
+}
+
+/**
+ * Get a list of all registered group status objects.
+ *
+ * @since 2.7.0
+ *
+ * @see bp_groups_register_group_status() for accepted arguments.
+ *
+ * @param array|string $args     Optional. An array of key => value arguments to match against
+ *                               the group type objects. Default empty array.
+ * @param string       $output   Optional. The type of output to return. Accepts 'names'
+ *                               or 'objects'. Default 'names'.
+ * @param string       $operator Optional. The logical operation to perform. 'or' means only one
+ *                               element from the array needs to match; 'and' means all elements
+ *                               must match. Accepts 'or' or 'and'. Default 'and'.
+ *
+ * @return array       $types    A list of groups status names or objects.
+ */
+function bp_groups_get_group_statuses( $args = array(), $output = 'names', $operator = 'and' ) {
+	$statuses = buddypress()->groups->statuses;
+
+	$statuses = wp_filter_object_list( $statuses, $args, $operator );
+
+	// Sort by status "priority".
+	$statuses = bp_sort_by_key( $statuses, 'priority', 'num', true );
+
+	/**
+	 * Filters the array of group status objects.
+	 *
+	 * This filter is run before the $output filter has been applied, so that
+	 * filtering functions have access to the entire group status objects.
+	 *
+	 * @since 2.6.0
+	 *
+	 * @param array  $statuses  Group status objects, keyed by name.
+	 * @param array  $args      Array of key=>value arguments for filtering.
+	 * @param string $operator  'or' to match any of $args, 'and' to require all.
+	 */
+	$statuses = apply_filters( 'bp_groups_get_group_statuses', $statuses, $args, $operator );
+
+	if ( 'names' === $output ) {
+		$statuses = wp_list_pluck( $statuses, 'name' );
+	}
+
+	return $statuses;
+}
+
+/**
+ * Retrieve a group status object by name.
+ *
+ * @since 2.7.0
+ *
+ * @param string $group_status The name of the group status.
+ *
+ * @return object A group status object.
+ */
+function bp_groups_get_group_status_object( $group_status ) {
+	$statuses = bp_groups_get_group_statuses( array(), 'objects' );
+
+	if ( empty( $statuses[ $group_status ] ) ) {
+		return null;
+	}
+
+	return $statuses[ $group_status ];
+}
+
+/**
+ * Retrieve a group status object's capabilities by the status name.
+ *
+ * @since 2.7.0
+ *
+ * @param string $group_status The name of the group status.
+ *
+ * @return array The capabilities array of the group status object.
+ */
+function bp_groups_get_group_status_capabilities( $group_status ) {
+
+	$status = bp_groups_get_group_status_object( $group_status );
+
+	if ( empty( $status->capabilities ) ) {
+		return null;
+	}
+
+	return $status->capabilities;
+}
+
+/**
+ * Add a group capability to an existing status.
+ *
+ * Edit an existing group capability by changing the value.
+ *
+ * @since 2.7.0
+ *
+ * @param string $status The name of the status to edit.
+ * @param string $cap    Capability to edit.
+ * @param string $value  New value of the capability.
+ *
+ * @return array $capabilities Capabilities array for specified group.
+ */
+function bp_groups_get_group_capabilities( $group ) {
+	// Have the group's capabilities been populated?
+	if ( is_object( $group ) && ! isset( $group->capabilities ) ) {
+		$group = groups_get_group( array( 'group_id' => $group->id, 'populate_extras' => true ) );
+	} elseif ( is_int( $group ) ) {
+		$group = groups_get_group( array( 'group_id' => (int) $group, 'populate_extras' => true ) );
+	}
+
+	if ( ! isset( $group->capabilities ) ) {
+		return false;
+	}
+
+	if ( ! empty( $group->capabilities ) ) {
+		return $group->capabilities;
+	} else {
+		return false;
+	}
+}
+
+/**
+ * Check whether a group status has a value for a capability.
+ *
+ * If the capability has a non-falsey value, it is returned, so this funciton
+ * can be used to check and fetch capability values. To check the capabilities
+ * of a specific group, use `bp_groups_group_has_cap()` below.
+ *
+ * @since 2.7.0
+ *
+ * @param string $status The name of the status to check.
+ * @param string $cap    Capability to check.
+ *
+ * @return mixed|bool Returns the value stored for the capability if set, false otherwise.
+ */
+function bp_groups_group_status_has_cap( $status, $cap ) {
+	$bp = buddypress();
+
+	if ( ! isset( $bp->groups->statuses[$status] ) || ! isset( $bp->groups->statuses[$status]->capabilities ) ) {
+		return false;
+	}
+
+	/**
+	 * Filter which capabilities are associated with a group status.
+	 *
+	 * @since 2.7.0
+	 *
+	 * @param array  $capabilities Array of capabilities for this status.
+	 * @param string $status       Status name.
+	 * @param string $cap          Capability name.
+	 */
+	$capabilities = apply_filters( 'bp_groups_group_status_has_cap', $bp->groups->statuses[$status]->capabilities, $status, $cap );
+
+	if ( ! empty( $capabilities[$cap] ) ) {
+		return $capabilities[$cap];
+	} else {
+		return false;
+	}
+}
+
+/**
+ * Check whether a group has a value for a capability.
+ *
+ * If the capability has a non-falsey value, it is returned, so this funciton
+ * can be used to check and fetch capability values. To check the capabilities
+ * of a status generally, use `bp_groups_group_status_has_cap()` above.
+ * To filter a particular group's capabilities, use the
+ * `bp_groups_group_object_set_caps` filter hook.
+ *
+ * @since 2.7.0
+ *
+ * @param object|int $group Group object or id of the group to check.
+ * @param string     $cap   Capability to check.
+ *
+ * @return mixed|bool Returns the value stored for the capability if set, false otherwise.
+ */
+function bp_groups_group_has_cap( $group, $cap ) {
+	// Have the group's capabilities been populated?
+	if ( is_object( $group ) && ! isset( $group->capabilities ) ) {
+		$group = groups_get_group( array( 'group_id' => $group->id, 'populate_extras' => true ) );
+	} elseif ( is_int( $group ) ) {
+		$group = groups_get_group( array( 'group_id' => (int) $group, 'populate_extras' => true ) );
+	}
+
+	if ( ! isset( $group->capabilities ) ) {
+		return false;
+	}
+
+	if ( ! empty( $group->capabilities[$cap] ) ) {
+		return $group->capabilities[$cap];
+	} else {
+		return false;
+	}
+}
+
+/**
+ * User-friendly descriptions for each group capability.
+ *
+ * Used on the group settings and create screens to describe the capabilities
+ * of each group status.
+ *
+ * @since 2.7.0
+ *
+ * @param string $cap   Capability to check.
+ * @param mixed  $value The value of the capability we'd like to describe.
+ *
+ * @return mixed|bool Returns the value stored for the capability if set, false otherwise.
+ */
+function bp_groups_group_capabilities_description( $cap, $value ) {
+	$retval = '';
+
+	switch ( $cap ) {
+		case 'join_method':
+			if ( 'anyone_can_join' == $value ) {
+				$retval = __( 'Any site member can join this group.', 'buddypress' );
+			} elseif ( 'accepts_membership_requests' == $value ) {
+				$retval = __( 'Only users who request membership and are accepted can join the group.', 'buddypress' );
+			} elseif ( 'invitation_only' == $value ) {
+				$retval = __( 'Only users who are invited can join the group.', 'buddypress' );
+			}
+			break;
+		case 'show_group' :
+			// @TODO: This could be an array of options.
+			if ( 'anyone' == $value ) {
+				$retval = __( 'This group will be listed in the groups directory and in search results.', 'buddypress' );
+			} else {
+				$retval = __( 'This group will not be listed in the groups directory or search results.', 'buddypress' );
+			}
+			break;
+		case 'access_group' :
+			// @TODO: This could be an array of options.
+			if ( 'anyone' == $value ) {
+				$retval = __( 'Group content and activity will be visible to any visitor to the site.', 'buddypress' );
+			} elseif ( 'loggedin' == $value ) {
+				$retval = __( 'Group content and activity will be visible to any site member.', 'buddypress' );
+			} else {
+				$retval = __( 'Group content and activity will only be visible to members of the group.', 'buddypress' );
+			}
+			break;
+		default:
+			/**
+			 * Provide the group capability description for custom capabilities.
+			 *
+			 * @since 2.7.0
+			 *
+			 * @param string $retval The description of the capability and value combination.
+			 * @param string $cap    Capability name.
+			 * @param string $name   Value for the capability.
+			 */
+			$retval = apply_filters( 'bp_groups_group_custom_capabilities_description', $retval, $cap, $value );
+			break;
+	}
+
+	return $retval;
+}
+
+/**
+ * Check whether a user meets an access condition for a group.
+ *
+ * Used to calculate whether the group is visible and accessible to the user.
+ *
+ * @since 2.7.0
+ *
+ * @param string $access_condition 'anyone', 'loggedin', 'member',
+ *                                 'mod', 'admin', or 'noone'.
+ *                                 Defaults to the current group.
+ * @param int    $group_id         Optional. ID of the group to check.
+ * @param int    $user_id          Optional. ID of the user to check.
+ *                                 Defaults to the current user.
+ *
+ * @return bool
+ */
+function bp_groups_user_meets_access_condition( $access_condition, $group_id = 0, $user_id = 0 ) {
+	if ( ! $group_id ) {
+		$group_id = bp_get_current_group_id();
+	}
+	if ( ! $group_id ) {
+		return false;
+	}
+	if ( ! $user_id ) {
+		$user_id = bp_loggedin_user_id();
+	}
+
+	switch ( $access_condition ) {
+		case 'admin' :
+			$meets_condition = groups_is_user_admin( $user_id, $group_id );
+			break;
+
+		case 'mod' :
+			$meets_condition = groups_is_user_mod( $user_id, $group_id );
+			break;
+
+		case 'member' :
+			$meets_condition = groups_is_user_member( $user_id, $group_id );
+			break;
+
+		case 'invited' :
+			$meets_condition = groups_check_user_has_invite( $user_id, $group_id );
+			break;
+
+		case 'loggedin' :
+			$meets_condition = is_user_logged_in();
+			break;
+
+		case 'noone' :
+			$meets_condition = false;
+			break;
+
+		case 'anyone' :
+		default :
+			$meets_condition = true;
+			break;
+	}
+
+	return (bool) $meets_condition;
+}
+
+/**
+ * Reset the 'last_changed' cache incrementor when groups are updated.
+ *
+ * @since 2.7.0
+ */
+function bp_groups_cache_invalidate_last_changed_incrementor() {
+	wp_cache_delete( 'last_changed', 'bp_groups' );
+}
+// @TODO: This is updated on every group creation, deletion, setting update, and membership change.
+// Is this a bad caching strategy?
+add_action( 'groups_created_group', 'bp_groups_cache_invalidate_last_changed_incrementor' );
+add_action( 'groups_settings_updated', 'bp_groups_cache_invalidate_last_changed_incrementor' );
+add_action( 'groups_delete_group', 'bp_groups_cache_invalidate_last_changed_incrementor' );
+add_action( 'groups_join_group', 'bp_groups_cache_invalidate_last_changed_incrementor' );
+add_action( 'groups_leave_group', 'bp_groups_cache_invalidate_last_changed_incrementor' );
+add_action( 'groups_invite_user', 'bp_groups_cache_invalidate_last_changed_incrementor' );
+add_action( 'groups_uninvite_user', 'bp_groups_cache_invalidate_last_changed_incrementor' );
+add_action( 'groups_accept_invite', 'bp_groups_cache_invalidate_last_changed_incrementor' );
+add_action( 'groups_reject_invite', 'bp_groups_cache_invalidate_last_changed_incrementor' );
+// More, too. Promotions, etc.
+
diff --git src/bp-groups/bp-groups-filters.php src/bp-groups/bp-groups-filters.php
index 4aefa10..ac28c83 100644
--- src/bp-groups/bp-groups-filters.php
+++ src/bp-groups/bp-groups-filters.php
@@ -316,7 +316,7 @@ function bp_groups_disable_at_mention_notification_for_non_public_groups( $send,
 
 	if ( 'groups' === $activity->component ) {
 		$group = groups_get_group( array( 'group_id' => $activity->item_id ) );
-		if ( 'public' !== $group->status && ! groups_is_user_member( $user_id, $group->id ) ) {
+		if ( 'anyone' != bp_groups_group_has_cap( $group, 'access_group' ) && ! groups_is_user_member( $user_id, $group->id ) ) {
 			$send = false;
 		}
 	}
diff --git src/bp-groups/bp-groups-functions.php src/bp-groups/bp-groups-functions.php
index cb0753e..c793b6a 100644
--- src/bp-groups/bp-groups-functions.php
+++ src/bp-groups/bp-groups-functions.php
@@ -86,7 +86,7 @@ function groups_get_group( $args = '' ) {
  *     @type string   $description  Optional. The group's description.
  *     @type string   $slug         The group slug.
  *     @type string   $status       The group's status. Accepts 'public', 'private' or
- *                                  'hidden'. Defaults to 'public'.
+ *                                  'hidden' or registered statuses. Defaults to 'public'.
  *     @type int      $enable_forum Optional. Whether the group has a forum enabled.
  *                                  If the legacy forums are enabled for this group
  *                                  or if a bbPress forum is enabled for the group,
@@ -272,20 +272,27 @@ function groups_edit_base_group_details( $group_id, $group_name, $group_desc, $n
  */
 function groups_edit_group_settings( $group_id, $enable_forum, $status, $invite_status = false ) {
 
-	$group = groups_get_group( array( 'group_id' => $group_id ) );
+	$group = groups_get_group( array( 'group_id' => $group_id, 'populate_extras' => true ) );
 	$group->enable_forum = $enable_forum;
 
 	/**
-	 * Before we potentially switch the group status, if it has been changed to public
-	 * from private and there are outstanding membership requests, auto-accept those requests.
+	 * Before we potentially switch the group status, if the old status required membership requests,
+	 * but the new status allows anyone to join, auto-accept those requests.
 	 */
-	if ( 'private' == $group->status && 'public' == $status )
+	if (
+		// Group membership is currently "by request"...
+		'accepts_membership_requests' == bp_groups_group_has_cap( $group, 'join_method' )
+		// and the group is changing to a status that allows open membership.
+		&& 'anyone_can_join' == bp_groups_group_status_has_cap( $status, $cap )
+		)
+	{
 		groups_accept_all_pending_membership_requests( $group->id );
+	}
 
 	// Now update the status.
 	$group->status = $status;
 
-	if ( !$group->save() )
+	if ( ! $group->save() )
 		return false;
 
 	// If forums have been enabled, and a forum does not yet exist, we need to create one.
@@ -296,8 +303,9 @@ function groups_edit_group_settings( $group_id, $enable_forum, $status, $invite_
 	}
 
 	// Set the invite status.
-	if ( $invite_status )
+	if ( $invite_status ) {
 		groups_update_groupmeta( $group->id, 'invite_status', $invite_status );
+	}
 
 	groups_update_groupmeta( $group->id, 'last_activity', bp_core_current_time() );
 
diff --git src/bp-groups/bp-groups-screens.php src/bp-groups/bp-groups-screens.php
index cbbc758..d248805 100644
--- src/bp-groups/bp-groups-screens.php
+++ src/bp-groups/bp-groups-screens.php
@@ -214,7 +214,7 @@ function groups_screen_group_forum() {
 			check_admin_referer( 'bp_forums_new_reply' );
 
 			// Auto join this user if they are not yet a member of this group.
-			if ( bp_groups_auto_join() && !bp_current_user_can( 'bp_moderate' ) && 'public' == $bp->groups->current_group->status && !groups_is_user_member( bp_loggedin_user_id(), $bp->groups->current_group->id ) ) {
+			if ( bp_groups_auto_join() && !bp_current_user_can( 'bp_moderate' ) && 'anyone_can_join' == bp_groups_group_has_cap( $bp->groups->current_group, 'join_method' ) && !groups_is_user_member( bp_loggedin_user_id(), $bp->groups->current_group->id ) ) {
 				groups_join_group( $bp->groups->current_group->id, bp_loggedin_user_id() );
 			}
 
@@ -521,7 +521,7 @@ function groups_screen_group_forum() {
 			if ( $user_is_banned ) {
 				$error_message = __( "You have been banned from this group.", 'buddypress' );
 
-			} elseif ( bp_groups_auto_join() && !bp_current_user_can( 'bp_moderate' ) && 'public' == $bp->groups->current_group->status && !groups_is_user_member( bp_loggedin_user_id(), $bp->groups->current_group->id ) ) {
+			} elseif ( bp_groups_auto_join() && !bp_current_user_can( 'bp_moderate' ) && 'anyone_can_join' == bp_groups_group_has_cap( $bp->groups->current_group, 'join_method' ) && !groups_is_user_member( bp_loggedin_user_id(), $bp->groups->current_group->id ) ) {
 				// Auto join this user if they are not yet a member of this group.
 				groups_join_group( $bp->groups->current_group->id, bp_loggedin_user_id() );
 			}
@@ -715,7 +715,7 @@ function groups_screen_group_request_membership() {
 
 	$bp = buddypress();
 
-	if ( 'private' != $bp->groups->current_group->status )
+	if ( 'accepts_membership_requests' != bp_groups_group_has_cap( $bp->groups->current_group, 'join_method' ) )
 		return false;
 
 	// If the user is already invited, accept invitation.
@@ -903,7 +903,7 @@ function groups_screen_group_admin_settings() {
 
 		// Checked against a whitelist for security.
 		/** This filter is documented in bp-groups/bp-groups-admin.php */
-		$allowed_status = apply_filters( 'groups_allowed_status', array( 'public', 'private', 'hidden' ) );
+		$allowed_status = apply_filters( 'groups_allowed_status', bp_groups_get_group_statuses( array(), 'names' ) );
 		$status         = ( in_array( $_POST['group-status'], (array) $allowed_status ) ) ? $_POST['group-status'] : 'public';
 
 		// Checked against a whitelist for security.
@@ -1269,7 +1269,7 @@ function groups_screen_group_admin_requests() {
 		return false;
 	}
 
-	if ( ! bp_is_item_admin() || ( 'public' == $bp->groups->current_group->status ) ) {
+	if ( ! bp_is_item_admin() || 'accepts_membership_requests' != bp_groups_group_has_cap( groups_get_current_group(), 'join_method' ) ) {
 		return false;
 	}
 
diff --git src/bp-groups/bp-groups-template.php src/bp-groups/bp-groups-template.php
index f08fea5..0a59e85 100644
--- src/bp-groups/bp-groups-template.php
+++ src/bp-groups/bp-groups-template.php
@@ -280,12 +280,8 @@ function bp_group_is_visible( $group = false ) {
 		$group =& $groups_template->group;
 	}
 
-	if ( 'public' == $group->status ) {
+	if ( $group->is_visible ) {
 		return true;
-	} else {
-		if ( groups_is_user_member( bp_loggedin_user_id(), $group->id ) ) {
-			return true;
-		}
 	}
 
 	return false;
@@ -470,12 +466,9 @@ function bp_get_group_type( $group = false ) {
 		$group =& $groups_template->group;
 	}
 
-	if ( 'public' == $group->status ) {
-		$type = __( "Public Group", 'buddypress' );
-	} elseif ( 'hidden' == $group->status ) {
-		$type = __( "Hidden Group", 'buddypress' );
-	} elseif ( 'private' == $group->status ) {
-		$type = __( "Private Group", 'buddypress' );
+	$status_obj = bp_groups_get_group_status_object( $group->status );
+	if ( ! empty( $status_obj->display_name ) ) {
+		$type = $status_obj->display_name . ' ' . __( 'Group', 'buddypress' );
 	} else {
 		$type = ucwords( $group->status ) . ' ' . __( 'Group', 'buddypress' );
 	}
@@ -529,7 +522,6 @@ function bp_group_status( $group = false ) {
 		 */
 		return apply_filters( 'bp_get_group_status', $group->status, $group );
 	}
-
 /**
  * Output the group avatar while in the groups loop.
  *
@@ -2605,8 +2597,7 @@ function bp_group_admin_tabs( $group = false ) {
 	}
 
 	$css_id = 'manage-members';
-
-	if ( 'private' == $group->status ) {
+	if ( 'accepts_membership_requests' == bp_groups_group_has_cap( $group, 'join_method' ) ) {
 		$css_id = 'membership-requests';
 	}
 
@@ -3202,6 +3193,8 @@ function bp_group_join_button( $group = false ) {
 			$group =& $groups_template->group;
 		}
 
+		var_dump( $group );
+
 		// Don't show button if not logged in or previously banned.
 		if ( ! is_user_logged_in() || bp_group_is_user_banned( $group ) ) {
 			return false;
@@ -3236,75 +3229,65 @@ function bp_group_join_button( $group = false ) {
 
 		// Not a member.
 		} else {
+			// Show different buttons based on group capabilities.
+							// Member has outstanding invitation -
+				// show an "Accept Invitation" button.
+			if ( $group->is_invited ) {
+				$button = array(
+					'id'                => 'accept_invite',
+					'component'         => 'groups',
+					'must_be_logged_in' => true,
+					'block_self'        => false,
+					'wrapper_class'     => 'group-button ' . $group->status,
+					'wrapper_id'        => 'groupbutton-' . $group->id,
+					'link_href'         => add_query_arg( 'redirect_to', bp_get_group_permalink( $group ), bp_get_group_accept_invite_link( $group ) ),
+					'link_text'         => __( 'Accept Invitation', 'buddypress' ),
+					'link_class'        => 'group-button accept-invite',
+				);
+			} elseif ( 'anyone_can_join' == bp_groups_group_has_cap( $group, 'join_method' ) ) {
+				$button = array(
+					'id'                => 'join_group',
+					'component'         => 'groups',
+					'must_be_logged_in' => true,
+					'block_self'        => false,
+					'wrapper_class'     => 'group-button ' . $group->status,
+					'wrapper_id'        => 'groupbutton-' . $group->id,
+					'link_href'         => wp_nonce_url( bp_get_group_permalink( $group ) . 'join', 'groups_join_group' ),
+					'link_text'         => __( 'Join Group', 'buddypress' ),
+					'link_class'        => 'group-button join-group',
+				);
+			} elseif ( 'accepts_membership_requests' == bp_groups_group_has_cap( $group, 'join_method' ) ) {
+
+				// Member has requested membership but request is pending -
+				// show a "Request Sent" button.
+				if ( $group->is_pending ) {
+					$button = array(
+						'id'                => 'membership_requested',
+						'component'         => 'groups',
+						'must_be_logged_in' => true,
+						'block_self'        => false,
+						'wrapper_class'     => 'group-button pending ' . $group->status,
+						'wrapper_id'        => 'groupbutton-' . $group->id,
+						'link_href'         => bp_get_group_permalink( $group ),
+						'link_text'         => __( 'Request Sent', 'buddypress' ),
+						'link_class'        => 'group-button pending membership-requested',
+					);
 
-			// Show different buttons based on group status.
-			switch ( $group->status ) {
-				case 'hidden' :
-					return false;
-
-				case 'public':
+				// Member has not requested membership yet -
+				// show a "Request Membership" button.
+				} else {
 					$button = array(
-						'id'                => 'join_group',
+						'id'                => 'request_membership',
 						'component'         => 'groups',
 						'must_be_logged_in' => true,
 						'block_self'        => false,
 						'wrapper_class'     => 'group-button ' . $group->status,
 						'wrapper_id'        => 'groupbutton-' . $group->id,
-						'link_href'         => wp_nonce_url( bp_get_group_permalink( $group ) . 'join', 'groups_join_group' ),
-						'link_text'         => __( 'Join Group', 'buddypress' ),
-						'link_class'        => 'group-button join-group',
+						'link_href'         => wp_nonce_url( bp_get_group_permalink( $group ) . 'request-membership', 'groups_request_membership' ),
+						'link_text'         => __( 'Request Membership', 'buddypress' ),
+						'link_class'        => 'group-button request-membership',
 					);
-					break;
-
-				case 'private' :
-
-					// Member has outstanding invitation -
-					// show an "Accept Invitation" button.
-					if ( $group->is_invited ) {
-						$button = array(
-							'id'                => 'accept_invite',
-							'component'         => 'groups',
-							'must_be_logged_in' => true,
-							'block_self'        => false,
-							'wrapper_class'     => 'group-button ' . $group->status,
-							'wrapper_id'        => 'groupbutton-' . $group->id,
-							'link_href'         => add_query_arg( 'redirect_to', bp_get_group_permalink( $group ), bp_get_group_accept_invite_link( $group ) ),
-							'link_text'         => __( 'Accept Invitation', 'buddypress' ),
-							'link_class'        => 'group-button accept-invite',
-						);
-
-					// Member has requested membership but request is pending -
-					// show a "Request Sent" button.
-					} elseif ( $group->is_pending ) {
-						$button = array(
-							'id'                => 'membership_requested',
-							'component'         => 'groups',
-							'must_be_logged_in' => true,
-							'block_self'        => false,
-							'wrapper_class'     => 'group-button pending ' . $group->status,
-							'wrapper_id'        => 'groupbutton-' . $group->id,
-							'link_href'         => bp_get_group_permalink( $group ),
-							'link_text'         => __( 'Request Sent', 'buddypress' ),
-							'link_class'        => 'group-button pending membership-requested',
-						);
-
-					// Member has not requested membership yet -
-					// show a "Request Membership" button.
-					} else {
-						$button = array(
-							'id'                => 'request_membership',
-							'component'         => 'groups',
-							'must_be_logged_in' => true,
-							'block_self'        => false,
-							'wrapper_class'     => 'group-button ' . $group->status,
-							'wrapper_id'        => 'groupbutton-' . $group->id,
-							'link_href'         => wp_nonce_url( bp_get_group_permalink( $group ) . 'request-membership', 'groups_request_membership' ),
-							'link_text'         => __( 'Request Membership', 'buddypress' ),
-							'link_class'        => 'group-button request-membership',
-						);
-					}
-
-					break;
+				}
 			}
 		}
 
@@ -3448,31 +3431,24 @@ function bp_group_status_message( $group = null ) {
 
 	// Group has a status.
 	} else {
-		switch( $group->status ) {
-
-			// Private group.
-			case 'private' :
-				if ( ! bp_group_has_requested_membership( $group ) ) {
-					if ( is_user_logged_in() ) {
-						if ( bp_group_is_invited( $group ) ) {
-							$message = __( 'You must accept your pending invitation before you can access this private group.', 'buddypress' );
-						} else {
-							$message = __( 'This is a private group and you must request group membership in order to join.', 'buddypress' );
-						}
+		if ( 'accepts_membership_requests' == bp_groups_group_has_cap( $group, 'join_method' ) ) {
+			if ( ! bp_group_has_requested_membership( $group ) ) {
+				if ( is_user_logged_in() ) {
+					if ( bp_group_is_invited( $group ) ) {
+						$message = __( 'You must accept your pending invitation before you can access this private group.', 'buddypress' );
 					} else {
-						$message = __( 'This is a private group. To join you must be a registered site member and request group membership.', 'buddypress' );
+						$message = __( 'This is a private group and you must request group membership in order to join.', 'buddypress' );
 					}
 				} else {
-					$message = __( 'This is a private group. Your membership request is awaiting approval from the group administrator.', 'buddypress' );
+					$message = __( 'This is a private group. To join you must be a registered site member and request group membership.', 'buddypress' );
 				}
+			} else {
+				$message = __( 'This is a private group. Your membership request is awaiting approval from the group administrator.', 'buddypress' );
+			}
 
-				break;
-
-			// Hidden group.
-			case 'hidden' :
-			default :
-				$message = __( 'This is a hidden group and only invited members can join.', 'buddypress' );
-				break;
+		} elseif ( 'invitation_only' == bp_groups_group_has_cap( $group, 'join_method' ) ) {
+			// This group neither accepts requests nor allows open membership.
+			$message = __( 'This is a hidden group and only invited members can join.', 'buddypress' );
 		}
 	}
 
diff --git src/bp-groups/classes/class-bp-group-extension.php src/bp-groups/classes/class-bp-group-extension.php
index d6cf777..5bbdbe4 100644
--- src/bp-groups/classes/class-bp-group-extension.php
+++ src/bp-groups/classes/class-bp-group-extension.php
@@ -586,8 +586,7 @@ class BP_Group_Extension {
 				$group = groups_get_group( array(
 					'group_id' => $this->group_id,
 				) );
-
-				if ( ! empty( $group->status ) && 'public' === $group->status ) {
+				if ( 'anyone' != bp_groups_group_has_cap( $group, 'access_group' ) ) {
 					// Tabs in public groups are accessible to anyone by default.
 					$this->params['access'] = 'anyone';
 				} else {
diff --git src/bp-groups/classes/class-bp-groups-component.php src/bp-groups/classes/class-bp-groups-component.php
index 93bb9b7..23bfbbc 100644
--- src/bp-groups/classes/class-bp-groups-component.php
+++ src/bp-groups/classes/class-bp-groups-component.php
@@ -85,6 +85,16 @@ class BP_Groups_Component extends BP_Component {
 	public $types = array();
 
 	/**
+	 * Group statuses.
+	 *
+	 * @see bp_groups_register_group_status()
+	 *
+	 * @since 2.7.0
+	 * @var array
+	 */
+	public $statuses = array();
+
+	/**
 	 * Start the groups component creation process.
 	 *
 	 * @since 1.5.0
@@ -122,7 +132,8 @@ class BP_Groups_Component extends BP_Component {
 			'template',
 			'adminbar',
 			'functions',
-			'notifications'
+			'notifications',
+			'capabilities'
 		);
 
 		if ( ! buddypress()->do_autoload ) {
@@ -232,7 +243,7 @@ class BP_Groups_Component extends BP_Component {
 
 			// If the user is not an admin, check if they are a moderator.
 			if ( ! bp_is_item_admin() ) {
-				bp_update_is_item_mod  ( groups_is_user_mod  ( bp_loggedin_user_id(), $this->current_group->id ), 'groups' );
+				bp_update_is_item_mod  ( groups_is_user_mod( bp_loggedin_user_id(), $this->current_group->id ), 'groups' );
 			}
 
 			// Is the logged in user a member of the group?
@@ -242,24 +253,49 @@ class BP_Groups_Component extends BP_Component {
 				$this->current_group->is_user_member = false;
 			}
 
-			// Should this group be visible to the logged in user?
-			if ( 'public' == $this->current_group->status || $this->current_group->is_user_member ) {
+			// Set group visibility and access.
+			if ( bp_current_user_can( 'bp_moderate' ) ) {
 				$this->current_group->is_visible = true;
+				$this->current_group->user_has_access = true;
 			} else {
+				// Should this group be visible to the current user?
 				$this->current_group->is_visible = false;
-			}
 
-			// If this is a private or hidden group, does the user have access?
-			if ( 'private' == $this->current_group->status || 'hidden' == $this->current_group->status ) {
-				if ( $this->current_group->is_user_member && is_user_logged_in() || bp_current_user_can( 'bp_moderate' ) ) {
-					$this->current_group->user_has_access = true;
-				} else {
-					$this->current_group->user_has_access = false;
+				// Parse multiple visibility conditions into an array.
+				$access_conditions = $this->current_group->capabilities['show_group'];
+				if ( ! is_array( $access_conditions ) ) {
+					$access_conditions = explode( ',', $access_conditions );
+				}
+
+				// If the current user meets at least one condition,
+				// allow visibility.
+				foreach ( $access_conditions as $access_condition ) {
+					if ( bp_groups_user_meets_access_condition( $access_condition, $this->current_group->id ) ) {
+						$this->current_group->is_visible = true;
+						break;
+					}
+				}
+
+				// Should the user have access to this group?
+				$this->current_group->user_has_access = false;
+
+				// Parse multiple visibility conditions into an array.
+				$access_conditions = $this->current_group->capabilities['access_group'];
+				if ( ! is_array( $access_conditions ) ) {
+					$access_conditions = explode( ',', $access_conditions );
+				}
+
+				// If the current user meets at least one condition,
+				// allow access.
+				foreach ( $access_conditions as $access_condition ) {
+					if ( bp_groups_user_meets_access_condition( $access_condition, $this->current_group->id ) ) {
+						$this->current_group->user_has_access = true;
+						break;
+					}
 				}
-			} else {
-				$this->current_group->user_has_access = true;
 			}
 
+
 			// Check once if the current group has a custom front template.
 			$this->current_group->front_template = bp_groups_get_front_template( $this->current_group );
 
@@ -350,11 +386,7 @@ class BP_Groups_Component extends BP_Component {
 		 *
 		 * @param array $value Array of valid group statuses.
 		 */
-		$this->valid_status = apply_filters( 'groups_valid_status', array(
-			'public',
-			'private',
-			'hidden'
-		) );
+		$this->valid_status = apply_filters( 'groups_valid_status', bp_groups_get_group_statuses() );
 
 		// Auto join group when non group member performs group activity.
 		$this->auto_join = defined( 'BP_DISABLE_AUTO_GROUP_JOIN' ) && BP_DISABLE_AUTO_GROUP_JOIN ? false : true;
@@ -541,7 +573,7 @@ class BP_Groups_Component extends BP_Component {
 			if ( is_user_logged_in() &&
 				 ! $this->current_group->is_user_member &&
 				 ! groups_check_for_membership_request( bp_loggedin_user_id(), $this->current_group->id ) &&
-				 $this->current_group->status == 'private' &&
+				 'accepts_membership_requests' == bp_groups_group_has_cap( $this->current_group, 'join_method' ) &&
 				 ! groups_check_user_has_invite( bp_loggedin_user_id(), $this->current_group->id )
 				) {
 
@@ -676,7 +708,7 @@ class BP_Groups_Component extends BP_Component {
 					'position' => 30,
 				), $default_params );
 
-				if ( 'private' == $this->current_group->status ) {
+				if ( 'accepts_membership_requests' == bp_groups_group_has_cap( $this->current_group, 'join_method' ) ) {
 					$sub_nav[] = array_merge( array(
 						'name'     => __( 'Requests', 'buddypress' ),
 						'slug'     => 'membership-requests',
diff --git src/bp-groups/classes/class-bp-groups-group.php src/bp-groups/classes/class-bp-groups-group.php
index bde4f1e..fda6852 100644
--- src/bp-groups/classes/class-bp-groups-group.php
+++ src/bp-groups/classes/class-bp-groups-group.php
@@ -68,6 +68,14 @@ class BP_Groups_Group {
 	public $status;
 
 	/**
+	 * Group capabilities.
+	 *
+	 * @since 2.7.0
+	 * @var array
+	 */
+	public $capabilities;
+
+	/**
 	 * Should (legacy) bbPress forums be enabled for this group?
 	 *
 	 * @since 1.6.0
@@ -140,7 +148,7 @@ class BP_Groups_Group {
 	public $last_activity;
 
 	/**
-	 * If this is a private or hidden group, does the current user have access?
+	 * Should the current user have access?
 	 *
 	 * @since 1.6.0
 	 * @var bool
@@ -148,6 +156,14 @@ class BP_Groups_Group {
 	public $user_has_access;
 
 	/**
+	 * Should the current user be able to see the group?
+	 *
+	 * @since 1.6.0
+	 * @var bool
+	 */
+	public $is_visible;
+
+	/**
 	 * Raw arguments passed to the constructor.
 	 *
 	 * @since 2.0.0
@@ -217,6 +233,18 @@ class BP_Groups_Group {
 		$this->enable_forum = $group->enable_forum;
 		$this->date_created = $group->date_created;
 
+		// Populate the group's capabilities.
+		$base_caps = bp_groups_get_group_status_capabilities( $group->status );
+		/**
+		 * Filters the capabilities for a specific group.
+		 *
+		 * @since 2.7.0
+		 *
+		 * @param array  $base_caps  The capabilities for this group.
+		 * @param array  $group      Group object as it exists so far.
+		 */
+		$this->capabilities = apply_filters( 'bp_groups_group_object_set_caps', $base_caps, $this );
+
 		// Are we getting extra group data?
 		if ( ! empty( $this->args['populate_extras'] ) ) {
 
@@ -249,18 +277,46 @@ class BP_Groups_Group {
 			$this->is_invited = groups_check_user_has_invite( $user_id, $this->id );
 			$this->is_pending = groups_check_for_membership_request( $user_id, $this->id );
 
-			// If this is a private or hidden group, does the current user have access?
-			if ( ( 'private' === $this->status ) || ( 'hidden' === $this->status ) ) {
+			// Set group visibility and access.
+			if ( bp_current_user_can( 'bp_moderate' ) ) {
+				$this->is_visible = true;
+				$this->user_has_access = true;
+			} else {
+				// Should this group be visible to the current user?
+				$this->is_visible = false;
+
+				// Parse multiple visibility conditions into an array.
+				$access_conditions = $this->capabilities['show_group'];
+				if ( ! is_array( $access_conditions ) ) {
+					$access_conditions = explode( ',', $access_conditions );
+				}
+
+				// If the current user meets at least one condition,
+				// allow visibility.
+				foreach ( $access_conditions as $access_condition ) {
+					if ( bp_groups_user_meets_access_condition( $access_condition, $this->id, $user_id ) ) {
+						$this->is_visible = true;
+						break;
+					}
+				}
 
-				// Assume user does not have access to hidden/private groups.
+				// Should the user have access to this group?
 				$this->user_has_access = false;
 
-				// Group members or community moderators have access.
-				if ( ( $this->is_member && is_user_logged_in() ) || bp_current_user_can( 'bp_moderate' ) ) {
-					$this->user_has_access = true;
+				// Parse multiple visibility conditions into an array.
+				$access_conditions = $this->capabilities['access_group'];
+				if ( ! is_array( $access_conditions ) ) {
+					$access_conditions = explode( ',', $access_conditions );
+				}
+
+				// If the current user meets at least one condition,
+				// allow access.
+				foreach ( $access_conditions as $access_condition ) {
+					if ( bp_groups_user_meets_access_condition( $access_condition, $this->id, $user_id ) ) {
+						$this->user_has_access = true;
+						break;
+					}
 				}
-			} else {
-				$this->user_has_access = true;
 			}
 		}
 	}
@@ -478,7 +534,14 @@ class BP_Groups_Group {
 
 		$bp = buddypress();
 
-		return $wpdb->get_col( $wpdb->prepare( "SELECT user_id FROM {$bp->groups->table_name_members} WHERE group_id = %d and is_confirmed = 0 AND inviter_id = %d", $group_id, $user_id ) );
+		// Hide invitations to unknowable groups.
+		$hidden_sql = '';
+		if ( $hidden_group_ids = self::get_hidden_group_ids() ) {
+			$hidden_group_ids = implode( ',', $hidden_group_ids );
+			$hidden_sql = " WHERE group_id NOT IN ({$hidden_group_ids})";
+		}
+
+		return $wpdb->get_col( $wpdb->prepare( "SELECT user_id FROM {$bp->groups->table_name_members} WHERE group_id = %d and is_confirmed = 0 AND inviter_id = %d{$hidden_sql}", $group_id, $user_id ) );
 	}
 
 	/**
@@ -522,8 +585,8 @@ class BP_Groups_Group {
 		$bp = buddypress();
 
 		$gids = esc_sql( implode( ',', wp_parse_id_list( $gids['groups'] ) ) );
-
-		$paged_groups = $wpdb->get_results( $wpdb->prepare( "SELECT id as group_id FROM {$bp->groups->table_name} WHERE ( name LIKE %s OR description LIKE %s ) AND id IN ({$gids}) {$pag_sql}", $search_terms_like, $search_terms_like ) );
+$sql = $wpdb->prepare( "SELECT id as group_id FROM {$bp->groups->table_name} WHERE ( name LIKE %s OR description LIKE %s ) AND id IN ({$gids}) {$pag_sql}", $search_terms_like, $search_terms_like );
+		$paged_groups = $wpdb->get_results( $sql );
 		$total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->groups->table_name} WHERE ( name LIKE %s OR description LIKE %s ) AND id IN ({$gids})", $search_terms_like, $search_terms_like ) );
 
 		return array( 'groups' => $paged_groups, 'total' => $total_groups );
@@ -564,8 +627,11 @@ class BP_Groups_Group {
 			$order_sql = "ORDER BY {$sort_by} {$order}";
 		}
 
-		if ( !bp_current_user_can( 'bp_moderate' ) )
-			$hidden_sql = "AND status != 'hidden'";
+		$hidden_sql = '';
+		if ( ! bp_current_user_can( 'bp_moderate' ) && $hidden_group_ids = self::get_hidden_group_ids() ) {
+			$hidden_group_ids = implode( ',', $hidden_group_ids );
+			$hidden_sql = " AND id NOT IN ({$hidden_group_ids})";
+		}
 
 		$bp = buddypress();
 
@@ -789,9 +855,6 @@ class BP_Groups_Group {
 
 		$sql['where'] = " g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count'";
 
-		if ( empty( $r['show_hidden'] ) ) {
-			$sql['hidden'] = " AND g.status != 'hidden'";
-		}
 
 		if ( ! empty( $r['search_terms'] ) ) {
 			$search_terms_like = '%' . bp_esc_like( $r['search_terms'] ) . '%';
@@ -835,9 +898,23 @@ class BP_Groups_Group {
 			$sql['include'] = " AND g.id IN ({$include})";
 		}
 
-		if ( ! empty( $r['exclude'] ) ) {
-			$exclude        = implode( ',', wp_parse_id_list( $r['exclude'] ) );
-			$sql['exclude'] = " AND g.id NOT IN ({$exclude})";
+		$exclude_group_ids = array();
+		if ( ! $r['show_hidden'] || ! empty( $r['exclude'] ) ) {
+			$hidden_group_ids = array();
+			if ( ! $r['show_hidden'] ) {
+				// Get the groups not visible to this user. Note: this could be empty.
+				$hidden_group_ids = self::get_hidden_group_ids();
+			}
+
+			if ( ! empty( $r['exclude'] ) ) {
+				$exclude_group_ids = wp_parse_id_list( $r['exclude'] );
+			}
+
+			$exclude_group_ids = array_merge( $exclude_group_ids, $hidden_group_ids );
+			if ( $exclude_group_ids ) {
+				$exclude_group_ids = implode( ',', $exclude_group_ids );
+				$sql['exclude'] = " AND g.id NOT IN ({$exclude_group_ids})";
+			}
 		}
 
 		/* Order/orderby ********************************************/
@@ -914,10 +991,6 @@ class BP_Groups_Group {
 			$total_sql['select'] .= ", {$bp->groups->table_name_members} m";
 		}
 
-		if ( ! empty( $sql['hidden'] ) ) {
-			$total_sql['where'][] = "g.status != 'hidden'";
-		}
-
 		if ( ! empty( $sql['search'] ) ) {
 			$total_sql['where'][] = $wpdb->prepare( "( g.name LIKE %s OR g.description LIKE %s )", $search_terms_like, $search_terms_like );
 		}
@@ -948,8 +1021,8 @@ class BP_Groups_Group {
 		}
 
 		// Already escaped in the paginated results block.
-		if ( ! empty( $exclude ) ) {
-			$total_sql['where'][] = "g.id NOT IN ({$exclude})";
+		if ( ! empty( $exclude_group_ids ) ) {
+			$total_sql['where'][] = "g.id NOT IN ({$exclude_group_ids})";
 		}
 
 		$total_sql['where'][] = "g.id = gm.group_id";
@@ -1127,28 +1200,32 @@ class BP_Groups_Group {
 			$pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
 		}
 
-		if ( !is_user_logged_in() || ( !bp_current_user_can( 'bp_moderate' ) && ( $user_id != bp_loggedin_user_id() ) ) )
-			$hidden_sql = " AND g.status != 'hidden'";
-
 		if ( !empty( $search_terms ) ) {
 			$search_terms_like = '%' . bp_esc_like( $search_terms ) . '%';
 			$search_sql        = $wpdb->prepare( ' AND ( g.name LIKE %s OR g.description LIKE %s ) ', $search_terms_like, $search_terms_like );
 		}
 
-		if ( !empty( $exclude ) ) {
-			$exclude     = implode( ',', wp_parse_id_list( $exclude ) );
-			$exclude_sql = " AND g.id NOT IN ({$exclude})";
+		$hidden_group_ids = self::get_hidden_group_ids();
+		$exclude_group_ids = array();
+		if ( ! empty( $exclude ) ) {
+			$exclude_group_ids = wp_parse_id_list( $exclude );
+		}
+
+		if ( $hidden_group_ids || $exclude_group_ids ) {
+			$exclude_group_ids = array_merge( $exclude_group_ids, $hidden_group_ids );
+			$exclude_group_ids = implode( ',', $exclude_group_ids );
+			$exclude_sql       = " AND g.id NOT IN ({$exclude_group_ids})";
 		}
 
 		$bp = buddypress();
 
 		if ( !empty( $user_id ) ) {
 			$user_id      = absint( esc_sql( $user_id ) );
-			$paged_groups = $wpdb->get_results( "SELECT DISTINCT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bp->groups->table_name_members} m, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.topics > 0 {$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql} ORDER BY f.topics DESC {$pag_sql}" );
-			$total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.topics > 0 {$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql}" );
+			$paged_groups = $wpdb->get_results( "SELECT DISTINCT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bp->groups->table_name_members} m, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.topics > 0{$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0{$exclude_sql} ORDER BY f.topics DESC {$pag_sql}" );
+			$total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.topics > 0{$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0{$exclude_sql}" );
 		} else {
-			$paged_groups = $wpdb->get_results( "SELECT DISTINCT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.topics > 0 {$hidden_sql} {$search_sql} {$exclude_sql} ORDER BY f.topics DESC {$pag_sql}" );
-			$total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.topics > 0 {$hidden_sql} {$search_sql} {$exclude_sql}" );
+			$paged_groups = $wpdb->get_results( "SELECT DISTINCT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.topics > 0{$search_sql}{$exclude_sql} ORDER BY f.topics DESC {$pag_sql}" );
+			$total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.topics > 0{$search_sql}{$exclude_sql}" );
 		}
 
 		if ( !empty( $populate_extras ) ) {
@@ -1233,28 +1310,32 @@ class BP_Groups_Group {
 			$pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
 		}
 
-		if ( !is_user_logged_in() || ( !bp_current_user_can( 'bp_moderate' ) && ( $user_id != bp_loggedin_user_id() ) ) )
-			$hidden_sql = " AND g.status != 'hidden'";
-
 		if ( !empty( $search_terms ) ) {
 			$search_terms_like = '%' . bp_esc_like( $search_terms ) . '%';
 			$search_sql        = $wpdb->prepare( ' AND ( g.name LIKE %s OR g.description LIKE %s ) ', $search_terms_like, $search_terms_like );
 		}
 
-		if ( !empty( $exclude ) ) {
-			$exclude     = implode( ',', wp_parse_id_list( $exclude ) );
-			$exclude_sql = " AND g.id NOT IN ({$exclude})";
+		$hidden_group_ids =  self::get_hidden_group_ids();
+		$exclude_group_ids = array();
+		if ( ! empty( $exclude ) ) {
+			$exclude_group_ids = wp_parse_id_list( $exclude );
+		}
+
+		if ( $hidden_group_ids || $exclude_group_ids ) {
+			$exclude_group_ids = array_merge( $exclude_group_ids, $hidden_group_ids );
+			$exclude_group_ids = implode( ',', $exclude_group_ids );
+			$sql['exclude'] = " AND g.id NOT IN ({$exclude_group_ids})";
 		}
 
 		$bp = buddypress();
 
 		if ( !empty( $user_id ) ) {
 			$user_id = esc_sql( $user_id );
-			$paged_groups = $wpdb->get_results( "SELECT DISTINCT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bp->groups->table_name_members} m, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) {$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql} ORDER BY f.posts ASC {$pag_sql}" );
-			$total_groups = $wpdb->get_results( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bp->groups->table_name_members} m, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.posts > 0 {$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql} " );
+			$paged_groups = $wpdb->get_results( "SELECT DISTINCT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bp->groups->table_name_members} m, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql} ORDER BY f.posts ASC {$pag_sql}" );
+			$total_groups = $wpdb->get_results( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bp->groups->table_name_members} m, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.posts > 0 {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql} " );
 		} else {
-			$paged_groups = $wpdb->get_results( "SELECT DISTINCT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.posts > 0 {$hidden_sql} {$search_sql} {$exclude_sql} ORDER BY f.posts ASC {$pag_sql}" );
-			$total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) {$hidden_sql} {$search_sql} {$exclude_sql}" );
+			$paged_groups = $wpdb->get_results( "SELECT DISTINCT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.posts > 0 {$search_sql} {$exclude_sql} ORDER BY f.posts ASC {$pag_sql}" );
+			$total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) {$search_sql} {$exclude_sql}" );
 		}
 
 		if ( !empty( $populate_extras ) ) {
@@ -1306,13 +1387,17 @@ class BP_Groups_Group {
 
 		$bp = buddypress();
 
-		if ( !empty( $exclude ) ) {
-			$exclude     = implode( ',', wp_parse_id_list( $exclude ) );
-			$exclude_sql = " AND g.id NOT IN ({$exclude})";
+		$hidden_group_ids = self::get_hidden_group_ids();
+		$exclude_group_ids = array();
+		if ( ! empty( $exclude ) ) {
+			$exclude_group_ids = wp_parse_id_list( $exclude );
 		}
 
-		if ( !bp_current_user_can( 'bp_moderate' ) )
-			$hidden_sql = " AND status != 'hidden'";
+		if ( $hidden_group_ids || $exclude_group_ids ) {
+			$exclude_group_ids = array_merge( $exclude_group_ids, $hidden_group_ids );
+			$exclude_group_ids = implode( ',', $exclude_group_ids );
+			$exclude_sql       = " AND g.id NOT IN ({$exclude_group_ids})";
+		}
 
 		$letter_like = bp_esc_like( $letter ) . '%';
 
@@ -1320,9 +1405,9 @@ class BP_Groups_Group {
 			$pag_sql      = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
 		}
 
-		$total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND g.name LIKE %s {$hidden_sql} {$exclude_sql}", $letter_like ) );
+		$total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND g.name LIKE %s{$exclude_sql}", $letter_like ) );
 
-		$paged_groups = $wpdb->get_results( $wpdb->prepare( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND g.name LIKE %s {$hidden_sql} {$exclude_sql} ORDER BY g.name ASC {$pag_sql}", $letter_like ) );
+		$paged_groups = $wpdb->get_results( $wpdb->prepare( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND g.name LIKE %s{$exclude_sql} ORDER BY g.name ASC {$pag_sql}", $letter_like ) );
 
 		if ( !empty( $populate_extras ) ) {
 			foreach ( (array) $paged_groups as $group ) {
@@ -1368,29 +1453,32 @@ class BP_Groups_Group {
 		if ( !empty( $limit ) && !empty( $page ) )
 			$pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
 
-		if ( !is_user_logged_in() || ( !bp_current_user_can( 'bp_moderate' ) && ( $user_id != bp_loggedin_user_id() ) ) )
-			$hidden_sql = "AND g.status != 'hidden'";
-
 		if ( !empty( $search_terms ) ) {
 			$search_terms_like = '%' . bp_esc_like( $search_terms ) . '%';
 			$search_sql = $wpdb->prepare( " AND ( g.name LIKE %s OR g.description LIKE %s )", $search_terms_like, $search_terms_like );
 		}
 
-		if ( !empty( $exclude ) ) {
-			$exclude     = wp_parse_id_list( $exclude );
-			$exclude     = esc_sql( implode( ',', $exclude ) );
-			$exclude_sql = " AND g.id NOT IN ({$exclude})";
+		$hidden_group_ids = self::get_hidden_group_ids();
+		$exclude_group_ids = array();
+		if ( ! empty( $exclude ) ) {
+			$exclude_group_ids = wp_parse_id_list( $exclude );
+		}
+
+		if ( $hidden_group_ids || $exclude_group_ids ) {
+			$exclude_group_ids = array_merge( $exclude_group_ids, $hidden_group_ids );
+			$exclude_group_ids = implode( ',', $exclude_group_ids );
+			$exclude_sql       = " AND g.id NOT IN ({$exclude_group_ids})";
 		}
 
 		$bp = buddypress();
 
 		if ( !empty( $user_id ) ) {
 			$user_id = esc_sql( $user_id );
-			$paged_groups = $wpdb->get_results( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' {$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql} ORDER BY rand() {$pag_sql}" );
-			$total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m LEFT JOIN {$bp->groups->table_name_groupmeta} gm ON m.group_id = gm.group_id INNER JOIN {$bp->groups->table_name} g ON m.group_id = g.id WHERE gm.meta_key = 'last_activity'{$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql}" );
+			$paged_groups = $wpdb->get_results( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0{$exclude_sql} ORDER BY rand() {$pag_sql}" );
+			$total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m LEFT JOIN {$bp->groups->table_name_groupmeta} gm ON m.group_id = gm.group_id INNER JOIN {$bp->groups->table_name} g ON m.group_id = g.id WHERE gm.meta_key = 'last_activity' {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0{$exclude_sql}" );
 		} else {
-			$paged_groups = $wpdb->get_results( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' {$hidden_sql} {$search_sql} {$exclude_sql} ORDER BY rand() {$pag_sql}" );
-			$total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm INNER JOIN {$bp->groups->table_name} g ON gm.group_id = g.id WHERE gm.meta_key = 'last_activity'{$hidden_sql} {$search_sql} {$exclude_sql}" );
+			$paged_groups = $wpdb->get_results( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count'{$search_sql}{$exclude_sql} ORDER BY rand() {$pag_sql}" );
+			$total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm INNER JOIN {$bp->groups->table_name} g ON gm.group_id = g.id WHERE gm.meta_key = 'last_activity'{$search_sql}{$exclude_sql}" );
 		}
 
 		if ( !empty( $populate_extras ) ) {
@@ -1429,6 +1517,66 @@ class BP_Groups_Group {
 			$group->is_invited = groups_is_user_invited( $user_id, $group->id ) ? '1' : '0';
 			$group->is_pending = groups_is_user_pending( $user_id, $group->id ) ? '1' : '0';
 			$group->is_banned  = (bool) groups_is_user_banned( $user_id, $group->id );
+
+			// Populate the group's capabilities.
+			if ( empty( $group->status ) ) {
+				// @TODO: This seems like a hack. Need to figure out when $paged_groups are not complete.
+				$temp_group    = groups_get_group( array( 'group_id' => $group->id ) );
+				$group->status = $temp_group->status;
+			}
+			$base_caps = bp_groups_get_group_status_capabilities( $group->status );
+			/**
+			 * Filters the capabilities for a specific group.
+			 *
+			 * @since 2.7.0
+			 *
+			 * @param array  $base_caps  The capabilities for this group.
+			 * @param array  $group      Group object as it exists so far.
+			 */
+			$group->capabilities = apply_filters( 'bp_groups_group_object_set_caps', $base_caps, $group );
+
+			// Set group visibility and access.
+			if ( bp_current_user_can( 'bp_moderate' ) ) {
+				$group->is_visible = true;
+				$group->user_has_access = true;
+			} else {
+				// Should this group be visible to the current user?
+				$group->is_visible = false;
+
+				// Parse multiple visibility conditions into an array.
+				$access_conditions = $group->capabilities['show_group'];
+				if ( ! is_array( $access_conditions ) ) {
+					$access_conditions = explode( ',', $access_conditions );
+				}
+
+				// If the current user meets at least one condition,
+				// allow visibility.
+				foreach ( $access_conditions as $access_condition ) {
+					if ( bp_groups_user_meets_access_condition( $access_condition, $group->id, $user_id ) ) {
+						$group->is_visible = true;
+						break;
+					}
+				}
+
+				// Should the user have access to this group?
+				$group->user_has_access = false;
+
+				// Parse multiple visibility conditions into an array.
+				$access_conditions = $group->capabilities['access_group'];
+				if ( ! is_array( $access_conditions ) ) {
+					$access_conditions = explode( ',', $access_conditions );
+				}
+
+				// If the current user meets at least one condition,
+				// allow access.
+				foreach ( $access_conditions as $access_condition ) {
+					if ( bp_groups_user_meets_access_condition( $access_condition, $group->id, $user_id ) ) {
+						$group->user_has_access = true;
+						break;
+					}
+				}
+			}
+
 		}
 
 		return $paged_groups;
@@ -1465,9 +1613,10 @@ class BP_Groups_Group {
 		global $wpdb;
 
 		$hidden_sql = '';
-		if ( !bp_current_user_can( 'bp_moderate' ) )
-			$hidden_sql = "WHERE status != 'hidden'";
-
+		if ( ! bp_current_user_can( 'bp_moderate' ) && $hidden_group_ids = self::get_hidden_group_ids() ) {
+			$hidden_group_ids = implode( ',', $hidden_group_ids );
+			$hidden_sql = " WHERE id NOT IN ({$hidden_group_ids})";
+		}
 		$bp = buddypress();
 
 		return $wpdb->get_var( "SELECT COUNT(id) FROM {$bp->groups->table_name} {$hidden_sql}" );
@@ -1537,12 +1686,11 @@ class BP_Groups_Group {
 	 */
 	public static function get_global_topic_count( $status = 'public', $search_terms = false ) {
 		global $bbdb, $wpdb;
-
+		// @TODO: Update this to account for custom statuses?
 		switch ( $status ) {
 			case 'all' :
 				$status_sql = '';
 				break;
-
 			case 'hidden' :
 				$status_sql = "AND g.status = 'hidden'";
 				break;
@@ -1590,9 +1738,11 @@ class BP_Groups_Group {
 		$ids = array();
 
 		$ids['all']     = $wpdb->get_col( "SELECT id FROM {$bp->groups->table_name}" );
-		$ids['public']  = $wpdb->get_col( "SELECT id FROM {$bp->groups->table_name} WHERE status = 'public'" );
-		$ids['private'] = $wpdb->get_col( "SELECT id FROM {$bp->groups->table_name} WHERE status = 'private'" );
-		$ids['hidden']  = $wpdb->get_col( "SELECT id FROM {$bp->groups->table_name} WHERE status = 'hidden'" );
+
+		$statuses = bp_groups_get_group_statuses( array(), 'names' );
+		foreach ( $statuses as $status ) {
+			$ids[$status]  = $wpdb->get_col( $wpdb->prepare( "SELECT id FROM {$bp->groups->table_name} WHERE status = %s", $status ) );
+		}
 
 		return $ids;
 	}
@@ -1665,4 +1815,49 @@ class BP_Groups_Group {
 
 		return $clause;
 	}
+
+	/**
+	 * Generate a list of groups that should be invisible to the current user.
+	 *
+	 * @since 2.7.0
+	 *
+	 * @return array $hidden_group_ids
+	 */
+	protected static function get_hidden_group_ids() {
+		// @TODO: Should this short-circuit for site admins?
+
+		// Check cache for group data.
+		$last_changed = wp_cache_get( 'last_changed', 'bp_groups' );
+		if ( false === $last_changed ) {
+			$last_changed = microtime();
+			wp_cache_set( 'last_changed', $last_changed, 'bp_groups' );
+		}
+
+		$cache_key = 'hidden_groups_for_user_' . bp_loggedin_user_id() . '_' . $last_changed;
+
+		// Cache missed, so query the DB.
+		if ( false === $hidden_group_ids ) {
+			// Fetch groups, check if the current user should be able to see each one.
+			$all_group_args = array(
+				'show_hidden'       => true, // Show hidden groups to non-admins.
+				'per_page'          => false, // Return all.
+				'page'              => false, // Return all.
+				'update_meta_cache' => false // Don't prime the meta cache.
+				);
+			$groups = groups_get_groups( $all_group_args );
+
+			$hidden_group_ids = array();
+			if ( ! empty( $groups['groups'] ) ) {
+				foreach ( $groups['groups'] as $group ) {
+					if ( ! $group->is_visible ) {
+						$hidden_group_ids[] = (int) $group->id;
+					}
+				}
+			}
+
+			wp_cache_set( $cache_key, $hidden_group_ids, 'bp_groups' );
+		}
+		return $hidden_group_ids;
+	}
+
 }
diff --git src/bp-groups/classes/class-bp-groups-list-table.php src/bp-groups/classes/class-bp-groups-list-table.php
index 7cca49d..afb693c 100644
--- src/bp-groups/classes/class-bp-groups-list-table.php
+++ src/bp-groups/classes/class-bp-groups-list-table.php
@@ -118,7 +118,7 @@ class BP_Groups_List_Table extends WP_List_Table {
 			$include_id = (int) $_REQUEST['gid'];
 
 		// Set the current view.
-		if ( isset( $_GET['group_status'] ) && in_array( $_GET['group_status'], array( 'public', 'private', 'hidden' ) ) ) {
+		if ( isset( $_GET['group_status'] ) && in_array( $_GET['group_status'], bp_groups_get_group_statuses( array(), 'names' ) ) ) {
 			$this->view = $_GET['group_status'];
 		}
 
@@ -294,11 +294,23 @@ class BP_Groups_List_Table extends WP_List_Table {
 
 		<ul class="subsubsub">
 			<li class="all"><a href="<?php echo esc_url( $url_base ); ?>" class="<?php if ( 'all' == $this->view ) echo 'current'; ?>"><?php _e( 'All', 'buddypress' ); ?></a> |</li>
-			<li class="public"><a href="<?php echo esc_url( add_query_arg( 'group_status', 'public', $url_base ) ); ?>" class="<?php if ( 'public' == $this->view ) echo 'current'; ?>"><?php printf( _n( 'Public <span class="count">(%s)</span>', 'Public <span class="count">(%s)</span>', $this->group_counts['public'], 'buddypress' ), number_format_i18n( $this->group_counts['public'] ) ); ?></a> |</li>
-			<li class="private"><a href="<?php echo esc_url( add_query_arg( 'group_status', 'private', $url_base ) ); ?>" class="<?php if ( 'private' == $this->view ) echo 'current'; ?>"><?php printf( _n( 'Private <span class="count">(%s)</span>', 'Private <span class="count">(%s)</span>', $this->group_counts['private'], 'buddypress' ), number_format_i18n( $this->group_counts['private'] ) ); ?></a> |</li>
-			<li class="hidden"><a href="<?php echo esc_url( add_query_arg( 'group_status', 'hidden', $url_base ) ); ?>" class="<?php if ( 'hidden' == $this->view ) echo 'current'; ?>"><?php printf( _n( 'Hidden <span class="count">(%s)</span>', 'Hidden <span class="count">(%s)</span>', $this->group_counts['hidden'], 'buddypress' ), number_format_i18n( $this->group_counts['hidden'] ) ); ?></a></li>
-
 			<?php
+			$group_statuses = bp_groups_get_group_statuses( array(), 'objects' );
+			$num_statuses   = count( $group_statuses );
+			$i              = 1;
+			foreach ( $group_statuses as $status ) : ?>
+			<li class="<?php echo $status->name; ?>"><a href="<?php echo esc_url( add_query_arg( 'group_status', $status->name, $url_base ) ); ?>" class="<?php if ( $status->name == $this->view ) echo 'current'; ?>"><?php printf( _n( '%s <span class="count">(%s)</span>', '%s <span class="count">(%s)</span>',
+					$status->display_name,
+					$this->group_counts[$status->name], 'buddypress' ),
+					$status->display_name,
+					number_format_i18n( $this->group_counts[$status->name] ) ); ?></a><?php
+				if ( $i < $num_statuses ) {
+					echo ' |';
+				}
+				$i++;
+			?></li>
+			<?php
+			endforeach;
 
 			/**
 			 * Fires inside listing of views so plugins can add their own.
@@ -553,22 +565,8 @@ class BP_Groups_List_Table extends WP_List_Table {
 	 * @param array $item Information about the current row.
 	 */
 	public function column_status( $item = array() ) {
-		$status      = $item['status'];
-		$status_desc = '';
-
-		// @todo This should be abstracted out somewhere for the whole
-		// Groups component.
-		switch ( $status ) {
-			case 'public' :
-				$status_desc = __( 'Public', 'buddypress' );
-				break;
-			case 'private' :
-				$status_desc = __( 'Private', 'buddypress' );
-				break;
-			case 'hidden' :
-				$status_desc = __( 'Hidden', 'buddypress' );
-				break;
-		}
+		$status_object = bp_groups_get_group_status_object( $item['status'] );
+		$status_desc   = $status_object->display_name;
 
 		/**
 		 * Filters the markup for the Status column.
diff --git src/bp-groups/classes/class-bp-groups-member.php src/bp-groups/classes/class-bp-groups-member.php
index bfb1a76..fbfea91 100644
--- src/bp-groups/classes/class-bp-groups-member.php
+++ src/bp-groups/classes/class-bp-groups-member.php
@@ -508,15 +508,15 @@ class BP_Groups_Member {
 
 		$bp = buddypress();
 
-		// If the user is logged in and viewing their own groups, we can show hidden and private groups.
-		if ( $user_id != bp_loggedin_user_id() ) {
-			$group_sql = $wpdb->prepare( "SELECT DISTINCT m.group_id FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE g.status != 'hidden' AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0{$pag_sql}", $user_id );
-			$total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE g.status != 'hidden' AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0", $user_id ) );
-		} else {
-			$group_sql = $wpdb->prepare( "SELECT DISTINCT group_id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND is_confirmed = 1 AND is_banned = 0{$pag_sql}", $user_id );
-			$total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT group_id) FROM {$bp->groups->table_name_members} WHERE user_id = %d AND is_confirmed = 1 AND is_banned = 0", $user_id ) );
+		$hidden_sql = '';
+		if ( $hidden_group_ids = self::get_hidden_group_ids() ) {
+			$hidden_group_ids = implode( ',', $hidden_group_ids );
+			$hidden_sql = " AND group_id NOT IN ({$hidden_group_ids})";
 		}
 
+		$group_sql = $wpdb->prepare( "SELECT DISTINCT group_id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND is_confirmed = 1 AND is_banned = 0{$hidden_sql}{$pag_sql}", $user_id );
+		$total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT group_id) FROM {$bp->groups->table_name_members} m WHERE user_id = %d AND is_confirmed = 1 AND is_banned = 0{$hidden_sql}", $user_id ) );
+
 		$groups = $wpdb->get_col( $group_sql );
 
 		return array( 'groups' => $groups, 'total' => (int) $total_groups );
@@ -554,8 +554,10 @@ class BP_Groups_Member {
 			$filter_sql = $wpdb->prepare( " AND ( g.name LIKE %s OR g.description LIKE %s )", $search_terms_like, $search_terms_like );
 		}
 
-		if ( $user_id != bp_loggedin_user_id() )
-			$hidden_sql = " AND g.status != 'hidden'";
+		if ( $hidden_group_ids = self::get_hidden_group_ids() ) {
+			$hidden_group_ids = implode( ',', $hidden_group_ids );
+			$hidden_sql = " AND g.id NOT IN ({$hidden_group_ids})";
+		}
 
 		$bp = buddypress();
 
@@ -597,8 +599,10 @@ class BP_Groups_Member {
 			$filter_sql = $wpdb->prepare( " AND ( g.name LIKE %s OR g.description LIKE %s )", $search_terms_like, $search_terms_like );
 		}
 
-		if ( $user_id != bp_loggedin_user_id() )
-			$hidden_sql = " AND g.status != 'hidden'";
+		if ( $hidden_group_ids = self::get_hidden_group_ids() ) {
+			$hidden_group_ids = implode( ',', $hidden_group_ids );
+			$hidden_sql = " AND g.id NOT IN ({$hidden_group_ids})";
+		}
 
 		$bp = buddypress();
 
@@ -640,8 +644,10 @@ class BP_Groups_Member {
 			$filter_sql = $wpdb->prepare( " AND ( g.name LIKE %s OR g.description LIKE %s )", $search_terms_like, $search_terms_like );
 		}
 
-		if ( $user_id != bp_loggedin_user_id() )
-			$hidden_sql = " AND g.status != 'hidden'";
+		if ( $hidden_group_ids = self::get_hidden_group_ids() ) {
+			$hidden_group_ids = implode( ',', $hidden_group_ids );
+			$hidden_sql = " AND g.id NOT IN ({$hidden_group_ids})";
+		}
 
 		$bp = buddypress();
 
@@ -685,8 +691,9 @@ class BP_Groups_Member {
 			$filter_sql        = $wpdb->prepare( " AND ( g.name LIKE %s OR g.description LIKE %s )", $search_terms_like, $search_terms_like );
 		}
 
-		if ( $user_id !== bp_loggedin_user_id() && ! bp_current_user_can( 'bp_moderate' ) ) {
-			$hidden_sql = " AND g.status != 'hidden'";
+		if ( $hidden_group_ids = self::get_hidden_group_ids() ) {
+			$hidden_group_ids = implode( ',', $hidden_group_ids );
+			$hidden_sql = " AND g.id NOT IN ({$hidden_group_ids})";
 		}
 
 		$paged_groups = $wpdb->get_results( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count'{$hidden_sql}{$filter_sql} AND {$user_id_sql} AND m.is_banned = 1  ORDER BY m.date_modified ASC {$pag_sql}" );
@@ -711,11 +718,13 @@ class BP_Groups_Member {
 
 		$bp = buddypress();
 
-		if ( $user_id != bp_loggedin_user_id() && !bp_current_user_can( 'bp_moderate' ) ) {
-			return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id AND g.status != 'hidden' AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0", $user_id ) );
-		} else {
-			return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0", $user_id ) );
+		$hidden_sql = '';
+		if ( $hidden_group_ids = self::get_hidden_group_ids() ) {
+			$hidden_group_ids = implode( ',', $hidden_group_ids );
+			$hidden_sql = " AND m.group_id NOT IN ({$hidden_group_ids})";
 		}
+
+		return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id{$hidden_sql} AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0", $user_id ) );
 	}
 
 	/**
@@ -740,11 +749,18 @@ class BP_Groups_Member {
 
 		$pag_sql = ( !empty( $limit ) && !empty( $page ) ) ? $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) ) : '';
 
-		if ( !empty( $exclude ) ) {
-			$exclude     = implode( ',', wp_parse_id_list( $exclude ) );
-			$exclude_sql = " AND g.id NOT IN ({$exclude})";
-		} else {
-			$exclude_sql = '';
+		$exclude_ids = array();
+		if ( ! empty( $exclude ) ) {
+			$exclude_ids = wp_parse_id_list( $exclude );
+		}
+
+		$hidden_group_ids = self::get_hidden_group_ids();
+
+		$exclude_sql = '';
+		if ( $exclude_ids || $hidden_group_ids ) {
+			$hidden_group_ids = array_merge( $exclude_ids, $hidden_group_ids );
+			$hidden_group_ids = implode( ',', $hidden_group_ids );
+			$exclude_sql = " AND g.id NOT IN ({$hidden_group_ids})";
 		}
 
 		$bp = buddypress();
@@ -770,7 +786,14 @@ class BP_Groups_Member {
 		$count = wp_cache_get( $user_id, 'bp_group_invite_count' );
 
 		if ( false === $count ) {
-			$count = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id AND m.is_confirmed = 0 AND m.inviter_id != 0 AND m.invite_sent = 1 AND m.user_id = %d", $user_id ) );
+
+			$exclude_sql = '';
+			if ( $hidden_group_ids = self::get_hidden_group_ids() ) {
+				$hidden_group_ids = implode( ',', $hidden_group_ids );
+				$exclude_sql = " AND m.group_id NOT IN ({$hidden_group_ids})";
+			}
+
+			$count = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id AND m.is_confirmed = 0 AND m.inviter_id != 0 AND m.invite_sent = 1 AND m.user_id = %d{$exclude_sql}", $user_id ) );
 			wp_cache_set( $user_id, $count, 'bp_group_invite_count' );
 		}
 
@@ -999,12 +1022,12 @@ class BP_Groups_Member {
 
 		$bp = buddypress();
 
-		// If the user is logged in and viewing their random groups, we can show hidden and private groups.
-		if ( bp_is_my_profile() ) {
-			return $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT group_id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND is_confirmed = 1 AND is_banned = 0 ORDER BY rand() LIMIT %d", $user_id, $total_groups ) );
-		} else {
-			return $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT m.group_id FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id AND g.status != 'hidden' AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0 ORDER BY rand() LIMIT %d", $user_id, $total_groups ) );
+		$hidden_sql = '';
+		if ( $hidden_group_ids = self::get_hidden_group_ids() ) {
+			$hidden_group_ids = implode( ',', $hidden_group_ids );
+			$hidden_sql = " AND group_id NOT IN ({$hidden_group_ids})";
 		}
+		return $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT m.group_id FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id{$hidden_sql} AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0 ORDER BY rand() LIMIT %d", $user_id, $total_groups ) );
 	}
 
 	/**
@@ -1246,4 +1269,47 @@ class BP_Groups_Member {
 
 		return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE user_id = %d", $user_id ) );
 	}
+
+	/**
+	 * Generate a list of groups that should be invisible to the current user.
+	 *
+	 * @since 2.7.0
+	 *
+	 * @return array $hidden_group_ids
+	 */
+	protected static function get_hidden_group_ids() {
+		// Check cache for group data.
+		$last_changed = wp_cache_get( 'last_changed', 'bp_groups' );
+		if ( false === $last_changed ) {
+			$last_changed = microtime();
+			wp_cache_set( 'last_changed', $last_changed, 'bp_groups' );
+		}
+
+		$cache_key = 'hidden_groups_for_user_' . bp_loggedin_user_id() . '_' . $last_changed;
+		$hidden_group_ids = wp_cache_get( $cache_key, 'bp_groups' );
+
+		// Cache missed, so query the DB.
+		if ( false === $hidden_group_ids ) {
+			// Fetch groups, check if the current user should be able to see each one.
+			$all_group_args = array(
+				'show_hidden'       => true, // Show hidden groups to non-admins.
+				'per_page'          => false, // Return all.
+				'page'              => false, // Return all.
+				'update_meta_cache' => false // Don't prime the meta cache.
+				);
+			$groups = groups_get_groups( $all_group_args );
+
+			$hidden_group_ids = array();
+			if ( ! empty( $groups['groups'] ) ) {
+				foreach ( $groups['groups'] as $group ) {
+					if ( ! $group->is_visible ) {
+						$hidden_group_ids[] = (int) $group->id;
+					}
+				}
+			}
+
+			wp_cache_set( $cache_key, $hidden_group_ids, 'bp_groups' );
+		}
+		return $hidden_group_ids;
+	}
 }
diff --git src/bp-groups/classes/class-bp-groups-template.php src/bp-groups/classes/class-bp-groups-template.php
index 043d8f4..b614d29 100644
--- src/bp-groups/classes/class-bp-groups-template.php
+++ src/bp-groups/classes/class-bp-groups-template.php
@@ -181,7 +181,7 @@ class BP_Groups_Template {
 		$this->pag_page = bp_sanitize_pagination_arg( $this->pag_arg, $r['page']     );
 		$this->pag_num  = bp_sanitize_pagination_arg( 'num',          $r['per_page'] );
 
-		if ( bp_current_user_can( 'bp_moderate' ) || ( is_user_logged_in() && $user_id == bp_loggedin_user_id() ) ) {
+		if ( bp_current_user_can( 'bp_moderate' ) ) {
 			$show_hidden = true;
 		}
 
diff --git src/bp-templates/bp-legacy/buddypress-functions.php src/bp-templates/bp-legacy/buddypress-functions.php
index 436fcac..1e72cd3 100644
--- src/bp-templates/bp-legacy/buddypress-functions.php
+++ src/bp-templates/bp-legacy/buddypress-functions.php
@@ -1437,7 +1437,7 @@ function bp_legacy_theme_ajax_joinleave_group() {
 		return;
 
 	if ( ! groups_is_user_member( bp_loggedin_user_id(), $group->id ) ) {
-		if ( 'public' == $group->status ) {
+		if ( 'anyone_can_join' == bp_groups_group_has_cap( $group, 'join_method' ) ) {
 			check_ajax_referer( 'groups_join_group' );
 
 			if ( ! groups_join_group( $group->id ) ) {
@@ -1446,7 +1446,7 @@ function bp_legacy_theme_ajax_joinleave_group() {
 				echo '<a id="group-' . esc_attr( $group->id ) . '" class="leave-group" rel="leave" title="' . __( 'Leave Group', 'buddypress' ) . '" href="' . wp_nonce_url( bp_get_group_permalink( $group ) . 'leave-group', 'groups_leave_group' ) . '">' . __( 'Leave Group', 'buddypress' ) . '</a>';
 			}
 
-		} elseif ( 'private' == $group->status ) {
+		} elseif ( 'accepts_membership_requests' == bp_groups_group_has_cap( $group, 'join_method' ) ) {
 
 			// If the user has already been invited, then this is
 			// an Accept Invitation button.
@@ -1476,9 +1476,9 @@ function bp_legacy_theme_ajax_joinleave_group() {
 
 		if ( ! groups_leave_group( $group->id ) ) {
 			_e( 'Error leaving group', 'buddypress' );
-		} elseif ( 'public' == $group->status ) {
+		} elseif ( bp_groups_group_has_cap( $group, 'anyone_can_join' ) ) {
 			echo '<a id="group-' . esc_attr( $group->id ) . '" class="join-group" rel="join" title="' . __( 'Join Group', 'buddypress' ) . '" href="' . wp_nonce_url( bp_get_group_permalink( $group ) . 'join', 'groups_join_group' ) . '">' . __( 'Join Group', 'buddypress' ) . '</a>';
-		} elseif ( 'private' == $group->status ) {
+		} elseif ( bp_groups_group_has_cap( $group, 'accepts_membership_requests' ) ) {
 			echo '<a id="group-' . esc_attr( $group->id ) . '" class="request-membership" rel="join" title="' . __( 'Request Membership', 'buddypress' ) . '" href="' . wp_nonce_url( bp_get_group_permalink( $group ) . 'request-membership', 'groups_request_membership' ) . '">' . __( 'Request Membership', 'buddypress' ) . '</a>';
 		}
 	}
diff --git src/bp-templates/bp-legacy/buddypress/forums/index.php src/bp-templates/bp-legacy/buddypress/forums/index.php
index 3508f30..1d32169 100644
--- src/bp-templates/bp-legacy/buddypress/forums/index.php
+++ src/bp-templates/bp-legacy/buddypress/forums/index.php
@@ -181,7 +181,7 @@
 
 						<?php while ( bp_groups() ) : bp_the_group(); ?>
 
-							<?php if ( bp_group_is_forum_enabled() && ( bp_current_user_can( 'bp_moderate' ) || 'public' == bp_get_group_status() || bp_group_is_member() ) ) : ?>
+							<?php if ( bp_group_is_forum_enabled() && ( bp_current_user_can( 'bp_moderate' ) || 'anyone' != bp_groups_group_has_cap( $group, 'post_in_forum' ) || bp_group_is_member() ) ) : ?>
 
 								<option value="<?php bp_group_id(); ?>"><?php bp_group_name(); ?></option>
 
diff --git src/bp-templates/bp-legacy/buddypress/groups/create.php src/bp-templates/bp-legacy/buddypress/groups/create.php
index 244b0fc..b4c2a3c 100644
--- src/bp-templates/bp-legacy/buddypress/groups/create.php
+++ src/bp-templates/bp-legacy/buddypress/groups/create.php
@@ -101,33 +101,26 @@ do_action( 'bp_before_create_group_page' ); ?>
 				<h4><?php _e( 'Privacy Options', 'buddypress' ); ?></h4>
 
 				<div class="radio">
-
-					<label for="group-status-public"><input type="radio" name="group-status" id="group-status-public" value="public"<?php if ( 'public' == bp_get_new_group_status() || !bp_get_new_group_status() ) { ?> checked="checked"<?php } ?> aria-describedby="public-group-description" /> <?php _e( 'This is a public group', 'buddypress' ); ?></label>
-
-					<ul id="public-group-description">
-						<li><?php _e( 'Any site member can join this group.', 'buddypress' ); ?></li>
-						<li><?php _e( 'This group will be listed in the groups directory and in search results.', 'buddypress' ); ?></li>
-						<li><?php _e( 'Group content and activity will be visible to any site member.', 'buddypress' ); ?></li>
-					</ul>
-
-
-					<label for="group-status-private"><input type="radio" name="group-status" id="group-status-private" value="private"<?php if ( 'private' == bp_get_new_group_status() ) { ?> checked="checked"<?php } ?> aria-describedby="private-group-description" /> <?php _e( 'This is a private group', 'buddypress' ); ?></label>
-
-					<ul id="private-group-description">
-						<li><?php _e( 'Only users who request membership and are accepted can join the group.', 'buddypress' ); ?></li>
-						<li><?php _e( 'This group will be listed in the groups directory and in search results.', 'buddypress' ); ?></li>
-						<li><?php _e( 'Group content and activity will only be visible to members of the group.', 'buddypress' ); ?></li>
-					</ul>
-
-
-					<label for="group-status-hidden"><input type="radio" name="group-status" id="group-status-hidden" value="hidden"<?php if ( 'hidden' == bp_get_new_group_status() ) { ?> checked="checked"<?php } ?> aria-describedby="hidden-group-description" /> <?php _e('This is a hidden group', 'buddypress' ); ?></label>
-
-					<ul id="hidden-group-description">
-						<li><?php _e( 'Only users who are invited can join the group.', 'buddypress' ); ?></li>
-						<li><?php _e( 'This group will not be listed in the groups directory or search results.', 'buddypress' ); ?></li>
-						<li><?php _e( 'Group content and activity will only be visible to members of the group.', 'buddypress' ); ?></li>
-					</ul>
-
+					<?php
+					$allowed_statuses = bp_groups_get_group_statuses( array(), 'objects' );
+					$new_group_status = bp_get_new_group_status();
+					if ( ! $new_group_status ) {
+						$new_group_status = current( $allowed_statuses )->name;
+					}
+					foreach( $allowed_statuses as $status ) :
+					?>
+						<label for="group-status-<?php echo $status->name ?>"><input type="radio" name="group-status" id="group-status-<?php echo $status->name ?>" value="<?php echo $status->name ?>"<?php checked( $status->name, $new_group_status ); ?> aria-describedby="<?php echo $status->name ?>-group-description" /> <?php echo $status->display_name; ?></label>
+
+						<ul id="<?php echo $status->name ?>-group-description">
+							<?php
+							foreach ( $status->capabilities as $cap => $value ) :
+								$cap_desc = bp_groups_group_capabilities_description( $cap, $value );
+								if ( $cap_desc ) : ?>
+								<li><?php echo $cap_desc; ?></li>
+								<?php endif;
+							endforeach; ?>
+						</ul>
+					<?php endforeach; ?>
 				</div>
 
 				<h4><?php _e( 'Group Invitations', 'buddypress' ); ?></h4>
diff --git src/bp-templates/bp-legacy/buddypress/groups/single/admin.php src/bp-templates/bp-legacy/buddypress/groups/single/admin.php
index abeab17..783fd20 100644
--- src/bp-templates/bp-legacy/buddypress/groups/single/admin.php
+++ src/bp-templates/bp-legacy/buddypress/groups/single/admin.php
@@ -100,31 +100,26 @@ do_action( 'bp_before_group_admin_content' ); ?>
 	<h4><?php _e( 'Privacy Options', 'buddypress' ); ?></h4>
 
 	<div class="radio">
-
-		<label for="group-status-public"><input type="radio" name="group-status" id="group-status-public" value="public"<?php if ( 'public' == bp_get_new_group_status() || !bp_get_new_group_status() ) { ?> checked="checked"<?php } ?> aria-describedby="public-group-description" /> <?php _e( 'This is a public group', 'buddypress' ); ?></label>
-
-		<ul id="public-group-description">
-			<li><?php _e( 'Any site member can join this group.', 'buddypress' ); ?></li>
-			<li><?php _e( 'This group will be listed in the groups directory and in search results.', 'buddypress' ); ?></li>
-			<li><?php _e( 'Group content and activity will be visible to any site member.', 'buddypress' ); ?></li>
-		</ul>
-
-		<label for="group-status-private"><input type="radio" name="group-status" id="group-status-private" value="private"<?php if ( 'private' == bp_get_new_group_status() ) { ?> checked="checked"<?php } ?> aria-describedby="private-group-description" /> <?php _e( 'This is a private group', 'buddypress' ); ?></label>
-
-		<ul id="private-group-description">
-			<li><?php _e( 'Only users who request membership and are accepted can join the group.', 'buddypress' ); ?></li>
-			<li><?php _e( 'This group will be listed in the groups directory and in search results.', 'buddypress' ); ?></li>
-			<li><?php _e( 'Group content and activity will only be visible to members of the group.', 'buddypress' ); ?></li>
-		</ul>
-
-		<label for="group-status-hidden"><input type="radio" name="group-status" id="group-status-hidden" value="hidden"<?php if ( 'hidden' == bp_get_new_group_status() ) { ?> checked="checked"<?php } ?> aria-describedby="hidden-group-description" /> <?php _e('This is a hidden group', 'buddypress' ); ?></label>
-
-		<ul id="hidden-group-description">
-			<li><?php _e( 'Only users who are invited can join the group.', 'buddypress' ); ?></li>
-			<li><?php _e( 'This group will not be listed in the groups directory or search results.', 'buddypress' ); ?></li>
-			<li><?php _e( 'Group content and activity will only be visible to members of the group.', 'buddypress' ); ?></li>
-		</ul>
-
+		<?php
+		$allowed_statuses = bp_groups_get_group_statuses( array(), 'objects' );
+		$new_group_status = bp_get_new_group_status();
+		if ( ! $new_group_status ) {
+			$new_group_status = current( $allowed_statuses )->name;
+		}
+		foreach( $allowed_statuses as $status ) :
+		?>
+			<label for="group-status-<?php echo $status->name ?>"><input type="radio" name="group-status" id="group-status-<?php echo $status->name ?>" value="<?php echo $status->name ?>"<?php checked( $status->name, $new_group_status ); ?> aria-describedby="<?php echo $status->name ?>-group-description" /> <?php echo $status->display_name; ?></label>
+
+			<ul id="<?php echo $status->name ?>-group-description">
+				<?php
+				foreach ( $status->capabilities as $cap => $value ) :
+					$cap_desc = bp_groups_group_capabilities_description( $cap, $value );
+					if ( $cap_desc ) : ?>
+						<li><?php echo $cap_desc; ?></li>
+					<?php endif;
+				endforeach; ?>
+			</ul>
+		<?php endforeach; ?>
 	</div>
 
 	<hr />
