From 2b10923a28c488497bc55d5e1c147f95c3d6b477 Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Mon, 24 Sep 2018 15:09:26 +0000 Subject: [PATCH] Multisite: Introduce a site initialization and uninitialization API. This changeset makes the new CRUD API for sites introduced in [43548] 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 #41333. See #40364. Built from https://develop.svn.wordpress.org/trunk@43654 git-svn-id: http://core.svn.wordpress.org/trunk@43483 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-admin/includes/ms.php | 116 ++------ wp-includes/ms-blogs.php | 428 ++++++++++++++++++++++++++++- wp-includes/ms-default-filters.php | 6 +- wp-includes/ms-deprecated.php | 100 +++++++ wp-includes/ms-functions.php | 175 ++---------- wp-includes/version.php | 2 +- 6 files changed, 574 insertions(+), 253 deletions(-) diff --git a/wp-admin/includes/ms.php b/wp-admin/includes/ms.php index 2b60a22b87..8672946da5 100644 --- a/wp-admin/includes/ms.php +++ b/wp-admin/includes/ms.php @@ -73,31 +73,6 @@ function wpmu_delete_blog( $blog_id, $drop = false ) { } $blog = get_site( $blog_id ); - /** - * 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 ); - - $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 ); $current_network = get_network(); @@ -119,88 +94,31 @@ function wpmu_delete_blog( $blog_id, $drop = false ) { } if ( $drop ) { - $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 ); - } - } - wp_delete_site( $blog_id ); + } else { + /** This action is documented in wp-includes/ms-blogs.php */ + do_action_deprecated( 'delete_blog', array( $blog_id, false ), '5.0.0' ); - /** - * 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; + $users = get_users( + array( + 'blog_id' => $blog_id, + 'fields' => 'ids', + ) + ); - 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 ); - } - $index++; - } - - $stack = array_reverse( $stack ); // Last added dirs are deepest - foreach ( (array) $stack as $dir ) { - if ( $dir != $top_dir ) { - @rmdir( $dir ); + // Remove users from this blog. + if ( ! empty( $users ) ) { + foreach ( $users as $user_id ) { + remove_user_from_blog( $user_id, $blog_id ); } } - clean_blog_cache( $blog ); + update_blog_status( $blog_id, 'deleted', 1 ); + + /** This action is documented in wp-includes/ms-blogs.php */ + do_action_deprecated( 'deleted_blog', array( $blog_id, false ), '5.0.0' ); } - /** - * 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 ); - if ( $switch ) { restore_current_blog(); } diff --git a/wp-includes/ms-blogs.php b/wp-includes/ms-blogs.php index a910789cb6..25424416fb 100644 --- a/wp-includes/ms-blogs.php +++ b/wp-includes/ms-blogs.php @@ -442,6 +442,12 @@ function wp_insert_site( array $data ) { 'lang_id' => 0, ); + // 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'] ); + } + $data = wp_prepare_site_data( $data, $defaults ); if ( is_wp_error( $data ) ) { return $data; @@ -464,6 +470,37 @@ function wp_insert_site( array $data ) { */ do_action( 'wp_insert_site', $new_site ); + /** + * 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' ); + } + return (int) $new_site->id; } @@ -543,6 +580,52 @@ function wp_delete_site( $site_id ) { return new WP_Error( 'site_not_exist', __( 'Site does not exist.' ) ); } + $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 ); + } + } + if ( false === $wpdb->delete( $wpdb->blogs, array( 'blog_id' => $old_site->id ) ) ) { return new WP_Error( 'db_delete_error', __( 'Could not delete site from the database.' ), $wpdb->last_error ); } @@ -558,6 +641,17 @@ function wp_delete_site( $site_id ) { */ do_action( 'wp_delete_site', $old_site ); + /** + * 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' ); + return $old_site; } @@ -619,7 +713,7 @@ function _prime_site_caches( $ids, $update_meta_cache = true ) { $non_cached_ids = _get_non_cached_ids( $ids, 'sites' ); if ( ! empty( $non_cached_ids ) ) { - $fresh_sites = $wpdb->get_results( sprintf( "SELECT * FROM $wpdb->blogs WHERE blog_id IN (%s)", join( ',', array_map( 'intval', $non_cached_ids ) ) ) ); + $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 update_site_cache( $fresh_sites, $update_meta_cache ); } @@ -912,6 +1006,322 @@ function wp_validate_site_data( $errors, $data, $old_site = null ) { } } +/** + * 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; +} + /** * Retrieve option value for a given blog id based on name of option. * @@ -1621,7 +2031,7 @@ function _prime_network_caches( $network_ids ) { $non_cached_ids = _get_non_cached_ids( $network_ids, 'networks' ); if ( ! empty( $non_cached_ids ) ) { - $fresh_networks = $wpdb->get_results( sprintf( "SELECT $wpdb->site.* FROM $wpdb->site WHERE id IN (%s)", join( ',', array_map( 'intval', $non_cached_ids ) ) ) ); + $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 update_network_cache( $fresh_networks ); } @@ -1757,7 +2167,7 @@ function wp_maybe_transition_site_statuses_on_update( $new_site, $old_site = nul } if ( $new_site->spam != $old_site->spam ) { - if ( $new_site->spam == 1 ) { + if ( 1 == $new_site->spam ) { /** * Fires when the 'spam' status is added to a site. @@ -1781,7 +2191,7 @@ function wp_maybe_transition_site_statuses_on_update( $new_site, $old_site = nul } if ( $new_site->mature != $old_site->mature ) { - if ( $new_site->mature == 1 ) { + if ( 1 == $new_site->mature ) { /** * Fires when the 'mature' status is added to a site. @@ -1805,7 +2215,7 @@ function wp_maybe_transition_site_statuses_on_update( $new_site, $old_site = nul } if ( $new_site->archived != $old_site->archived ) { - if ( $new_site->archived == 1 ) { + if ( 1 == $new_site->archived ) { /** * Fires when the 'archived' status is added to a site. @@ -1829,7 +2239,7 @@ function wp_maybe_transition_site_statuses_on_update( $new_site, $old_site = nul } if ( $new_site->deleted != $old_site->deleted ) { - if ( $new_site->deleted == 1 ) { + if ( 1 == $new_site->deleted ) { /** * Fires when the 'deleted' status is added to a site. @@ -1889,5 +2299,11 @@ function wp_maybe_clean_new_site_cache_on_update( $new_site, $old_site ) { * @param string $public The value of the site status. */ function wp_update_blog_public_option_on_site_update( $site_id, $public ) { + + // Bail if the site's database tables do not exist (yet). + if ( ! wp_is_site_initialized( $site_id ) ) { + return; + } + update_blog_option( $site_id, 'blog_public', $public ); } diff --git a/wp-includes/ms-default-filters.php b/wp-includes/ms-default-filters.php index 4542eccf1f..93fbca193c 100644 --- a/wp-includes/ms-default-filters.php +++ b/wp-includes/ms-default-filters.php @@ -37,8 +37,6 @@ add_action( 'switch_blog', 'wp_switch_roles_and_user', 1, 2 ); // Blogs add_filter( 'wpmu_validate_blog_signup', 'signup_nonce_check' ); -add_action( 'wpmu_new_blog', 'wpmu_log_new_registrations', 10, 2 ); -add_action( 'wpmu_new_blog', 'newblog_notify_siteadmin', 10, 2 ); add_action( 'wpmu_activate_blog', 'wpmu_welcome_notification', 10, 5 ); add_action( 'after_signup_site', 'wpmu_signup_blog_notification', 10, 7 ); add_filter( 'wp_normalize_site_data', 'wp_normalize_site_data', 10, 1 ); @@ -49,6 +47,10 @@ add_action( 'wp_delete_site', 'wp_maybe_update_network_site_counts_on_update', 1 add_action( 'wp_insert_site', 'wp_maybe_transition_site_statuses_on_update', 10, 1 ); add_action( 'wp_update_site', 'wp_maybe_transition_site_statuses_on_update', 10, 2 ); add_action( 'wp_update_site', 'wp_maybe_clean_new_site_cache_on_update', 10, 2 ); +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 ); add_action( 'update_blog_public', 'wp_update_blog_public_option_on_site_update', 1, 2 ); // Register Nonce diff --git a/wp-includes/ms-deprecated.php b/wp-includes/ms-deprecated.php index 2b9ff493bb..e25384ea01 100644 --- a/wp-includes/ms-deprecated.php +++ b/wp-includes/ms-deprecated.php @@ -580,3 +580,103 @@ function insert_blog($domain, $path, $site_id) { return $site_id; } + +/** + * 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( '

