<!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>[43654] trunk: Multisite: Introduce a site initialization and uninitialization API.</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="https://core.trac.wordpress.org/changeset/43654">43654</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/43654","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-09-24 15:08:32 +0000 (Mon, 24 Sep 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 a site initialization and uninitialization API.

This changeset makes the new CRUD API for sites introduced in <a href="https://core.trac.wordpress.org/changeset/43548">[43548]</a> usable for real-world sites. A new function `wp_initialize_site()`, which takes care of creating a site's database tables and populating them with initial values, is hooked into the site insertion process that is initiated when calling `wp_insert_site()`. Similarly, a new function `wp_uninitialize_site()`, which takes care of dropping a site's database tables, is hooked into the site deletion process that is initiated when calling `wp_delete_site()`.

A new function `wp_is_site_initialized()` completes the API, allowing to check whether a site is initialized. Since this function always makes a database request in its default behavior, it should be called with caution. Plugins that would like to use site initialization in special ways can leverage a `pre_wp_is_site_initialized` filter to alter that default behavior.

The separate handling of the site's row in the `wp_blogs` database table and the actual site setup allows for more flexibility in controlling whether or how a site's data is set up. For example, a unit test that only checks data from the site's database table row can unhook the site initialization process to improve performance. At the same time, developers consuming the new sites API only need to know about the CRUD functions, since the initialization and uninitialization processes happen internally.

With this changeset, the foundation for a sites REST API endpoint is fully available. The previously recommended functions `wpmu_create_blog()` and `wpmu_delete_blog()` now call the new respective function internally. Further follow-up work to this includes replacing calls to `wpmu_create_blog()` with `wp_insert_site()`, `update_blog_details()` with `wp_update_site()` and `wpmu_delete_blog()` with `wp_delete_blog()` throughout the codebase.

As a side-effect of this work, the `wpmu_new_blog`, `delete_blog`, and `deleted_blog` actions and the `install_blog()` function have been deprecated.

Fixes <a href="https://core.trac.wordpress.org/ticket/41333">#41333</a>. See <a href="https://core.trac.wordpress.org/ticket/40364">#40364</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpadminincludesmsphp">trunk/src/wp-admin/includes/ms.php</a></li>
<li><a href="#trunksrcwpincludesmsblogsphp">trunk/src/wp-includes/ms-blogs.php</a></li>
<li><a href="#trunksrcwpincludesmsdefaultfiltersphp">trunk/src/wp-includes/ms-default-filters.php</a></li>
<li><a href="#trunksrcwpincludesmsdeprecatedphp">trunk/src/wp-includes/ms-deprecated.php</a></li>
<li><a href="#trunksrcwpincludesmsfunctionsphp">trunk/src/wp-includes/ms-functions.php</a></li>
<li><a href="#trunktestsphpunittestsmultisitesitephp">trunk/tests/phpunit/tests/multisite/site.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-09-23 22:49:02 UTC (rev 43653)
+++ trunk/src/wp-admin/includes/ms.php  2018-09-24 15:08:32 UTC (rev 43654)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -73,32 +73,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">        $blog = get_site( $blog_id );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        /**
-        * Fires before a site is deleted.
-        *
-        * @since MU (3.0.0)
-        *
-        * @param int  $blog_id The site ID.
-        * @param bool $drop    True if site's table should be dropped. Default is false.
-        */
-       do_action( 'delete_blog', $blog_id, $drop );
</del><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $users = get_users(
-               array(
-                       'blog_id' => $blog_id,
-                       'fields'  => 'ids',
-               )
-       );
-
-       // Remove users from this blog.
-       if ( ! empty( $users ) ) {
-               foreach ( $users as $user_id ) {
-                       remove_user_from_blog( $user_id, $blog_id );
-               }
-       }
-
-       update_blog_status( $blog_id, 'deleted', 1 );
-
</del><span class="cx" style="display: block; padding: 0 10px">         $current_network = get_network();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        // If a full blog object is not available, do not destroy anything.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -119,88 +94,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">        if ( $drop ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $uploads = wp_get_upload_dir();
-
-               $tables = $wpdb->tables( 'blog' );
-               /**
-                * Filters the tables to drop when the site is deleted.
-                *
-                * @since MU (3.0.0)
-                *
-                * @param string[] $tables  Array of names of the site tables to be dropped.
-                * @param int      $blog_id The ID of the site to drop tables for.
-                */
-               $drop_tables = apply_filters( 'wpmu_drop_tables', $tables, $blog_id );
-
-               foreach ( (array) $drop_tables as $table ) {
-                       $wpdb->query( "DROP TABLE IF EXISTS `$table`" );
-               }
-
-               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 );
-                       }
-               }
-
</del><span class="cx" style="display: block; padding: 0 10px">                 wp_delete_site( $blog_id );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        } else {
+               /** This action is documented in wp-includes/ms-blogs.php */
+               do_action_deprecated( 'delete_blog', array( $blog_id, false ), '5.0.0' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                /**
-                * Filters the upload base directory to delete when the site is deleted.
-                *
-                * @since MU (3.0.0)
-                *
-                * @param string $uploads['basedir'] Uploads path without subdirectory. @see wp_upload_dir()
-                * @param int    $blog_id            The site ID.
-                */
-               $dir     = apply_filters( 'wpmu_delete_blog_upload_dir', $uploads['basedir'], $blog_id );
-               $dir     = rtrim( $dir, DIRECTORY_SEPARATOR );
-               $top_dir = $dir;
-               $stack   = array( $dir );
-               $index   = 0;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $users = get_users(
+                       array(
+                               'blog_id' => $blog_id,
+                               'fields'  => 'ids',
+                       )
+               );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                while ( $index < count( $stack ) ) {
-                       // Get indexed directory from stack
-                       $dir = $stack[ $index ];
-
-                       $dh = @opendir( $dir );
-                       if ( $dh ) {
-                               while ( ( $file = @readdir( $dh ) ) !== false ) {
-                                       if ( $file == '.' || $file == '..' ) {
-                                               continue;
-                                       }
-
-                                       if ( @is_dir( $dir . DIRECTORY_SEPARATOR . $file ) ) {
-                                               $stack[] = $dir . DIRECTORY_SEPARATOR . $file;
-                                       } elseif ( @is_file( $dir . DIRECTORY_SEPARATOR . $file ) ) {
-                                               @unlink( $dir . DIRECTORY_SEPARATOR . $file );
-                                       }
-                               }
-                               @closedir( $dh );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         // Remove users from this blog.
+               if ( ! empty( $users ) ) {
+                       foreach ( $users as $user_id ) {
+                               remove_user_from_blog( $user_id, $blog_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">-                        $index++;
</del><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $stack = array_reverse( $stack ); // Last added dirs are deepest
-               foreach ( (array) $stack as $dir ) {
-                       if ( $dir != $top_dir ) {
-                               @rmdir( $dir );
-                       }
-               }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         update_blog_status( $blog_id, 'deleted', 1 );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                clean_blog_cache( $blog );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         /** This action is documented in wp-includes/ms-blogs.php */
+               do_action_deprecated( 'deleted_blog', array( $blog_id, false ), '5.0.0' );
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        /**
-        * Fires after the site is deleted from the network.
-        *
-        * @since 4.8.0
-        *
-        * @param int  $blog_id The site ID.
-        * @param bool $drop    True if site's tables should be dropped. Default is false.
-        */
-       do_action( 'deleted_blog', $blog_id, $drop );
-
</del><span class="cx" style="display: block; padding: 0 10px">         if ( $switch ) {
</span><span class="cx" style="display: block; padding: 0 10px">                restore_current_blog();
</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-09-23 22:49:02 UTC (rev 43653)
+++ trunk/src/wp-includes/ms-blogs.php  2018-09-24 15:08:32 UTC (rev 43654)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -442,6 +442,12 @@
</span><span class="cx" style="display: block; padding: 0 10px">                'lang_id'      => 0,
</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">+        // Extract the passed arguments that may be relevant for site initialization.
+       $args = array_diff_key( $data, $defaults );
+       if ( isset( $args['site_id'] ) ) {
+               unset( $args['site_id'] );
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         $data = wp_prepare_site_data( $data, $defaults );
</span><span class="cx" style="display: block; padding: 0 10px">        if ( is_wp_error( $data ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                return $data;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -464,6 +470,37 @@
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        do_action( 'wp_insert_site', $new_site );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        /**
+        * Fires when a site's initialization routine should be executed.
+        *
+        * @since 5.0.0
+        *
+        * @param WP_Site $new_site New site object.
+        * @param array   $args     Arguments for the initialization.
+        */
+       do_action( 'wp_initialize_site', $new_site, $args );
+
+       // Only compute extra hook parameters if the deprecated hook is actually in use.
+       if ( has_action( 'wpmu_new_blog' ) ) {
+               $user_id = ! empty( $args['user_id'] ) ? $args['user_id'] : 0;
+               $meta    = ! empty( $args['options'] ) ? $args['options'] : array();
+
+               /**
+                * Fires immediately after a new site is created.
+                *
+                * @since MU (3.0.0)
+                * @deprecated 5.0.0 Use wp_insert_site
+                *
+                * @param int    $site_id    Site ID.
+                * @param int    $user_id    User ID.
+                * @param string $domain     Site domain.
+                * @param string $path       Site path.
+                * @param int    $network_id Network ID. Only relevant on multi-network installations.
+                * @param array  $meta       Meta data. Used to set initial site options.
+                */
+               do_action_deprecated( 'wpmu_new_blog', array( $new_site->id, $user_id, $new_site->domain, $new_site->path, $new_site->network_id, $meta ), '5.0.0', 'wp_insert_site' );
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         return (int) $new_site->id;
</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">@@ -543,6 +580,52 @@
</span><span class="cx" style="display: block; padding: 0 10px">                return new WP_Error( 'site_not_exist', __( 'Site does not exist.' ) );
</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">+        $errors = new WP_Error();
+
+       /**
+        * Fires before a site should be deleted from the database.
+        *
+        * Plugins should amend the `$errors` object via its `WP_Error::add()` method. If any errors
+        * are present, the site will not be deleted.
+        *
+        * @since 5.0.0
+        *
+        * @param WP_Error $errors   Error object to add validation errors to.
+        * @param WP_Site  $old_site The site object to be deleted.
+        */
+       do_action( 'wp_validate_site_deletion', $errors, $old_site );
+
+       if ( ! empty( $errors->errors ) ) {
+               return $errors;
+       }
+
+       /**
+        * Fires before a site is deleted.
+        *
+        * @since MU (3.0.0)
+        * @deprecated 5.0.0
+        *
+        * @param int  $site_id The site ID.
+        * @param bool $drop    True if site's table should be dropped. Default is false.
+        */
+       do_action_deprecated( 'delete_blog', array( $old_site->id, true ), '5.0.0' );
+
+       /**
+        * Fires when a site's uninitialization routine should be executed.
+        *
+        * @since 5.0.0
+        *
+        * @param WP_Site $old_site Deleted site object.
+        */
+       do_action( 'wp_uninitialize_site', $old_site );
+
+       if ( is_site_meta_supported() ) {
+               $blog_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->blogmeta WHERE blog_id = %d ", $old_site->id ) );
+               foreach ( $blog_meta_ids as $mid ) {
+                       delete_metadata_by_mid( 'blog', $mid );
+               }
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         if ( false === $wpdb->delete( $wpdb->blogs, array( 'blog_id' => $old_site->id ) ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                return new WP_Error( 'db_delete_error', __( 'Could not delete site from the database.' ), $wpdb->last_error );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -558,6 +641,17 @@
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        do_action( 'wp_delete_site', $old_site );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        /**
+        * Fires after the site is deleted from the network.
+        *
+        * @since 4.8.0
+        * @deprecated 5.0.0
+        *
+        * @param int  $site_id The site ID.
+        * @param bool $drop    True if site's tables should be dropped. Default is false.
+        */
+       do_action_deprecated( 'deleted_blog', array( $old_site->id, true ), '5.0.0' );
+
</ins><span class="cx" style="display: block; padding: 0 10px">         return $old_site;
</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">@@ -619,7 +713,7 @@
</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><del style="background-color: #fdd; text-decoration:none; 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 ) ) ) );
</del><ins style="background-color: #dfd; text-decoration:none; 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 ) ) ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                update_site_cache( $fresh_sites, $update_meta_cache );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -913,6 +1007,322 @@
</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">+ * Runs the initialization routine for a given site.
+ *
+ * This process includes creating the site's database tables and
+ * populating them with defaults.
+ *
+ * @since 5.0.0
+ *
+ * @global wpdb     $wpdb     WordPress database abstraction object.
+ * @global WP_Roles $wp_roles WordPress role management object.
+ *
+ * @param int|WP_Site $site_id Site ID or object.
+ * @param array       $args    {
+ *     Optional. Arguments to modify the initialization behavior.
+ *
+ *     @type int    $user_id Required. User ID for the site administrator.
+ *     @type string $title   Site title. Default is 'Site %d' where %d is the
+ *                           site ID.
+ *     @type array  $options Custom option $key => $value pairs to use. Default
+ *                           empty array.
+ *     @type array  $meta    Custom site metadata $key => $value pairs to use.
+ *                           Default empty array.
+ * }
+ * @return bool|WP_Error True on success, or error object on failure.
+ */
+function wp_initialize_site( $site_id, array $args = array() ) {
+       global $wpdb, $wp_roles;
+
+       if ( empty( $site_id ) ) {
+               return new WP_Error( 'site_empty_id', __( 'Site ID must not be empty.' ) );
+       }
+
+       $site = get_site( $site_id );
+       if ( ! $site ) {
+               return new WP_Error( 'site_invalid_id', __( 'Site with the ID does not exist.' ) );
+       }
+
+       if ( wp_is_site_initialized( $site ) ) {
+               return new WP_Error( 'site_already_initialized', __( 'The site appears to be already initialized.' ) );
+       }
+
+       $network = get_network( $site->network_id );
+       if ( ! $network ) {
+               $network = get_network();
+       }
+
+       $args = wp_parse_args(
+               $args,
+               array(
+                       'user_id' => 0,
+                       /* translators: %d: site ID */
+                       'title'   => sprintf( __( 'Site %d' ), $site->id ),
+                       'options' => array(),
+                       'meta'    => array(),
+               )
+       );
+
+       /**
+        * Filters the arguments for initializing a site.
+        *
+        * @since 5.0.0
+        *
+        * @param array      $args    Arguments to modify the initialization behavior.
+        * @param WP_Site    $site    Site that is being initialized.
+        * @param WP_Network $network Network that the site belongs to.
+        */
+       $args = apply_filters( 'wp_initialize_site_args', $args, $site, $network );
+
+       $orig_installing = wp_installing();
+       if ( ! $orig_installing ) {
+               wp_installing( true );
+       }
+
+       $switch = false;
+       if ( get_current_blog_id() !== $site->id ) {
+               $switch = true;
+               switch_to_blog( $site->id );
+       }
+
+       require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
+
+       // Set up the database tables.
+       make_db_current_silent( 'blog' );
+
+       $home_scheme    = 'http';
+       $siteurl_scheme = 'http';
+       if ( ! is_subdomain_install() ) {
+               if ( 'https' === parse_url( get_home_url( $network->site_id ), PHP_URL_SCHEME ) ) {
+                       $home_scheme = 'https';
+               }
+               if ( 'https' === parse_url( get_network_option( $network->id, 'siteurl' ), PHP_URL_SCHEME ) ) {
+                       $siteurl_scheme = 'https';
+               }
+       }
+
+       // Populate the site's options.
+       populate_options(
+               array_merge(
+                       array(
+                               'home'        => untrailingslashit( $home_scheme . '://' . $site->domain . $site->path ),
+                               'siteurl'     => untrailingslashit( $siteurl_scheme . '://' . $site->domain . $site->path ),
+                               'blogname'    => wp_unslash( $args['title'] ),
+                               'admin_email' => '',
+                               'upload_path' => get_network_option( $network->id, 'ms_files_rewriting' ) ? UPLOADBLOGSDIR . "/{$site->id}/files" : get_blog_option( $network->site_id, 'upload_path' ),
+                               'blog_public' => (int) $site->public,
+                               'WPLANG'      => get_network_option( $network->id, 'WPLANG' ),
+                       ),
+                       $args['options']
+               )
+       );
+
+       // Populate the site's roles.
+       populate_roles();
+       $wp_roles = new WP_Roles();
+
+       // Populate metadata for the site.
+       populate_site_meta( $site->id, $args['meta'] );
+
+       // Remove all permissions that may exist for the site.
+       $table_prefix = $wpdb->get_blog_prefix();
+       delete_metadata( 'user', 0, $table_prefix . 'user_level', null, true ); // delete all
+       delete_metadata( 'user', 0, $table_prefix . 'capabilities', null, true ); // delete all
+
+       // Install default site content.
+       wp_install_defaults( $args['user_id'] );
+
+       // Set the site administrator.
+       add_user_to_blog( $site->id, $args['user_id'], 'administrator' );
+       if ( ! user_can( $args['user_id'], 'manage_network' ) && ! get_user_meta( $args['user_id'], 'primary_blog', true ) ) {
+               update_user_meta( $args['user_id'], 'primary_blog', $site->id );
+       }
+
+       if ( $switch ) {
+               restore_current_blog();
+       }
+
+       wp_installing( $orig_installing );
+
+       return true;
+}
+
+/**
+ * Runs the uninitialization routine for a given site.
+ *
+ * This process includes dropping the site's database tables and deleting its uploads directory.
+ *
+ * @since 5.0.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int|WP_Site $site_id Site ID or object.
+ * @return bool|WP_Error True on success, or error object on failure.
+ */
+function wp_uninitialize_site( $site_id ) {
+       global $wpdb;
+
+       if ( empty( $site_id ) ) {
+               return new WP_Error( 'site_empty_id', __( 'Site ID must not be empty.' ) );
+       }
+
+       $site = get_site( $site_id );
+       if ( ! $site ) {
+               return new WP_Error( 'site_invalid_id', __( 'Site with the ID does not exist.' ) );
+       }
+
+       if ( ! wp_is_site_initialized( $site ) ) {
+               return new WP_Error( 'site_already_uninitialized', __( 'The site appears to be already uninitialized.' ) );
+       }
+
+       $users = get_users( array(
+               'blog_id' => $site->id,
+               'fields'  => 'ids',
+       ) );
+
+       // Remove users from the site.
+       if ( ! empty( $users ) ) {
+               foreach ( $users as $user_id ) {
+                       remove_user_from_blog( $user_id, $site->id );
+               }
+       }
+
+       $switch = false;
+       if ( get_current_blog_id() !== $site->id ) {
+               $switch = true;
+               switch_to_blog( $site->id );
+       }
+
+       $uploads = wp_get_upload_dir();
+
+       $tables = $wpdb->tables( 'blog' );
+
+       /**
+        * Filters the tables to drop when the site is deleted.
+        *
+        * @since MU (3.0.0)
+        *
+        * @param string[] $tables  Array of names of the site tables to be dropped.
+        * @param int      $site_id The ID of the site to drop tables for.
+        */
+       $drop_tables = apply_filters( 'wpmu_drop_tables', $tables, $site->id );
+
+       foreach ( (array) $drop_tables as $table ) {
+               $wpdb->query( "DROP TABLE IF EXISTS `$table`" ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+       }
+
+       /**
+        * Filters the upload base directory to delete when the site is deleted.
+        *
+        * @since MU (3.0.0)
+        *
+        * @param string $uploads['basedir'] Uploads path without subdirectory. @see wp_upload_dir()
+        * @param int    $site_id            The site ID.
+        */
+       $dir     = apply_filters( 'wpmu_delete_blog_upload_dir', $uploads['basedir'], $site->id );
+       $dir     = rtrim( $dir, DIRECTORY_SEPARATOR );
+       $top_dir = $dir;
+       $stack   = array( $dir );
+       $index   = 0;
+
+       while ( $index < count( $stack ) ) {
+               // Get indexed directory from stack
+               $dir = $stack[ $index ];
+
+               // phpcs:disable Generic.PHP.NoSilencedErrors.Discouraged
+               $dh = @opendir( $dir );
+               if ( $dh ) {
+                       $file = @readdir( $dh );
+                       while ( false !== $file ) {
+                               if ( '.' === $file || '..' === $file ) {
+                                       $file = @readdir( $dh );
+                                       continue;
+                               }
+
+                               if ( @is_dir( $dir . DIRECTORY_SEPARATOR . $file ) ) {
+                                       $stack[] = $dir . DIRECTORY_SEPARATOR . $file;
+                               } elseif ( @is_file( $dir . DIRECTORY_SEPARATOR . $file ) ) {
+                                       @unlink( $dir . DIRECTORY_SEPARATOR . $file );
+                               }
+
+                               $file = @readdir( $dh );
+                       }
+                       @closedir( $dh );
+               }
+               $index++;
+       }
+
+       $stack = array_reverse( $stack ); // Last added dirs are deepest
+       foreach ( (array) $stack as $dir ) {
+               if ( $dir != $top_dir ) {
+                       @rmdir( $dir );
+               }
+       }
+
+       // phpcs:enable Generic.PHP.NoSilencedErrors.Discouraged
+       if ( $switch ) {
+               restore_current_blog();
+       }
+
+       return true;
+}
+
+/**
+ * Checks whether a site is initialized.
+ *
+ * A site is considered initialized when its database tables are present.
+ *
+ * @since 5.0.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int|WP_Site $site_id Site ID or object.
+ * @return bool True if the site is initialized, false otherwise.
+ */
+function wp_is_site_initialized( $site_id ) {
+       global $wpdb;
+
+       if ( is_object( $site_id ) ) {
+               $site_id = $site_id->blog_id;
+       }
+       $site_id = (int) $site_id;
+
+       /**
+        * Filters the check for whether a site is initialized before the database is accessed.
+        *
+        * Returning a non-null value will effectively short-circuit the function, returning
+        * that value instead.
+        *
+        * @since 5.0.0
+        *
+        * @param bool|null $pre     The value to return, if not null.
+        * @param int       $site_id The site ID that is being checked.
+        */
+       $pre = apply_filters( 'pre_wp_is_site_initialized', null, $site_id );
+       if ( null !== $pre ) {
+               return (bool) $pre;
+       }
+
+       $switch = false;
+       if ( get_current_blog_id() !== $site_id ) {
+               $switch = true;
+               remove_action( 'switch_blog', 'wp_switch_roles_and_user', 1 );
+               switch_to_blog( $site_id );
+       }
+
+       $suppress = $wpdb->suppress_errors();
+       $result   = (bool) $wpdb->get_results( "DESCRIBE {$wpdb->posts}" );
+       $wpdb->suppress_errors( $suppress );
+
+       if ( $switch ) {
+               restore_current_blog();
+               add_action( 'switch_blog', 'wp_switch_roles_and_user', 1, 2 );
+       }
+
+       return $result;
+}
+
+/**
</ins><span class="cx" style="display: block; padding: 0 10px">  * Retrieve option value for a given blog id based on name of option.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * If the option does not exist or does not have a value, then the return value
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1621,7 +2031,7 @@
</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( $network_ids, 'networks' );
</span><span class="cx" style="display: block; padding: 0 10px">        if ( ! empty( $non_cached_ids ) ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $fresh_networks = $wpdb->get_results( sprintf( "SELECT $wpdb->site.* FROM $wpdb->site WHERE id IN (%s)", join( ',', array_map( 'intval', $non_cached_ids ) ) ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $fresh_networks = $wpdb->get_results( sprintf( "SELECT $wpdb->site.* FROM $wpdb->site WHERE id IN (%s)", join( ',', array_map( 'intval', $non_cached_ids ) ) ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                update_network_cache( $fresh_networks );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1757,7 +2167,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 ( $new_site->spam != $old_site->spam ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( $new_site->spam == 1 ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( 1 == $new_site->spam ) {
</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 when the 'spam' status is added to a site.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1781,7 +2191,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 ( $new_site->mature != $old_site->mature ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( $new_site->mature == 1 ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( 1 == $new_site->mature ) {
</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 when the 'mature' status is added to a site.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1805,7 +2215,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 ( $new_site->archived != $old_site->archived ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( $new_site->archived == 1 ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( 1 == $new_site->archived ) {
</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 when the 'archived' status is added to a site.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1829,7 +2239,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 ( $new_site->deleted != $old_site->deleted ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( $new_site->deleted == 1 ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( 1 == $new_site->deleted ) {
</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 when the 'deleted' status is added to a site.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1889,5 +2299,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">  * @param string $public  The value of the site status.
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> function wp_update_blog_public_option_on_site_update( $site_id, $public ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       // Bail if the site's database tables do not exist (yet).
+       if ( ! wp_is_site_initialized( $site_id ) ) {
+               return;
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         update_blog_option( $site_id, 'blog_public', $public );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="trunksrcwpincludesmsdefaultfiltersphp"></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-default-filters.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/ms-default-filters.php      2018-09-23 22:49:02 UTC (rev 43653)
+++ trunk/src/wp-includes/ms-default-filters.php        2018-09-24 15:08:32 UTC (rev 43654)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -37,8 +37,6 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> // Blogs
</span><span class="cx" style="display: block; padding: 0 10px"> add_filter( 'wpmu_validate_blog_signup', 'signup_nonce_check' );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-add_action( 'wpmu_new_blog', 'wpmu_log_new_registrations', 10, 2 );
-add_action( 'wpmu_new_blog', 'newblog_notify_siteadmin', 10, 2 );
</del><span class="cx" style="display: block; padding: 0 10px"> add_action( 'wpmu_activate_blog', 'wpmu_welcome_notification', 10, 5 );
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'after_signup_site', 'wpmu_signup_blog_notification', 10, 7 );
</span><span class="cx" style="display: block; padding: 0 10px"> add_filter( 'wp_normalize_site_data', 'wp_normalize_site_data', 10, 1 );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -49,6 +47,10 @@
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'wp_insert_site', 'wp_maybe_transition_site_statuses_on_update', 10, 1 );
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'wp_update_site', 'wp_maybe_transition_site_statuses_on_update', 10, 2 );
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'wp_update_site', 'wp_maybe_clean_new_site_cache_on_update', 10, 2 );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+add_action( 'wp_initialize_site', 'wp_initialize_site', 10, 2 );
+add_action( 'wp_initialize_site', 'wpmu_log_new_registrations', 100, 2 );
+add_action( 'wp_initialize_site', 'newblog_notify_siteadmin', 100, 1 );
+add_action( 'wp_uninitialize_site', 'wp_uninitialize_site', 10, 1 );
</ins><span class="cx" style="display: block; padding: 0 10px"> add_action( 'update_blog_public', 'wp_update_blog_public_option_on_site_update', 1, 2 );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> // Register Nonce
</span></span></pre></div>
<a id="trunksrcwpincludesmsdeprecatedphp"></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-deprecated.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/ms-deprecated.php   2018-09-23 22:49:02 UTC (rev 43653)
+++ trunk/src/wp-includes/ms-deprecated.php     2018-09-24 15:08:32 UTC (rev 43654)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -580,3 +580,103 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        return $site_id;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+/**
+ * Install an empty blog.
+ *
+ * Creates the new blog tables and options. If calling this function
+ * directly, be sure to use switch_to_blog() first, so that $wpdb
+ * points to the new blog.
+ *
+ * @since MU (3.0.0)
+ * @deprecated 5.0.0
+ *
+ * @global wpdb     $wpdb
+ * @global WP_Roles $wp_roles
+ *
+ * @param int    $blog_id    The value returned by wp_insert_site().
+ * @param string $blog_title The title of the new site.
+ */
+function install_blog( $blog_id, $blog_title = '' ) {
+       global $wpdb, $wp_roles;
+
+       _deprecated_function( __FUNCTION__, '5.0.0' );
+
+       // Cast for security
+       $blog_id = (int) $blog_id;
+
+       require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
+
+       $suppress = $wpdb->suppress_errors();
+       if ( $wpdb->get_results( "DESCRIBE {$wpdb->posts}" ) ) {
+               die( '<h1>' . __( 'Already Installed' ) . '</h1><p>' . __( 'You appear to have already installed WordPress. To reinstall please clear your old database tables first.' ) . '</p></body></html>' );
+       }
+       $wpdb->suppress_errors( $suppress );
+
+       $url = get_blogaddress_by_id( $blog_id );
+
+       // Set everything up
+       make_db_current_silent( 'blog' );
+       populate_options();
+       populate_roles();
+
+       // populate_roles() clears previous role definitions so we start over.
+       $wp_roles = new WP_Roles();
+
+       $siteurl = $home = untrailingslashit( $url );
+
+       if ( ! is_subdomain_install() ) {
+
+               if ( 'https' === parse_url( get_site_option( 'siteurl' ), PHP_URL_SCHEME ) ) {
+                       $siteurl = set_url_scheme( $siteurl, 'https' );
+               }
+               if ( 'https' === parse_url( get_home_url( get_network()->site_id ), PHP_URL_SCHEME ) ) {
+                       $home = set_url_scheme( $home, 'https' );
+               }
+       }
+
+       update_option( 'siteurl', $siteurl );
+       update_option( 'home', $home );
+
+       if ( get_site_option( 'ms_files_rewriting' ) ) {
+               update_option( 'upload_path', UPLOADBLOGSDIR . "/$blog_id/files" );
+       } else {
+               update_option( 'upload_path', get_blog_option( get_network()->site_id, 'upload_path' ) );
+       }
+
+       update_option( 'blogname', wp_unslash( $blog_title ) );
+       update_option( 'admin_email', '' );
+
+       // remove all perms
+       $table_prefix = $wpdb->get_blog_prefix();
+       delete_metadata( 'user', 0, $table_prefix . 'user_level', null, true ); // delete all
+       delete_metadata( 'user', 0, $table_prefix . 'capabilities', null, true ); // delete all
+}
+
+/**
+ * Set blog defaults.
+ *
+ * This function creates a row in the wp_blogs table.
+ *
+ * @since MU (3.0.0)
+ * @deprecated MU
+ * @deprecated Use wp_install_defaults()
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int $blog_id Ignored in this function.
+ * @param int $user_id
+ */
+function install_blog_defaults( $blog_id, $user_id ) {
+       global $wpdb;
+
+       _deprecated_function( __FUNCTION__, 'MU' );
+
+       require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
+
+       $suppress = $wpdb->suppress_errors();
+
+       wp_install_defaults( $user_id );
+
+       $wpdb->suppress_errors( $suppress );
+}
</ins></span></pre></div>
<a id="trunksrcwpincludesmsfunctionsphp"></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-functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/ms-functions.php    2018-09-23 22:49:02 UTC (rev 43653)
+++ trunk/src/wp-includes/ms-functions.php      2018-09-24 15:08:32 UTC (rev 43654)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1285,7 +1285,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">  * @param string $path       The new site's path.
</span><span class="cx" style="display: block; padding: 0 10px">  * @param string $title      The new site's title.
</span><span class="cx" style="display: block; padding: 0 10px">  * @param int    $user_id    The user ID of the new site's admin.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @param array  $meta       Optional. Array of key=>value pairs used to set initial site options.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @param array  $options    Optional. Array of key=>value pairs used to set initial site options.
</ins><span class="cx" style="display: block; padding: 0 10px">  *                           If valid status keys are included ('public', 'archived', 'mature',
</span><span class="cx" style="display: block; padding: 0 10px">  *                           'spam', 'deleted', or 'lang_id') the given site status(es) will be
</span><span class="cx" style="display: block; padding: 0 10px">  *                           updated. Otherwise, keys and values will be used to set options for
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1293,12 +1293,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">  * @param int    $network_id Optional. Network ID. Only relevant on multi-network installations.
</span><span class="cx" style="display: block; padding: 0 10px">  * @return int|WP_Error Returns WP_Error object on failure, the new site ID on success.
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-function wpmu_create_blog( $domain, $path, $title, $user_id, $meta = array(), $network_id = 1 ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+function wpmu_create_blog( $domain, $path, $title, $user_id, $options = array(), $network_id = 1 ) {
</ins><span class="cx" style="display: block; padding: 0 10px">         $defaults = array(
</span><span class="cx" style="display: block; padding: 0 10px">                'public' => 0,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                'WPLANG' => get_network_option( $network_id, 'WPLANG' ),
</del><span class="cx" style="display: block; padding: 0 10px">         );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $meta     = wp_parse_args( $meta, $defaults );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $options  = wp_parse_args( $options, $defaults );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        $title   = strip_tags( $title );
</span><span class="cx" style="display: block; padding: 0 10px">        $user_id = (int) $user_id;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1320,56 +1319,22 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'path'       => $path,
</span><span class="cx" style="display: block; padding: 0 10px">                        'network_id' => $network_id,
</span><span class="cx" style="display: block; padding: 0 10px">                ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                array_intersect_key(
-                       $meta,
-                       array_flip( $site_data_whitelist )
-               )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         array_intersect_key( $options, array_flip( $site_data_whitelist ) )
</ins><span class="cx" style="display: block; padding: 0 10px">         );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $meta = array_diff_key( $meta, array_flip( $site_data_whitelist ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // Data to pass to wp_initialize_site().
+       $site_initialization_data = array(
+               'title'   => $title,
+               'user_id' => $user_id,
+               'options' => array_diff_key( $options, array_flip( $site_data_whitelist ) ),
+       );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        remove_action( 'update_blog_public', 'wp_update_blog_public_option_on_site_update', 1 );
-       $blog_id = wp_insert_site( $site_data );
-       add_action( 'update_blog_public', 'wp_update_blog_public_option_on_site_update', 1, 2 );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $blog_id = wp_insert_site( array_merge( $site_data, $site_initialization_data ) );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        if ( is_wp_error( $blog_id ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                return $blog_id;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        switch_to_blog( $blog_id );
-       install_blog( $blog_id, $title );
-       wp_install_defaults( $user_id );
-
-       add_user_to_blog( $blog_id, $user_id, 'administrator' );
-
-       foreach ( $meta as $key => $value ) {
-               update_option( $key, $value );
-       }
-
-       update_option( 'blog_public', (int) $site_data['public'] );
-
-       if ( ! is_super_admin( $user_id ) && ! get_user_meta( $user_id, 'primary_blog', true ) ) {
-               update_user_meta( $user_id, 'primary_blog', $blog_id );
-       }
-
-       restore_current_blog();
-
-       $site = get_site( $blog_id );
-
-       /**
-        * Fires immediately after a new site is created.
-        *
-        * @since MU (3.0.0)
-        *
-        * @param int    $blog_id    Site ID.
-        * @param int    $user_id    User ID.
-        * @param string $domain     Site domain.
-        * @param string $path       Site path.
-        * @param int    $network_id Network ID. Only relevant on multi-network installations.
-        * @param array  $meta       Meta data. Used to set initial site options.
-        */
-       do_action( 'wpmu_new_blog', $blog_id, $user_id, $site->domain, $site->path, $site->network_id, $meta );
-
</del><span class="cx" style="display: block; padding: 0 10px">         wp_cache_set( 'last_changed', microtime(), 'sites' );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        return $blog_id;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1382,12 +1347,17 @@
</span><span class="cx" style="display: block; padding: 0 10px">  * the notification email.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @since MU (3.0.0)
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @since 5.0.0 $blog_id now supports input from the {@see 'wp_initialize_site'} action.
</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 int    $blog_id    The new site's ID.
- * @param string $deprecated Not used.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @param WP_Site|int $blog_id    The new site's object or ID.
+ * @param string      $deprecated Not used.
</ins><span class="cx" style="display: block; padding: 0 10px">  * @return bool
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> function newblog_notify_siteadmin( $blog_id, $deprecated = '' ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        if ( is_object( $blog_id ) ) {
+               $blog_id = $blog_id->blog_id;
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         if ( get_site_option( 'registrationnotification' ) != 'yes' ) {
</span><span class="cx" style="display: block; padding: 0 10px">                return false;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1529,101 +1499,6 @@
</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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * Install an empty blog.
- *
- * Creates the new blog tables and options. If calling this function
- * directly, be sure to use switch_to_blog() first, so that $wpdb
- * points to the new blog.
- *
- * @since MU (3.0.0)
- *
- * @global wpdb     $wpdb
- * @global WP_Roles $wp_roles
- *
- * @param int    $blog_id    The value returned by wp_insert_site().
- * @param string $blog_title The title of the new site.
- */
-function install_blog( $blog_id, $blog_title = '' ) {
-       global $wpdb, $wp_roles;
-
-       // Cast for security
-       $blog_id = (int) $blog_id;
-
-       require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
-
-       $suppress = $wpdb->suppress_errors();
-       if ( $wpdb->get_results( "DESCRIBE {$wpdb->posts}" ) ) {
-               die( '<h1>' . __( 'Already Installed' ) . '</h1><p>' . __( 'You appear to have already installed WordPress. To reinstall please clear your old database tables first.' ) . '</p></body></html>' );
-       }
-       $wpdb->suppress_errors( $suppress );
-
-       $url = get_blogaddress_by_id( $blog_id );
-
-       // Set everything up
-       make_db_current_silent( 'blog' );
-       populate_options();
-       populate_roles();
-
-       // populate_roles() clears previous role definitions so we start over.
-       $wp_roles = new WP_Roles();
-
-       $siteurl = $home = untrailingslashit( $url );
-
-       if ( ! is_subdomain_install() ) {
-
-               if ( 'https' === parse_url( get_site_option( 'siteurl' ), PHP_URL_SCHEME ) ) {
-                       $siteurl = set_url_scheme( $siteurl, 'https' );
-               }
-               if ( 'https' === parse_url( get_home_url( get_network()->site_id ), PHP_URL_SCHEME ) ) {
-                       $home = set_url_scheme( $home, 'https' );
-               }
-       }
-
-       update_option( 'siteurl', $siteurl );
-       update_option( 'home', $home );
-
-       if ( get_site_option( 'ms_files_rewriting' ) ) {
-               update_option( 'upload_path', UPLOADBLOGSDIR . "/$blog_id/files" );
-       } else {
-               update_option( 'upload_path', get_blog_option( get_network()->site_id, 'upload_path' ) );
-       }
-
-       update_option( 'blogname', wp_unslash( $blog_title ) );
-       update_option( 'admin_email', '' );
-
-       // remove all perms
-       $table_prefix = $wpdb->get_blog_prefix();
-       delete_metadata( 'user', 0, $table_prefix . 'user_level', null, true ); // delete all
-       delete_metadata( 'user', 0, $table_prefix . 'capabilities', null, true ); // delete all
-}
-
-/**
- * Set blog defaults.
- *
- * This function creates a row in the wp_blogs table.
- *
- * @since MU (3.0.0)
- * @deprecated MU
- * @deprecated Use wp_install_defaults()
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int $blog_id Ignored in this function.
- * @param int $user_id
- */
-function install_blog_defaults( $blog_id, $user_id ) {
-       global $wpdb;
-
-       require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
-
-       $suppress = $wpdb->suppress_errors();
-
-       wp_install_defaults( $user_id );
-
-       $wpdb->suppress_errors( $suppress );
-}
-
-/**
</del><span class="cx" style="display: block; padding: 0 10px">  * Notify a user that their blog activation has been successful.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * Filter {@see 'wpmu_welcome_notification'} to disable or bypass.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2024,14 +1899,24 @@
</span><span class="cx" style="display: block; padding: 0 10px">  * Logs the user email, IP, and registration date of a new site.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @since MU (3.0.0)
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @since 5.0.0 Parameters now support input from the {@see 'wp_initialize_site'} action.
</ins><span class="cx" style="display: block; padding: 0 10px">  *
</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 int $blog_id
- * @param int $user_id
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @param WP_Site|int $blog_id The new site's object or ID.
+ * @param int|array   $user_id User ID, or array of arguments including 'user_id'.
</ins><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> function wpmu_log_new_registrations( $blog_id, $user_id ) {
</span><span class="cx" style="display: block; padding: 0 10px">        global $wpdb;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       if ( is_object( $blog_id ) ) {
+               $blog_id = $blog_id->blog_id;
+       }
+
+       if ( is_array( $user_id ) ) {
+               $user_id = ! empty( $user_id['user_id'] ) ? $user_id['user_id'] : 0;
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         $user = get_userdata( (int) $user_id );
</span><span class="cx" style="display: block; padding: 0 10px">        if ( $user ) {
</span><span class="cx" style="display: block; padding: 0 10px">                $wpdb->insert(
</span></span></pre></div>
<a id="trunktestsphpunittestsmultisitesitephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/multisite/site.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/multisite/site.php      2018-09-23 22:49:02 UTC (rev 43653)
+++ trunk/tests/phpunit/tests/multisite/site.php        2018-09-24 15:08:32 UTC (rev 43654)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -9,10 +9,12 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @group multisite
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        class Tests_Multisite_Site extends WP_UnitTestCase {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                protected $suppress          = false;
-               protected $site_status_hooks = array();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         protected $suppress                = false;
+               protected $site_status_hooks       = array();
+               protected $wp_initialize_site_args = array();
</ins><span class="cx" style="display: block; padding: 0 10px">                 protected static $network_ids;
</span><span class="cx" style="display: block; padding: 0 10px">                protected static $site_ids;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                protected static $uninitialized_site_id;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                function setUp() {
</span><span class="cx" style="display: block; padding: 0 10px">                        global $wpdb;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -56,11 +58,23 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                $id = $factory->blog->create( $id );
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px">                        unset( $id );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+                       remove_action( 'wp_initialize_site', 'wp_initialize_site', 10 );
+                       self::$uninitialized_site_id = wp_insert_site( array(
+                               'domain'  => 'uninitialized.org',
+                               'path'    => '/',
+                               'site_id' => self::$network_ids['make.wordpress.org/'],
+                       ) );
+                       add_action( 'wp_initialize_site', 'wp_initialize_site', 10, 2 );
</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 static function wpTearDownAfterClass() {
</span><span class="cx" style="display: block; padding: 0 10px">                        global $wpdb;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        remove_action( 'wp_uninitialize_site', 'wp_uninitialize_site', 10 );
+                       wp_delete_site( self::$uninitialized_site_id );
+                       add_action( 'wp_uninitialize_site', 'wp_uninitialize_site', 10, 1 );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         foreach ( self::$site_ids as $id ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                wpmu_delete_blog( $id, true );
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1266,6 +1280,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 * @dataProvider data_wp_insert_site
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="cx" style="display: block; padding: 0 10px">                public function test_wp_insert_site( $site_data, $expected_data ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        remove_action( 'wp_initialize_site', 'wp_initialize_site', 10 );
</ins><span class="cx" style="display: block; padding: 0 10px">                         $site_id = wp_insert_site( $site_data );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        $this->assertInternalType( 'integer', $site_id );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1360,6 +1375,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 * @ticket 40364
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="cx" style="display: block; padding: 0 10px">                public function test_wp_insert_site_empty_domain() {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        remove_action( 'wp_initialize_site', 'wp_initialize_site', 10 );
</ins><span class="cx" style="display: block; padding: 0 10px">                         $site_id = wp_insert_site( array( 'public' => 0 ) );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        $this->assertWPError( $site_id );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1497,6 +1513,20 @@
</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">+                 * @ticket 41333
+                */
+               public function test_wp_delete_site_validate_site_deletion_action() {
+                       add_action( 'wp_validate_site_deletion', array( $this, 'action_wp_validate_site_deletion_prevent_deletion' ) );
+                       $result = wp_delete_site( self::$site_ids['make.wordpress.org/'] );
+                       $this->assertWPError( $result );
+                       $this->assertSame( 'action_does_not_like_deletion', $result->get_error_code() );
+               }
+
+               public function action_wp_validate_site_deletion_prevent_deletion( $errors ) {
+                       $errors->add( 'action_does_not_like_deletion', 'You cannot delete this site because the action does not like it.' );
+               }
+
+               /**
</ins><span class="cx" style="display: block; padding: 0 10px">                  * @ticket 40364
</span><span class="cx" style="display: block; padding: 0 10px">                 * @dataProvider data_wp_normalize_site_data
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1709,7 +1739,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="cx" style="display: block; padding: 0 10px">                public function test_site_dates_are_gmt() {
</span><span class="cx" style="display: block; padding: 0 10px">                        $first_date = current_time( 'mysql', true );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $site_id    = wp_insert_site(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+                       remove_action( 'wp_initialize_site', 'wp_initialize_site', 10 );
+                       $site_id = wp_insert_site(
</ins><span class="cx" style="display: block; padding: 0 10px">                                 array(
</span><span class="cx" style="display: block; padding: 0 10px">                                        'domain'     => 'valid-domain.com',
</span><span class="cx" style="display: block; padding: 0 10px">                                        'path'       => '/valid-path/',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2015,6 +2047,277 @@
</span><span class="cx" style="display: block; padding: 0 10px">                public function action_site_status_hook( $site_id ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $this->site_status_hooks[ current_action() ] = $site_id;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               /**
+                * @ticket 41333
+                * @dataProvider data_wp_initialize_site
+                */
+               public function test_wp_initialize_site( $args, $expected_options, $expected_meta ) {
+                       $result = wp_initialize_site( self::$uninitialized_site_id, $args );
+
+                       switch_to_blog( self::$uninitialized_site_id );
+
+                       $options = array();
+                       foreach ( $expected_options as $option => $value ) {
+                               $options[ $option ] = get_option( $option );
+                       }
+
+                       $meta = array();
+                       foreach ( $expected_meta as $meta_key => $value ) {
+                               $meta[ $meta_key ] = get_site_meta( self::$uninitialized_site_id, $meta_key, true );
+                       }
+
+                       restore_current_blog();
+
+                       $initialized = wp_is_site_initialized( self::$uninitialized_site_id );
+
+                       wp_uninitialize_site( self::$uninitialized_site_id );
+
+                       $this->assertTrue( $result );
+                       $this->assertTrue( $initialized );
+                       $this->assertEquals( $expected_options, $options );
+                       $this->assertEquals( $expected_meta, $meta );
+               }
+
+               public function data_wp_initialize_site() {
+                       return array(
+                               array(
+                                       array(),
+                                       array(
+                                               'home'        => 'http://uninitialized.org',
+                                               'siteurl'     => 'http://uninitialized.org',
+                                               'admin_email' => '',
+                                               'blog_public' => '1',
+                                       ),
+                                       array(),
+                               ),
+                               array(
+                                       array(
+                                               'options' => array(
+                                                       'home'    => 'https://uninitialized.org',
+                                                       'siteurl' => 'https://uninitialized.org',
+                                                       'key'     => 'value',
+                                               ),
+                                               'meta'    => array(
+                                                       'key1' => 'value1',
+                                                       'key2' => 'value2',
+                                               ),
+                                       ),
+                                       array(
+                                               'home'    => 'https://uninitialized.org',
+                                               'siteurl' => 'https://uninitialized.org',
+                                               'key'     => 'value',
+                                       ),
+                                       array(
+                                               'key1' => 'value1',
+                                               'key2' => 'value2',
+                                               'key3' => '',
+                                       ),
+                               ),
+                               array(
+                                       array(
+                                               'title'   => 'My New Site',
+                                               'options' => array(
+                                                       'blogdescription' => 'Just My New Site',
+                                               ),
+                                       ),
+                                       array(
+                                               'blogname'        => 'My New Site',
+                                               'blogdescription' => 'Just My New Site',
+                                       ),
+                                       array(),
+                               ),
+                       );
+               }
+
+               /**
+                * @ticket 41333
+                */
+               public function test_wp_initialize_site_user_roles() {
+                       global $wpdb;
+
+                       $result = wp_initialize_site( self::$uninitialized_site_id, array() );
+
+                       switch_to_blog( self::$uninitialized_site_id );
+                       $table_prefix = $wpdb->get_blog_prefix( self::$uninitialized_site_id );
+                       $roles        = get_option( $table_prefix . 'user_roles' );
+                       restore_current_blog();
+
+                       wp_uninitialize_site( self::$uninitialized_site_id );
+
+                       $this->assertTrue( $result );
+                       $this->assertEqualSets(
+                               array(
+                                       'administrator',
+                                       'editor',
+                                       'author',
+                                       'contributor',
+                                       'subscriber',
+                               ),
+                               array_keys( $roles )
+                       );
+               }
+
+               /**
+                * @ticket 41333
+                */
+               public function test_wp_initialize_site_user_is_admin() {
+                       $result = wp_initialize_site( self::$uninitialized_site_id, array( 'user_id' => 1 ) );
+
+                       switch_to_blog( self::$uninitialized_site_id );
+                       $user_is_admin = user_can( 1, 'manage_options' );
+                       $admin_email   = get_option( 'admin_email' );
+                       restore_current_blog();
+
+                       wp_uninitialize_site( self::$uninitialized_site_id );
+
+                       $this->assertTrue( $result );
+                       $this->assertTrue( $user_is_admin );
+                       $this->assertEquals( get_userdata( 1 )->user_email, $admin_email );
+               }
+
+               /**
+                * @ticket 41333
+                */
+               public function test_wp_initialize_site_args_filter() {
+                       add_filter( 'wp_initialize_site_args', array( $this, 'filter_wp_initialize_site_args' ), 10, 3 );
+                       $result = wp_initialize_site( self::$uninitialized_site_id, array( 'title' => 'My Site' ) );
+
+                       switch_to_blog( self::$uninitialized_site_id );
+                       $site_title = get_option( 'blogname' );
+                       restore_current_blog();
+
+                       wp_uninitialize_site( self::$uninitialized_site_id );
+
+                       $this->assertSame(
+                               sprintf( 'My Site %1$d in Network %2$d', self::$uninitialized_site_id, get_site( self::$uninitialized_site_id )->network_id ),
+                               $site_title
+                       );
+               }
+
+               public function filter_wp_initialize_site_args( $args, $site, $network ) {
+                       $args['title'] = sprintf( 'My Site %1$d in Network %2$d', $site->id, $network->id );
+
+                       return $args;
+               }
+
+               /**
+                * @ticket 41333
+                */
+               public function test_wp_initialize_site_empty_id() {
+                       $result = wp_initialize_site( 0 );
+                       $this->assertWPError( $result );
+                       $this->assertSame( 'site_empty_id', $result->get_error_code() );
+               }
+
+               /**
+                * @ticket 41333
+                */
+               public function test_wp_initialize_site_invalid_id() {
+                       $result = wp_initialize_site( 123 );
+                       $this->assertWPError( $result );
+                       $this->assertSame( 'site_invalid_id', $result->get_error_code() );
+               }
+
+               /**
+                * @ticket 41333
+                */
+               public function test_wp_initialize_site_already_initialized() {
+                       $result = wp_initialize_site( get_current_blog_id() );
+                       $this->assertWPError( $result );
+                       $this->assertSame( 'site_already_initialized', $result->get_error_code() );
+               }
+
+               /**
+                * @ticket 41333
+                */
+               public function test_wp_uninitialize_site() {
+                       $site_id = self::factory()->blog->create();
+
+                       $result = wp_uninitialize_site( $site_id );
+                       $this->assertTrue( $result );
+                       $this->assertFalse( wp_is_site_initialized( $site_id ) );
+               }
+
+               /**
+                * @ticket 41333
+                */
+               public function test_wp_uninitialize_site_empty_id() {
+                       $result = wp_uninitialize_site( 0 );
+                       $this->assertWPError( $result );
+                       $this->assertSame( 'site_empty_id', $result->get_error_code() );
+               }
+
+               /**
+                * @ticket 41333
+                */
+               public function test_wp_uninitialize_site_invalid_id() {
+                       $result = wp_uninitialize_site( 123 );
+                       $this->assertWPError( $result );
+                       $this->assertSame( 'site_invalid_id', $result->get_error_code() );
+               }
+
+               /**
+                * @ticket 41333
+                */
+               public function test_wp_uninitialize_site_already_uninitialized() {
+                       $result = wp_uninitialize_site( self::$uninitialized_site_id );
+                       $this->assertWPError( $result );
+                       $this->assertSame( 'site_already_uninitialized', $result->get_error_code() );
+               }
+
+               /**
+                * @ticket 41333
+                */
+               public function test_wp_is_site_initialized() {
+                       $this->assertTrue( wp_is_site_initialized( get_current_blog_id() ) );
+                       $this->assertFalse( wp_is_site_initialized( self::$uninitialized_site_id ) );
+               }
+
+               /**
+                * @ticket 41333
+                */
+               public function test_wp_is_site_initialized_prefilter() {
+                       add_filter( 'pre_wp_is_site_initialized', '__return_false' );
+                       $this->assertFalse( wp_is_site_initialized( get_current_blog_id() ) );
+
+                       add_filter( 'pre_wp_is_site_initialized', '__return_true' );
+                       $this->assertTrue( wp_is_site_initialized( self::$uninitialized_site_id ) );
+               }
+
+               /**
+                * @ticket 41333
+                */
+               public function test_wp_insert_site_forwards_args_to_wp_initialize_site() {
+                       $args = array(
+                               'user_id' => 1,
+                               'title'   => 'My Site',
+                               'options' => array( 'option1' => 'value1' ),
+                               'meta'    => array( 'meta1' => 'value1' ),
+                       );
+
+                       add_filter( 'wp_initialize_site_args', array( $this, 'filter_wp_initialize_site_args_catch_args' ) );
+                       $site_id = wp_insert_site(
+                               array_merge(
+                                       array(
+                                               'domain' => 'testsite.org',
+                                               'path'   => '/',
+                                       ),
+                                       $args
+                               )
+                       );
+                       $passed_args = $this->wp_initialize_site_args;
+
+                       $this->wp_initialize_site_args = null;
+
+                       $this->assertEqualSetsWithIndex( $args, $passed_args );
+               }
+
+               public function filter_wp_initialize_site_args_catch_args( $args ) {
+                       $this->wp_initialize_site_args = $args;
+
+                       return $args;
+               }
</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"> endif;
</span></span></pre>
</div>
</div>

</body>
</html>