From 12ce2cffbc551cdd4ee5b646ef1feaad17831c6d Mon Sep 17 00:00:00 2001 From: Mike Wilkerson <11575183+mlwilkerson@users.noreply.github.com> Date: Mon, 24 Oct 2022 17:31:10 -0700 Subject: [PATCH 01/25] add wp-multi-network-functions --- tests/_support/wp-multi-network-functions.php | 1017 +++++++++++++++++ 1 file changed, 1017 insertions(+) create mode 100644 tests/_support/wp-multi-network-functions.php diff --git a/tests/_support/wp-multi-network-functions.php b/tests/_support/wp-multi-network-functions.php new file mode 100644 index 000000000..2a0877d3b --- /dev/null +++ b/tests/_support/wp-multi-network-functions.php @@ -0,0 +1,1017 @@ +ID; + $user_login = $user_info->user_login; + } else { + $user_id = (int) $user_id; + $user_info = get_userdata( $user_id ); + $user_login = $user_info->user_login; + } + + /** + * Filters the networks a user is the administrator of, to short-circuit the process. + * + * @since 2.0.0 + * + * @param array|bool|null List of network IDs or false. Anything but null will short-circuit + * the process. + * @param int User ID for which the networks should be returned. + */ + $my_networks = apply_filters( 'networks_pre_user_is_network_admin', null, $user_id ); + if ( null !== $my_networks ) { + if ( empty( $my_networks ) ) { + $my_networks = false; + } + + /** + * Filters the networks a user is the administrator of. + * + * @since 2.0.0 + * + * @param array|bool List of network IDs or false if no networks for the user. + * @param int User ID for which the networks should be returned. + */ + return apply_filters( 'networks_user_is_network_admin', $my_networks, $user_id ); + } + + $my_networks = array(); + + if ( is_multisite() ) { + + // phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery,WordPress.VIP.DirectDatabaseQuery.NoCaching + $my_networks = array_map( 'intval', $wpdb->get_col( $wpdb->prepare( "SELECT site_id FROM {$wpdb->sitemeta} WHERE meta_key = %s AND meta_value LIKE %s", 'site_admins', '%"' . $user_login . '"%' ) ) ); + } + + // If there are no networks, return false. + if ( empty( $my_networks ) ) { + $my_networks = false; + } + + /** This filter is documented in wp-multi-network/includes/functions.php */ + return apply_filters( 'networks_user_is_network_admin', $my_networks, $user_id ); + } +endif; + +if ( ! function_exists( 'get_main_site_for_network' ) ) : + /** + * Gets the main site for a network. + * + * @since 1.3.0 + * + * @param int|WP_Network $network Optional. Network ID or object. Default is the current network. + * @return int Main site ID for the network. + */ + function get_main_site_for_network( $network = null ) { + $network = get_network( $network ); + + // Bail if network not found. + if ( empty( $network ) ) { + return false; + } + + if ( ! empty( $network->blog_id ) ) { + $primary_id = $network->blog_id; + } else { + $primary_id = get_network_option( $network->id, 'main_site' ); + + if ( false === $primary_id ) { + $sites = get_sites( array( + 'network_id' => $network->id, + 'domain' => $network->domain, + 'path' => $network->path, + 'fields' => 'ids', + 'number' => 1, + ) ); + + $primary_id = ! empty( $sites ) ? reset( $sites ) : 0; + + if ( ! empty( $primary_id ) ) { + update_network_option( $network->id, 'main_site', $primary_id ); + } + } + } + + return (int) $primary_id; + } +endif; + +if ( ! function_exists( 'is_main_site_for_network' ) ) : + /** + * Checks whether a main site is a given site for a network. + * + * @since 1.7.0 + * + * @param int $site_id Site ID to check if it's the main site. + * @return bool True if it is the main site, false otherwise. + */ + function is_main_site_for_network( $site_id ) { + $site = get_site( $site_id ); + $main = get_main_site_id( $site->network_id ); + + // Bail if no site or network was found. + if ( empty( $main ) ) { + return false; + } + + return (int) $main === (int) $site_id; + } +endif; + +if ( ! function_exists( 'get_network_name' ) ) : + /** + * Gets the name of the current network. + * + * @since 1.7.0 + * + * @global WP_Network $current_site Current network object. + * + * @return string Name of the current network. + */ + function get_network_name() { + global $current_site; + + $site_name = get_site_option( 'site_name' ); + if ( ! $site_name ) { + $site_name = ucfirst( $current_site->domain ); + } + + return $site_name; + } +endif; + +if ( ! function_exists( 'switch_to_network' ) ) : + /** + * Switches the current context to the given network. + * + * @since 1.3.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * @global bool $switched_network Whether the network context is switched. + * @global array $switched_network_stack Stack of switched network objects. + * @global WP_Network $current_site Current network object. + * + * @param int $new_network Optional. ID of the network to switch to. Default is the current network ID. + * @param bool $validate Optional. Whether to validate that the given network exists. Default false. + * @return bool True on successful switch, false on failure. + */ + function switch_to_network( $new_network = 0, $validate = false ) { + global $wpdb, $switched_network, $switched_network_stack, $current_site; + + if ( empty( $new_network ) ) { + $new_network = $current_site->id; + } + + // Bail if network does not exist. + if ( ( true === (bool) $validate ) && ! get_network( $new_network ) ) { + return false; + } + + if ( empty( $switched_network_stack ) ) { + $switched_network_stack = array(); + } + + array_push( $switched_network_stack, $current_site ); + + // If the same network, fire the hook and bail. + if ( $current_site->id === $new_network ) { + + /** + * Fires when the current network context is switched. + * + * @since 1.3.0 + * + * @param int $new_network_id ID of the network that is being switched to. + * @param int $old_network_id ID of the previously current network. + */ + do_action( 'switch_network', $current_site->id, $current_site->id ); + $switched_network = true; + return true; + } + + $prev_site_id = $current_site->id; + $current_site = get_network( $new_network ); // phpcs:ignore WordPress.Variables.GlobalVariables.OverrideProhibited + + // Populate extra properties if not set already. + if ( ! isset( $current_site->blog_id ) ) { + $current_site->blog_id = get_main_site_id( $current_site->id ); + } + if ( ! isset( $current_site->site_name ) ) { + $current_site->site_name = get_network_name(); + } + + // Update network globals. + $wpdb->siteid = $current_site->id; + $GLOBALS['site_id'] = $current_site->id; // phpcs:ignore WordPress.Variables.GlobalVariables.OverrideProhibited + $GLOBALS['domain'] = $current_site->domain; // phpcs:ignore WordPress.Variables.GlobalVariables.OverrideProhibited + + /** This action is documented in wp-multi-network/includes/functions.php */ + do_action( 'switch_network', $current_site->id, $prev_site_id ); + + $switched_network = true; + + return true; + } +endif; + +if ( ! function_exists( 'restore_current_network' ) ) : + /** + * Restores the current context to the previous network. + * + * @since 1.3.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * @global bool $switched_network Whether the network context is switched. + * @global array $switched_network_stack Stack of switched network objects. + * @global WP_Network $current_site Current network object. + * + * @return bool True on successful restore, false on failure. + */ + function restore_current_network() { + global $wpdb, $switched_network, $switched_network_stack, $current_site; + + // Bail if not switched. + if ( true !== $switched_network ) { + return false; + } + + // Bail if no stack. + if ( ! is_array( $switched_network_stack ) ) { + return false; + } + + $new_network = array_pop( $switched_network_stack ); + + // If the same network, fire the hook and bail. + if ( (int) $new_network->id === (int) $current_site->id ) { + + /** This action is documented in wp-multi-network/includes/functions.php */ + do_action( 'switch_network', $current_site->id, $current_site->id ); + $switched_network = ( ! empty( $switched_network_stack ) ); + return true; + } + + $prev_network_id = $current_site->id; + + // Update network globals. + $current_site = $new_network; // phpcs:ignore WordPress.Variables.GlobalVariables.OverrideProhibited + $wpdb->siteid = $new_network->id; + $GLOBALS['site_id'] = $new_network->id; // phpcs:ignore WordPress.Variables.GlobalVariables.OverrideProhibited + $GLOBALS['domain'] = $new_network->domain; // phpcs:ignore WordPress.Variables.GlobalVariables.OverrideProhibited + + /** This action is documented in wp-multi-network/includes/functions.php */ + do_action( 'switch_network', $new_network->id, $prev_network_id ); + + $switched_network = ! empty( $switched_network_stack ); + + return true; + } +endif; + +if ( ! function_exists( 'insert_network' ) ) : + /** + * Stores basic network info in the sites table. + * + * This function creates a row in the wp_site table and returns + * the new network ID. It is the first step in creating a new network. + * + * @since 2.2.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param string $domain The domain of the new network. + * @param string $path The path of the new network. + * @return int|bool|WP_Error The ID of the new network, or false on failure. + */ + function insert_network( $domain = '', $path = '/' ) { + global $wpdb; + + // Bail if no domain or path. + if ( empty( $domain ) ) { + return new WP_Error( + 'network_empty', + esc_html__( 'Domain and path cannot be empty.', 'wp-multi-network' ) + ); + } + + // Always end path with a slash. + $path = trailingslashit( $path ); + + // Query for networks. + $networks = get_networks( + array( + 'domain' => $domain, + 'path' => $path, + 'number' => '1', + ) + ); + + // Bail if network already exists. + if ( ! empty( $networks ) ) { + return new WP_Error( + 'network_exists', + esc_html__( 'Network already exists.', 'wp-multi-network' ) + ); + } + + // phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery + $result = $wpdb->insert( + $wpdb->site, + array( + 'domain' => $domain, + 'path' => $path, + ) + ); + + // Bail if database error. + if ( is_wp_error( $result ) ) { + return $result; + } + + // Bail if no result. + if ( empty( $result ) ) { + return false; + } + + // Cast return value as int. + $network_id = (int) $wpdb->insert_id; + + // Clean the network cache. + clean_network_cache( $network_id ); + + // Return network ID. + return $network_id; + } +endif; + +if ( ! function_exists( 'add_network' ) ) : + /** + * Adds a new network. + * + * @since 1.3.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param array $args { + * Array of network arguments. + * + * @type string $domain Domain name for new network - for VHOST=no, + * this should be FQDN, otherwise domain only. + * @type string $path Path to root of network hierarchy - should + * be '/' unless WP is cohabiting with another + * product on a domain. + * @type string $site_name Name of the root blog to be created on + * the new network. + * @type string $network_name Name of the new network. + * @type integer $user_id ID of the user to add as the site owner. + * Defaults to current user ID. + * @type integer $network_admin_id ID of the user to add as the network administrator. + * Defaults to current user ID. + * @type array $meta Array of metadata to save to this network. + * Defaults to array( 'public' => false ). + * @type integer $clone_network ID of network whose networkmeta values are + * to be copied - default NULL. + * @type array $options_to_clone Override default network meta options to copy + * when cloning - default NULL. + * } + * @return int|WP_Error ID of newly created network, or WP_Error on failure. + */ + function add_network( $args = array() ) { + global $wpdb, $wp_version, $wp_db_version; + + $func_args = func_get_args(); + + // Backward compatibility with old method of passing arguments. + if ( ! is_array( $args ) || count( $func_args ) > 1 ) { + + // Log the deprecated arguments. + _deprecated_argument( + __METHOD__, + '1.7.0', + sprintf( + /* translators: 1: method name, 2: file name */ + esc_html__( 'Arguments passed to %1$s should be in an associative array. See the inline documentation at %2$s for more details.', 'wp-multi-network' ), + __METHOD__, + __FILE__ + ) + ); + + // Juggle function parameters. + $old_args_keys = array( + 0 => 'domain', + 1 => 'path', + 2 => 'site_name', + 3 => 'clone_network', + 4 => 'options_to_clone', + ); + + // Reset arguments to empty array. + $args = array(); + + // Loop through deprecated keys and add items to array. + foreach ( $old_args_keys as $arg_num => $arg_key ) { + if ( isset( $func_args[ $arg_num ] ) ) { + $args[ $arg_key ] = $func_args[ $arg_num ]; + } + } + } + + // Get the current user ID. + $current_user_id = get_current_user_id(); + + // Default site meta. + $default_site_meta = array( + 'public' => get_option( 'blog_public', false ), + ); + + // Default network meta. + $default_network_meta = array( + 'wpmu_upgrade_site' => $wp_db_version, + 'initial_db_version' => $wp_db_version, + ); + + // Parse all of the arguments. + $r = wp_parse_args( $args, array( + + // Site & network arguments. + 'domain' => '', + 'path' => '/', + + // Site arguments. + 'site_name' => esc_attr__( 'New Site', 'wp-multi-network' ), + 'user_id' => $current_user_id, + 'meta' => $default_site_meta, + + // Network arguments. + 'network_name' => esc_attr__( 'New Network', 'wp-multi-network' ), + 'network_admin_id' => $current_user_id, + 'network_meta' => $default_network_meta, + 'clone_network' => false, + 'options_to_clone' => array_keys( network_options_to_copy() ), + ) ); + + // Bail if no user with the given ID for the site exists. + if ( empty( $r['user_id'] ) || ! get_userdata( $r['user_id'] ) ) { + return new WP_Error( + 'network_user', + esc_html__( 'User does not exist.', 'wp-multi-network' ), + array( + 'status' => 403, + ) + ); + } + + // Bail if no user with the given ID for the network exists. + if ( empty( $r['network_admin_id'] ) || ! get_userdata( $r['network_admin_id'] ) ) { + return new WP_Error( + 'network_super_admin', + esc_html__( 'User does not exist.', 'wp-multi-network' ), + array( + 'status' => 403, + ) + ); + } + + // Strip spaces out of domain & path. + $r['domain'] = str_replace( ' ', '', strtolower( $r['domain'] ) ); + $r['path'] = str_replace( ' ', '', strtolower( $r['path'] ) ); + + // Insert the new network. + $new_network_id = insert_network( $r['domain'], $r['path'] ); + + // Bail if insert returned an error. + if ( empty( $new_network_id ) || is_wp_error( $new_network_id ) ) { + return $new_network_id; + } + + // Set the installation constant to true. + if ( ! defined( 'WP_INSTALLING' ) ) { + define( 'WP_INSTALLING', true ); + } + + // Switch to new network. + switch_to_network( $new_network_id ); + + // Make sure upload constants are defined. + ms_upload_constants(); + + // Attempt to create the site. + $new_blog_id = wpmu_create_blog( + $r['domain'], + $r['path'], + $r['site_name'], + $r['user_id'], + $r['meta'], + $new_network_id + ); + + // Grant super admin priviledges. + grant_super_admin( $r['network_admin_id'] ); + + // Restore current network. + restore_current_network(); + + // Bail if main site could not be created. + if ( is_wp_error( $new_blog_id ) ) { + return $new_blog_id; + } + + $r['network_meta']['main_site'] = $new_blog_id; + + if ( empty( $r['network_meta']['site_name'] ) ) { + $r['network_meta']['site_name'] = ! empty( $r['network_name'] ) + ? $r['network_name'] + : $r['site_name']; + } + + foreach ( $r['network_meta'] as $key => $value ) { + update_network_option( $new_network_id, $key, $value ); + } + + // Fix upload path and URLs in WP < 3.7. + $use_files_rewriting = defined( 'SITE_ID_CURRENT_SITE' ) && get_network( SITE_ID_CURRENT_SITE ) + ? get_network_option( SITE_ID_CURRENT_SITE, 'ms_files_rewriting' ) + : get_site_option( 'ms_files_rewriting' ); + + // Not using rewriting, and using a newer version of WordPress than 3.7. + if ( empty( $use_files_rewriting ) && version_compare( $wp_version, '3.7', '>' ) ) { + + // WP_CONTENT_URL is locked to the current site and can't be overridden, + // so we have to replace the hostname the hard way. + $current_siteurl = get_option( 'siteurl' ); + $new_siteurl = untrailingslashit( get_blogaddress_by_id( $new_blog_id ) ); + $upload_url = str_replace( $current_siteurl, $new_siteurl, WP_CONTENT_URL ); + $upload_url = $upload_url . '/uploads'; + + $upload_dir = WP_CONTENT_DIR; + if ( 0 === strpos( $upload_dir, ABSPATH ) ) { + $upload_dir = substr( $upload_dir, strlen( ABSPATH ) ); + } + $upload_dir .= '/uploads'; + + if ( defined( 'MULTISITE' ) ) { + $ms_dir = '/sites/' . $new_blog_id; + } else { + $ms_dir = '/' . $new_blog_id; + } + + $upload_dir .= $ms_dir; + $upload_url .= $ms_dir; + + update_blog_option( $new_blog_id, 'upload_path', $upload_dir ); + update_blog_option( $new_blog_id, 'upload_url_path', $upload_url ); + } + + // Clone network meta from existing network. + if ( ! empty( $r['clone_network'] ) && get_network( $r['clone_network'] ) ) { + + // Define empty options cache array. + $options_cache = array(); + + // Get the values of options to clone. + foreach ( $r['options_to_clone'] as $option ) { + $options_cache[ $option ] = get_network_option( $r['clone_network'], $option ); + } + + // Clone options. + foreach ( $r['options_to_clone'] as $option ) { + + // Skip if not set. + if ( ! isset( $options_cache[ $option ] ) ) { + continue; + } + + // Fix for bug that prevents writing the ms_files_rewriting value for new networks. + if ( 'ms_files_rewriting' === $option ) { + // phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery + $wpdb->insert( $wpdb->sitemeta, array( + 'site_id' => $new_network_id, + // phpcs:ignore WordPress.VIP.SlowDBQuery + 'meta_key' => $option, + // phpcs:ignore WordPress.VIP.SlowDBQuery + 'meta_value' => $options_cache[ $option ], + ) ); + } else { + update_network_option( $new_network_id, $option, $options_cache[ $option ] ); + } + } + } + + // Update network counts. + wp_update_network_counts( $new_network_id ); + + // Clean the network cache. + clean_network_cache( $new_network_id ); + + /** + * Fires after a new network has been added. + * + * @since 1.3.0 + * + * @param int $new_network_id ID of the added network. + * @param array $r Full associative array of network arguments. + */ + do_action( 'add_network', $new_network_id, $r ); + + // Return new network ID. + return $new_network_id; + } +endif; + +if ( ! function_exists( 'update_network' ) ) : + /** + * Modifies the domain/path of a network, and updates all of its sites. + * + * @since 1.3.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param int $id ID of network to modify. + * @param string $domain New domain for network. + * @param string $path New path for network. + * @return bool|WP_Error True on success, or WP_Error on failure. + */ + function update_network( $id, $domain, $path = '' ) { + global $wpdb; + + $network = get_network( $id ); + + // Bail if network not found. + if ( empty( $network ) ) { + return new WP_Error( 'network_not_exist', __( 'Network does not exist.', 'wp-multi-network' ) ); + } + + $site_id = get_main_site_id( $id ); + $path = wp_sanitize_site_path( $path ); + + // Bail if site URL is invalid. + if ( ! wp_validate_site_url( $domain, $path, $site_id ) ) { + /* translators: %s: site domain and path */ + return new WP_Error( 'blog_bad', sprintf( __( 'The site "%s" is invalid, not available, or already exists.', 'wp-multi-network' ), $domain . $path ) ); + } + + // phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery + $update_result = $wpdb->update( + $wpdb->site, + array( + 'domain' => $domain, + 'path' => $path, + ), + array( + 'id' => $network->id, + ) + ); + if ( is_wp_error( $update_result ) ) { + return new WP_Error( 'network_not_updated', __( 'Network could not be updated.', 'wp-multi-network' ) ); + } + + $path = ! empty( $path ) ? $path : $network->path; + $full_path = untrailingslashit( $domain . $path ); + $old_path = untrailingslashit( $network->domain . $network->path ); + + $sites = get_sites( array( + 'network_id' => $network->id, + ) ); + + // Update network site domains and paths as necessary. + if ( ! empty( $sites ) ) { + foreach ( $sites as $site ) { + $update = array(); + + if ( $network->domain !== $domain ) { + $update['domain'] = str_replace( $network->domain, $domain, $site->domain ); + } + + if ( $network->path !== $path ) { + $search = sprintf( '|^%s|', preg_quote( $network->path, '|' ) ); + $update['path'] = preg_replace( $search, $path, $site->path, 1 ); + } + + if ( empty( $update ) ) { + continue; + } + + // phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery + $wpdb->update( $wpdb->blogs, $update, array( + 'blog_id' => (int) $site->id, + ) ); + + $option_table = $wpdb->get_blog_prefix( $site->id ) . 'options'; + + // Loop through URL-dependent options and correct them. + foreach ( network_options_list() as $option_name ) { + $value = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$option_table} WHERE option_name = %s", $option_name ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared + + if ( ! empty( $value ) && ( false !== strpos( $value->option_value, $old_path ) ) ) { + $new_value = str_replace( $old_path, $full_path, $value->option_value ); + update_blog_option( $site->id, $option_name, $new_value ); + } + } + + // Clean the blog cache. + clean_blog_cache( $site->id ); + } + } + + // Update network counts. + wp_update_network_counts( $network->id ); + + // Clean the network cache. + clean_network_cache( $network->id ); + + /** + * Fires after an existing network has been updated. + * + * @since 1.3.0 + * + * @param int $network_id ID of the added network. + * @param array $args Associative array of network arguments. + */ + do_action( 'update_network', $network->id, array( + 'domain' => $network->domain, + 'path' => $network->path, + ) ); + + return true; + } +endif; + +if ( ! function_exists( 'delete_network' ) ) : + /** + * Deletes a network and all its sites. + * + * @since 1.3.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param int $network_id ID of network to delete. + * @param bool $delete_blogs Flag to permit site deletion - default setting + * of false will prevent deletion of occupied networks. + * @return bool|WP_Error True on success, or WP_Error on failure. + */ + function delete_network( $network_id, $delete_blogs = false ) { + global $wpdb; + + $network = get_network( $network_id ); + + // Bail if network does not exist. + if ( empty( $network ) ) { + return new WP_Error( 'network_not_exist', __( 'Network does not exist.', 'wp-multi-network' ) ); + } + + $sites = get_sites( array( + 'network_id' => $network->id, + ) ); + if ( ! empty( $sites ) ) { + + // Bail if site deletion is off. + if ( empty( $delete_blogs ) ) { + return new WP_Error( 'network_not_empty', __( 'Cannot delete network with sites.', 'wp-multi-network' ) ); + } + + if ( true === $delete_blogs ) { + foreach ( $sites as $site ) { + if ( wp_should_rescue_orphaned_sites() ) { + move_site( $site->id, 0 ); + continue; + } + + wpmu_delete_blog( $site->id, true ); + } + } + } + + // phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery + $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->site} WHERE id = %d", $network->id ) ); + + // phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery + $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->sitemeta} WHERE site_id = %d", $network->id ) ); + + // Clean the network cache. + clean_network_cache( $network->id ); + + /** + * Fires after a network has been deleted. + * + * @since 1.3.0 + * + * @param WP_Network $network The deleted network object. + */ + do_action( 'delete_network', $network ); + + return true; + } +endif; + +if ( ! function_exists( 'move_site' ) ) : + /** + * Moves a site to a new network. + * + * @since 1.3.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param int $site_id ID of site to move. + * @param int $new_network_id ID of destination network. + * @return int|bool|WP_Error New network ID on success, true if site cannot be moved, + * or WP_Error on failure. + */ + function move_site( $site_id = 0, $new_network_id = 0 ) { + global $wpdb; + + $site = get_site( $site_id ); + + // Cast network IDs to ints. + $old_network_id = (int) $site->network_id; + $new_network_id = (int) $new_network_id; + + // Bail if site does not exist. + if ( empty( $site ) ) { + return new WP_Error( 'blog_not_exist', __( 'Site does not exist.', 'wp-multi-network' ) ); + } + + // Bail if site is the main site. + if ( is_main_site( $site->id, $old_network_id ) ) { + return true; + } + + // Bail if no change. + if ( $new_network_id === $old_network_id ) { + return true; + } + + // Update the database entry. + $result = $wpdb->update( // phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery + $wpdb->blogs, + array( + 'site_id' => $new_network_id, + ), + array( + 'blog_id' => $site->id, + ) + ); + + // Bail if error. + if ( empty( $result ) ) { + return new WP_Error( 'blog_not_moved', __( 'Site could not be moved.', 'wp-multi-network' ) ); + } + + // Update old network counts. + if ( 0 !== $old_network_id ) { + wp_update_network_counts( $old_network_id ); + } + + // Update new network counts. + if ( 0 !== $new_network_id ) { + wp_update_network_counts( $new_network_id ); + } + + // Clean the blog cache. + clean_blog_cache( $site_id ); + + // Clean the network caches. + clean_network_cache( + array_filter( + array( + $site->network_id, + $new_network_id, + ) + ) + ); + + /** + * Fires after a site has been moved to a new network. + * + * @since 1.3.0 + * + * @param int $site_id ID of the site that has been moved. + * @param int $old_network_id ID of the original network for the site. + * @param int $new_network_id ID of the network the site has been moved to. + */ + do_action( 'move_site', $site_id, $site->network_id, $new_network_id ); + + return $new_network_id; + } +endif; + +if ( ! function_exists( 'network_options_list' ) ) : + /** + * Lists the URL-dependent options. + * + * @since 1.3.0 + * + * @return array List of network option names. + */ + function network_options_list() { + $network_options = array( + 'siteurl', + 'home', + ); + + /** + * Filters the list of network options that depend on the domain and path of a network. + * + * @since 1.3.0 + * + * @param array $network_options List of network option names. + */ + return apply_filters( 'network_options_list', $network_options ); + } +endif; + +if ( ! function_exists( 'network_options_to_copy' ) ) : + /** + * Lists the default network options to copy. + * + * @since 1.3.0 + * + * @return array List of network $option_name => $option_label pairs. + */ + function network_options_to_copy() { + $network_options = array( + 'admin_email' => __( 'Network admin email', 'wp-multi-network' ), + 'admin_user_id' => __( 'Admin user ID - deprecated', 'wp-multi-network' ), + 'allowed_themes' => __( 'OLD List of allowed themes - deprecated', 'wp-multi-network' ), + 'allowedthemes' => __( 'List of allowed themes', 'wp-multi-network' ), + 'banned_email_domains' => __( 'Banned email domains', 'wp-multi-network' ), + 'first_post' => __( 'Content of first post on a new blog', 'wp-multi-network' ), + 'limited_email_domains' => __( 'Permitted email domains', 'wp-multi-network' ), + 'ms_files_rewriting' => __( 'Uploaded file handling', 'wp-multi-network' ), + 'site_admins' => __( 'List of network admin usernames', 'wp-multi-network' ), + 'upload_filetypes' => __( 'List of allowed file types for uploads', 'wp-multi-network' ), + 'welcome_email' => __( 'Content of welcome email', 'wp-multi-network' ), + ); + + /** + * Filters the default network options to copy. + * + * @since 1.3.0 + * + * @return array List of network $option_name => $option_label pairs. + */ + return apply_filters( 'network_options_to_copy', $network_options ); + } +endif; From 04471bfb490ad23b47c212b2598e7a63c39632fd Mon Sep 17 00:00:00 2001 From: Mike Wilkerson <11575183+mlwilkerson@users.noreply.github.com> Date: Mon, 24 Oct 2022 17:32:12 -0700 Subject: [PATCH 02/25] WIP: working toward a test of adding a network after activating this plugin --- tests/test-multisite-activation.php | 81 ++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/tests/test-multisite-activation.php b/tests/test-multisite-activation.php index a1b53d7e2..1c0ad4b94 100644 --- a/tests/test-multisite-activation.php +++ b/tests/test-multisite-activation.php @@ -4,6 +4,7 @@ require_once dirname( __FILE__ ) . '/../includes/class-fontawesome-activator.php'; require_once dirname( __FILE__ ) . '/../includes/class-fontawesome-exception.php'; require_once dirname( __FILE__ ) . '/_support/font-awesome-phpunit-util.php'; +require_once dirname( __FILE__ ) . '/_support/wp-multi-network-functions.php'; use Yoast\WPTestUtils\WPIntegration\TestCase; @@ -11,8 +12,10 @@ * Class MultisiteActivationTest */ class MultisiteActivationTest extends TestCase { - protected $sub_sites = array(); - protected $original_blog_id = null; + protected $sub_sites = array(); + protected $original_blog_id = null; + protected $original_network_id = null; + protected $added_network_ids = array(); public function set_up() { parent::set_up(); @@ -29,10 +32,12 @@ public function set_up() { } $this->original_blog_id = get_current_blog_id(); + $this->original_network_id = get_current_network_id(); reset_db(); remove_all_actions( 'font_awesome_preferences' ); remove_all_filters( 'wp_is_large_network' ); + add_action( 'add_network', [$this, 'handle_add_network'], 99, 2 ); FontAwesome::reset(); ( new Mock_FontAwesome_Metadata_Provider() )->mock( array( @@ -64,8 +69,26 @@ public function set_up() { public function tear_down() { parent::tear_down(); + remove_all_actions( 'add_network' ); + switch_to_blog( $this->original_blog_id ); + // Delete all sites on the non-original network + foreach ( $this->added_network_ids as $network_id ) { + \switch_to_network( $network_id ); + $sites = get_sites( + array( + 'network_id' => $network_id + ) + ); + + foreach( $sites as $site ) { + wp_delete_site( $site->ID ); + } + } + + \switch_to_network( $this->original_network_id ); + foreach ( $this->sub_sites as $blog_id ) { wp_delete_site( $blog_id ); } @@ -246,4 +269,58 @@ function( $blog_id ) use ( &$visited_blog_ids ) { $this->assertEquals( $all_site_blog_ids, $visited_blog_ids ); } + + public function test_add_network_after_activation() { + if ( ! $this->is_wp_version_compatible() ) { + $this->assertTrue( true ); + return; + } + + if ( ! is_network_admin() ) { + // Do nothing when we're not in network admin mode. + $this->assertTrue( true ); + return; + } + + $test_obj = $this; + + // This activates network wide, for all sites that exist at the time. + FontAwesome_Activator::initialize(); + + // Now create a new network + $new_network_id = self::add_network(); + + // switch to it + \switch_to_network( $new_network_id ); + + fa()->latest_version_6(); + } + + public static function add_network() { + $sub_domain = dechex(rand(PHP_INT_MIN, PHP_INT_MAX)); + $domain = "$sub_domain.example.com"; + $path = "/"; + + $admin_user = get_users( [ 'role' => 'administrator' ] )[0]; + $result = \add_network( + array( + 'domain' => $domain, + 'path' => '/', + 'site_name' => $domain, + 'network_name' => $domain, + 'user_id' => $admin_user->ID, + 'network_admin_id' => $admin_user->ID + ) + ); + + if ( is_wp_error( $result ) ) { + throw new \Exception("failed creating network . \n" . print_r($result, true)); + } + + return $result; + } + + public function handle_add_network($network_id, $params) { + array_push( $this->added_network_ids, $network_id ); + } } From d812349a5752b00291b4c4b2aeabde598ed4c7e0 Mon Sep 17 00:00:00 2001 From: Mike Wilkerson <11575183+mlwilkerson@users.noreply.github.com> Date: Mon, 24 Oct 2022 17:52:42 -0700 Subject: [PATCH 03/25] complete a failing test for adding a network after activating this plugin --- tests/test-multisite-activation.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test-multisite-activation.php b/tests/test-multisite-activation.php index 1c0ad4b94..7b57dc42b 100644 --- a/tests/test-multisite-activation.php +++ b/tests/test-multisite-activation.php @@ -293,7 +293,11 @@ public function test_add_network_after_activation() { // switch to it \switch_to_network( $new_network_id ); - fa()->latest_version_6(); + FontAwesome_Release_Provider::reset(); + + // This should not throw an exception, despite switching networks. + $ver = fa()->latest_version_6(); + $this->assertEquals($ver, "6.1.1"); } public static function add_network() { From f27c5c6922d63b604ed47173207eed1337d55a00 Mon Sep 17 00:00:00 2001 From: Mike Wilkerson <11575183+mlwilkerson@users.noreply.github.com> Date: Mon, 24 Oct 2022 17:55:16 -0700 Subject: [PATCH 04/25] Fix ReleaseProvider options storage in multisite to use main network --- includes/class-fontawesome-release-provider.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-fontawesome-release-provider.php b/includes/class-fontawesome-release-provider.php index 27bdb9315..3474a14a7 100644 --- a/includes/class-fontawesome-release-provider.php +++ b/includes/class-fontawesome-release-provider.php @@ -466,7 +466,7 @@ public function latest_version_6() { */ public static function update_option( $option_value ) { if ( is_multisite() ) { - $network_id = get_current_network_id(); + $network_id = get_main_network_id(); return update_network_option( $network_id, self::OPTIONS_KEY, $option_value ); } else { return update_option( self::OPTIONS_KEY, $option_value, false ); @@ -484,7 +484,7 @@ public static function update_option( $option_value ) { */ public static function get_option() { if ( is_multisite() ) { - $network_id = get_current_network_id(); + $network_id = get_main_network_id(); return get_network_option( $network_id, self::OPTIONS_KEY ); } else { return get_option( self::OPTIONS_KEY ); From c9ad87f3a98489c553ed8c9c84de3d0278b068fe Mon Sep 17 00:00:00 2001 From: Mike Wilkerson <11575183+mlwilkerson@users.noreply.github.com> Date: Mon, 24 Oct 2022 17:59:26 -0700 Subject: [PATCH 05/25] code cleanup and auto-formatting --- tests/test-multisite-activation.php | 32 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/test-multisite-activation.php b/tests/test-multisite-activation.php index 7b57dc42b..bf52e59da 100644 --- a/tests/test-multisite-activation.php +++ b/tests/test-multisite-activation.php @@ -31,13 +31,13 @@ public function set_up() { throw new \Exception(); } - $this->original_blog_id = get_current_blog_id(); + $this->original_blog_id = get_current_blog_id(); $this->original_network_id = get_current_network_id(); reset_db(); remove_all_actions( 'font_awesome_preferences' ); remove_all_filters( 'wp_is_large_network' ); - add_action( 'add_network', [$this, 'handle_add_network'], 99, 2 ); + add_action( 'add_network', array( $this, 'handle_add_network' ), 99, 2 ); FontAwesome::reset(); ( new Mock_FontAwesome_Metadata_Provider() )->mock( array( @@ -73,16 +73,16 @@ public function tear_down() { switch_to_blog( $this->original_blog_id ); - // Delete all sites on the non-original network + // Delete all sites on the non-original network. foreach ( $this->added_network_ids as $network_id ) { \switch_to_network( $network_id ); $sites = get_sites( array( - 'network_id' => $network_id + 'network_id' => $network_id, ) ); - foreach( $sites as $site ) { + foreach ( $sites as $site ) { wp_delete_site( $site->ID ); } } @@ -287,44 +287,44 @@ public function test_add_network_after_activation() { // This activates network wide, for all sites that exist at the time. FontAwesome_Activator::initialize(); - // Now create a new network + // Now create a new network. $new_network_id = self::add_network(); - // switch to it + // Switch to it. \switch_to_network( $new_network_id ); FontAwesome_Release_Provider::reset(); // This should not throw an exception, despite switching networks. $ver = fa()->latest_version_6(); - $this->assertEquals($ver, "6.1.1"); + $this->assertEquals( $ver, '6.1.1' ); } public static function add_network() { - $sub_domain = dechex(rand(PHP_INT_MIN, PHP_INT_MAX)); - $domain = "$sub_domain.example.com"; - $path = "/"; + $sub_domain = dechex( wp_rand( PHP_INT_MIN, PHP_INT_MAX ) ); + $domain = "$sub_domain.example.com"; + $path = '/'; - $admin_user = get_users( [ 'role' => 'administrator' ] )[0]; - $result = \add_network( + $admin_user = get_users( array( 'role' => 'administrator' ) )[0]; + $result = \add_network( array( 'domain' => $domain, 'path' => '/', 'site_name' => $domain, 'network_name' => $domain, 'user_id' => $admin_user->ID, - 'network_admin_id' => $admin_user->ID + 'network_admin_id' => $admin_user->ID, ) ); if ( is_wp_error( $result ) ) { - throw new \Exception("failed creating network . \n" . print_r($result, true)); + throw new \Exception( 'failed creating network' ); } return $result; } - public function handle_add_network($network_id, $params) { + public function handle_add_network( $network_id, $params ) { array_push( $this->added_network_ids, $network_id ); } } From e637cac9d6f9e9bc5240d24b4392868f9fa2e50c Mon Sep 17 00:00:00 2001 From: Mike Wilkerson <11575183+mlwilkerson@users.noreply.github.com> Date: Tue, 25 Oct 2022 10:25:31 -0700 Subject: [PATCH 06/25] add failing tests for multisite upgrade --- tests/test-multisite-upgrade.php | 173 +++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 tests/test-multisite-upgrade.php diff --git a/tests/test-multisite-upgrade.php b/tests/test-multisite-upgrade.php new file mode 100644 index 000000000..3953a5f52 --- /dev/null +++ b/tests/test-multisite-upgrade.php @@ -0,0 +1,173 @@ +mock( + array( + wp_json_encode( + array( + 'data' => graphql_releases_query_fixture(), + ) + ), + wp_json_encode( + array( + 'data' => graphql_releases_query_fixture(), + ) + ) + ) + ); + + // This activates network wide, for all sites that exist at the time. + FontAwesome_Activator::initialize(); + + add_action( 'add_network', array( $this, 'handle_add_network' ), 99, 2 ); + } + + public function tear_down() { + FontAwesome_Metadata_Provider::reset(); + remove_all_actions( 'add_network' ); + } + + public function test_try_upgrade_on_main_network_when_release_metatdata_stored_in_non_main_network() { + if ( ! is_multisite() ) { + throw new \Exception(); + } + + /** + * As of 4.3.2, the initialize() that will have run in the set_up() above will have put the release metadata + * in a network option associated with the main network. + * In 4.3.1, it would have been stored in a network option associated with the *current* network + * at the time of retrieval and storage. + * + * So to simulate the scenario that would have been possible in 4.3.1, we'll move it to a non-main network, + * such that the release metadata are stored on an option associated with the *current* network at the time + * of retrieval and storage. + */ + + // Create a new, non-main network. + $new_network_id = self::add_network(); + $main_network_id = get_main_network_id(); + + // Get the metadata that would have been stored on a main network option. + $opt = get_network_option( $main_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ); + + // Put it on an option associated with the new network. + update_network_option( $new_network_id, FontAwesome_Release_Provider::OPTIONS_KEY, $opt ); + + // And get rid of the original one on the main network. + delete_network_option( $main_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ); + + $this->assertFalse( get_network_option( $main_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ) ); + $this->assertArrayHasKey( 'refreshed_at', get_network_option( $new_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ) ); + + // Clear options cache. + wp_cache_delete ( 'alloptions', 'options' ); + + // Expecting no exception to be thrown. + $this->assertNull( fa()->try_upgrade() ); + + $this->assertFalse( get_network_option( $new_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ) ); + $this->assertArrayHasKey( 'refreshed_at', get_network_option( $main_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ) ); + } + + public function test_try_upgrade_on_non_main_network_when_release_metatdata_stored_in_non_main_network() { + if ( ! is_multisite() ) { + throw new \Exception(); + } + + /** + * As of 4.3.2, the initialize() that will have run in the set_up() above will have put the release metadata + * in a network option associated with the main network. + * In 4.3.1, it would have been stored in a network option associated with the *current* network + * at the time of retrieval and storage. + * + * So to simulate the scenario that would have been possible in 4.3.1, we'll move it to a non-main network, + * such that the release metadata are stored on an option associated with the *current* network at the time + * of retrieval and storage. + */ + + // Create a new, non-main network. + $new_network_id = self::add_network(); + $main_network_id = get_main_network_id(); + + // Get the metadata that would have been stored on a main network option. + $opt = get_network_option( $main_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ); + + // Put it on an option associated with the new network. + update_network_option( $new_network_id, FontAwesome_Release_Provider::OPTIONS_KEY, $opt ); + + // And get rid of the original one on the main network. + delete_network_option( $main_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ); + + $this->assertFalse( get_network_option( $main_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ) ); + $this->assertArrayHasKey( 'refreshed_at', get_network_option( $new_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ) ); + + // Clear options cache. + wp_cache_delete ( 'alloptions', 'options' ); + + // Now switch to that new network. + \switch_to_network( $new_network_id ); + + // Expecting no exception to be thrown. + $this->assertNull( fa()->try_upgrade() ); + + $this->assertFalse( get_network_option( $new_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ) ); + $this->assertArrayHasKey( 'refreshed_at', get_network_option( $main_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ) ); + } + + public function handle_add_network( $network_id, $params ) { + array_push( $this->added_network_ids, $network_id ); + } + + public static function add_network() { + $sub_domain = dechex( wp_rand( PHP_INT_MIN, PHP_INT_MAX ) ); + $domain = "$sub_domain.example.com"; + $path = '/'; + + $admin_user = get_users( array( 'role' => 'administrator' ) )[0]; + $result = \add_network( + array( + 'domain' => $domain, + 'path' => '/', + 'site_name' => $domain, + 'network_name' => $domain, + 'user_id' => $admin_user->ID, + 'network_admin_id' => $admin_user->ID, + ) + ); + + if ( is_wp_error( $result ) ) { + throw new \Exception( 'failed creating network' ); + } + + return $result; + } +} From 8f4700ce7c5f387bf98ff24284e931e3e49197cb Mon Sep 17 00:00:00 2001 From: Mike Wilkerson <11575183+mlwilkerson@users.noreply.github.com> Date: Tue, 25 Oct 2022 10:29:13 -0700 Subject: [PATCH 07/25] test refactoring --- tests/test-multisite-upgrade.php | 52 ++++++-------------------------- 1 file changed, 10 insertions(+), 42 deletions(-) diff --git a/tests/test-multisite-upgrade.php b/tests/test-multisite-upgrade.php index 3953a5f52..3f6efecb6 100644 --- a/tests/test-multisite-upgrade.php +++ b/tests/test-multisite-upgrade.php @@ -57,48 +57,14 @@ public function tear_down() { } public function test_try_upgrade_on_main_network_when_release_metatdata_stored_in_non_main_network() { - if ( ! is_multisite() ) { - throw new \Exception(); - } - - /** - * As of 4.3.2, the initialize() that will have run in the set_up() above will have put the release metadata - * in a network option associated with the main network. - * In 4.3.1, it would have been stored in a network option associated with the *current* network - * at the time of retrieval and storage. - * - * So to simulate the scenario that would have been possible in 4.3.1, we'll move it to a non-main network, - * such that the release metadata are stored on an option associated with the *current* network at the time - * of retrieval and storage. - */ - - // Create a new, non-main network. - $new_network_id = self::add_network(); - $main_network_id = get_main_network_id(); - - // Get the metadata that would have been stored on a main network option. - $opt = get_network_option( $main_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ); - - // Put it on an option associated with the new network. - update_network_option( $new_network_id, FontAwesome_Release_Provider::OPTIONS_KEY, $opt ); - - // And get rid of the original one on the main network. - delete_network_option( $main_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ); - - $this->assertFalse( get_network_option( $main_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ) ); - $this->assertArrayHasKey( 'refreshed_at', get_network_option( $new_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ) ); - - // Clear options cache. - wp_cache_delete ( 'alloptions', 'options' ); - - // Expecting no exception to be thrown. - $this->assertNull( fa()->try_upgrade() ); - - $this->assertFalse( get_network_option( $new_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ) ); - $this->assertArrayHasKey( 'refreshed_at', get_network_option( $main_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ) ); + $this->run_multisite_upgrade_test( true ); } public function test_try_upgrade_on_non_main_network_when_release_metatdata_stored_in_non_main_network() { + $this->run_multisite_upgrade_test( false ); + } + + public function run_multisite_upgrade_test($run_on_main_network = true) { if ( ! is_multisite() ) { throw new \Exception(); } @@ -133,9 +99,11 @@ public function test_try_upgrade_on_non_main_network_when_release_metatdata_stor // Clear options cache. wp_cache_delete ( 'alloptions', 'options' ); - // Now switch to that new network. - \switch_to_network( $new_network_id ); - + if ( ! $run_on_main_network ) { + // Now switch to that new network. + \switch_to_network( $new_network_id ); + } + // Expecting no exception to be thrown. $this->assertNull( fa()->try_upgrade() ); From 2ed559be7f55f40205e9fefc44d57963fc26e3e8 Mon Sep 17 00:00:00 2001 From: Mike Wilkerson <11575183+mlwilkerson@users.noreply.github.com> Date: Tue, 25 Oct 2022 11:04:44 -0700 Subject: [PATCH 08/25] add exception for multisite upgrade --- includes/class-fontawesome-exception.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/includes/class-fontawesome-exception.php b/includes/class-fontawesome-exception.php index effb2b139..c1b72b060 100644 --- a/includes/class-fontawesome-exception.php +++ b/includes/class-fontawesome-exception.php @@ -591,4 +591,19 @@ public static function main_option_delete() { ) ); } + + /** + * Internal use only. + * + * @internal + * @ignore + */ + public static function multisite_network_option_update() { + return new static( + esc_html__( + 'Failed updating release metadata on a main network option when trying to upgrade the Font Awesome plugin in multisite mode.', + 'font-awesome' + ) + ); + } } From 6cf8697add1cb0ccc9e0a543b5ad4d49171243b0 Mon Sep 17 00:00:00 2001 From: Mike Wilkerson <11575183+mlwilkerson@users.noreply.github.com> Date: Tue, 25 Oct 2022 11:05:43 -0700 Subject: [PATCH 09/25] implement multisite upgrade for moving release metadata network option to main network --- includes/class-fontawesome.php | 82 +++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/includes/class-fontawesome.php b/includes/class-fontawesome.php index 9f3348e9e..4e5168c0e 100644 --- a/includes/class-fontawesome.php +++ b/includes/class-fontawesome.php @@ -489,12 +489,28 @@ public function try_upgrade() { $should_upgrade = true; } + if ( is_multisite() && ! boolval( get_network_option( get_main_network_id(), FontAwesome_Release_Provider::OPTIONS_KEY ) ) ) { + /** + * Handle possible multisite upgrade from 4.3.1. + * In 4.3.1 the release metadata might have been stored in a network + * option associated with a non-main network. As of 4.3.2, it's + * always stored on a network option associated with the main network. + * So if we're in multisite mode and we don't find the release metadata + * on a network option associated with the main network, we need to fix it up. + */ + $should_upgrade = true; + } + if ( $should_upgrade ) { $this->validate_options( $options ); $this->maybe_update_last_used_release_schema_for_upgrade(); - $this->maybe_move_release_metadata_for_upgrade(); + if ( is_multisite() ) { + $this->maybe_move_release_metadata_for_upgrade_multisite(); + } else { + $this->maybe_move_release_metadata_for_upgrade_single_site(); + } /** * Delete the main option to make sure it's removed entirely, including @@ -529,7 +545,7 @@ public function try_upgrade() { * @ignore * @internal */ - private function maybe_move_release_metadata_for_upgrade() { + private function maybe_move_release_metadata_for_upgrade_single_site() { if ( boolval( get_option( FontAwesome_Release_Provider::OPTIONS_KEY ) ) ) { // If this option is set, then we're all caught up. return; @@ -568,6 +584,68 @@ private function maybe_move_release_metadata_for_upgrade() { FontAwesome_Release_Provider::reset(); } + /** + * If upgrading from 4.3.1 to 4.3.2 or beyond, and we don't have the release + * metadata stored on a network option associated with the main network, + * we need to find it on a non-main network and move it to main network. + * + * If we can't find it on a non-main network, either, then that's an + * exception. We intentionally will not query the API server, since + * issuing a blocking request on upgrade is known to cause load problems + * and request timeouts. + * + * Internal use only. + * + * @throws ReleaseMetadataMissingException + * @ignore + * @internal + */ + private function maybe_move_release_metadata_for_upgrade_multisite() { + if ( ! is_multisite() ) { + return; + } + + if ( boolval( get_network_option( get_main_network_id(), FontAwesome_Release_Provider::OPTIONS_KEY ) ) ) { + // If there's already release metadata on a network option for the main network, we're done. + return; + } + + foreach ( get_networks() as $network ) { + $option_value = get_network_option( $network->id, FontAwesome_Release_Provider::OPTIONS_KEY ); + + if ( is_array( $option_value ) ) { + $result = update_network_option( get_main_network_id(), FontAwesome_Release_Provider::OPTIONS_KEY, $option_value ); + + if ( ! $result ) { + throw UpgradeException::multisite_network_option_update(); + } + + delete_network_option( $network->id, FontAwesome_Release_Provider::OPTIONS_KEY ); + + /** + * Return early, once we've found what we're looking for. + * While it's possible that there are additional non-main networks that also + * have options with release metadata, it's unlikely. Regardless, + * since this upgrade process happens on a normal front-end page load, + * we don't want to do any unnecessary processing here. The only reason + * to go searching through other network options would be to clean up + * any additional obsolete data. We'll leave that clean up to the plugin's + * uninstall logic. + */ + return; + } + } + + /** + * Now we'll reset the release provider. + * + * If we've fallen through to this point, and we haven't found the release + * metadata stored in one of the previous locations, then this will throw an + * exception. + */ + FontAwesome_Release_Provider::reset(); + } + /** * With 4.1.0, the name of one of the keys in the LAST_USED_RELEASE_TRANSIENT changed. * We can fix it up. From 6705c24f2ed961f7af5b3ad3ebbba624e1c4d73e Mon Sep 17 00:00:00 2001 From: Mike Wilkerson <11575183+mlwilkerson@users.noreply.github.com> Date: Tue, 25 Oct 2022 16:16:25 -0700 Subject: [PATCH 10/25] when delete_network_option when cleaning up ReleaseProvider --- includes/class-fontawesome-release-provider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-fontawesome-release-provider.php b/includes/class-fontawesome-release-provider.php index 3474a14a7..a0775c996 100644 --- a/includes/class-fontawesome-release-provider.php +++ b/includes/class-fontawesome-release-provider.php @@ -499,7 +499,7 @@ public static function get_option() { */ public static function delete_option() { if ( is_multisite() ) { - $network_id = get_current_network_id(); + $network_id = get_main_network_id(); return delete_network_option( $network_id, self::OPTIONS_KEY ); } else { return delete_option( self::OPTIONS_KEY ); From 15e70274f726bd28669a4d7de421c4c9a0202dda Mon Sep 17 00:00:00 2001 From: Mike Wilkerson <11575183+mlwilkerson@users.noreply.github.com> Date: Tue, 25 Oct 2022 16:16:42 -0700 Subject: [PATCH 11/25] include network option deletion in the cleanup utility plugin --- integrations/plugins/font-awesome-cleanup/index.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/integrations/plugins/font-awesome-cleanup/index.php b/integrations/plugins/font-awesome-cleanup/index.php index 5ab9fbb5b..1fbac5691 100644 --- a/integrations/plugins/font-awesome-cleanup/index.php +++ b/integrations/plugins/font-awesome-cleanup/index.php @@ -72,6 +72,10 @@ function( ) { cleanup_site(); } ); + + foreach ( get_options() as $option ) { + delete_network_option( get_current_network_id(), $option ); + } } else { cleanup_site(); } @@ -204,6 +208,8 @@ function display_cleanup_scope_multisite() { if ( $is_cleanup_network_active ) { $network_id = get_current_network_id(); + $networks = get_networks(); + for_each_blog( function( $site ) use (&$sites) { array_push( $sites, $site ); @@ -212,6 +218,10 @@ function( $site ) use (&$sites) { ?>
Cleaning ALL sites in network with network_id: = $network_id ?>.
To clean up only one site, activate this cleanup plugin only on that one site instead of activating it network-wide.
+ 0 ) { ?> +There are other networks that will not be affected by this cleanup.
+To clean up multiple networks, network activate this cleanup plugin for each network, and run this cleanup for each network.
+ Date: Tue, 25 Oct 2022 16:49:05 -0700 Subject: [PATCH 12/25] bump to verson 4.3.2-1 --- admin/package-lock.json | 4 ++-- admin/package.json | 2 +- compat-js/package-lock.json | 4 ++-- compat-js/package.json | 2 +- includes/class-fontawesome.php | 2 +- index.php | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/admin/package-lock.json b/admin/package-lock.json index 0e3e47e92..59a879b89 100644 --- a/admin/package-lock.json +++ b/admin/package-lock.json @@ -1,12 +1,12 @@ { "name": "font-awesome-admin", - "version": "4.3.1", + "version": "4.3.2-1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "font-awesome-admin", - "version": "4.3.1", + "version": "4.3.2-1", "dependencies": { "@fortawesome/fa-icon-chooser-react": "0.4.1", "@fortawesome/fontawesome-svg-core": "^6.2.0", diff --git a/admin/package.json b/admin/package.json index 796085fdc..514efa727 100644 --- a/admin/package.json +++ b/admin/package.json @@ -1,6 +1,6 @@ { "name": "font-awesome-admin", - "version": "4.3.1", + "version": "4.3.2-1", "private": true, "dependencies": { "@fortawesome/fa-icon-chooser-react": "0.4.1", diff --git a/compat-js/package-lock.json b/compat-js/package-lock.json index 7d97ff64f..2003fc53a 100644 --- a/compat-js/package-lock.json +++ b/compat-js/package-lock.json @@ -1,12 +1,12 @@ { "name": "fa-compat-js", - "version": "4.3.1", + "version": "4.3.2-1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "fa-compat-js", - "version": "4.3.1", + "version": "4.3.2-1", "dependencies": { "@wordpress/api-fetch": "^5.1.1", "@wordpress/components": "14.1.10", diff --git a/compat-js/package.json b/compat-js/package.json index 2a65bdce8..75823cdb7 100644 --- a/compat-js/package.json +++ b/compat-js/package.json @@ -1,6 +1,6 @@ { "name": "fa-compat-js", - "version": "4.3.1", + "version": "4.3.2-1", "private": true, "dependencies": { "@wordpress/api-fetch": "^5.1.1", diff --git a/includes/class-fontawesome.php b/includes/class-fontawesome.php index 4e5168c0e..db7f17d8f 100644 --- a/includes/class-fontawesome.php +++ b/includes/class-fontawesome.php @@ -126,7 +126,7 @@ class FontAwesome { * * @since 4.0.0 */ - const PLUGIN_VERSION = '4.3.1'; + const PLUGIN_VERSION = '4.3.2-1'; /** * The namespace for this plugin's REST API. * diff --git a/index.php b/index.php index 1cee02f37..1da2a2167 100644 --- a/index.php +++ b/index.php @@ -3,7 +3,7 @@ * Plugin Name: Font Awesome * Plugin URI: https://fontawesome.com/how-to-use/on-the-web/using-with/wordpress * Description: The official way to use Font Awesome Free or Pro icons on your site, brought to you by the Font Awesome team. - * Version: 4.3.1 + * Version: 4.3.2-1 * Author: Font Awesome * Author URI: https://fontawesome.com/ * License: GPLv2 (or later) From 3861bc7e13616c7aa61ac248823a3f8b1c083ad9 Mon Sep 17 00:00:00 2001 From: Mike Wilkerson <11575183+mlwilkerson@users.noreply.github.com> Date: Tue, 25 Oct 2022 17:07:46 -0700 Subject: [PATCH 13/25] fix and test multisite uninstall --- includes/class-fontawesome-deactivator.php | 4 +- .../class-fontawesome-release-provider.php | 18 ++- tests/test-multisite-deactivation.php | 138 ++++++++++++++++++ 3 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 tests/test-multisite-deactivation.php diff --git a/includes/class-fontawesome-deactivator.php b/includes/class-fontawesome-deactivator.php index 90f517cfb..b479efd9f 100644 --- a/includes/class-fontawesome-deactivator.php +++ b/includes/class-fontawesome-deactivator.php @@ -46,11 +46,13 @@ function( $blog_id ) { } else { self::delete_options(); } + + // This one handles its own multisite considerations. + FontAwesome_Release_Provider::delete_option(); } private static function delete_options() { delete_option( FontAwesome::OPTIONS_KEY ); - FontAwesome_Release_Provider::delete_option(); delete_option( FontAwesome::CONFLICT_DETECTION_OPTIONS_KEY ); delete_option( FontAwesome_API_Settings::OPTIONS_KEY ); } diff --git a/includes/class-fontawesome-release-provider.php b/includes/class-fontawesome-release-provider.php index a0775c996..1ba9aae09 100644 --- a/includes/class-fontawesome-release-provider.php +++ b/includes/class-fontawesome-release-provider.php @@ -499,8 +499,22 @@ public static function get_option() { */ public static function delete_option() { if ( is_multisite() ) { - $network_id = get_main_network_id(); - return delete_network_option( $network_id, self::OPTIONS_KEY ); + $result_accumulator = true; + + /** + * Delete the network option for all networks. + * In 4.3.1, it's possible that this option could have been created in + * any network, which ever one was the current network at the time the plugin + * refreshed releases metadata. + * + * Starting in 4.3.2, we only store the releases metadata on an option associated with the main network. + */ + foreach ( get_networks() as $network ) { + $current_result = delete_network_option( $network->id, self::OPTIONS_KEY ); + $result_accumulator = $result_accumulator && $current_result; + } + + return $result_accumulator; } else { return delete_option( self::OPTIONS_KEY ); } diff --git a/tests/test-multisite-deactivation.php b/tests/test-multisite-deactivation.php new file mode 100644 index 000000000..bf629b319 --- /dev/null +++ b/tests/test-multisite-deactivation.php @@ -0,0 +1,138 @@ +mock( + array( + wp_json_encode( + array( + 'data' => graphql_releases_query_fixture(), + ) + ), + wp_json_encode( + array( + 'data' => graphql_releases_query_fixture(), + ) + ) + ) + ); + + // This activates network wide, for all sites that exist at the time. + FontAwesome_Activator::initialize(); + + add_action( 'add_network', array( $this, 'handle_add_network' ), 99, 2 ); + } + + public function tear_down() { + FontAwesome_Metadata_Provider::reset(); + remove_all_actions( 'add_network' ); + } + + public function test_uninstall_on_main_network() { + $this->run_multisite_uninstall_test( true ); + } + + public function test_uninstall_on_non_main_network() { + $this->run_multisite_uninstall_test( false ); + } + + public function run_multisite_uninstall_test( $run_on_main_network = true ) { + if ( ! is_multisite() ) { + throw new \Exception(); + } + + /** + * As of 4.3.2, the initialize() that will have run in the set_up() above will have put the release metadata + * in a network option associated with the main network. + * In 4.3.1, it would have been stored in a network option associated with the *current* network + * at the time of retrieval and storage. + * + * So to simulate the scenario that would have been possible in 4.3.1, we'll add it also to a non-main network. + * + * We want to ensure that any possible network option is cleaned up at the time of uninstall. + */ + + // Create a new, non-main network. + $new_network_id = self::add_network(); + $main_network_id = get_main_network_id(); + + // Get the metadata that would have been stored on a main network option. + $opt = get_network_option( $main_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ); + + // Put it on an option associated with the new network. + update_network_option( $new_network_id, FontAwesome_Release_Provider::OPTIONS_KEY, $opt ); + + // Make sure they're both there. + $this->assertArrayHasKey( 'refreshed_at', get_network_option( $new_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ) ); + $this->assertArrayHasKey( 'refreshed_at', get_network_option( $main_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ) ); + + if ( ! $run_on_main_network ) { + // Now switch to that new network. + \switch_to_network( $new_network_id ); + } + + //FontAwesome_Deactivator::deactivate(); + FontAwesome_Deactivator::uninstall(); + //delete_network_option( $new_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ); + //delete_network_option( $main_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ); + + $this->assertFalse( get_network_option( $new_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ) ); + $this->assertFalse( get_network_option( $main_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ) ); + } + + public function handle_add_network( $network_id, $params ) { + array_push( $this->added_network_ids, $network_id ); + } + + public static function add_network() { + $sub_domain = dechex( wp_rand( PHP_INT_MIN, PHP_INT_MAX ) ); + $domain = "$sub_domain.example.com"; + $path = '/'; + + $admin_user = get_users( array( 'role' => 'administrator' ) )[0]; + $result = \add_network( + array( + 'domain' => $domain, + 'path' => '/', + 'site_name' => $domain, + 'network_name' => $domain, + 'user_id' => $admin_user->ID, + 'network_admin_id' => $admin_user->ID, + ) + ); + + if ( is_wp_error( $result ) ) { + throw new \Exception( 'failed creating network' ); + } + + return $result; + } +} From 7d26a84f8e5495a04c962478b5415b6f3758e24c Mon Sep 17 00:00:00 2001 From: Mike Wilkerson <11575183+mlwilkerson@users.noreply.github.com> Date: Tue, 25 Oct 2022 17:22:09 -0700 Subject: [PATCH 14/25] WIP: multisite test refactoring --- tests/_support/font-awesome-phpunit-util.php | 38 ++++++++++++++++++++ tests/test-multisite-deactivation.php | 33 ++--------------- 2 files changed, 40 insertions(+), 31 deletions(-) diff --git a/tests/_support/font-awesome-phpunit-util.php b/tests/_support/font-awesome-phpunit-util.php index 698adb132..5495e6b4d 100644 --- a/tests/_support/font-awesome-phpunit-util.php +++ b/tests/_support/font-awesome-phpunit-util.php @@ -131,3 +131,41 @@ function create_subsites($domains = ['alpha.example.com', 'beta.example.com']) { return $results; } + +if ( is_multisite() ) : + require_once dirname( __FILE__ ) . '/wp-multi-network-functions.php'; + + function add_network() { + $sub_domain = dechex( wp_rand( PHP_INT_MIN, PHP_INT_MAX ) ); + $domain = "$sub_domain.example.com"; + $path = '/'; + + $admin_user = get_users( array( 'role' => 'administrator' ) )[0]; + $result = \add_network( + array( + 'domain' => $domain, + 'path' => '/', + 'site_name' => $domain, + 'network_name' => $domain, + 'user_id' => $admin_user->ID, + 'network_admin_id' => $admin_user->ID, + ) + ); + + if ( is_wp_error( $result ) ) { + throw new \Exception( 'failed creating network' ); + } + + return $result; + } + + function curry_add_network_handler( &$network_ids ) { + return function ( $network_id, $params ) use ( &$network_ids ) { + if ( ! is_array( $network_ids ) ) { + return null; + } + + array_push( $network_ids, $network_id ); + }; + } +endif; diff --git a/tests/test-multisite-deactivation.php b/tests/test-multisite-deactivation.php index bf629b319..fbbfc8de6 100644 --- a/tests/test-multisite-deactivation.php +++ b/tests/test-multisite-deactivation.php @@ -9,7 +9,6 @@ // phpcs:ignoreFile Generic.Commenting.DocComment.MissingShort require_once dirname( __FILE__ ) . '/../includes/class-fontawesome-deactivator.php'; require_once dirname( __FILE__ ) . '/_support/font-awesome-phpunit-util.php'; -require_once dirname( __FILE__ ) . '/_support/wp-multi-network-functions.php'; use Yoast\WPTestUtils\WPIntegration\TestCase; class MultisiteDeactivationTest extends TestCase { @@ -48,7 +47,7 @@ public function set_up() { // This activates network wide, for all sites that exist at the time. FontAwesome_Activator::initialize(); - add_action( 'add_network', array( $this, 'handle_add_network' ), 99, 2 ); + add_action( 'add_network', curry_add_network_handler( $this->added_network_ids ), 99, 2 ); } public function tear_down() { @@ -81,7 +80,7 @@ public function run_multisite_uninstall_test( $run_on_main_network = true ) { */ // Create a new, non-main network. - $new_network_id = self::add_network(); + $new_network_id = add_network(); $main_network_id = get_main_network_id(); // Get the metadata that would have been stored on a main network option. @@ -107,32 +106,4 @@ public function run_multisite_uninstall_test( $run_on_main_network = true ) { $this->assertFalse( get_network_option( $new_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ) ); $this->assertFalse( get_network_option( $main_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ) ); } - - public function handle_add_network( $network_id, $params ) { - array_push( $this->added_network_ids, $network_id ); - } - - public static function add_network() { - $sub_domain = dechex( wp_rand( PHP_INT_MIN, PHP_INT_MAX ) ); - $domain = "$sub_domain.example.com"; - $path = '/'; - - $admin_user = get_users( array( 'role' => 'administrator' ) )[0]; - $result = \add_network( - array( - 'domain' => $domain, - 'path' => '/', - 'site_name' => $domain, - 'network_name' => $domain, - 'user_id' => $admin_user->ID, - 'network_admin_id' => $admin_user->ID, - ) - ); - - if ( is_wp_error( $result ) ) { - throw new \Exception( 'failed creating network' ); - } - - return $result; - } } From 0029e3cf344250452cf0a6360f9da7cf3fea9c16 Mon Sep 17 00:00:00 2001 From: Mike Wilkerson <11575183+mlwilkerson@users.noreply.github.com> Date: Tue, 25 Oct 2022 17:49:11 -0700 Subject: [PATCH 15/25] fix multisite-upgrade test --- tests/test-multisite-upgrade.php | 40 ++++++-------------------------- 1 file changed, 7 insertions(+), 33 deletions(-) diff --git a/tests/test-multisite-upgrade.php b/tests/test-multisite-upgrade.php index 3f6efecb6..0c767c8d9 100644 --- a/tests/test-multisite-upgrade.php +++ b/tests/test-multisite-upgrade.php @@ -9,7 +9,6 @@ // phpcs:ignoreFile Generic.Commenting.DocComment.MissingShort require_once dirname( __FILE__ ) . '/../includes/class-fontawesome-activator.php'; require_once dirname( __FILE__ ) . '/_support/font-awesome-phpunit-util.php'; -require_once dirname( __FILE__ ) . '/_support/wp-multi-network-functions.php'; use Yoast\WPTestUtils\WPIntegration\TestCase; class MultisiteUpgradeTest extends TestCase { @@ -45,10 +44,9 @@ public function set_up() { ) ); - // This activates network wide, for all sites that exist at the time. - FontAwesome_Activator::initialize(); + FontAwesome_Release_Provider::load_releases(); - add_action( 'add_network', array( $this, 'handle_add_network' ), 99, 2 ); + add_action( 'add_network', curry_add_network_handler( $this->added_network_ids ), 99, 2 ); } public function tear_down() { @@ -81,7 +79,7 @@ public function run_multisite_upgrade_test($run_on_main_network = true) { */ // Create a new, non-main network. - $new_network_id = self::add_network(); + $new_network_id = add_network(); $main_network_id = get_main_network_id(); // Get the metadata that would have been stored on a main network option. @@ -104,38 +102,14 @@ public function run_multisite_upgrade_test($run_on_main_network = true) { \switch_to_network( $new_network_id ); } + // This activates network wide, for all sites that exist at the time on the current network. + $expected_options = array_merge( FontAwesome::DEFAULT_USER_OPTIONS, array( 'version' => fa()->latest_version_6() ) ); + update_option( FontAwesome::OPTIONS_KEY, $expected_options ); + // Expecting no exception to be thrown. $this->assertNull( fa()->try_upgrade() ); $this->assertFalse( get_network_option( $new_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ) ); $this->assertArrayHasKey( 'refreshed_at', get_network_option( $main_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ) ); } - - public function handle_add_network( $network_id, $params ) { - array_push( $this->added_network_ids, $network_id ); - } - - public static function add_network() { - $sub_domain = dechex( wp_rand( PHP_INT_MIN, PHP_INT_MAX ) ); - $domain = "$sub_domain.example.com"; - $path = '/'; - - $admin_user = get_users( array( 'role' => 'administrator' ) )[0]; - $result = \add_network( - array( - 'domain' => $domain, - 'path' => '/', - 'site_name' => $domain, - 'network_name' => $domain, - 'user_id' => $admin_user->ID, - 'network_admin_id' => $admin_user->ID, - ) - ); - - if ( is_wp_error( $result ) ) { - throw new \Exception( 'failed creating network' ); - } - - return $result; - } } From 15572f398be7bf8cc748f197e63461d834fe4ea5 Mon Sep 17 00:00:00 2001 From: Mike Wilkerson <11575183+mlwilkerson@users.noreply.github.com> Date: Tue, 25 Oct 2022 17:49:25 -0700 Subject: [PATCH 16/25] add additional assertion to multisite-activation test --- tests/test-multisite-activation.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test-multisite-activation.php b/tests/test-multisite-activation.php index bf52e59da..13aad9f3f 100644 --- a/tests/test-multisite-activation.php +++ b/tests/test-multisite-activation.php @@ -298,6 +298,9 @@ public function test_add_network_after_activation() { // This should not throw an exception, despite switching networks. $ver = fa()->latest_version_6(); $this->assertEquals( $ver, '6.1.1' ); + + $expected_options = array_merge( FontAwesome::DEFAULT_USER_OPTIONS, array( 'version' => fa()->latest_version_6() ) ); + $this->assertEquals( $expected_options, fa()->options() ); } public static function add_network() { From e5af8e0f41ffeea4ea314073f7bc72cbcd72c6b6 Mon Sep 17 00:00:00 2001 From: Mike Wilkerson <11575183+mlwilkerson@users.noreply.github.com> Date: Tue, 25 Oct 2022 17:50:26 -0700 Subject: [PATCH 17/25] more multisite test refactoring --- tests/test-multisite-activation.php | 33 ++--------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/tests/test-multisite-activation.php b/tests/test-multisite-activation.php index 13aad9f3f..3bf53682e 100644 --- a/tests/test-multisite-activation.php +++ b/tests/test-multisite-activation.php @@ -4,7 +4,6 @@ require_once dirname( __FILE__ ) . '/../includes/class-fontawesome-activator.php'; require_once dirname( __FILE__ ) . '/../includes/class-fontawesome-exception.php'; require_once dirname( __FILE__ ) . '/_support/font-awesome-phpunit-util.php'; -require_once dirname( __FILE__ ) . '/_support/wp-multi-network-functions.php'; use Yoast\WPTestUtils\WPIntegration\TestCase; @@ -37,7 +36,7 @@ public function set_up() { reset_db(); remove_all_actions( 'font_awesome_preferences' ); remove_all_filters( 'wp_is_large_network' ); - add_action( 'add_network', array( $this, 'handle_add_network' ), 99, 2 ); + add_action( 'add_network', curry_add_network_handler( $this->added_network_ids ), 99, 2 ); FontAwesome::reset(); ( new Mock_FontAwesome_Metadata_Provider() )->mock( array( @@ -288,7 +287,7 @@ public function test_add_network_after_activation() { FontAwesome_Activator::initialize(); // Now create a new network. - $new_network_id = self::add_network(); + $new_network_id = add_network(); // Switch to it. \switch_to_network( $new_network_id ); @@ -302,32 +301,4 @@ public function test_add_network_after_activation() { $expected_options = array_merge( FontAwesome::DEFAULT_USER_OPTIONS, array( 'version' => fa()->latest_version_6() ) ); $this->assertEquals( $expected_options, fa()->options() ); } - - public static function add_network() { - $sub_domain = dechex( wp_rand( PHP_INT_MIN, PHP_INT_MAX ) ); - $domain = "$sub_domain.example.com"; - $path = '/'; - - $admin_user = get_users( array( 'role' => 'administrator' ) )[0]; - $result = \add_network( - array( - 'domain' => $domain, - 'path' => '/', - 'site_name' => $domain, - 'network_name' => $domain, - 'user_id' => $admin_user->ID, - 'network_admin_id' => $admin_user->ID, - ) - ); - - if ( is_wp_error( $result ) ) { - throw new \Exception( 'failed creating network' ); - } - - return $result; - } - - public function handle_add_network( $network_id, $params ) { - array_push( $this->added_network_ids, $network_id ); - } } From 4169edddf932bbf934b97566825ffb902ca79a5f Mon Sep 17 00:00:00 2001 From: Mike Wilkerson <11575183+mlwilkerson@users.noreply.github.com> Date: Tue, 25 Oct 2022 17:51:01 -0700 Subject: [PATCH 18/25] test cleanup --- tests/test-multisite-deactivation.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/test-multisite-deactivation.php b/tests/test-multisite-deactivation.php index fbbfc8de6..7f980e4d7 100644 --- a/tests/test-multisite-deactivation.php +++ b/tests/test-multisite-deactivation.php @@ -98,10 +98,7 @@ public function run_multisite_uninstall_test( $run_on_main_network = true ) { \switch_to_network( $new_network_id ); } - //FontAwesome_Deactivator::deactivate(); FontAwesome_Deactivator::uninstall(); - //delete_network_option( $new_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ); - //delete_network_option( $main_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ); $this->assertFalse( get_network_option( $new_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ) ); $this->assertFalse( get_network_option( $main_network_id, FontAwesome_Release_Provider::OPTIONS_KEY ) ); From 0e1e7feb924b26ede8b8e794cb0501ec962b8537 Mon Sep 17 00:00:00 2001 From: Mike Wilkerson <11575183+mlwilkerson@users.noreply.github.com> Date: Tue, 25 Oct 2022 17:54:26 -0700 Subject: [PATCH 19/25] auto-formatting --- includes/class-fontawesome-release-provider.php | 4 ++-- includes/class-fontawesome.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/class-fontawesome-release-provider.php b/includes/class-fontawesome-release-provider.php index 1ba9aae09..35d4ffbae 100644 --- a/includes/class-fontawesome-release-provider.php +++ b/includes/class-fontawesome-release-provider.php @@ -500,7 +500,7 @@ public static function get_option() { public static function delete_option() { if ( is_multisite() ) { $result_accumulator = true; - + /** * Delete the network option for all networks. * In 4.3.1, it's possible that this option could have been created in @@ -510,7 +510,7 @@ public static function delete_option() { * Starting in 4.3.2, we only store the releases metadata on an option associated with the main network. */ foreach ( get_networks() as $network ) { - $current_result = delete_network_option( $network->id, self::OPTIONS_KEY ); + $current_result = delete_network_option( $network->id, self::OPTIONS_KEY ); $result_accumulator = $result_accumulator && $current_result; } diff --git a/includes/class-fontawesome.php b/includes/class-fontawesome.php index db7f17d8f..2bfbdb61d 100644 --- a/includes/class-fontawesome.php +++ b/includes/class-fontawesome.php @@ -614,7 +614,7 @@ private function maybe_move_release_metadata_for_upgrade_multisite() { $option_value = get_network_option( $network->id, FontAwesome_Release_Provider::OPTIONS_KEY ); if ( is_array( $option_value ) ) { - $result = update_network_option( get_main_network_id(), FontAwesome_Release_Provider::OPTIONS_KEY, $option_value ); + $result = update_network_option( get_main_network_id(), FontAwesome_Release_Provider::OPTIONS_KEY, $option_value ); if ( ! $result ) { throw UpgradeException::multisite_network_option_update(); From e61cf138a83b2e149a3ee69dd906ac76460ffd36 Mon Sep 17 00:00:00 2001 From: Mike Wilkerson <11575183+mlwilkerson@users.noreply.github.com> Date: Tue, 25 Oct 2022 17:55:53 -0700 Subject: [PATCH 20/25] ignoreFile for phpcs --- tests/_support/wp-multi-network-functions.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/_support/wp-multi-network-functions.php b/tests/_support/wp-multi-network-functions.php index 2a0877d3b..cb59165a6 100644 --- a/tests/_support/wp-multi-network-functions.php +++ b/tests/_support/wp-multi-network-functions.php @@ -1,4 +1,5 @@ Date: Tue, 25 Oct 2022 17:56:32 -0700 Subject: [PATCH 21/25] add missing @throws --- includes/class-fontawesome.php | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/class-fontawesome.php b/includes/class-fontawesome.php index 2bfbdb61d..cd04ffa73 100644 --- a/includes/class-fontawesome.php +++ b/includes/class-fontawesome.php @@ -597,6 +597,7 @@ private function maybe_move_release_metadata_for_upgrade_single_site() { * Internal use only. * * @throws ReleaseMetadataMissingException + * @throws UpgradeException * @ignore * @internal */ From 55c94a70065d71e4e8024e1659e24a25ae07d108 Mon Sep 17 00:00:00 2001 From: Mike Wilkerson <11575183+mlwilkerson@users.noreply.github.com> Date: Tue, 25 Oct 2022 20:22:13 -0700 Subject: [PATCH 22/25] exclude new multisite tests from non-multisite test config --- phpunit.xml.dist | 2 ++ 1 file changed, 2 insertions(+) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 418774263..f4cf60480 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -15,6 +15,8 @@