' . __( 'Already Installed' ) . '

' . __( 'You appear to have already installed WordPress. To reinstall please clear your old database tables first.' ) . '

' ); + } + $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 ); +} diff --git a/wp-includes/ms-functions.php b/wp-includes/ms-functions.php index 80c988ae36..eca785f201 100644 --- a/wp-includes/ms-functions.php +++ b/wp-includes/ms-functions.php @@ -1285,7 +1285,7 @@ function wpmu_create_user( $user_name, $password, $email ) { * @param string $path The new site's path. * @param string $title The new site's title. * @param int $user_id The user ID of the new site's admin. - * @param array $meta Optional. Array of key=>value pairs used to set initial site options. + * @param array $options Optional. Array of key=>value pairs used to set initial site options. * If valid status keys are included ('public', 'archived', 'mature', * 'spam', 'deleted', or 'lang_id') the given site status(es) will be * updated. Otherwise, keys and values will be used to set options for @@ -1293,12 +1293,11 @@ function wpmu_create_user( $user_name, $password, $email ) { * @param int $network_id Optional. Network ID. Only relevant on multi-network installations. * @return int|WP_Error Returns WP_Error object on failure, the new site ID on success. */ -function wpmu_create_blog( $domain, $path, $title, $user_id, $meta = array(), $network_id = 1 ) { +function wpmu_create_blog( $domain, $path, $title, $user_id, $options = array(), $network_id = 1 ) { $defaults = array( 'public' => 0, - 'WPLANG' => get_network_option( $network_id, 'WPLANG' ), ); - $meta = wp_parse_args( $meta, $defaults ); + $options = wp_parse_args( $options, $defaults ); $title = strip_tags( $title ); $user_id = (int) $user_id; @@ -1320,56 +1319,22 @@ function wpmu_create_blog( $domain, $path, $title, $user_id, $meta = array(), $n 'path' => $path, 'network_id' => $network_id, ), - array_intersect_key( - $meta, - array_flip( $site_data_whitelist ) - ) + array_intersect_key( $options, array_flip( $site_data_whitelist ) ) ); - $meta = array_diff_key( $meta, array_flip( $site_data_whitelist ) ); + // 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 ) ), + ); - 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 ); + $blog_id = wp_insert_site( array_merge( $site_data, $site_initialization_data ) ); if ( is_wp_error( $blog_id ) ) { return $blog_id; } - 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 ); - wp_cache_set( 'last_changed', microtime(), 'sites' ); return $blog_id; @@ -1382,12 +1347,17 @@ function wpmu_create_blog( $domain, $path, $title, $user_id, $meta = array(), $n * the notification email. * * @since MU (3.0.0) + * @since 5.0.0 $blog_id now supports input from the {@see 'wp_initialize_site'} action. * - * @param int $blog_id The new site's ID. - * @param string $deprecated Not used. + * @param WP_Site|int $blog_id The new site's object or ID. + * @param string $deprecated Not used. * @return bool */ function newblog_notify_siteadmin( $blog_id, $deprecated = '' ) { + if ( is_object( $blog_id ) ) { + $blog_id = $blog_id->blog_id; + } + if ( get_site_option( 'registrationnotification' ) != 'yes' ) { return false; } @@ -1528,101 +1498,6 @@ function domain_exists( $domain, $path, $network_id = 1 ) { return apply_filters( 'domain_exists', $result, $domain, $path, $network_id ); } -/** - * 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( '

' . __( 'Already Installed' ) . '

' . __( 'You appear to have already installed WordPress. To reinstall please clear your old database tables first.' ) . '

' ); - } - $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 ); -} - /** * Notify a user that their blog activation has been successful. * @@ -2024,14 +1899,24 @@ function update_posts_count( $deprecated = '' ) { * Logs the user email, IP, and registration date of a new site. * * @since MU (3.0.0) + * @since 5.0.0 Parameters now support input from the {@see 'wp_initialize_site'} action. * * @global wpdb $wpdb WordPress database abstraction object. * - * @param int $blog_id - * @param int $user_id + * @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'. */ function wpmu_log_new_registrations( $blog_id, $user_id ) { global $wpdb; + + 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; + } + $user = get_userdata( (int) $user_id ); if ( $user ) { $wpdb->insert( diff --git a/wp-includes/version.php b/wp-includes/version.php index 2511917c59..0d8a3b5c2f 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -4,7 +4,7 @@ * * @global string $wp_version */ -$wp_version = '5.0-alpha-43653'; +$wp_version = '5.0-alpha-43654'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.