<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[BuddyPress][6842] trunk/bp-core/admin/bp-core-components.php: Improves the save routine for the Components Dashboard page</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg > ul, #logmsg > ol { margin-left: 0; margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://buddypress.trac.wordpress.org/changeset/6842">6842</a></dd>
<dt>Author</dt> <dd>boonebgorges</dd>
<dt>Date</dt> <dd>2013-03-07 14:52:46 +0000 (Thu, 07 Mar 2013)</dd>
</dl>

<h3>Log Message</h3>
<pre>Improves the save routine for the Components Dashboard page

The subtabs of the Components Dashboard page (All, Active, Inactive, Must Use,
Retired) all work differently in terms of how data is returned from them. Some
of the subtabs - All and Active - are accurate reflections of the components
that the user currently wants to be active. Retired and Inactive, on the other
hand, don't contain complete data about which components are supposed to be
turned on. For that reason, we can't trust the checkbox data submitted through
the POST when setting the active components.

This changeset introduces bp_core_admin_get_active_components_from_submitted_settings(),
which handles the logic necessary to determine which components the user
intends to be active, based on the data submitted in the POST request. It's
been separated out into a standalone function for better unit testing.

Test: https://github.com/buddypress/BuddyPress-Unit-Tests/commit/49e72ea3fc1f103ab6336c79f5c5b66294f4aad6

