<!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>[42836] trunk: Multisite: Introduce metadata for sites.</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 { 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="https://core.trac.wordpress.org/changeset/42836">42836</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/42836","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>flixos90</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2018-03-16 02:14:04 +0000 (Fri, 16 Mar 2018)</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'>Multisite: Introduce metadata for sites.

A new global multisite table `wp_blogmeta` is added to the database schema, and a set of `*_site_meta()` API functions are introduced.

The implementation fails gracefully when the new table is not yet available, which may happen especially shortly after the core update, before the network has been upgraded to the new database schema. The presence of the table is detected once and stored as a global setting on the main network.

Core does not yet use site metadata, but there are several use-cases to be implemented or explored in the near future, and it allows plugins to extend sites with arbitrary data, which will come in particularly handy with the upcoming REST API endpoint for sites.

Props spacedmonkey, johnjamesjacoby, jeremyfelt, flixos90.
Fixes <a href="https://core.trac.wordpress.org/ticket/37923">#37923</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpadminincludesmsphp">trunk/src/wp-admin/includes/ms.php</a></li>
<li><a href="#trunksrcwpadminincludesschemaphp">trunk/src/wp-admin/includes/schema.php</a></li>
<li><a href="#trunksrcwpadminincludesupgradephp">trunk/src/wp-admin/includes/upgrade.php</a></li>
<li><a href="#trunksrcwpincludesclasswpsitequeryphp">trunk/src/wp-includes/class-wp-site-query.php</a></li>
<li><a href="#trunksrcwpincludesfunctionsphp">trunk/src/wp-includes/functions.php</a></li>
<li><a href="#trunksrcwpincludesloadphp">trunk/src/wp-includes/load.php</a></li>
<li><a href="#trunksrcwpincludesmsblogsphp">trunk/src/wp-includes/ms-blogs.php</a></li>
<li><a href="#trunksrcwpincludesversionphp">trunk/src/wp-includes/version.php</a></li>
<li><a href="#trunksrcwpincludeswpdbphp">trunk/src/wp-includes/wp-db.php</a></li>
<li><a href="#trunktestsphpunitincludestestcasephp">trunk/tests/phpunit/includes/testcase.php</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunktestsphpunittestsmultisitesiteMetaphp">trunk/tests/phpunit/tests/multisite/siteMeta.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpadminincludesmsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/includes/ms.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/ms.php        2018-03-16 02:00:34 UTC (rev 42835)
+++ trunk/src/wp-admin/includes/ms.php  2018-03-16 02:14:04 UTC (rev 42836)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -135,6 +135,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $wpdb->query( "DROP TABLE IF EXISTS `$table`" );
</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">+                if ( is_site_meta_supported() ) {
+                       $blog_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->blogmeta WHERE blog_id = %d ", $blog_id ) );
+                       foreach ( $blog_meta_ids as $mid ) {
+                               delete_metadata_by_mid( 'blog', $mid );
+                       }
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 $wpdb->delete( $wpdb->blogs, array( 'blog_id' => $blog_id ) );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                /**
</span></span></pre></div>
<a id="trunksrcwpadminincludesschemaphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/includes/schema.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/schema.php    2018-03-16 02:00:34 UTC (rev 42835)
+++ trunk/src/wp-admin/includes/schema.php      2018-03-16 02:14:04 UTC (rev 42836)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -267,6 +267,15 @@
</span><span class="cx" style="display: block; padding: 0 10px">   PRIMARY KEY  (blog_id),
</span><span class="cx" style="display: block; padding: 0 10px">   KEY db_version (db_version)
</span><span class="cx" style="display: block; padding: 0 10px"> ) $charset_collate;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+CREATE TABLE $wpdb->blogmeta (
+  meta_id bigint(20) unsigned NOT NULL auto_increment,
+  blog_id bigint(20) NOT NULL default '0',
+  meta_key varchar(255) default NULL,
+  meta_value longtext,
+  PRIMARY KEY  (meta_id),
+  KEY meta_key (meta_key($max_index_length)),
+  KEY blog_id (blog_id)
+) $charset_collate;
</ins><span class="cx" style="display: block; padding: 0 10px"> CREATE TABLE $wpdb->registration_log (
</span><span class="cx" style="display: block; padding: 0 10px">   ID bigint(20) NOT NULL auto_increment,
</span><span class="cx" style="display: block; padding: 0 10px">   email varchar(255) NOT NULL default '',
</span></span></pre></div>
<a id="trunksrcwpadminincludesupgradephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/includes/upgrade.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/upgrade.php   2018-03-16 02:00:34 UTC (rev 42835)
+++ trunk/src/wp-admin/includes/upgrade.php     2018-03-16 02:14:04 UTC (rev 42836)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2133,6 +2133,13 @@
</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">+
+       // 5.0
+       if ( $wp_current_db_version < 42836 ) {
+               $network_id = get_main_network_id();
+               delete_network_option( $network_id, 'site_meta_supported' );
+               is_site_meta_supported();
+       }
</ins><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></span></pre></div>
<a id="trunksrcwpincludesclasswpsitequeryphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/class-wp-site-query.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-site-query.php     2018-03-16 02:00:34 UTC (rev 42835)
+++ trunk/src/wp-includes/class-wp-site-query.php       2018-03-16 02:14:04 UTC (rev 42836)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -92,86 +92,89 @@
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.6.0
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.8.0 Introduced the 'lang_id', 'lang__in', and 'lang__not_in' parameters.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 5.0.0 Introduced the 'update_site_meta_cache' parameter.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @param string|array $query {
</span><span class="cx" style="display: block; padding: 0 10px">         *     Optional. Array or query string of site query parameters. Default empty.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         *     @type array        $site__in          Array of site IDs to include. Default empty.
-        *     @type array        $site__not_in      Array of site IDs to exclude. Default empty.
-        *     @type bool         $count             Whether to return a site count (true) or array of site objects.
-        *                                           Default false.
-        *     @type array        $date_query        Date query clauses to limit sites by. See WP_Date_Query.
-        *                                           Default null.
-        *     @type string       $fields            Site fields to return. Accepts 'ids' (returns an array of site IDs)
-        *                                           or empty (returns an array of complete site objects). Default empty.
-        *     @type int          $ID                A site ID to only return that site. Default empty.
-        *     @type int          $number            Maximum number of sites to retrieve. Default 100.
-        *     @type int          $offset            Number of sites to offset the query. Used to build LIMIT clause.
-        *                                           Default 0.
-        *     @type bool         $no_found_rows     Whether to disable the `SQL_CALC_FOUND_ROWS` query. Default true.
-        *     @type string|array $orderby           Site status or array of statuses. Accepts 'id', 'domain', 'path',
-        *                                           'network_id', 'last_updated', 'registered', 'domain_length',
-        *                                           'path_length', 'site__in' and 'network__in'. Also accepts false,
-        *                                           an empty array, or 'none' to disable `ORDER BY` clause.
-        *                                           Default 'id'.
-        *     @type string       $order             How to order retrieved sites. Accepts 'ASC', 'DESC'. Default 'ASC'.
-        *     @type int          $network_id        Limit results to those affiliated with a given network ID. If 0,
-        *                                           include all networks. Default 0.
-        *     @type array        $network__in       Array of network IDs to include affiliated sites for. Default empty.
-        *     @type array        $network__not_in   Array of network IDs to exclude affiliated sites for. Default empty.
-        *     @type string       $domain            Limit results to those affiliated with a given domain. Default empty.
-        *     @type array        $domain__in        Array of domains to include affiliated sites for. Default empty.
-        *     @type array        $domain__not_in    Array of domains to exclude affiliated sites for. Default empty.
-        *     @type string       $path              Limit results to those affiliated with a given path. Default empty.
-        *     @type array        $path__in          Array of paths to include affiliated sites for. Default empty.
-        *     @type array        $path__not_in      Array of paths to exclude affiliated sites for. Default empty.
-        *     @type int          $public            Limit results to public sites. Accepts '1' or '0'. Default empty.
-        *     @type int          $archived          Limit results to archived sites. Accepts '1' or '0'. Default empty.
-        *     @type int          $mature            Limit results to mature sites. Accepts '1' or '0'. Default empty.
-        *     @type int          $spam              Limit results to spam sites. Accepts '1' or '0'. Default empty.
-        *     @type int          $deleted           Limit results to deleted sites. Accepts '1' or '0'. Default empty.
-        *     @type int          $lang_id           Limit results to a language ID. Default empty.
-        *     @type array        $lang__in          Array of language IDs to include affiliated sites for. Default empty.
-        *     @type array        $lang__not_in      Array of language IDs to exclude affiliated sites for. Default empty.
-        *     @type string       $search            Search term(s) to retrieve matching sites for. Default empty.
-        *     @type array        $search_columns    Array of column names to be searched. Accepts 'domain' and 'path'.
-        *                                           Default empty array.
-        *     @type bool         $update_site_cache Whether to prime the cache for found sites. Default true.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  *     @type array        $site__in               Array of site IDs to include. Default empty.
+        *     @type array        $site__not_in           Array of site IDs to exclude. Default empty.
+        *     @type bool         $count                  Whether to return a site count (true) or array of site objects.
+        *                                                Default false.
+        *     @type array        $date_query             Date query clauses to limit sites by. See WP_Date_Query.
+        *                                                Default null.
+        *     @type string       $fields                 Site fields to return. Accepts 'ids' (returns an array of site IDs)
+        *                                                or empty (returns an array of complete site objects). Default empty.
+        *     @type int          $ID                     A site ID to only return that site. Default empty.
+        *     @type int          $number                 Maximum number of sites to retrieve. Default 100.
+        *     @type int          $offset                 Number of sites to offset the query. Used to build LIMIT clause.
+        *                                                Default 0.
+        *     @type bool         $no_found_rows          Whether to disable the `SQL_CALC_FOUND_ROWS` query. Default true.
+        *     @type string|array $orderby                Site status or array of statuses. Accepts 'id', 'domain', 'path',
+        *                                                'network_id', 'last_updated', 'registered', 'domain_length',
+        *                                                'path_length', 'site__in' and 'network__in'. Also accepts false,
+        *                                                an empty array, or 'none' to disable `ORDER BY` clause.
+        *                                                Default 'id'.
+        *     @type string       $order                  How to order retrieved sites. Accepts 'ASC', 'DESC'. Default 'ASC'.
+        *     @type int          $network_id             Limit results to those affiliated with a given network ID. If 0,
+        *                                                include all networks. Default 0.
+        *     @type array        $network__in            Array of network IDs to include affiliated sites for. Default empty.
+        *     @type array        $network__not_in        Array of network IDs to exclude affiliated sites for. Default empty.
+        *     @type string       $domain                 Limit results to those affiliated with a given domain. Default empty.
+        *     @type array        $domain__in             Array of domains to include affiliated sites for. Default empty.
+        *     @type array        $domain__not_in         Array of domains to exclude affiliated sites for. Default empty.
+        *     @type string       $path                   Limit results to those affiliated with a given path. Default empty.
+        *     @type array        $path__in               Array of paths to include affiliated sites for. Default empty.
+        *     @type array        $path__not_in           Array of paths to exclude affiliated sites for. Default empty.
+        *     @type int          $public                 Limit results to public sites. Accepts '1' or '0'. Default empty.
+        *     @type int          $archived               Limit results to archived sites. Accepts '1' or '0'. Default empty.
+        *     @type int          $mature                 Limit results to mature sites. Accepts '1' or '0'. Default empty.
+        *     @type int          $spam                   Limit results to spam sites. Accepts '1' or '0'. Default empty.
+        *     @type int          $deleted                Limit results to deleted sites. Accepts '1' or '0'. Default empty.
+        *     @type int          $lang_id                Limit results to a language ID. Default empty.
+        *     @type array        $lang__in               Array of language IDs to include affiliated sites for. Default empty.
+        *     @type array        $lang__not_in           Array of language IDs to exclude affiliated sites for. Default empty.
+        *     @type string       $search                 Search term(s) to retrieve matching sites for. Default empty.
+        *     @type array        $search_columns         Array of column names to be searched. Accepts 'domain' and 'path'.
+        *                                                Default empty array.
+        *     @type bool         $update_site_cache      Whether to prime the cache for found sites. Default true.
+        *     @type bool         $update_site_meta_cache Whether to prime the metadata cache for found sites. Default true.
</ins><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">        public function __construct( $query = '' ) {
</span><span class="cx" style="display: block; padding: 0 10px">                $this->query_var_defaults = array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'fields'            => '',
-                       'ID'                => '',
-                       'site__in'          => '',
-                       'site__not_in'      => '',
-                       'number'            => 100,
-                       'offset'            => '',
-                       'no_found_rows'     => true,
-                       'orderby'           => 'id',
-                       'order'             => 'ASC',
-                       'network_id'        => 0,
-                       'network__in'       => '',
-                       'network__not_in'   => '',
-                       'domain'            => '',
-                       'domain__in'        => '',
-                       'domain__not_in'    => '',
-                       'path'              => '',
-                       'path__in'          => '',
-                       'path__not_in'      => '',
-                       'public'            => null,
-                       'archived'          => null,
-                       'mature'            => null,
-                       'spam'              => null,
-                       'deleted'           => null,
-                       'lang_id'           => null,
-                       'lang__in'          => '',
-                       'lang__not_in'      => '',
-                       'search'            => '',
-                       'search_columns'    => array(),
-                       'count'             => false,
-                       'date_query'        => null, // See WP_Date_Query
-                       'update_site_cache' => true,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'fields'                 => '',
+                       'ID'                     => '',
+                       'site__in'               => '',
+                       'site__not_in'           => '',
+                       'number'                 => 100,
+                       'offset'                 => '',
+                       'no_found_rows'          => true,
+                       'orderby'                => 'id',
+                       'order'                  => 'ASC',
+                       'network_id'             => 0,
+                       'network__in'            => '',
+                       'network__not_in'        => '',
+                       'domain'                 => '',
+                       'domain__in'             => '',
+                       'domain__not_in'         => '',
+                       'path'                   => '',
+                       'path__in'               => '',
+                       'path__not_in'           => '',
+                       'public'                 => null,
+                       'archived'               => null,
+                       'mature'                 => null,
+                       'spam'                   => null,
+                       'deleted'                => null,
+                       'lang_id'                => null,
+                       'lang__in'               => '',
+                       'lang__not_in'           => '',
+                       'search'                 => '',
+                       'search_columns'         => array(),
+                       'count'                  => false,
+                       'date_query'             => null, // See WP_Date_Query
+                       'update_site_cache'      => true,
+                       'update_site_meta_cache' => true,
</ins><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">                if ( ! empty( $query ) ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -288,7 +291,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Prime site network caches.
</span><span class="cx" style="display: block; padding: 0 10px">                if ( $this->query_vars['update_site_cache'] ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        _prime_site_caches( $site_ids );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 _prime_site_caches( $site_ids, $this->query_vars['update_site_meta_cache'] );
</ins><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">                // Fetch full site objects from the primed cache.
</span></span></pre></div>
<a id="trunksrcwpincludesfunctionsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/functions.php       2018-03-16 02:00:34 UTC (rev 42835)
+++ trunk/src/wp-includes/functions.php 2018-03-16 02:14:04 UTC (rev 42836)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4727,6 +4727,38 @@
</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">+ * Determines whether site meta is enabled.
+ *
+ * This function checks whether the 'blogmeta' database table exists. The result is saved as
+ * a setting for the main network, making it essentially a global setting. Subsequent requests
+ * will refer to this setting instead of running the query.
+ *
+ * @since 5.0.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @return bool True if site meta is supported, false otherwise.
+ */
+function is_site_meta_supported() {
+       global $wpdb;
+
+       if ( ! is_multisite() ) {
+               return false;
+       }
+
+       $network_id = get_main_network_id();
+
+       $supported = get_network_option( $network_id, 'site_meta_supported', false );
+       if ( false === $supported ) {
+               $supported = $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->blogmeta}'" ) ? 1 : 0;
+
+               update_network_option( $network_id, 'site_meta_supported', $supported );
+       }
+
+       return (bool) $supported;
+}
+
+/**
</ins><span class="cx" style="display: block; padding: 0 10px">  * gmt_offset modification for smart timezone handling.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * Overrides the gmt_offset option if we have a timezone_string available.
</span></span></pre></div>
<a id="trunksrcwpincludesloadphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/load.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/load.php    2018-03-16 02:00:34 UTC (rev 42835)
+++ trunk/src/wp-includes/load.php      2018-03-16 02:14:04 UTC (rev 42836)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -581,7 +581,7 @@
</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">        if ( function_exists( 'wp_cache_add_global_groups' ) ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                wp_cache_add_global_groups( array( 'users', 'userlogins', 'usermeta', 'user_meta', 'useremail', 'userslugs', 'site-transient', 'site-options', 'blog-lookup', 'blog-details', 'site-details', 'rss', 'global-posts', 'blog-id-cache', 'networks', 'sites' ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         wp_cache_add_global_groups( array( 'users', 'userlogins', 'usermeta', 'user_meta', 'useremail', 'userslugs', 'site-transient', 'site-options', 'blog-lookup', 'blog-details', 'site-details', 'rss', 'global-posts', 'blog-id-cache', 'networks', 'sites', 'blog_meta' ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                 wp_cache_add_non_persistent_groups( array( 'counts', 'plugins' ) );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre></div>
<a id="trunksrcwpincludesmsblogsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/ms-blogs.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/ms-blogs.php        2018-03-16 02:00:34 UTC (rev 42835)
+++ trunk/src/wp-includes/ms-blogs.php  2018-03-16 02:14:04 UTC (rev 42836)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -474,6 +474,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        wp_cache_delete( $domain_path_key, 'blog-id-cache' );
</span><span class="cx" style="display: block; padding: 0 10px">        wp_cache_delete( 'current_blog_' . $blog->domain, 'site-options' );
</span><span class="cx" style="display: block; padding: 0 10px">        wp_cache_delete( 'current_blog_' . $blog->domain . $blog->path, 'site-options' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        wp_cache_delete( $blog_id, 'blog_meta' );
</ins><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">         * Fires immediately after a site has been removed from the object cache.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -560,21 +561,23 @@
</span><span class="cx" style="display: block; padding: 0 10px">  * Adds any sites from the given ids to the cache that do not already exist in cache.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @since 4.6.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @since 5.0.0 Introduced the `$update_meta_cache` parameter.
</ins><span class="cx" style="display: block; padding: 0 10px">  * @access private
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @see update_site_cache()
</span><span class="cx" style="display: block; padding: 0 10px">  * @global wpdb $wpdb WordPress database abstraction object.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @param array $ids ID list.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @param array $ids               ID list.
+ * @param bool  $update_meta_cache Optional. Whether to update the meta cache. Default true.
</ins><span class="cx" style="display: block; padding: 0 10px">  */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-function _prime_site_caches( $ids ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+function _prime_site_caches( $ids, $update_meta_cache = true ) {
</ins><span class="cx" style="display: block; padding: 0 10px">         global $wpdb;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        $non_cached_ids = _get_non_cached_ids( $ids, 'sites' );
</span><span class="cx" style="display: block; padding: 0 10px">        if ( ! empty( $non_cached_ids ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                $fresh_sites = $wpdb->get_results( sprintf( "SELECT * FROM $wpdb->blogs WHERE blog_id IN (%s)", join( ',', array_map( 'intval', $non_cached_ids ) ) ) );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                update_site_cache( $fresh_sites );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         update_site_cache( $fresh_sites, $update_meta_cache );
</ins><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><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -582,21 +585,47 @@
</span><span class="cx" style="display: block; padding: 0 10px">  * Updates sites in cache.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @since 4.6.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @since 5.0.0 Introduced the `$update_meta_cache` parameter.
</ins><span class="cx" style="display: block; padding: 0 10px">  *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @param array $sites Array of site objects.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @param array $sites             Array of site objects.
+ * @param bool  $update_meta_cache Whether to update site meta cache. Default true.
</ins><span class="cx" style="display: block; padding: 0 10px">  */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-function update_site_cache( $sites ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+function update_site_cache( $sites, $update_meta_cache = true ) {
</ins><span class="cx" style="display: block; padding: 0 10px">         if ( ! $sites ) {
</span><span class="cx" style="display: block; padding: 0 10px">                return;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $site_ids = array();
</ins><span class="cx" style="display: block; padding: 0 10px">         foreach ( $sites as $site ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $site_ids[] = $site->blog_id;
</ins><span class="cx" style="display: block; padding: 0 10px">                 wp_cache_add( $site->blog_id, $site, 'sites' );
</span><span class="cx" style="display: block; padding: 0 10px">                wp_cache_add( $site->blog_id . 'short', $site, 'blog-details' );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       if ( $update_meta_cache ) {
+               update_sitemeta_cache( $site_ids );
+       }
</ins><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">+ * Updates metadata cache for list of site IDs.
+ *
+ * Performs SQL query to retrieve all metadata for the sites matching `$site_ids` and stores them in the cache.
+ * Subsequent calls to `get_site_meta()` will not need to query the database.
+ *
+ * @since 5.0.0
+ *
+ * @param array $site_ids List of site IDs.
+ * @return array|false Returns false if there is nothing to update. Returns an array of metadata on success.
+ */
+function update_sitemeta_cache( $site_ids ) {
+       if ( ! is_site_meta_supported() ) {
+               return false;
+       }
+
+       return update_meta_cache( 'blog', $site_ids );
+}
+
+/**
</ins><span class="cx" style="display: block; padding: 0 10px">  * Retrieves a list of sites matching requested arguments.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @since 4.6.0
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -797,6 +826,154 @@
</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">+ * Adds metadata to a site.
+ *
+ * @since 5.0.0
+ *
+ * @param int    $site_id    Site ID.
+ * @param string $meta_key   Metadata name.
+ * @param mixed  $meta_value Metadata value. Must be serializable if non-scalar.
+ * @param bool   $unique     Optional. Whether the same key should not be added.
+ *                           Default false.
+ * @return int|false Meta ID on success, false on failure.
+ */
+function add_site_meta( $site_id, $meta_key, $meta_value, $unique = false ) {
+       // Bail if site meta table is not installed.
+       if ( ! is_site_meta_supported() ) {
+               /* translators: %s: database table name */
+               _doing_it_wrong( __FUNCTION__, sprintf( __( 'The %s table is not installed. Please run the network database upgrade.' ), $GLOBALS['wpdb']->blogmeta ), '5.0.0' );
+               return false;
+       }
+
+       $added = add_metadata( 'blog', $site_id, $meta_key, $meta_value, $unique );
+
+       // Bust site query cache.
+       if ( $added ) {
+               wp_cache_set( 'last_changed', microtime(), 'sites' );
+       }
+
+       return $added;
+}
+
+/**
+ * Removes metadata matching criteria from a site.
+ *
+ * You can match based on the key, or key and value. Removing based on key and
+ * value, will keep from removing duplicate metadata with the same key. It also
+ * allows removing all metadata matching key, if needed.
+ *
+ * @since 5.0.0
+ *
+ * @param int    $site_id    Site ID.
+ * @param string $meta_key   Metadata name.
+ * @param mixed  $meta_value Optional. Metadata value. Must be serializable if
+ *                           non-scalar. Default empty.
+ * @return bool True on success, false on failure.
+ */
+function delete_site_meta( $site_id, $meta_key, $meta_value = '' ) {
+       // Bail if site meta table is not installed.
+       if ( ! is_site_meta_supported() ) {
+               /* translators: %s: database table name */
+               _doing_it_wrong( __FUNCTION__, sprintf( __( 'The %s table is not installed. Please run the network database upgrade.' ), $GLOBALS['wpdb']->blogmeta ), '5.0.0' );
+               return false;
+       }
+
+       $deleted = delete_metadata( 'blog', $site_id, $meta_key, $meta_value );
+
+       // Bust site query cache.
+       if ( $deleted ) {
+               wp_cache_set( 'last_changed', microtime(), 'sites' );
+       }
+
+       return $deleted;
+}
+
+/**
+ * Retrieves metadata for a site.
+ *
+ * @since 5.0.0
+ *
+ * @param int    $site_id Site ID.
+ * @param string $key     Optional. The meta key to retrieve. By default, returns
+ *                        data for all keys. Default empty.
+ * @param bool   $single  Optional. Whether to return a single value. Default false.
+ * @return mixed Will be an array if $single is false. Will be value of meta data
+ *               field if $single is true.
+ */
+function get_site_meta( $site_id, $key = '', $single = false ) {
+       // Bail if site meta table is not installed.
+       if ( ! is_site_meta_supported() ) {
+               /* translators: %s: database table name */
+               _doing_it_wrong( __FUNCTION__, sprintf( __( 'The %s table is not installed. Please run the network database upgrade.' ), $GLOBALS['wpdb']->blogmeta ), '5.0.0' );
+               return false;
+       }
+
+       return get_metadata( 'blog', $site_id, $key, $single );
+}
+
+/**
+ * Updates metadata for a site.
+ *
+ * Use the $prev_value parameter to differentiate between meta fields with the
+ * same key and site ID.
+ *
+ * If the meta field for the site does not exist, it will be added.
+ *
+ * @since 5.0.0
+ *
+ * @param int    $site_id    Site ID.
+ * @param string $meta_key   Metadata key.
+ * @param mixed  $meta_value Metadata value. Must be serializable if non-scalar.
+ * @param mixed  $prev_value Optional. Previous value to check before removing.
+ *                           Default empty.
+ * @return int|bool Meta ID if the key didn't exist, true on successful update,
+ *                  false on failure.
+ */
+function update_site_meta( $site_id, $meta_key, $meta_value, $prev_value = '' ) {
+       // Bail if site meta table is not installed.
+       if ( ! is_site_meta_supported() ) {
+               /* translators: %s: database table name */
+               _doing_it_wrong( __FUNCTION__, sprintf( __( 'The %s table is not installed. Please run the network database upgrade.' ), $GLOBALS['wpdb']->blogmeta ), '5.0.0' );
+               return false;
+       }
+
+       $updated = update_metadata( 'blog', $site_id, $meta_key, $meta_value, $prev_value );
+
+       // Bust site query cache.
+       if ( $updated ) {
+               wp_cache_set( 'last_changed', microtime(), 'sites' );
+       }
+
+       return $updated;
+}
+
+/**
+ * Deletes everything from site meta matching meta key.
+ *
+ * @since 5.0.0
+ *
+ * @param string $meta_key Metadata key to search for when deleting.
+ * @return bool Whether the site meta key was deleted from the database.
+ */
+function delete_site_meta_by_key( $meta_key ) {
+       // Bail if site meta table is not installed.
+       if ( ! is_site_meta_supported() ) {
+               /* translators: %s: database table name */
+               _doing_it_wrong( __FUNCTION__, sprintf( __( 'The %s table is not installed. Please run the network database upgrade.' ), $GLOBALS['wpdb']->blogmeta ), '5.0.0' );
+               return false;
+       }
+
+       $deleted = delete_metadata( 'blog', null, $meta_key, '', true );
+
+       // Bust site query cache.
+       if ( $deleted ) {
+               wp_cache_set( 'last_changed', microtime(), 'sites' );
+       }
+
+       return $deleted;
+}
+
+/**
</ins><span class="cx" style="display: block; padding: 0 10px">  * Switch the current blog.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * This function is useful if you need to pull posts, or other information,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -869,7 +1046,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( is_array( $global_groups ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                wp_cache_add_global_groups( $global_groups );
</span><span class="cx" style="display: block; padding: 0 10px">                        } else {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                wp_cache_add_global_groups( array( 'users', 'userlogins', 'usermeta', 'user_meta', 'useremail', 'userslugs', 'site-transient', 'site-options', 'blog-lookup', 'blog-details', 'rss', 'global-posts', 'blog-id-cache', 'networks', 'sites', 'site-details' ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         wp_cache_add_global_groups( array( 'users', 'userlogins', 'usermeta', 'user_meta', 'useremail', 'userslugs', 'site-transient', 'site-options', 'blog-lookup', 'blog-details', 'rss', 'global-posts', 'blog-id-cache', 'networks', 'sites', 'site-details', 'blog_meta' ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                         }
</span><span class="cx" style="display: block; padding: 0 10px">                        wp_cache_add_non_persistent_groups( array( 'counts', 'plugins' ) );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -937,7 +1114,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( is_array( $global_groups ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                wp_cache_add_global_groups( $global_groups );
</span><span class="cx" style="display: block; padding: 0 10px">                        } else {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                wp_cache_add_global_groups( array( 'users', 'userlogins', 'usermeta', 'user_meta', 'useremail', 'userslugs', 'site-transient', 'site-options', 'blog-lookup', 'blog-details', 'rss', 'global-posts', 'blog-id-cache', 'networks', 'sites', 'site-details' ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         wp_cache_add_global_groups( array( 'users', 'userlogins', 'usermeta', 'user_meta', 'useremail', 'userslugs', 'site-transient', 'site-options', 'blog-lookup', 'blog-details', 'rss', 'global-posts', 'blog-id-cache', 'networks', 'sites', 'site-details', 'blog_meta' ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                         }
</span><span class="cx" style="display: block; padding: 0 10px">                        wp_cache_add_non_persistent_groups( array( 'counts', 'plugins' ) );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span></span></pre></div>
<a id="trunksrcwpincludesversionphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/version.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/version.php 2018-03-16 02:00:34 UTC (rev 42835)
+++ trunk/src/wp-includes/version.php   2018-03-16 02:14:04 UTC (rev 42836)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -11,7 +11,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @global int $wp_db_version
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-$wp_db_version = 38590;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+$wp_db_version = 42836;
</ins><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">  * Holds the TinyMCE version
</span></span></pre></div>
<a id="trunksrcwpincludeswpdbphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/wp-db.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/wp-db.php   2018-03-16 02:00:34 UTC (rev 42835)
+++ trunk/src/wp-includes/wp-db.php     2018-03-16 02:14:04 UTC (rev 42836)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -297,6 +297,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        var $ms_global_tables = array(
</span><span class="cx" style="display: block; padding: 0 10px">                'blogs',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                'blogmeta',
</ins><span class="cx" style="display: block; padding: 0 10px">                 'signups',
</span><span class="cx" style="display: block; padding: 0 10px">                'site',
</span><span class="cx" style="display: block; padding: 0 10px">                'sitemeta',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -414,6 +415,14 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public $blogs;
</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">+         * Multisite Blog Metadata table
+        *
+        * @since 5.0.0
+        * @var string
+        */
+       public $blogmeta;
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Multisite Blog Versions table
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 3.0.0
</span></span></pre></div>
<a id="trunktestsphpunitincludestestcasephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/includes/testcase.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/includes/testcase.php 2018-03-16 02:00:34 UTC (rev 42835)
+++ trunk/tests/phpunit/includes/testcase.php   2018-03-16 02:14:04 UTC (rev 42836)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -328,7 +328,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $wp_object_cache->__remoteset();
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px">                wp_cache_flush();
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                wp_cache_add_global_groups( array( 'users', 'userlogins', 'usermeta', 'user_meta', 'useremail', 'userslugs', 'site-transient', 'site-options', 'blog-lookup', 'blog-details', 'rss', 'global-posts', 'blog-id-cache', 'networks', 'sites', 'site-details' ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         wp_cache_add_global_groups( array( 'users', 'userlogins', 'usermeta', 'user_meta', 'useremail', 'userslugs', 'site-transient', 'site-options', 'blog-lookup', 'blog-details', 'rss', 'global-posts', 'blog-id-cache', 'networks', 'sites', 'site-details', 'blog_meta' ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                 wp_cache_add_non_persistent_groups( array( 'comment', 'counts', 'plugins' ) );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre></div>
<a id="trunktestsphpunittestsmultisitesiteMetaphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/phpunit/tests/multisite/siteMeta.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/multisite/siteMeta.php                          (rev 0)
+++ trunk/tests/phpunit/tests/multisite/siteMeta.php    2018-03-16 02:14:04 UTC (rev 42836)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,261 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+if ( is_multisite() ) :
+
+/**
+ * @group ms-site
+ * @group multisite
+ * @group meta
+ * @ticket 37923
+ */
+class Tests_Multisite_Site_Meta extends WP_UnitTestCase {
+       protected static $site_id;
+       protected static $site_id2;
+       protected static $flag_was_set;
+
+       public static function wpSetUpBeforeClass( $factory ) {
+               self::$site_id = $factory->blog->create( array( 'domain' => 'wordpress.org', 'path' => '/' ) );
+               self::$site_id2 = $factory->blog->create( array( 'domain' => 'wordpress.org', 'path' => '/foo/' ) );
+
+               // Populate the main network flag as necessary.
+               self::$flag_was_set = true;
+               if ( false === get_network_option( get_main_network_id(), 'site_meta_supported', false ) ) {
+                       self::$flag_was_set = false;
+                       is_site_meta_supported();
+               }
+       }
+
+       public static function wpTearDownAfterClass() {
+               // Delete the possibly previously populated main network flag.
+               if ( ! self::$flag_was_set ) {
+                       delete_network_option( get_main_network_id(), 'site_meta_supported' );
+               }
+
+               wpmu_delete_blog( self::$site_id, true );
+               wpmu_delete_blog( self::$site_id2, true );
+
+               wp_update_network_site_counts();
+       }
+
+       public function test_is_site_meta_supported() {
+               $this->assertTrue( is_site_meta_supported() );
+       }
+
+       public function test_is_site_meta_supported_filtered() {
+               add_filter( 'pre_site_option_site_meta_supported', '__return_zero' );
+               $this->assertFalse( is_site_meta_supported() );
+       }
+
+       public function test_add() {
+               if ( ! is_site_meta_supported() ) {
+                       $this->markTestSkipped( 'Tests only runs with the blogmeta database table installed' );
+               }
+
+               $this->assertNotEmpty( add_site_meta( self::$site_id, 'foo', 'bar' ) );
+               $this->assertSame( 'bar', get_site_meta( self::$site_id, 'foo', true ) );
+       }
+
+       public function test_add_unique() {
+               if ( ! is_site_meta_supported() ) {
+                       $this->markTestSkipped( 'Tests only runs with the blogmeta database table installed' );
+               }
+
+               $this->assertNotEmpty( add_site_meta( self::$site_id, 'foo', 'bar' ) );
+               $this->assertFalse( add_site_meta( self::$site_id, 'foo', 'bar', true ) );
+       }
+
+       public function test_delete() {
+               if ( ! is_site_meta_supported() ) {
+                       $this->markTestSkipped( 'Tests only runs with the blogmeta database table installed' );
+               }
+
+               add_site_meta( self::$site_id, 'foo', 'bar' );
+
+               $this->assertTrue( delete_site_meta( self::$site_id, 'foo' ) );
+               $this->assertEmpty( get_site_meta( self::$site_id, 'foo', true ) );
+       }
+
+       public function test_delete_with_invalid_meta_key_should_return_false() {
+               if ( ! is_site_meta_supported() ) {
+                       $this->markTestSkipped( 'Tests only runs with the blogmeta database table installed' );
+               }
+
+               $this->assertFalse( delete_site_meta( self::$site_id, 'foo' ) );
+       }
+
+       public function test_delete_should_respect_meta_value() {
+               if ( ! is_site_meta_supported() ) {
+                       $this->markTestSkipped( 'Tests only runs with the blogmeta database table installed' );
+               }
+
+               add_site_meta( self::$site_id, 'foo', 'bar' );
+               add_site_meta( self::$site_id, 'foo', 'baz' );
+
+               $this->assertTrue( delete_site_meta( self::$site_id, 'foo', 'bar' ) );
+
+               $metas = get_site_meta( self::$site_id, 'foo' );
+               $this->assertSame( array( 'baz' ), $metas );
+       }
+
+       public function test_get_with_no_key_should_fetch_all_keys() {
+               if ( ! is_site_meta_supported() ) {
+                       $this->markTestSkipped( 'Tests only runs with the blogmeta database table installed' );
+               }
+
+               add_site_meta( self::$site_id, 'foo', 'bar' );
+               add_site_meta( self::$site_id, 'foo1', 'baz' );
+
+               $found = get_site_meta( self::$site_id );
+               $expected = array(
+                       'foo'  => array( 'bar' ),
+                       'foo1' => array( 'baz' ),
+               );
+
+               $this->assertEqualSets( $expected, $found );
+       }
+
+       public function test_get_with_key_should_fetch_all_for_key() {
+               if ( ! is_site_meta_supported() ) {
+                       $this->markTestSkipped( 'Tests only runs with the blogmeta database table installed' );
+               }
+
+               add_site_meta( self::$site_id, 'foo', 'bar' );
+               add_site_meta( self::$site_id, 'foo', 'baz' );
+               add_site_meta( self::$site_id, 'foo1', 'baz' );
+
+               $found = get_site_meta( self::$site_id, 'foo' );
+               $expected = array( 'bar', 'baz' );
+
+               $this->assertEqualSets( $expected, $found );
+       }
+
+       public function test_get_should_respect_single_true() {
+               if ( ! is_site_meta_supported() ) {
+                       $this->markTestSkipped( 'Tests only runs with the blogmeta database table installed' );
+               }
+
+               add_site_meta( self::$site_id, 'foo', 'bar' );
+               add_site_meta( self::$site_id, 'foo', 'baz' );
+
+               $found = get_site_meta( self::$site_id, 'foo', true );
+               $this->assertSame( 'bar', $found );
+       }
+
+       public function test_update_should_pass_to_add_when_no_value_exists_for_key() {
+               if ( ! is_site_meta_supported() ) {
+                       $this->markTestSkipped( 'Tests only runs with the blogmeta database table installed' );
+               }
+
+               $actual = update_site_meta( self::$site_id, 'foo', 'bar' );
+               $this->assertInternalType( 'int', $actual );
+               $this->assertNotEmpty( $actual );
+
+               $meta = get_site_meta( self::$site_id, 'foo', true );
+               $this->assertSame( 'bar', $meta );
+       }
+
+       public function test_update_should_return_true_when_updating_existing_value_for_key() {
+               if ( ! is_site_meta_supported() ) {
+                       $this->markTestSkipped( 'Tests only runs with the blogmeta database table installed' );
+               }
+
+               add_site_meta( self::$site_id, 'foo', 'bar' );
+
+               $actual = update_site_meta( self::$site_id, 'foo', 'baz' );
+               $this->assertTrue( $actual );
+
+               $meta = get_site_meta( self::$site_id, 'foo', true );
+               $this->assertSame( 'baz', $meta );
+       }
+
+       public function test_delete_by_key() {
+               if ( ! is_site_meta_supported() ) {
+                       $this->markTestSkipped( 'Tests only runs with the blogmeta database table installed' );
+               }
+
+               add_site_meta( self::$site_id, 'unique_delete_by_key', 'value', true );
+               add_site_meta( self::$site_id2, 'unique_delete_by_key', 'value', true );
+
+               $this->assertEquals( 'value', get_site_meta( self::$site_id, 'unique_delete_by_key', true ) );
+               $this->assertEquals( 'value', get_site_meta( self::$site_id2, 'unique_delete_by_key', true ) );
+
+               $this->assertTrue( delete_site_meta_by_key( 'unique_delete_by_key' ) );
+
+               $this->assertEquals( '', get_site_meta( self::$site_id, 'unique_delete_by_key', true ) );
+               $this->assertEquals( '', get_site_meta( self::$site_id2, 'unique_delete_by_key', true ) );
+       }
+
+       public function test_site_meta_should_be_deleted_when_site_is_deleted() {
+               if ( ! is_site_meta_supported() ) {
+                       $this->markTestSkipped( 'Tests only runs with the blogmeta database table installed' );
+               }
+
+               $site_id = self::factory()->blog->create( array( 'domain' => 'foo.org', 'path' => '/' ) );
+
+               add_site_meta( $site_id, 'foo', 'bar' );
+               add_site_meta( $site_id, 'foo1', 'bar' );
+
+               $this->assertSame( 'bar', get_site_meta( $site_id, 'foo', true ) );
+               $this->assertSame( 'bar', get_site_meta( $site_id, 'foo1', true ) );
+
+               wpmu_delete_blog( $site_id, true );
+
+               $this->assertSame( '', get_site_meta( $site_id, 'foo', true ) );
+               $this->assertSame( '', get_site_meta( $site_id, 'foo1', true ) );
+       }
+
+       public function test_update_site_meta_cache() {
+               global $wpdb;
+
+               if ( ! is_site_meta_supported() ) {
+                       $this->markTestSkipped( 'Tests only runs with the blogmeta database table installed' );
+               }
+
+               update_site_meta( self::$site_id, 'foo', 'bar' );
+               update_sitemeta_cache( array( self::$site_id ) );
+
+               $num_queries = $wpdb->num_queries;
+               get_site_meta( self::$site_id, 'foo', true );
+               $this->assertSame( $num_queries, $wpdb->num_queries);
+       }
+
+       public function test_query_update_site_meta_cache_true() {
+               global $wpdb;
+
+               if ( ! is_site_meta_supported() ) {
+                       $this->markTestSkipped( 'Tests only runs with the blogmeta database table installed' );
+               }
+
+               update_site_meta( self::$site_id, 'foo', 'bar' );
+
+               // Do not include 'update_site_meta_cache' as true as its the default.
+               new WP_Site_Query( array(
+                       'ID' => self::$site_id,
+               ) );
+
+               $num_queries = $wpdb->num_queries;
+               get_site_meta( self::$site_id, 'foo', true );
+               $this->assertSame( $num_queries, $wpdb->num_queries);
+       }
+
+       public function test_query_update_site_meta_cache_false() {
+               global $wpdb;
+
+               if ( ! is_site_meta_supported() ) {
+                       $this->markTestSkipped( 'Tests only runs with the blogmeta database table installed' );
+               }
+
+               update_site_meta( self::$site_id, 'foo', 'bar' );
+
+               new WP_Site_Query( array(
+                       'ID'                     => self::$site_id,
+                       'update_site_meta_cache' => false,
+               ) );
+
+               $num_queries = $wpdb->num_queries;
+               get_site_meta( self::$site_id, 'foo', true );
+               $this->assertSame( $num_queries + 1, $wpdb->num_queries);
+       }
+}
+
+endif;
</ins></span></pre>
</div>
</div>

</body>
</html>