get_error_message()); wu_log_add('site-duplication', $message, LogLevel::ERROR); return $duplicate_site; } // translators: %1$d is the ID of the site template used, and %2$d is the id of the new site. $message = sprintf(__('Attempt to duplicate site %1$d successful - New site id: %2$d', 'wp-multisite-waas'), $from_site_id, $duplicate_site); wu_log_add('site-duplication', $message); return $duplicate_site; } /** * Replace the contents of a site with the contents of another. * * @since 2.0.0 * * @param int $from_site_id Site to get the data from. * @param int $to_site_id Site to override. * @param array $args List of duplication parameters, check Site_Duplicator::process_duplication for reference. * @return int|false ID of the created site. */ public static function override_site($from_site_id, $to_site_id, $args = []) { $to_site = wu_get_site($to_site_id); $to_site_membership_id = $to_site->get_membership_id(); $to_site_membership = $to_site->get_membership(); $to_site_customer = $to_site_membership->get_customer(); $args = wp_parse_args( $args, [ 'email' => $to_site_customer->get_email_address(), 'title' => $to_site->get_title(), 'path' => $to_site->get_path(), 'from_site_id' => $from_site_id, 'to_site_id' => $to_site_id, 'meta' => $to_site->meta, ] ); $duplicate_site_id = self::process_duplication($args); if (is_wp_error($duplicate_site_id)) { // translators: %s id the template site id and %s is the error message returned. $message = sprintf(__('Attempt to override site %1$d with data from site %2$d failed: %3$s', 'wp-multisite-waas'), $from_site_id, $to_site_id, $duplicate_site_id->get_error_message()); wu_log_add('site-duplication', $message, LogLevel::ERROR); return false; } $new_to_site = wu_get_site($duplicate_site_id); $new_to_site->set_membership_id($to_site_membership_id); $new_to_site->set_customer_id($to_site->get_customer_id()); $new_to_site->set_template_id($from_site_id); $new_to_site->set_type('customer_owned'); $new_to_site->set_title($to_site->get_title()); $saved = $new_to_site->save(); if ($saved) { // translators: %1$d is the ID of the site template used, and %2$d is the ID of the overriden site. $message = sprintf(__('Attempt to override site %1$d with data from site %2$d successful.', 'wp-multisite-waas'), $from_site_id, $duplicate_site_id); wu_log_add('site-duplication', $message); return $saved; } } /** * Processes a site duplication. * * @since 2.0.0 * * @param array $args List of parameters of the duplication. * - email Email of the admin user to be created. * - title Title of the (new or not) site. * - path Path of the new site. * - from_site_id ID of the template site being used. * - to_site_id ID of the target site. Can be false to create new site. * - keep_users If we should keep users or not. Defaults to true. * - copy_files If we should copy the uploaded files or not. Defaults to true. * - public If the (new or not) site should be public. Defaults to true. * - domain The domain of the new site. * - network_id The network ID to allow for multi-network support. * @return int|\WP_Error The Site ID. */ protected static function process_duplication($args) { global $current_site, $wpdb; $args = wp_parse_args( $args, [ 'email' => '', // Required arguments. 'title' => '', // Required arguments. 'path' => '/', // Required arguments. 'from_site_id' => false, // Required arguments. 'to_site_id' => false, 'keep_users' => true, 'public' => true, 'domain' => $current_site->domain, 'copy_files' => wu_get_setting('copy_media', true), 'network_id' => get_current_network_id(), 'meta' => [], 'user_id' => 0, ] ); // Checks $args = (object) $args; $site_domain = $args->domain . $args->path; $wpdb->hide_errors(); if ( ! $args->from_site_id) { return new \WP_Error('from_site_id_required', __('You need to provide a valid site to duplicate.', 'wp-multisite-waas')); } $user_id = ! empty($args->user_id) ? $args->user_id : self::create_admin($args->email, $site_domain); if (is_wp_error($user_id)) { return $user_id; } if ( ! $args->to_site_id) { $meta = array_merge($args->meta, ['public' => $args->public]); $args->to_site_id = wpmu_create_blog($args->domain, $args->path, $args->title, $user_id, $meta, $args->network_id); $wpdb->show_errors(); } if (is_wp_error($args->to_site_id)) { return $args->to_site_id; } if ( ! is_numeric($args->to_site_id)) { return new \WP_Error('site_creation_failed', __('An attempt to create a new site failed.', 'wp-multisite-waas')); } if ( ! is_super_admin($user_id) && ! get_user_option('primary_blog', $user_id)) { update_user_option($user_id, 'primary_blog', $args->to_site_id, true); } \MUCD_Duplicate::bypass_server_limit(); if ($args->copy_files) { $result = \MUCD_Files::copy_files($args->from_site_id, $args->to_site_id); } /** * Supress email change notification on site duplication processes. */ add_filter('send_site_admin_email_change_email', '__return_false'); $result = \MUCD_Data::copy_data($args->from_site_id, $args->to_site_id); if ($args->keep_users) { $result = \MUCD_Duplicate::copy_users($args->from_site_id, $args->to_site_id); } wp_cache_flush(); /** * Allow developers to hook after a site duplication happens. * * @since 1.9.4 * @return void */ do_action( 'wu_duplicate_site', [ 'site_id' => $args->to_site_id, ] ); return $args->to_site_id; } /** * Creates an admin user if no user exists with this email. * * @since 2.0.0 * @param string $email The email. * @param string $domain The domain. * @return int Id of the user created. */ public static function create_admin($email, $domain) { // Create New site Admin if not exists $password = 'N/A'; $user_id = email_exists($email); if ( ! $user_id) { // Create a new user with a random password $password = wp_generate_password(12, false); $user_id = wpmu_create_user($domain, $password, $email); if (false === $user_id) { return new \WP_Error('user_creation_error', __('We were not able to create a new admin user for the site being duplicated.', 'wp-multisite-waas')); } else { wp_new_user_notification($user_id); } } return $user_id; } }