diff --git a/inc/ui/class-tours.php b/inc/ui/class-tours.php index 29ac7f888..dda3a5043 100644 --- a/inc/ui/class-tours.php +++ b/inc/ui/class-tours.php @@ -90,6 +90,68 @@ protected function get_meta_key($id) { return 'wu_tour_finished_' . str_replace('-', '_', $id); } + /** + * Returns legacy user-settings keys that may hold a finished tour flag. + * + * Older tour persistence used WordPress user settings directly. Depending + * on the WordPress version and whether the value travelled through the + * wp-settings-* cookie sanitizer, hyphenated tour IDs may have been stored + * either with underscores or with hyphens stripped entirely. Check both + * shapes so users who already dismissed those tours do not see them again. + * + * @since 2.5.2 + * + * @param string $id The tour ID. + * @return array + */ + protected function get_legacy_setting_keys($id) { + + return array_values( + array_unique( + [ + $this->get_setting_key($id), + 'wu_tour_' . preg_replace('/[^A-Za-z0-9_]/', '', $id), + ] + ) + ); + } + + /** + * Whether a legacy user-settings value marks the tour as finished. + * + * @since 2.5.2 + * + * @param string $id The tour ID. + * @param int $user_id User ID. + * @return bool + */ + protected function is_legacy_tour_finished($id, $user_id) { + + foreach ($this->get_legacy_setting_keys($id) as $setting_key) { + if (get_user_setting($setting_key, false)) { + return true; + } + } + + $stored_settings = get_user_option('user-settings', $user_id); + + if ( ! is_string($stored_settings) || '' === $stored_settings) { + return false; + } + + $parsed_settings = []; + + parse_str($stored_settings, $parsed_settings); + + foreach ($this->get_legacy_setting_keys($id) as $setting_key) { + if ( ! empty($parsed_settings[ $setting_key ])) { + return true; + } + } + + return false; + } + /** * Whether the tour has been finished for the current user. * @@ -117,7 +179,13 @@ protected function is_tour_finished($id, $user_id = 0) { return true; } - return (bool) get_user_setting($this->get_setting_key($id), false); + $legacy_finished = $this->is_legacy_tour_finished($id, $user_id); + + if ($legacy_finished) { + update_user_meta($user_id, $this->get_meta_key($id), 1); + } + + return $legacy_finished; } /** diff --git a/tests/WP_Ultimo/UI/Tours_Test.php b/tests/WP_Ultimo/UI/Tours_Test.php index b015d5e75..d6ccdd7eb 100644 --- a/tests/WP_Ultimo/UI/Tours_Test.php +++ b/tests/WP_Ultimo/UI/Tours_Test.php @@ -321,6 +321,41 @@ public function test_is_tour_finished_falls_back_to_legacy_user_setting(): void } } + /** + * Test is_tour_finished reads legacy stripped keys from user settings meta. + * + * Regression test for users who dismissed hyphenated tours before the tour ID + * normalisation fix. WordPress could persist keys with hyphens stripped (for + * example, wu_tour_checkoutformlist), while newer code looks for the + * underscore-normalised key (wu_tour_checkout_form_list). The meta fallback + * must recognise the stripped legacy shape and backfill the new user meta flag. + */ + public function test_is_tour_finished_reads_stripped_legacy_user_settings_meta(): void { + + $instance = $this->get_instance(); + + $user_id = self::factory()->user->create(['role' => 'administrator']); + wp_set_current_user($user_id); + + $reflection = new \ReflectionClass($instance); + $is_finished = $reflection->getMethod('is_tour_finished'); + $is_finished->setAccessible(true); + $get_meta_key = $reflection->getMethod('get_meta_key'); + $get_meta_key->setAccessible(true); + $get_legacy_keys = $reflection->getMethod('get_legacy_setting_keys'); + $get_legacy_keys->setAccessible(true); + + $meta_key = $get_meta_key->invoke($instance, 'checkout-form-list'); + + update_user_option($user_id, 'user-settings', 'wu_tour_checkoutformlist=1', false); + + $this->assertSame('wu_tour_checkoutformlist=1', get_user_option('user-settings', $user_id)); + $this->assertContains('wu_tour_checkoutformlist', $get_legacy_keys->invoke($instance, 'checkout-form-list')); + + $this->assertTrue($is_finished->invoke($instance, 'checkout-form-list', $user_id)); + $this->assertSame('1', get_user_meta($user_id, $meta_key, true)); + } + /** * Test enqueue_scripts uses wp_add_inline_script on 'underscore', not wu-admin. *