<!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][12665] trunk: Members: When marking a user as a spammer, do not mark sites as spam if the site has more than one administrator.</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 { white-space: pre-line; 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 { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { 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" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="http://buddypress.trac.wordpress.org/changeset/12665">12665</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"http://buddypress.trac.wordpress.org/changeset/12665","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>r-a-y</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2020-06-16 04:30:46 +0000 (Tue, 16 Jun 2020)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Members: When marking a user as a spammer, do not mark sites as spam if the site has more than one administrator.

Previously on a multisite install, we would mark all the spammer's
sites as spam. This is pretty aggressive and could unintentionally
mark legitmiate sites as spam as well.

To address this, we now only mark a site as spam if the spammer is the
sole administrator of the site.

See <a href="http://buddypress.trac.wordpress.org/ticket/8316">#8316</a> (for trunk).</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcbpmembersbpmembersfunctionsphp">trunk/src/bp-members/bp-members-functions.php</a></li>
<li><a href="#trunktestsphpunittestcasesmembersfunctionsphp">trunk/tests/phpunit/testcases/members/functions.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcbpmembersbpmembersfunctionsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/bp-members/bp-members-functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/bp-members/bp-members-functions.php     2020-06-13 16:42:35 UTC (rev 12664)
+++ trunk/src/bp-members/bp-members-functions.php       2020-06-16 04:30:46 UTC (rev 12665)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -696,18 +696,49 @@
</span><span class="cx" style="display: block; padding: 0 10px">        // Force the cleanup of WordPress content and status for multisite configs.
</span><span class="cx" style="display: block; padding: 0 10px">        if ( $do_wp_cleanup ) {
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                // Get the blogs for the user.
-               $blogs = get_blogs_of_user( $user_id, true );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         // Mark blogs as spam if the user is the sole admin of a site.
+               if ( is_multisite() ) {
+                       /*
+                        * No native function to fetch a user's blogs by role, so do it manually.
+                        *
+                        * This logic is mostly copied from get_blogs_of_user().
+                        */
+                       $meta = get_user_meta( $user_id );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                foreach ( (array) array_values( $blogs ) as $details ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 foreach ( $meta as $key => $val ) {
+                               if ( 'capabilities' !== substr( $key, -12 ) ) {
+                                       continue;
+                               }
+                               if ( $wpdb->base_prefix && 0 !== strpos( $key, $wpdb->base_prefix ) ) {
+                                       continue;
+                               }
+                               $site_id = str_replace( array( $wpdb->base_prefix, '_capabilities' ), '', $key );
+                               if ( ! is_numeric( $site_id ) ) {
+                                       continue;
+                               }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        // Do not mark the main or current root blog as spam.
-                       if ( 1 == $details->userblog_id || bp_get_root_blog_id() == $details->userblog_id ) {
-                               continue;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         $site_id = (int) $site_id;
+
+                               // Do not mark the main or current root blog as spam.
+                               if ( 1 === $site_id || bp_get_root_blog_id() === $site_id ) {
+                                       continue;
+                               }
+
+                               // Now, do check for administrator role.
+                               $role = maybe_unserialize( $val );
+                               if ( empty( $role['administrator'] ) ) {
+                                       continue;
+                               }
+
+                               // Check if the site has more than 1 admin. If so, bail.
+                               $counts = count_users( 'time', $site_id );
+                               if ( empty( $counts['avail_roles']['administrator'] ) || $counts['avail_roles']['administrator'] > 1 ) {
+                                       continue;
+                               }
+
+                               // Now we can spam the blog.
+                               update_blog_status( $site_id, 'spam', $is_spam );
</ins><span class="cx" style="display: block; padding: 0 10px">                         }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-                       // Update the blog status.
-                       update_blog_status( $details->userblog_id, 'spam', $is_spam );
</del><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Finally, mark this user as a spammer.
</span></span></pre></div>
<a id="trunktestsphpunittestcasesmembersfunctionsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/testcases/members/functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/testcases/members/functions.php       2020-06-13 16:42:35 UTC (rev 12664)
+++ trunk/tests/phpunit/testcases/members/functions.php 2020-06-16 04:30:46 UTC (rev 12665)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -539,6 +539,31 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        /**
+        * @group bp_core_process_spammer_status
+        * @ticket BP8316
+        */
+       public function test_bp_core_process_spammer_status_ms_should_only_spam_sites_with_one_admin() {
+               if ( ! is_multisite() ) {
+                       $this->markTestSkipped();
+               }
+
+               $u1 = self::factory()->user->create();
+               $u2 = self::factory()->user->create();
+
+               $b1 = self::factory()->blog->create( array( 'user_id' => $u1 ) );
+
+               // Add user 2 to site as administrator.
+               add_user_to_blog( $b1, $u2, 'administrator' );
+
+               // Mark user 2 as a spammer.
+               bp_core_process_spammer_status( $u2, 'spam' );
+
+               // Ensure site isn't marked as spam because there is more than one admin.
+               $site = get_site( $b1 );
+               $this->assertEmpty( $site->spam );
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         public function notification_filter_callback( $value ) {
</span><span class="cx" style="display: block; padding: 0 10px">                $this->filter_fired = current_filter();
</span><span class="cx" style="display: block; padding: 0 10px">                return $value;
</span></span></pre>
</div>
</div>

</body>
</html>