See <a href="http://buddypress.trac.wordpress.org/ticket/4671">#4671</a></pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkbpcoreadminbpcorecomponentsphp">trunk/bp-core/admin/bp-core-components.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkbpcoreadminbpcorecomponentsphp"></a>
<div class="modfile"><h4>Modified: trunk/bp-core/admin/bp-core-components.php (6841 => 6842)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-core/admin/bp-core-components.php        2013-03-05 14:36:35 UTC (rev 6841)
+++ trunk/bp-core/admin/bp-core-components.php        2013-03-07 14:52:46 UTC (rev 6842)
</span><span class="lines">@@ -61,70 +61,15 @@
</span><span class="cx">                 )
</span><span class="cx">         );
</span><span class="cx"> 
</span><del>-        // Required components
-        $required_components = array(
-                'core' =&gt; array(
-                        'title'       =&gt; __( 'BuddyPress Core', 'buddypress' ),
-                        'description' =&gt; __( 'It&amp;#8216;s what makes &lt;del&gt;time travel&lt;/del&gt; BuddyPress possible!', 'buddypress' )
-                ),
-                'members' =&gt; array(
-                        'title'       =&gt; __( 'Community Members', 'buddypress' ),
-                        'description' =&gt; __( 'Everything in a BuddyPress community revolves around its members.', 'buddypress' )
-                ),
-        );
</del><ins>+        $optional_components = bp_core_admin_get_components( 'optional' );
+        $required_components = bp_core_admin_get_components( 'required' );
+        $retired_components = bp_core_admin_get_components( 'retired' );
</ins><span class="cx"> 
</span><del>-        // Retired components
-        $retired_components['forums'] = array(
-                'title'       =&gt; __( 'Group Forums', 'buddypress' ),
-                'description' =&gt; sprintf( __( 'BuddyPress Forums are retired. Use %s.', 'buddypress' ), '&lt;a href=&quot;http://bbpress.org&quot;&gt;bbPress&lt;/a&gt;' )
-        );
-
-        // Optional core components
-        $optional_components = array(
-                'xprofile' =&gt; array(
-                        'title'       =&gt; __( 'Extended Profiles', 'buddypress' ),
-                        'description' =&gt; __( 'Customize your community with fully editable profile fields that allow your users to describe themselves.', 'buddypress' )
-                ),
-                'settings' =&gt; array(
-                        'title'       =&gt; __( 'Account Settings', 'buddypress' ),
-                        'description' =&gt; __( 'Allow your users to modify their account and notification settings directly from within their profiles.', 'buddypress' )
-                ),
-                'friends'  =&gt; array(
-                        'title'       =&gt; __( 'Friend Connections', 'buddypress' ),
-                        'description' =&gt; __( 'Let your users make connections so they can track the activity of others and focus on the people they care about the most.', 'buddypress' )
-                ),
-                'messages' =&gt; array(
-                        'title'       =&gt; __( 'Private Messaging', 'buddypress' ),
-                        'description' =&gt; __( 'Allow your users to talk to each other directly and in private. Not just limited to one-on-one discussions, messages can be sent between any number of members.', 'buddypress' )
-                ),
-                'activity' =&gt; array(
-                        'title'       =&gt; __( 'Activity Streams', 'buddypress' ),
-                        'description' =&gt; __( 'Global, personal, and group activity streams with threaded commenting, direct posting, favoriting and @mentions, all with full RSS feed and email notification support.', 'buddypress' )
-                ),
-                'groups'   =&gt; array(
-                        'title'       =&gt; __( 'User Groups', 'buddypress' ),
-                        'description' =&gt; __( 'Groups allow your users to organize themselves into specific public, private or hidden sections with separate activity streams and member listings.', 'buddypress' )
-                ),
-                'forums'   =&gt; array(
-                        'title'       =&gt; __( 'Group Forums (Legacy)', 'buddypress' ),
-                        'description' =&gt; __( 'Group forums allow for focused, bulletin-board style conversations.', 'buddypress' )
-                ),
-                'blogs'    =&gt; array(
-                        'title'       =&gt; __( 'Site Tracking', 'buddypress' ),
-                        'description' =&gt; __( 'Record activity for new posts and comments from your site.', 'buddypress' )
-                )
-        );
-
</del><span class="cx">         // Don't show Forums component in optional components if it's disabled
</span><span class="cx">         if ( ! bp_is_active( 'forums' ) ) {
</span><span class="cx">                 unset( $optional_components['forums'] );
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        // Add blogs tracking if multisite
-        if ( is_multisite() ) {
-                $optional_components['blogs']['description'] = __( 'Record activity for new sites, posts, and comments across your network.', 'buddypress' );
-        }
-
</del><span class="cx">         // Merge optional and required together
</span><span class="cx">         $all_components = $optional_components + $required_components;
</span><span class="cx"> 
</span><span class="lines">@@ -297,7 +242,8 @@
</span><span class="cx">                 // Save settings and upgrade schema
</span><span class="cx">                 require_once( BP_PLUGIN_DIR . '/bp-core/admin/bp-core-schema.php' );
</span><span class="cx"> 
</span><del>-                $bp-&gt;active_components = stripslashes_deep( $_POST['bp_components'] );
</del><ins>+                $submitted = stripslashes_deep( $_POST['bp_components'] );
+                $bp-&gt;active_components = bp_core_admin_get_active_components_from_submitted_settings( $submitted );
</ins><span class="cx"> 
</span><span class="cx">                 bp_core_install( $bp-&gt;active_components );
</span><span class="cx">                 bp_core_add_page_mappings( $bp-&gt;active_components );
</span><span class="lines">@@ -311,3 +257,157 @@
</span><span class="cx">         wp_redirect( $base_url );
</span><span class="cx"> }
</span><span class="cx"> add_action( 'bp_admin_init', 'bp_core_admin_components_settings_handler' );
</span><ins>+
+/**
+ * Calculates the components that should be active after save, based on submitted settings
+ *
+ * The way that active components must be set after saving your settings must
+ * be calculated differently depending on which of the Components subtabs you
+ * are coming from:
+ * - When coming from All or Active, the submitted checkboxes accurately
+ *   reflect the desired active components, so we simply pass them through
+ * - When coming from Inactive, components can only be activated - already
+ *   active components will not be passed in the $_POST global. Thus, we must
+ *   parse the newly activated components with the already active components
+ *   saved in the $bp global
+ * - When activating a Retired component, the situation is similar to Inactive.
+ * - When deactivating a Retired component, no value is passed in the $_POST
+ *   global (because the component settings are checkboxes). So, in order to
+ *   determine whether a retired component is being deactivated, we retrieve a
+ *   list of retired components, and check each one to ensure that its checkbox
+ *   is not present, before merging the submitted components with the active
+ *   ones.
+ *
+ * @since (BuddyPress) 1.7
+ *
+ * @param array This is the array of component settings coming from the POST
+ *   global. You should stripslashes_deep() before passing to this function
+ * @return array The calculated list of component settings
+ */
+function bp_core_admin_get_active_components_from_submitted_settings( $submitted ) {
+        $current_action = 'all';
+
+        if ( isset( $_GET['action'] ) &amp;&amp; in_array( $_GET['action'], array( 'active', 'inactive', 'retired' ) ) ) {
+                $current_action = $_GET['action'];
+        }
+
+        $current_components = buddypress()-&gt;active_components;
+
+        switch ( $current_action ) {
+                case 'retired' :
+                        $retired_components = bp_core_admin_get_components( 'retired' );
+                        foreach ( array_keys( $retired_components ) as $retired_component ) {
+                                if ( ! isset( $submitted[ $retired_component ] ) ) {
+                                        unset( $current_components[ $retired_component ] );
+                                }
+                        }
+                        // fall through
+
+                case 'inactive' :
+                        $components = array_merge( $submitted, $current_components );
+                        break;
+
+                case 'all' :
+                case 'active' :
+                default :
+                        $components = $submitted;
+                        break;
+        }
+
+        return $components;
+}
+
+/**
+ * Return a list of component information, optionally filtered by type
+ *
+ * We use this information both to build the markup for the admin screens, as
+ * well as to do some processing on settings data submitted from those screens.
+ *
+ * @since (BuddyPress) 1.7
+ *
+ * @param string $type 'all', 'optional', 'retired', 'required'
+ * @return array An array of requested component data
+ */
+function bp_core_admin_get_components( $type = 'all' ) {
+
+        // Required components
+        $required_components = array(
+                'core' =&gt; array(
+                        'title'       =&gt; __( 'BuddyPress Core', 'buddypress' ),
+                        'description' =&gt; __( 'It&amp;#8216;s what makes &lt;del&gt;time travel&lt;/del&gt; BuddyPress possible!', 'buddypress' )
+                ),
+                'members' =&gt; array(
+                        'title'       =&gt; __( 'Community Members', 'buddypress' ),
+                        'description' =&gt; __( 'Everything in a BuddyPress community revolves around its members.', 'buddypress' )
+                ),
+        );
+
+        // Retired components
+        $retired_components = array(
+                'forums' =&gt; array(
+                        'title'       =&gt; __( 'Group Forums', 'buddypress' ),
+                        'description' =&gt; sprintf( __( 'BuddyPress Forums are retired. Use %s.', 'buddypress' ), '&lt;a href=&quot;http://bbpress.org&quot;&gt;bbPress&lt;/a&gt;' )
+                ),
+        );
+
+        // Optional core components
+        $optional_components = array(
+                'xprofile' =&gt; array(
+                        'title'       =&gt; __( 'Extended Profiles', 'buddypress' ),
+                        'description' =&gt; __( 'Customize your community with fully editable profile fields that allow your users to describe themselves.', 'buddypress' )
+                ),
+                'settings' =&gt; array(
+                        'title'       =&gt; __( 'Account Settings', 'buddypress' ),
+                        'description' =&gt; __( 'Allow your users to modify their account and notification settings directly from within their profiles.', 'buddypress' )
+                ),
+                'friends'  =&gt; array(
+                        'title'       =&gt; __( 'Friend Connections', 'buddypress' ),
+                        'description' =&gt; __( 'Let your users make connections so they can track the activity of others and focus on the people they care about the most.', 'buddypress' )
+                ),
+                'messages' =&gt; array(
+                        'title'       =&gt; __( 'Private Messaging', 'buddypress' ),
+                        'description' =&gt; __( 'Allow your users to talk to each other directly and in private. Not just limited to one-on-one discussions, messages can be sent between any number of members.', 'buddypress' )
+                ),
+                'activity' =&gt; array(
+                        'title'       =&gt; __( 'Activity Streams', 'buddypress' ),
+                        'description' =&gt; __( 'Global, personal, and group activity streams with threaded commenting, direct posting, favoriting and @mentions, all with full RSS feed and email notification support.', 'buddypress' )
+                ),
+                'groups'   =&gt; array(
+                        'title'       =&gt; __( 'User Groups', 'buddypress' ),
+                        'description' =&gt; __( 'Groups allow your users to organize themselves into specific public, private or hidden sections with separate activity streams and member listings.', 'buddypress' )
+                ),
+                'forums'   =&gt; array(
+                        'title'       =&gt; __( 'Group Forums (Legacy)', 'buddypress' ),
+                        'description' =&gt; __( 'Group forums allow for focused, bulletin-board style conversations.', 'buddypress' )
+                ),
+                'blogs'    =&gt; array(
+                        'title'       =&gt; __( 'Site Tracking', 'buddypress' ),
+                        'description' =&gt; __( 'Record activity for new posts and comments from your site.', 'buddypress' )
+                )
+        );
+
+
+        // Add blogs tracking if multisite
+        if ( is_multisite() ) {
+                $optional_components['blogs']['description'] = __( 'Record activity for new sites, posts, and comments across your network.', 'buddypress' );
+        }
+
+        switch ( $type ) {
+                case 'required' :
+                        $components = $required_components;
+                        break;
+                case 'optional' :
+                        $components = $optional_components;
+                        break;
+                case 'retired' :
+                        $components = $retired_components;
+                        break;
+                case 'all' :
+                default :
+                        $components = array_merge( $required_components, $optional_components, $retired_components );
+                        break;
+
+        }
+
+        return $components;
+}
</ins></span></pre>
</div>
</div>

</body>
</html>