diff --git a/inc/helpers/class-site-duplicator.php b/inc/helpers/class-site-duplicator.php index 4594d57c..9a5fdb11 100644 --- a/inc/helpers/class-site-duplicator.php +++ b/inc/helpers/class-site-duplicator.php @@ -58,10 +58,17 @@ private function __construct() {} */ public static function duplicate_site($from_site_id, $title, $args = []) { - $args['from_site_id'] = $from_site_id; - $args['title'] = $title; + $from_site = get_site($from_site_id); - $duplicate_site = self::process_duplication($args); + if ( ! $from_site) { + // translators: %d is the source template site ID. + $duplicate_site = new \WP_Error('source_template_site_not_found', sprintf(__('Source template site %d not found. Cannot duplicate site.', 'ultimate-multisite'), $from_site_id)); + } else { + $args['from_site_id'] = $from_site_id; + $args['title'] = $title; + + $duplicate_site = self::process_duplication($args); + } if (is_wp_error($duplicate_site)) { diff --git a/inc/models/class-site.php b/inc/models/class-site.php index d968eafb..37862c3c 100644 --- a/inc/models/class-site.php +++ b/inc/models/class-site.php @@ -2029,9 +2029,9 @@ public function save() { ); } - if ($saved && $this->get_public()) { + if ( ! is_wp_error($saved) && is_numeric($saved) && $this->get_public()) { $profile_stage = microtime(true); - $site_id = $saved; + $site_id = (int) $saved; wp_update_site( $site_id, @@ -2040,7 +2040,7 @@ public function save() { ] ); $this->profile_sovereign_provisioning_stage( - (int) $saved, + $site_id, 'um_site.save_wp_update_site_public', microtime(true) - $profile_stage ); @@ -2258,7 +2258,7 @@ public function save() { * @param bool $is_new If this object is a new one. */ $profile_stage = microtime(true); - do_action('wu_model_post_save', $this->model, $data, $data_unserialized, $this, $new); // @phpstan-ignore-line + do_action('wu_model_post_save', $this->model, $data, $data_unserialized, $this, $new); /** * Fires after an object is stored into the database. diff --git a/tests/WP_Ultimo/Helpers/Site_Duplicator_Postmeta_Test.php b/tests/WP_Ultimo/Helpers/Site_Duplicator_Postmeta_Test.php index 7b6e665d..ddb9c3a6 100644 --- a/tests/WP_Ultimo/Helpers/Site_Duplicator_Postmeta_Test.php +++ b/tests/WP_Ultimo/Helpers/Site_Duplicator_Postmeta_Test.php @@ -444,6 +444,47 @@ public function test_all_postmeta_catch_all_does_not_overwrite_existing() { restore_current_blog(); } + /** + * Test that title fallback copies meta only when the target title is unique. + */ + public function test_all_postmeta_title_fallback_requires_unique_target_title() { + // Create a source post with an ID that will not exist on the target. + switch_to_blog($this->from_blog_id); + $page_id = self::factory()->post->create( + [ + 'import_id' => 900001, + 'post_type' => 'page', + 'post_title' => 'Ambiguous title fallback page', + ] + ); + update_post_meta($page_id, '_custom_field', 'source-value'); + restore_current_blog(); + + // Create two target posts with the same type and title. + switch_to_blog($this->to_blog_id); + $first_target_id = self::factory()->post->create( + [ + 'post_type' => 'page', + 'post_title' => 'Ambiguous title fallback page', + ] + ); + $second_target_id = self::factory()->post->create( + [ + 'post_type' => 'page', + 'post_title' => 'Ambiguous title fallback page', + ] + ); + restore_current_blog(); + + Testable_Site_Duplicator::backfill_all_postmeta($this->from_blog_id, $this->to_blog_id); + + // Ambiguous title matches must not receive source metadata. + switch_to_blog($this->to_blog_id); + $this->assertEmpty(get_post_meta($first_target_id, '_custom_field', true)); + $this->assertEmpty(get_post_meta($second_target_id, '_custom_field', true)); + restore_current_blog(); + } + /** * Test that backfill_all_postmeta only copies meta for posts that exist in target. */ diff --git a/tests/WP_Ultimo/Helpers/Site_Duplicator_Test.php b/tests/WP_Ultimo/Helpers/Site_Duplicator_Test.php index f6eed0dc..00d4b70a 100644 --- a/tests/WP_Ultimo/Helpers/Site_Duplicator_Test.php +++ b/tests/WP_Ultimo/Helpers/Site_Duplicator_Test.php @@ -211,8 +211,8 @@ public function test_duplicate_invalid_source_site() { $result = Site_Duplicator::duplicate_site($invalid_site_id, 'New Site', $args); - // The result should be either a WP_Error or a failure case - $this->assertTrue(is_wp_error($result) || ! $result || is_int($result)); + $this->assertInstanceOf(\WP_Error::class, $result); + $this->assertSame('source_template_site_not_found', $result->get_error_code()); } /**