<!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][5789] trunk: First pass at per-field visibility/privacy for XProfile:</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/5789">5789</a></dd>
<dt>Author</dt> <dd>boonebgorges</dd>
<dt>Date</dt> <dd>2012-02-15 20:33:09 +0000 (Wed, 15 Feb 2012)</dd>
</dl>

<h3>Log Message</h3>
<pre>First pass at per-field visibility/privacy for XProfile:
- Allows admins to set default levels for specific fields
- Allows users to set visibility/privacy on a field-by-field basis
- Modifies the profile templates to show markup for editing visibility status
See <a href="http://buddypress.trac.wordpress.org/ticket/3695">#3695</a></pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkbpthemesbpdefault_inccssdefaultcss">trunk/bp-themes/bp-default/_inc/css/default.css</a></li>
<li><a href="#trunkbpthemesbpdefault_incglobaljs">trunk/bp-themes/bp-default/_inc/global.js</a></li>
<li><a href="#trunkbpthemesbpdefaultmemberssingleprofileeditphp">trunk/bp-themes/bp-default/members/single/profile/edit.php</a></li>
<li><a href="#trunkbpxprofilebpxprofileadminphp">trunk/bp-xprofile/bp-xprofile-admin.php</a></li>
<li><a href="#trunkbpxprofilebpxprofileclassesphp">trunk/bp-xprofile/bp-xprofile-classes.php</a></li>
<li><a href="#trunkbpxprofilebpxprofilefunctionsphp">trunk/bp-xprofile/bp-xprofile-functions.php</a></li>
<li><a href="#trunkbpxprofilebpxprofileloaderphp">trunk/bp-xprofile/bp-xprofile-loader.php</a></li>
<li><a href="#trunkbpxprofilebpxprofilescreensphp">trunk/bp-xprofile/bp-xprofile-screens.php</a></li>
<li><a href="#trunkbpxprofilebpxprofiletemplatephp">trunk/bp-xprofile/bp-xprofile-template.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkbpthemesbpdefault_inccssdefaultcss"></a>
<div class="modfile"><h4>Modified: trunk/bp-themes/bp-default/_inc/css/default.css (5788 => 5789)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-themes/bp-default/_inc/css/default.css        2012-02-14 19:36:19 UTC (rev 5788)
+++ trunk/bp-themes/bp-default/_inc/css/default.css        2012-02-15 20:33:09 UTC (rev 5789)
</span><span class="lines">@@ -34,6 +34,7 @@
</span><span class="cx">         6.7 - Topics and Tables - Forums and General
</span><span class="cx">         6.8 - Headers, Lists and Tabs - Activity, Groups, Blogs, Forums
</span><span class="cx">         6.9 - Private Messaging Threads
</span><ins>+        6.10 - Extended Profiles
</ins><span class="cx"> --------------------------------------------------------------*/
</span><span class="cx"> 
</span><span class="cx"> /*--------------------------------------------------------------
</span><span class="lines">@@ -2292,13 +2293,6 @@
</span><span class="cx"> #admins-list li {
</span><span class="cx">         overflow: auto;
</span><span class="cx"> }
</span><del>-div.profile h4 {
-        margin-bottom: auto;
-        margin-top: 15px;
-}
-#profile-edit-form ul.button-nav {
-        margin-top: 15px;
-}
</del><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> /*--------------------------------------------------------------
</span><span class="lines">@@ -2364,4 +2358,33 @@
</span><span class="cx"> }
</span><span class="cx"> div#message-thread div.message-options {
</span><span class="cx">         text-align: right;
</span><ins>+}
+
+/*--------------------------------------------------------------
+6.9 - Extended Profiles
+--------------------------------------------------------------*/
+
+div.profile h4 {
+        margin-bottom: auto;
+        margin-top: 15px;
+}
+#profile-edit-form ul.button-nav {
+        margin-top: 15px;
+}
+body.no-js .field-privacy-settings-toggle,
+body.no-js .field-privacy-settings-close {
+        display: none;
+}
+.field-privacy-settings {
+        display: none;
+}
+        body.no-js .field-privacy-settings {
+                display: block;
+        }
+.current-privacy-level {
+        font-weight: bold;
+}
+.field-privacy-settings-toggle a,
+.field-privacy-settings a {
+        font-size: .9em;
</ins><span class="cx"> }
</span><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunkbpthemesbpdefault_incglobaljs"></a>
<div class="modfile"><h4>Modified: trunk/bp-themes/bp-default/_inc/global.js (5788 => 5789)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-themes/bp-default/_inc/global.js        2012-02-14 19:36:19 UTC (rev 5788)
+++ trunk/bp-themes/bp-default/_inc/global.js        2012-02-15 20:33:09 UTC (rev 5789)
</span><span class="lines">@@ -807,6 +807,29 @@
</span><span class="cx">                 return false;
</span><span class="cx">         });
</span><span class="cx"> 
</span><ins>+        /** Profile Privacy Settings *********************************/
+        
+        jq('.privacy-toggle-link').click( function() {
+                var toggle_div = jq(this).parent();
+                
+                jq(toggle_div).slideUp( 200, function(){
+                        jq(toggle_div).siblings('.field-privacy-settings').slideDown(200);
+                });
+                
+                return false;
+        } );
+
+        jq('.field-privacy-settings-close').click( function() {
+                var settings_div = jq(this).parent();
+                
+                jq(settings_div).slideUp( 200, function(){
+                        jq(settings_div).siblings('.field-privacy-settings-toggle').slideDown(200);
+                });
+                
+                return false;
+        } );
+
+
</ins><span class="cx">         /** Friendship Requests **************************************/
</span><span class="cx"> 
</span><span class="cx">         /* Accept and Reject friendship request buttons */
</span></span></pre></div>
<a id="trunkbpthemesbpdefaultmemberssingleprofileeditphp"></a>
<div class="modfile"><h4>Modified: trunk/bp-themes/bp-default/members/single/profile/edit.php (5788 => 5789)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-themes/bp-default/members/single/profile/edit.php        2012-02-14 19:36:19 UTC (rev 5788)
+++ trunk/bp-themes/bp-default/members/single/profile/edit.php        2012-02-15 20:33:09 UTC (rev 5789)
</span><span class="lines">@@ -112,6 +112,21 @@
</span><span class="cx">                                         &lt;/div&gt;
</span><span class="cx"> 
</span><span class="cx">                                 &lt;?php endif; ?&gt;
</span><ins>+                                
+                                &lt;?php /* The fullname field is always public */ ?&gt;
+                                &lt;?php if ( 1 != bp_get_the_profile_field_id() ) : ?&gt;
+                                        &lt;div class=&quot;field-privacy-settings-toggle&quot; id=&quot;field-privacy-settings-toggle-&lt;?php bp_the_profile_field_id() ?&gt;&quot;&gt;
+                                                &lt;?php printf( __( 'This field can be seen by: &lt;span class=&quot;current-privacy-level&quot;&gt;%s&lt;/span&gt;', 'buddypress' ), bp_get_the_profile_field_privacy_level_label() ) ?&gt; &lt;a href=&quot;#&quot; class=&quot;privacy-toggle-link&quot;&gt;Change&lt;/a&gt;
+                                        &lt;/div&gt;
+                                        
+                                        &lt;div class=&quot;field-privacy-settings&quot; id=&quot;field-privacy-settings-&lt;?php bp_the_profile_field_id() ?&gt;&quot;&gt;
+                                                &lt;label for=&quot;field-privacy&quot;&gt;&lt;?php _e( 'Who can see this field?', 'buddypress' ) ?&gt;&lt;/label&gt;
+                                                
+                                                &lt;?php bp_profile_privacy_radio_buttons() ?&gt;
+                                                
+                                                &lt;a class=&quot;field-privacy-settings-close&quot; href=&quot;#&quot;&gt;&lt;?php _e( 'Close', 'buddypress' ) ?&gt;&lt;/a&gt;
+                                        &lt;/div&gt;
+                                &lt;?php endif ?&gt;
</ins><span class="cx"> 
</span><span class="cx">                                 &lt;?php do_action( 'bp_custom_profile_edit_fields' ); ?&gt;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkbpxprofilebpxprofileadminphp"></a>
<div class="modfile"><h4>Modified: trunk/bp-xprofile/bp-xprofile-admin.php (5788 => 5789)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-xprofile/bp-xprofile-admin.php        2012-02-14 19:36:19 UTC (rev 5788)
+++ trunk/bp-xprofile/bp-xprofile-admin.php        2012-02-15 20:33:09 UTC (rev 5789)
</span><span class="lines">@@ -262,6 +262,7 @@
</span><span class="cx">                                 $type = 'error';
</span><span class="cx"> 
</span><span class="cx">                                 unset( $_GET['mode'] );
</span><ins>+                                
</ins><span class="cx">                                 xprofile_admin( $message, $type );
</span><span class="cx">                         } else {
</span><span class="cx">                                 $message = __( 'The field was saved successfully.', 'buddypress' );
</span><span class="lines">@@ -270,6 +271,10 @@
</span><span class="cx">                                 if ( 1 == $field_id )
</span><span class="cx">                                         bp_update_option( 'bp-xprofile-fullname-field-name', $field-&gt;name );
</span><span class="cx"> 
</span><ins>+                                if ( !empty( $_POST['default-privacy'] ) ) {
+                                        bp_xprofile_update_field_meta( $field_id, 'default_privacy', $_POST['default-privacy'] );
+                                }
+
</ins><span class="cx">                                 unset( $_GET['mode'] );
</span><span class="cx"> 
</span><span class="cx">                                 do_action( 'xprofile_fields_saved_field', $field );
</span></span></pre></div>
<a id="trunkbpxprofilebpxprofileclassesphp"></a>
<div class="modfile"><h4>Modified: trunk/bp-xprofile/bp-xprofile-classes.php (5788 => 5789)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-xprofile/bp-xprofile-classes.php        2012-02-14 19:36:19 UTC (rev 5788)
+++ trunk/bp-xprofile/bp-xprofile-classes.php        2012-02-15 20:33:09 UTC (rev 5789)
</span><span class="lines">@@ -114,14 +114,15 @@
</span><span class="cx">                 global $wpdb, $bp;
</span><span class="cx"> 
</span><span class="cx">                 $defaults = array(
</span><del>-                        'profile_group_id'  =&gt; false,
-                        'user_id'           =&gt; bp_displayed_user_id(),
-                        'hide_empty_groups' =&gt; false,
-                        'hide_empty_fields' =&gt; false,
-                        'fetch_fields'      =&gt; false,
-                        'fetch_field_data'  =&gt; false,
-                        'exclude_groups'    =&gt; false,
-                        'exclude_fields'    =&gt; false
</del><ins>+                        'profile_group_id'    =&gt; false,
+                        'user_id'             =&gt; bp_displayed_user_id(),
+                        'hide_empty_groups'   =&gt; false,
+                        'hide_empty_fields'   =&gt; false,
+                        'fetch_fields'        =&gt; false,
+                        'fetch_field_data'    =&gt; false,
+                        'fetch_privacy_level' =&gt; false,
+                        'exclude_groups'      =&gt; false,
+                        'exclude_fields'      =&gt; false
</ins><span class="cx">                 );
</span><span class="cx"> 
</span><span class="cx">                 $r = wp_parse_args( $args, $defaults );
</span><span class="lines">@@ -151,11 +152,33 @@
</span><span class="cx"> 
</span><span class="cx">                 if ( empty( $group_ids ) )
</span><span class="cx">                         return $groups;
</span><ins>+                
+                // Support arrays and comma-separated strings
+                if ( !empty( $exclude_fields ) &amp;&amp; !is_array( $exclude_fields ) ) {
+                        $exclude_fields = explode( ',', $exclude_fields );
+                }
+                
+                // Sanitization - ensure that each field_id passed is an integer
+                $exclude_fields_cs = array();
+                foreach( (array)$exclude_fields as $exclude_field ) {
+                        $efint = (int)$exclude_field;
+                        if ( !empty( $efint ) ) {
+                                $exclude_fields_cs[] = $efint;
+                        }
+                }
+                
+                // Privacy - Handled here so as not to be overridden by sloppy use of the
+                // exclude_fields parameter. See bp_xprofile_get_hidden_fields_for_user()
+                $exclude_fields_cs = array_merge( $exclude_fields_cs, bp_xprofile_get_hidden_fields_for_user( $user_id ) );
+                
+                $exclude_fields_cs = implode( ',', $exclude_fields_cs );
+                
+                if ( !empty( $exclude_fields_cs ) ) {
+                        $exclude_fields_sql = $wpdb-&gt;prepare( &quot;AND id NOT IN ({$exclude_fields_cs})&quot; ); 
+                } else {
+                        $exclude_fields_sql = '';
+                }
</ins><span class="cx"> 
</span><del>-                $exclude_fields_sql = '';
-                if ( !empty( $exclude_fields ) )
-                        $exclude_fields_sql = $wpdb-&gt;prepare( &quot;AND id NOT IN ({$exclude_fields})&quot; );
-
</del><span class="cx">                 // Fetch the fields
</span><span class="cx">                 $fields = $wpdb-&gt;get_results( $wpdb-&gt;prepare( &quot;SELECT id, name, description, type, group_id, is_required FROM {$bp-&gt;profile-&gt;table_name_fields} WHERE group_id IN ( {$group_ids} ) AND parent_id = 0 {$exclude_fields_sql} ORDER BY field_order&quot; ) );
</span><span class="cx"> 
</span><span class="lines">@@ -172,7 +195,7 @@
</span><span class="cx">                         $field_ids_sql = implode( ',', (array) $field_ids );
</span><span class="cx"> 
</span><span class="cx">                         if ( !empty( $field_ids ) )
</span><del>-                                $field_data = $wpdb-&gt;get_results( $wpdb-&gt;prepare( &quot;SELECT field_id, value FROM {$bp-&gt;profile-&gt;table_name_data} WHERE field_id IN ( {$field_ids_sql} ) AND user_id = %d&quot;, $user_id ) );
</del><ins>+                                $field_data = $wpdb-&gt;get_results( $wpdb-&gt;prepare( &quot;SELECT id, field_id, value FROM {$bp-&gt;profile-&gt;table_name_data} WHERE field_id IN ( {$field_ids_sql} ) AND user_id = %d&quot;, $user_id ) );
</ins><span class="cx"> 
</span><span class="cx">                         // Remove data-less fields, if necessary
</span><span class="cx">                         if ( !empty( $hide_empty_fields ) ) {
</span><span class="lines">@@ -210,10 +233,16 @@
</span><span class="cx">                                         foreach( (array) $field_data as $data ) {
</span><span class="cx"> 
</span><span class="cx">                                                 // Assign correct data value to the field
</span><del>-                                                if ( $field-&gt;id == $data-&gt;field_id )
</del><ins>+                                                if ( $field-&gt;id == $data-&gt;field_id ) {
</ins><span class="cx">                                                         $fields[$field_key]-&gt;data-&gt;value = $data-&gt;value;
</span><ins>+                                                        $fields[$field_key]-&gt;data-&gt;id = $data-&gt;id;
+                                                }
</ins><span class="cx">                                         }
</span><span class="cx">                                 }
</span><ins>+                                                
+                                if ( $fetch_privacy_level ) {
+                                        $fields = self::fetch_privacy_level( $user_id, $fields );
+                                }
</ins><span class="cx">                         }
</span><span class="cx">                 }
</span><span class="cx"> 
</span><span class="lines">@@ -263,7 +292,53 @@
</span><span class="cx"> 
</span><span class="cx">                 return $wpdb-&gt;query( $wpdb-&gt;prepare( &quot;UPDATE {$bp-&gt;profile-&gt;table_name_groups} SET group_order = %d WHERE id = %d&quot;, $position, $field_group_id ) );
</span><span class="cx">         }
</span><del>-
</del><ins>+        
+        /**
+         * Fetch the field privacy level for the returned fielddata
+         */
+        function fetch_privacy_level( $user_id, $fields ) {
+                global $wpdb, $bp;
+                
+                // Get the user's privacy level preferences
+                $privacy_levels = get_user_meta( $user_id, 'bp_xprofile_privacy_levels', true );
+                
+                foreach( (array)$fields as $key =&gt; $field ) {
+                        // Look to see if the user has set the privacy for this field
+                        if ( isset( $privacy_levels[$field-&gt;id] ) ) {
+                                $field_privacy = $privacy_levels[$field-&gt;id];
+                        } else {
+                                // If not, bring up the admin-set defaults
+                                if ( !isset( $default_privacy_levels ) ) {
+                                        $default_privacy_levels = self::fetch_default_privacy_levels();
+                                }
+                                
+                                // If no admin-set default is saved, fall back on a global default
+                                $field_privacy = !empty( $default_privacy_levels[$field-&gt;id] ) ? $default_privacy_levels[$field-&gt;id] : apply_filters( 'bp_xprofile_default_privacy_level', 'public' );
+                        }
+                        
+                        $fields[$key]-&gt;privacy_level = $field_privacy;
+                }
+                
+                return $fields;
+        }
+        
+        /**
+         * Fetch the admin-set default privacy levels for all fields
+         */
+        function fetch_default_privacy_levels() {
+                global $wpdb, $bp;
+                
+                $levels = $wpdb-&gt;get_results( $wpdb-&gt;prepare( &quot;SELECT object_id, meta_value FROM {$bp-&gt;profile-&gt;table_name_meta} WHERE object_type = 'field' AND meta_key = 'default_privacy'&quot; ) );
+                
+                // Arrange so that the field id is the key and the privacy level the value
+                $default_privacy_levels = array();
+                foreach( $levels as $level ) {
+                        $default_privacy_levels[$level-&gt;object_id] = $level-&gt;meta_value;
+                }
+                
+                return $default_privacy_levels;
+        }
+        
</ins><span class="cx">         /* ADMIN AREA HTML.
</span><span class="cx">         * TODO: Get this out of here and replace with standard loops
</span><span class="cx">         */
</span><span class="lines">@@ -343,6 +418,7 @@
</span><span class="cx">         var $option_order;
</span><span class="cx">         var $order_by;
</span><span class="cx">         var $is_default_option;
</span><ins>+        var $default_privacy;
</ins><span class="cx"> 
</span><span class="cx">         var $data;
</span><span class="cx">         var $message = null;
</span><span class="lines">@@ -380,6 +456,12 @@
</span><span class="cx">                         if ( $get_data &amp;&amp; $user_id ) {
</span><span class="cx">                                 $this-&gt;data          = $this-&gt;get_field_data( $user_id );
</span><span class="cx">                         }
</span><ins>+                        
+                        $this-&gt;default_privacy = bp_xprofile_get_meta( $id, 'field', 'default_privacy' );
+                        
+                        if ( empty( $this-&gt;default_privacy ) ) {
+                                $this-&gt;default_privacy = 'public';
+                        }
</ins><span class="cx">                 }
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="lines">@@ -639,6 +721,10 @@
</span><span class="cx">                         if ( $this-&gt;type != $type ) { 
</span><span class="cx">                                 $class = 'display: none;';
</span><span class="cx">                         }
</span><ins>+                        
+                        if ( empty( $this-&gt;default_privacy ) ) {
+                                $this-&gt;default_privacy = 'public';
+                        }
</ins><span class="cx"> 
</span><span class="cx">                         ?&gt;
</span><span class="cx"> 
</span><span class="lines">@@ -787,6 +873,22 @@
</span><span class="cx"> 
</span><span class="cx">                                         &lt;?php } ?&gt;
</span><span class="cx"> 
</span><ins>+                                        &lt;?php /* The fullname field cannot be hidden */ ?&gt;
+                                        &lt;?php if ( 1 != $this-&gt;id ) : ?&gt;
+
+                                                &lt;div id=&quot;titlediv&quot;&gt;
+                                                        &lt;h3&gt;&lt;label for=&quot;default-privacy&quot;&gt;&lt;?php _e( &quot;Default Privacy Level&quot;, 'buddypress' ); ?&gt;&lt;/label&gt;&lt;/h3&gt;
+                                                        &lt;div id=&quot;titlewrap&quot;&gt;
+                                                                &lt;ul&gt;
+                                                                &lt;?php foreach( bp_xprofile_get_privacy_levels() as $level ) : ?&gt;
+                                                                        &lt;li&gt;&lt;input type=&quot;radio&quot; name=&quot;default-privacy&quot; value=&quot;&lt;?php echo esc_attr( $level['id'] ) ?&gt;&quot; &lt;?php checked( $this-&gt;default_privacy, $level['id'] ) ?&gt;&gt; &lt;?php echo esc_html( $level['label'] ) ?&gt;&lt;/li&gt; 
+                                                                &lt;?php endforeach ?&gt;
+                                                                &lt;/ul&gt;
+                                                        &lt;/div&gt;
+                                                &lt;/div&gt;
+                                        
+                                        &lt;?php endif ?&gt;
+
</ins><span class="cx">                                         &lt;p class=&quot;submit&quot;&gt;
</span><span class="cx">                                                 &lt;input type=&quot;hidden&quot; name=&quot;field_order&quot; id=&quot;field_order&quot; value=&quot;&lt;?php echo esc_attr( $this-&gt;field_order ); ?&gt;&quot; /&gt;
</span><span class="cx">                                                 &lt;input type=&quot;submit&quot; value=&quot;&lt;?php _e( 'Save', 'buddypress' ); ?&gt;&quot; name=&quot;saveField&quot; id=&quot;saveField&quot; style=&quot;font-weight: bold&quot; class=&quot;button-primary&quot; /&gt;
</span><span class="lines">@@ -967,6 +1069,25 @@
</span><span class="cx"> 
</span><span class="cx">                 return $profile_data;
</span><span class="cx">         }
</span><ins>+        
+        /**
+         * Get the user's field data id by the id of the xprofile field
+         *
+         * @param int $field_id
+         * @param int $user_id
+         * @return int $fielddata_id
+         */
+        function get_fielddataid_byid( $field_id, $user_id ) {
+                global $wpdb, $bp;
+                
+                if ( empty( $field_id ) || empty( $user_id ) ) {
+                        $fielddata_id = 0;
+                } else {
+                        $fielddata_id = $wpdb-&gt;get_var( $wpdb-&gt;prepare( &quot;SELECT id FROM {$bp-&gt;profile-&gt;table_name_data} WHERE field_id = %d AND user_id = %d&quot;, $field_id, $user_id ) );        
+                }
+                
+                return $fielddata_id;
+        }
</ins><span class="cx"> 
</span><span class="cx">         function get_value_byid( $field_id, $user_ids = null ) {
</span><span class="cx">                 global $wpdb, $bp;
</span></span></pre></div>
<a id="trunkbpxprofilebpxprofilefunctionsphp"></a>
<div class="modfile"><h4>Modified: trunk/bp-xprofile/bp-xprofile-functions.php (5788 => 5789)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-xprofile/bp-xprofile-functions.php        2012-02-14 19:36:19 UTC (rev 5788)
+++ trunk/bp-xprofile/bp-xprofile-functions.php        2012-02-15 20:33:09 UTC (rev 5789)
</span><span class="lines">@@ -250,6 +250,44 @@
</span><span class="cx">         return $field-&gt;save();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+/**
+ * Set the privacy level for this field
+ *
+ * @param int $field_id The ID of the xprofile field
+ * @param int $user_id The ID of the user to whom the data belongs
+ * @param string $privacy_level
+ * @return bool True on success
+ */
+function xprofile_set_field_privacy_level( $field_id = 0, $user_id = 0, $privacy_level = '' ) {
+        if ( empty( $field_id ) || empty( $user_id ) || empty( $privacy_level ) ) {
+                return false;
+        }
+        
+        // Get the fielddata id
+        $fielddata_id = BP_XProfile_ProfileData::get_fielddataid_byid( $field_id, $user_id );
+        
+        if ( empty( $fielddata_id ) ) {
+                return false;
+        }
+        
+        // Check against a whitelist
+        $allowed_values = bp_xprofile_get_privacy_levels();
+        if ( !array_key_exists( $privacy_level, $allowed_values ) ) {
+                return false;
+        }
+        
+        // Stored in an array in usermeta
+        $current_privacy_levels = get_user_meta( $user_id, 'bp_xprofile_privacy_levels', true );
+        
+        if ( !$current_privacy_levels ) {
+                $current_privacy_levels = array();
+        }
+        
+        $current_privacy_levels[$field_id] = $privacy_level;
+        
+        return update_user_meta( $user_id, 'bp_xprofile_privacy_levels', $current_privacy_levels );
+}
+
</ins><span class="cx"> function xprofile_delete_field_data( $field, $user_id ) {
</span><span class="cx">         if ( is_numeric( $field ) )
</span><span class="cx">                 $field_id = $field;
</span><span class="lines">@@ -605,4 +643,107 @@
</span><span class="cx">         return apply_filters( 'bp_xprofile_fullname_field_name', BP_XPROFILE_FULLNAME_FIELD_NAME );
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+/**
+ * Get privacy levels out of the $bp global
+ *
+ * @return array
+ */
+function bp_xprofile_get_privacy_levels() {
+        global $bp;
+        
+        return apply_filters( 'bp_xprofile_get_privacy_levels', $bp-&gt;profile-&gt;privacy_levels );
+}
+
+/**
+ * Get the ids of fields that are hidden for this displayed/loggedin user pair
+ *
+ * This is the function primarily responsible for profile field privacy. It works by determining
+ * the relationship between the displayed_user (ie the profile owner) and the current_user (ie the
+ * profile viewer). Then, based on that relationship, we query for the set of fields that should
+ * be excluded from the profile loop.
+ *
+ * @since 1.6
+ * @see BP_XProfile_Group::get()
+ * @uses apply_filters() Filter bp_xprofile_get_hidden_fields_for_user to modify privacy levels,
+ *   or if you have added your own custom levels
+ *
+ * @param int $displayed_user_id The id of the user the profile fields belong to
+ * @param int $current_user_id The id of the user viewing the profile
+ * @return array An array of field ids that should be excluded from the profile query
+ */
+function bp_xprofile_get_hidden_fields_for_user( $displayed_user_id = 0, $current_user_id = 0 ) {
+        if ( !$displayed_user_id ) {
+                $displayed_user_id = bp_displayed_user_id();
+        }
+        
+        if ( !$displayed_user_id ) {
+                return array();
+        }
+        
+        if ( !$current_user_id ) {
+                $current_user_id = bp_loggedin_user_id();
+        }
+        
+        // @todo - This is where you'd swap out for current_user_can() checks
+        
+        if ( $current_user_id ) {
+                // Current user is logged in
+                if ( $displayed_user_id == $current_user_id ) {
+                        // If you're viewing your own profile, nothing's private
+                        $hidden_fields = array();        
+                        
+                } else if ( bp_is_active( 'friends' ) &amp;&amp; friends_check_friendship( $displayed_user_id, $current_user_id ) ) {
+                        // If the current user and displayed user are friends, show all
+                        $hidden_fields = array();
+                        
+                } else {
+                        // current user is logged-in but not friends, so exclude friends-only        
+                        $hidden_levels = array( 'friends' );                        
+                        $hidden_fields = bp_xprofile_get_fields_by_privacy_levels( $displayed_user_id, $hidden_levels );
+                }
+                
+        } else {
+                // Current user is not logged in, so exclude friends-only and loggedin
+                $hidden_levels = array( 'friends', 'loggedin' );
+                $hidden_fields = bp_xprofile_get_fields_by_privacy_levels( $displayed_user_id, $hidden_levels );
+        }
+        
+        return apply_filters( 'bp_xprofile_get_hidden_fields_for_user', $hidden_fields, $displayed_user_id, $current_user_id );
+}
+
+/**
+ * Fetch an array of the xprofile fields that a given user has marked with certain privacy levels
+ *
+ * @since 1.6
+ * @see bp_xprofile_get_hidden_fields_for_user()
+ *
+ * @param int $user_id The id of the profile owner
+ * @param array $levels An array of privacy levels ('public', 'friends', 'loggedin', etc) to be
+ *    checked against
+ * @return array $field_ids The fields that match the requested privacy levels for the given user
+ */
+function bp_xprofile_get_fields_by_privacy_levels( $user_id, $levels = array() ) {
+        if ( !is_array( $levels ) ) {
+                $levels = (array)$levels;
+        }
+        
+        $user_privacy_levels = get_user_meta( $user_id, 'bp_xprofile_privacy_levels', true );
+        
+        $field_ids = array();
+        foreach( (array)$user_privacy_levels as $field_id =&gt; $field_privacy ) {
+                if ( in_array( $field_privacy, $levels ) ) {
+                        $field_ids[] = $field_id;
+                }
+        }
+        
+        // Never allow the fullname field to be excluded
+        if ( in_array( 1, $field_ids ) ) {
+                $key = array_search( 1, $field_ids );
+                unset( $field_ids[$key] );
+        }
+        
+        return $field_ids;
+}
+
+
</ins><span class="cx"> ?&gt;
</span></span></pre></div>
<a id="trunkbpxprofilebpxprofileloaderphp"></a>
<div class="modfile"><h4>Modified: trunk/bp-xprofile/bp-xprofile-loader.php (5788 => 5789)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-xprofile/bp-xprofile-loader.php        2012-02-14 19:36:19 UTC (rev 5788)
+++ trunk/bp-xprofile/bp-xprofile-loader.php        2012-02-15 20:33:09 UTC (rev 5789)
</span><span class="lines">@@ -14,6 +14,12 @@
</span><span class="cx"> if ( !defined( 'ABSPATH' ) ) exit;
</span><span class="cx"> 
</span><span class="cx"> class BP_XProfile_Component extends BP_Component {
</span><ins>+        /**
+         * The acceptable privacy levels for xprofile fields.
+         * @since 1.6
+         * @see bp_xprofile_get_privacy_levels()
+         */
+        var $privacy_levels = array();
</ins><span class="cx"> 
</span><span class="cx">         /**
</span><span class="cx">          * Start the xprofile component creation process
</span><span class="lines">@@ -82,6 +88,25 @@
</span><span class="cx">                         'multiselectbox',
</span><span class="cx">                         'datebox'
</span><span class="cx">                 ) );
</span><ins>+                
+                // Register the privacy levels. See bp_xprofile_get_privacy_levels() to filter                
+                $this-&gt;privacy_levels = array(
+                        'public'  =&gt; array(
+                                'id'        =&gt; 'public',
+                                'label' =&gt; __( 'Anyone', 'buddypress' )
+                        ),
+                        'loggedin' =&gt; array(
+                                'id'        =&gt; 'loggedin',
+                                'label' =&gt; __( 'Logged In Users', 'buddypress' )
+                        )
+                );
+                
+                if ( bp_is_active( 'friends' ) ) {
+                        $this-&gt;privacy_levels['friends'] = array(
+                                'id'        =&gt; 'friends',
+                                'label'        =&gt; __( 'My Friends', 'buddypress' )
+                        );
+                }
</ins><span class="cx"> 
</span><span class="cx">                 // Tables
</span><span class="cx">                 $global_tables = array(
</span></span></pre></div>
<a id="trunkbpxprofilebpxprofilescreensphp"></a>
<div class="modfile"><h4>Modified: trunk/bp-xprofile/bp-xprofile-screens.php (5788 => 5789)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-xprofile/bp-xprofile-screens.php        2012-02-14 19:36:19 UTC (rev 5788)
+++ trunk/bp-xprofile/bp-xprofile-screens.php        2012-02-15 20:33:09 UTC (rev 5789)
</span><span class="lines">@@ -113,6 +113,10 @@
</span><span class="cx">                                         $errors = true;
</span><span class="cx">                                 else
</span><span class="cx">                                         do_action( 'xprofile_profile_field_data_updated', $field_id, $value );
</span><ins>+                                
+                                // Save the privacy level
+                                $privacy_level = !empty( $_POST['field_' . $field_id . '_privacy'] ) ? $_POST['field_' . $field_id . '_privacy'] : 'public';
+                                xprofile_set_field_privacy_level( $field_id, bp_displayed_user_id(), $privacy_level );
</ins><span class="cx">                         }
</span><span class="cx"> 
</span><span class="cx">                         do_action( 'xprofile_updated_profile', bp_displayed_user_id(), $posted_field_ids, $errors );
</span></span></pre></div>
<a id="trunkbpxprofilebpxprofiletemplatephp"></a>
<div class="modfile"><h4>Modified: trunk/bp-xprofile/bp-xprofile-template.php (5788 => 5789)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-xprofile/bp-xprofile-template.php        2012-02-14 19:36:19 UTC (rev 5788)
+++ trunk/bp-xprofile/bp-xprofile-template.php        2012-02-15 20:33:09 UTC (rev 5789)
</span><span class="lines">@@ -24,16 +24,17 @@
</span><span class="cx">         var $in_the_loop;
</span><span class="cx">         var $user_id;
</span><span class="cx"> 
</span><del>-        function __construct( $user_id, $profile_group_id, $hide_empty_groups = false, $fetch_fields = false, $fetch_field_data = false, $exclude_groups = false, $exclude_fields = false, $hide_empty_fields = false ) {
</del><ins>+        function __construct( $user_id, $profile_group_id, $hide_empty_groups = false, $fetch_fields = false, $fetch_field_data = false, $exclude_groups = false, $exclude_fields = false, $hide_empty_fields = false, $fetch_privacy_level = false ) {
</ins><span class="cx">                 $this-&gt;groups = BP_XProfile_Group::get( array(
</span><del>-                        'profile_group_id'  =&gt; $profile_group_id,
-                        'user_id'           =&gt; $user_id,
-                        'hide_empty_groups' =&gt; $hide_empty_groups,
-                        'hide_empty_fields' =&gt; $hide_empty_fields,
-                        'fetch_fields'      =&gt; $fetch_fields,
-                        'fetch_field_data'  =&gt; $fetch_field_data,
-                        'exclude_groups'    =&gt; $exclude_groups,
-                        'exclude_fields'    =&gt; $exclude_fields
</del><ins>+                        'profile_group_id'    =&gt; $profile_group_id,
+                        'user_id'             =&gt; $user_id,
+                        'hide_empty_groups'   =&gt; $hide_empty_groups,
+                        'hide_empty_fields'   =&gt; $hide_empty_fields,
+                        'fetch_fields'        =&gt; $fetch_fields,
+                        'fetch_field_data'    =&gt; $fetch_field_data,
+                        'fetch_privacy_level' =&gt; $fetch_privacy_level,
+                        'exclude_groups'      =&gt; $exclude_groups,
+                        'exclude_fields'      =&gt; $exclude_fields
</ins><span class="cx">                 ) );
</span><span class="cx"> 
</span><span class="cx">                 $this-&gt;group_count = count($this-&gt;groups);
</span><span class="lines">@@ -156,22 +157,30 @@
</span><span class="cx">         // Only show empty fields if we're on the Dashboard, or we're on a user's profile edit page,
</span><span class="cx">         // or this is a registration page
</span><span class="cx">         $hide_empty_fields_default = ( !is_network_admin() &amp;&amp; !is_admin() &amp;&amp; !bp_is_user_profile_edit() &amp;&amp; !bp_is_register_page() );
</span><del>-
</del><ins>+        
+        // We only need to fetch privacy levels when viewing your own profile
+        if ( bp_is_my_profile() || bp_current_user_can( 'bp_moderate' ) ) {
+                $fetch_privacy_level_default = true;
+        } else {
+                $fetch_privacy_level_default = false;
+        }
+        
</ins><span class="cx">         $defaults = array(
</span><del>-                'user_id'           =&gt; bp_displayed_user_id(),
-                'profile_group_id'  =&gt; false,
-                'hide_empty_groups' =&gt; true,
-                'hide_empty_fields' =&gt; $hide_empty_fields_default,
-                'fetch_fields'      =&gt; true,
-                'fetch_field_data'  =&gt; true,
-                'exclude_groups'    =&gt; false, // Comma-separated list of profile field group IDs to exclude
-                'exclude_fields'    =&gt; false  // Comma-separated list of profile field IDs to exclude
</del><ins>+                'user_id'             =&gt; bp_displayed_user_id(),
+                'profile_group_id'    =&gt; false,
+                'hide_empty_groups'   =&gt; true,
+                'hide_empty_fields'   =&gt; $hide_empty_fields_default,
+                'fetch_fields'        =&gt; true,
+                'fetch_field_data'    =&gt; true,
+                'fetch_privacy_level' =&gt; $fetch_privacy_level_default,
+                'exclude_groups'      =&gt; false, // Comma-separated list of profile field group IDs to exclude
+                'exclude_fields'      =&gt; false  // Comma-separated list of profile field IDs to exclude
</ins><span class="cx">         );
</span><span class="cx"> 
</span><span class="cx">         $r = wp_parse_args( $args, $defaults );
</span><span class="cx">         extract( $r, EXTR_SKIP );
</span><span class="cx"> 
</span><del>-        $profile_template = new BP_XProfile_Data_Template( $user_id, $profile_group_id, $hide_empty_groups, $fetch_fields, $fetch_field_data, $exclude_groups, $exclude_fields, $hide_empty_fields );
</del><ins>+        $profile_template = new BP_XProfile_Data_Template( $user_id, $profile_group_id, $hide_empty_groups, $fetch_fields, $fetch_field_data, $exclude_groups, $exclude_fields, $hide_empty_fields, $fetch_privacy_level );
</ins><span class="cx">         return apply_filters( 'bp_has_profile', $profile_template-&gt;has_groups(), $profile_template );
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -697,6 +706,42 @@
</span><span class="cx">                 return apply_filters( 'bp_get_the_profile_field_is_required', (bool) $retval );
</span><span class="cx">         }
</span><span class="cx"> 
</span><ins>+/**
+ * Echo the privacy level of this field
+ */
+function bp_the_profile_field_privacy_level() {
+        echo bp_get_the_profile_field_privacy_level();
+}
+        /**
+         * Return the privacy level of this field
+         */
+        function bp_get_the_profile_field_privacy_level() {
+                global $field;
+                
+                $retval = !empty( $field-&gt;privacy_level ) ? $field-&gt;privacy_level : 'public';
+                
+                return apply_filters( 'bp_get_the_profile_field_privacy_level', $retval );
+        }
+
+/**
+ * Echo the privacy level label of this field
+ */
+function bp_the_profile_field_privacy_level_label() {
+        echo bp_get_the_profile_field_privacy_level_label();
+}
+        /**
+         * Return the privacy level label of this field
+         */
+        function bp_get_the_profile_field_privacy_level_label() {
+                global $field;
+                
+                $level  = !empty( $field-&gt;privacy_level ) ? $field-&gt;privacy_level : 'public';
+                $fields = bp_xprofile_get_privacy_levels();
+                
+                return apply_filters( 'bp_get_the_profile_field_privacy_level_label', $fields[$level]['label'] );
+        }
+
+
</ins><span class="cx"> function bp_unserialize_profile_field( $value ) {
</span><span class="cx">         if ( is_serialized($value) ) {
</span><span class="cx">                 $field_value = maybe_unserialize($value);
</span><span class="lines">@@ -839,4 +884,26 @@
</span><span class="cx">         ) );
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+/**
+ * Echo the field privacy radio buttons
+ */
+function bp_profile_privacy_radio_buttons() {
+        echo bp_profile_get_privacy_radio_buttons();
+}
+        /**
+         * Return the field privacy radio buttons
+         */
+        function bp_profile_get_privacy_radio_buttons() {                
+                $html = '&lt;ul class=&quot;radio&quot;&gt;';
+                
+                foreach( bp_xprofile_get_privacy_levels() as $level ) {
+                        $checked = $level['id'] == bp_get_the_profile_field_privacy_level() ? ' checked=&quot;checked&quot; ' : '';
+                        
+                        $html .= '&lt;li&gt;&lt;input type=&quot;radio&quot; name=&quot;field_' . bp_get_the_profile_field_id() . '_privacy&quot; value=&quot;' . esc_attr( $level['id'] ) . '&quot;' . $checked . '&gt; ' . esc_html( $level['label'] ) . '&lt;/li&gt;';
+                }
+                
+                $html .= '&lt;/ul&gt;';
+                
+                return apply_filters( 'bp_profile_get_privacy_radio_buttons', $html );
+        }
</ins><span class="cx"> ?&gt;
</span></span></pre>
</div>
</div>

</body>
</html>