From 8053fd73eba54fc0ae43efc359d0c8fe9db63c45 Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Tue, 7 Oct 2025 15:38:33 +0800 Subject: [PATCH 01/10] Member Content: Improve query performance --- .../class-convertkit-admin-cache-plugins.php | 37 +--------- ...lass-convertkit-admin-restrict-content.php | 73 +++++++++++++++++++ includes/class-convertkit-post.php | 15 +++- 3 files changed, 88 insertions(+), 37 deletions(-) diff --git a/admin/class-convertkit-admin-cache-plugins.php b/admin/class-convertkit-admin-cache-plugins.php index 21cd27bd3..a5b44add1 100644 --- a/admin/class-convertkit-admin-cache-plugins.php +++ b/admin/class-convertkit-admin-cache-plugins.php @@ -56,7 +56,7 @@ public function maybe_configure_cache_plugins() { // If no Pages, Posts or CPTs are configured to use Restrict Content, don't // configure any caching plugins. - if ( ! $this->restrict_content_enabled() ) { + if ( ! WP_ConvertKit()->get_class( 'admin_restrict_content' )->restrict_content_enabled() ) { return; } @@ -69,41 +69,6 @@ public function maybe_configure_cache_plugins() { } - /** - * Check if any Pages, Posts or CPTs are configured to use Restrict Content. - * - * @since 2.7.6 - * - * @return bool - */ - private function restrict_content_enabled() { - - // Check if any Pages, Posts or CPTs are configured to use Restrict Content. - $query = new WP_Query( - array( - 'post_type' => convertkit_get_supported_post_types(), - 'post_status' => 'publish', - 'meta_query' => array( - array( - 'key' => '_wp_convertkit_post_meta', - 'value' => '"restrict_content";', - 'compare' => 'LIKE', - ), - array( - 'key' => '_wp_convertkit_post_meta', - 'value' => '"restrict_content";s:1:"0"', - 'compare' => 'NOT LIKE', - ), - ), - 'posts_per_page' => 1, - 'fields' => 'ids', - ) - ); - - return $query->have_posts(); - - } - /** * Show a notice in the WordPress Administration interface if * Litespeed Cache is active, its caching enabled and no rule to disable caching diff --git a/admin/class-convertkit-admin-restrict-content.php b/admin/class-convertkit-admin-restrict-content.php index 4a1e04290..7a379a319 100644 --- a/admin/class-convertkit-admin-restrict-content.php +++ b/admin/class-convertkit-admin-restrict-content.php @@ -63,6 +63,15 @@ class ConvertKit_Admin_Restrict_Content { */ public $restrict_content_filter = 0; + /** + * Holds the key for the restrict content enabled flag in the WordPress options table. + * + * @since 3.0.4 + * + * @var string + */ + public $restrict_content_enabled_key = 'convertkit_restrict_content_enabled'; + /** * Registers action and filter hooks. * @@ -81,6 +90,70 @@ public function __construct() { add_action( 'pre_get_posts', array( $this, 'filter_wp_list_table_output' ) ); add_action( 'restrict_manage_posts', array( $this, 'output_wp_list_table_filters' ) ); + // Update whether any Pages, Posts or CPTs are configured to use Restrict Content when a Page, Post or CPT is created, edited, trashed or deleted. + add_action( 'convertkit_post_save', array( $this, 'update_restrict_content_enabled' ) ); + add_action( 'trashed_post', array( $this, 'update_restrict_content_enabled' ) ); + add_action( 'delete_post', array( $this, 'update_restrict_content_enabled' ) ); + + } + + /** + * Update whether any Pages, Posts or CPTs are configured to use Restrict Content when a + * Page, Post or CPT is created, edited or deleted. + * + * @since 3.0.4 + * + * @return bool + */ + public function update_restrict_content_enabled() { + + global $wpdb; + + // We don't use WP_Query, as it's expensive on sites with many Pages/Posts/CPTs. + $results = $wpdb->get_results( + $wpdb->prepare( + 'SELECT ' . $wpdb->posts . '.ID + FROM ' . $wpdb->postmeta . ' + LEFT JOIN ' . $wpdb->posts . ' ON ' . $wpdb->postmeta . '.post_id = ' . $wpdb->posts . ".ID + WHERE ( + (meta_key = '_wp_convertkit_post_meta' AND meta_value LIKE %s) + AND + (meta_key = '_wp_convertkit_post_meta' AND meta_value NOT LIKE %s) + AND + (meta_key = '_wp_convertkit_post_meta' AND meta_value NOT LIKE %s) + ) + AND + " . $wpdb->posts . ".post_status = 'publish' + AND + " . $wpdb->posts . ".post_type IN ('" . implode( "', '", convertkit_get_supported_post_types() ) . "') LIMIT 1;", // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.QuotedDynamicPlaceholderGeneration + '%' . $wpdb->esc_like( '"restrict_content";' ) . '%', + '%' . $wpdb->esc_like( '"restrict_content";s:0:' ) . '%', + '%' . $wpdb->esc_like( '"restrict_content";s:1:"0"' ) . '%', + ) + ); + + // If results are not empty, a Page, Post or CPT is configured to use Restrict Content. + $restrict_content_enabled = ! empty( $results ); + + // Update option. + update_option( $this->restrict_content_enabled_key, $restrict_content_enabled ); + + // Return. + return $restrict_content_enabled; + + } + + /** + * Returns whether any Pages, Posts or CPTs are configured to use Restrict Content. + * + * @since 3.0.4 + * + * @return bool + */ + public function restrict_content_enabled() { + + return (bool) get_option( $this->restrict_content_enabled_key, false ); + } /** diff --git a/includes/class-convertkit-post.php b/includes/class-convertkit-post.php index 58d1a4ab8..8c42c5444 100644 --- a/includes/class-convertkit-post.php +++ b/includes/class-convertkit-post.php @@ -298,7 +298,20 @@ public function restrict_content_enabled() { */ public function save( $meta ) { - return update_post_meta( $this->post_id, self::POST_META_KEY, $meta ); + // Save settings. + $result = update_post_meta( $this->post_id, self::POST_META_KEY, $meta ); + + /** + * Action hook to run when a Post's settings are saved. + * + * @since 3.0.4 + * + * @param int $post_id Post ID. + * @param array $meta Settings. + */ + do_action( 'convertkit_post_save', $this->post_id, $meta ); + + return $result; } From bce2e5b69a2d14e92ddcc3b0b86c6282391fc829 Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Tue, 7 Oct 2025 15:54:34 +0800 Subject: [PATCH 02/10] Added tests --- ...lass-convertkit-admin-restrict-content.php | 2 +- .../RestrictContentIPInRangeTest.php | 108 -------- tests/Integration/RestrictContentTest.php | 259 ++++++++++++++++++ 3 files changed, 260 insertions(+), 109 deletions(-) delete mode 100644 tests/Integration/RestrictContentIPInRangeTest.php create mode 100644 tests/Integration/RestrictContentTest.php diff --git a/admin/class-convertkit-admin-restrict-content.php b/admin/class-convertkit-admin-restrict-content.php index 7a379a319..1205e505d 100644 --- a/admin/class-convertkit-admin-restrict-content.php +++ b/admin/class-convertkit-admin-restrict-content.php @@ -91,7 +91,7 @@ public function __construct() { add_action( 'restrict_manage_posts', array( $this, 'output_wp_list_table_filters' ) ); // Update whether any Pages, Posts or CPTs are configured to use Restrict Content when a Page, Post or CPT is created, edited, trashed or deleted. - add_action( 'convertkit_post_save', array( $this, 'update_restrict_content_enabled' ) ); + add_action( 'wp_insert_post', array( $this, 'update_restrict_content_enabled' ) ); add_action( 'trashed_post', array( $this, 'update_restrict_content_enabled' ) ); add_action( 'delete_post', array( $this, 'update_restrict_content_enabled' ) ); diff --git a/tests/Integration/RestrictContentIPInRangeTest.php b/tests/Integration/RestrictContentIPInRangeTest.php deleted file mode 100644 index 85d981947..000000000 --- a/tests/Integration/RestrictContentIPInRangeTest.php +++ /dev/null @@ -1,108 +0,0 @@ -resource = new \ConvertKit_Output_Restrict_Content(); - - // Confirm initialization didn't result in an error. - $this->assertNotInstanceOf(\WP_Error::class, $this->resource); - } - - /** - * Performs actions after each test. - * - * @since 2.4.2 - */ - public function tearDown(): void - { - // Destroy the class we tested. - unset($this->resource); - - // Deactivate Plugin. - deactivate_plugins('convertkit/wp-convertkit.php'); - - parent::tearDown(); - } - - /** - * Test that IP addresses 34.100.182.96 through .111 (i.e. in the CIDR range /28) - * are returned as true by the ip_in_range() function. - * - * @since 2.4.2 - */ - public function testIPAddressInRange() - { - for ($i = 96; $i <= 111; $i++) { - $this->assertTrue($this->resource->ip_in_range('34.100.182.' . $i, '34.100.182.96/28')); - } - } - - /** - * Test that IP address 34.100.182.112 in the range 34.100.182.96/28 - * are returned as false by the ip_in_range() function. - * - * @since 2.4.2 - */ - public function testIPAddressOutsideRange() - { - $this->assertFalse($this->resource->ip_in_range('34.100.182.112', '34.100.182.96/28')); - } - - /** - * Test that invalid IP addresses are returned as false by the ip_in_range() function. - * - * @since 2.4.2 - */ - public function testInvalidIPAddresses() - { - $this->assertFalse($this->resource->ip_in_range('0.0.0.0', '34.100.182.96/28')); - $this->assertFalse($this->resource->ip_in_range('999.999.999.999', '34.100.182.96/28')); - $this->assertFalse($this->resource->ip_in_range('not-an-ip-address', '34.100.182.96/28')); - } - - /** - * Test that invalid ranges return false by the ip_in_range() function. - * - * @since 2.4.2 - */ - public function testInvalidRanges() - { - $this->assertFalse($this->resource->ip_in_range('34.100.182.96', '34.100.182.96')); - $this->assertFalse($this->resource->ip_in_range('34.100.182.96', '34.100.182.96/999')); - $this->assertFalse($this->resource->ip_in_range('34.100.182.96', '34.100.182.96/not-a-range')); - $this->assertFalse($this->resource->ip_in_range('34.100.182.96', '0.0.0.0')); - $this->assertFalse($this->resource->ip_in_range('34.100.182.96', 'not-an-ip-address')); - $this->assertFalse($this->resource->ip_in_range('34.100.182.96', '999.999.999.999/999')); - $this->assertFalse($this->resource->ip_in_range('34.100.182.96', '999.999.999.999/not-a-range')); - $this->assertFalse($this->resource->ip_in_range('34.100.182.96', 'not-an-ip-address/not-a-range')); - } -} diff --git a/tests/Integration/RestrictContentTest.php b/tests/Integration/RestrictContentTest.php new file mode 100644 index 000000000..db70b1198 --- /dev/null +++ b/tests/Integration/RestrictContentTest.php @@ -0,0 +1,259 @@ +resource = new \ConvertKit_Output_Restrict_Content(); + + // Confirm initialization didn't result in an error. + $this->assertNotInstanceOf(\WP_Error::class, $this->resource); + } + + /** + * Performs actions after each test. + * + * @since 2.4.2 + */ + public function tearDown(): void + { + // Destroy the class we tested. + unset($this->resource); + + // Deactivate Plugin. + deactivate_plugins('convertkit/wp-convertkit.php'); + + parent::tearDown(); + } + + /** + * Test that the Restrict Content enabled option is set to true when a published WordPress + * Post has the Restrict Content setting defined on the Post's creation. + * + * @since 3.0.4 + */ + public function testRestrictContentEnabledOptionTrueWhenPublishedPostIsRestrictedOnCreation() + { + // Initialise class. + $class = new \ConvertKit_Admin_Restrict_Content(); + + // Delete any existing Restrict Content enabled option from the options table. + delete_option($class->restrict_content_enabled_key); + + // Create a Post, restricting to a Kit Product. + $post_id = wp_insert_post( + [ + 'post_type' => 'page', + 'post_title' => 'Restrict Content', + 'post_status' => 'publish', + 'meta_input' => [ + '_wp_convertkit_post_meta' => [ + 'form' => '0', + 'landing_page' => '', + 'tag' => '', + 'restrict_content' => 'product_' . $_ENV['CONVERTKIT_API_PRODUCT_ID'], + ], + ], + ] + ); + + // Check that the Restrict Content enabled option is set to true. + $this->assertTrue($class->restrict_content_enabled()); + + // Trash the Post. + wp_trash_post($post_id); + + // Check that the Restrict Content enabled option is set to false. + $this->assertFalse($class->restrict_content_enabled()); + } + + /** + * Test that the Restrict Content enabled option is set to true when a published WordPress + * Post has the Restrict Content setting defined on the Post's update. + * + * @since 3.0.4 + */ + public function testRestrictContentEnabledOptionTrueWhenPublishedPostIsRestrictedOnUpdate() + { + // Initialise class. + $class = new \ConvertKit_Admin_Restrict_Content(); + + // Delete any existing Restrict Content enabled option from the options table. + delete_option($class->restrict_content_enabled_key); + + // Create a Post. + $post_id = wp_insert_post( + [ + 'post_type' => 'page', + 'post_title' => 'Restrict Content', + 'post_status' => 'publish', + ] + ); + + // Check that the Restrict Content enabled option is set to false. + $this->assertFalse($class->restrict_content_enabled()); + + // Update the Post, restricting to a Kit Product. + wp_update_post( + [ + 'ID' => $post_id, + 'meta_input' => [ + '_wp_convertkit_post_meta' => [ + 'form' => '0', + 'landing_page' => '', + 'tag' => '', + 'restrict_content' => 'product_' . $_ENV['CONVERTKIT_API_PRODUCT_ID'], + ], + ], + ] + ); + + // Check that the Restrict Content enabled option is set to true. + $this->assertTrue($class->restrict_content_enabled()); + + // Delete the Post. + wp_delete_post($post_id, true); + + // Check that the Restrict Content enabled option is set to false. + $this->assertFalse($class->restrict_content_enabled()); + } + + /** + * Test that the Restrict Content enabled option is set to true when a published WordPress + * Post has the Restrict Content setting removed on the Post's update. + * + * @since 3.0.4 + */ + public function testRestrictContentEnabledOptionFalseWhenPublishedPostIsUnrestrictedOnUpdate() + { + // Initialise class. + $class = new \ConvertKit_Admin_Restrict_Content(); + + // Delete any existing Restrict Content enabled option from the options table. + delete_option($class->restrict_content_enabled_key); + + // Create a Post, restricting to a Kit Product. + $post_id = wp_insert_post( + [ + 'post_type' => 'page', + 'post_title' => 'Restrict Content', + 'post_status' => 'publish', + 'meta_input' => [ + '_wp_convertkit_post_meta' => [ + 'form' => '0', + 'landing_page' => '', + 'tag' => '', + 'restrict_content' => 'product_' . $_ENV['CONVERTKIT_API_PRODUCT_ID'], + ], + ], + ] + ); + + // Check that the Restrict Content enabled option is set to true. + $this->assertTrue($class->restrict_content_enabled()); + + // Update the Post, removing the Restrict Content setting. + wp_update_post( + [ + 'ID' => $post_id, + 'meta_input' => [ + '_wp_convertkit_post_meta' => [ + 'form' => '0', + 'landing_page' => '', + 'tag' => '', + 'restrict_content' => '', + ], + ], + ] + ); + + // Check that the Restrict Content enabled option is set to false. + $this->assertFalse($class->restrict_content_enabled()); + + // Trash the Post. + wp_trash_post($post_id); + + // Check that the Restrict Content enabled option is set to false. + $this->assertFalse($class->restrict_content_enabled()); + } + + /** + * Test that IP addresses 34.100.182.96 through .111 (i.e. in the CIDR range /28) + * are returned as true by the ip_in_range() function. + * + * @since 2.4.2 + */ + public function testIPAddressInRange() + { + for ($i = 96; $i <= 111; $i++) { + $this->assertTrue($this->resource->ip_in_range('34.100.182.' . $i, '34.100.182.96/28')); + } + } + + /** + * Test that IP address 34.100.182.112 in the range 34.100.182.96/28 + * are returned as false by the ip_in_range() function. + * + * @since 2.4.2 + */ + public function testIPAddressOutsideRange() + { + $this->assertFalse($this->resource->ip_in_range('34.100.182.112', '34.100.182.96/28')); + } + + /** + * Test that invalid IP addresses are returned as false by the ip_in_range() function. + * + * @since 2.4.2 + */ + public function testInvalidIPAddresses() + { + $this->assertFalse($this->resource->ip_in_range('0.0.0.0', '34.100.182.96/28')); + $this->assertFalse($this->resource->ip_in_range('999.999.999.999', '34.100.182.96/28')); + $this->assertFalse($this->resource->ip_in_range('not-an-ip-address', '34.100.182.96/28')); + } + + /** + * Test that invalid ranges return false by the ip_in_range() function. + * + * @since 2.4.2 + */ + public function testInvalidRanges() + { + $this->assertFalse($this->resource->ip_in_range('34.100.182.96', '34.100.182.96')); + $this->assertFalse($this->resource->ip_in_range('34.100.182.96', '34.100.182.96/999')); + $this->assertFalse($this->resource->ip_in_range('34.100.182.96', '34.100.182.96/not-a-range')); + $this->assertFalse($this->resource->ip_in_range('34.100.182.96', '0.0.0.0')); + $this->assertFalse($this->resource->ip_in_range('34.100.182.96', 'not-an-ip-address')); + $this->assertFalse($this->resource->ip_in_range('34.100.182.96', '999.999.999.999/999')); + $this->assertFalse($this->resource->ip_in_range('34.100.182.96', '999.999.999.999/not-a-range')); + $this->assertFalse($this->resource->ip_in_range('34.100.182.96', 'not-an-ip-address/not-a-range')); + } +} From 277a5027b5a2911a6ee8be599ac55dea3028970d Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Tue, 7 Oct 2025 16:01:24 +0800 Subject: [PATCH 03/10] Revert change to `save` method --- includes/class-convertkit-post.php | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/includes/class-convertkit-post.php b/includes/class-convertkit-post.php index 8c42c5444..58d1a4ab8 100644 --- a/includes/class-convertkit-post.php +++ b/includes/class-convertkit-post.php @@ -298,20 +298,7 @@ public function restrict_content_enabled() { */ public function save( $meta ) { - // Save settings. - $result = update_post_meta( $this->post_id, self::POST_META_KEY, $meta ); - - /** - * Action hook to run when a Post's settings are saved. - * - * @since 3.0.4 - * - * @param int $post_id Post ID. - * @param array $meta Settings. - */ - do_action( 'convertkit_post_save', $this->post_id, $meta ); - - return $result; + return update_post_meta( $this->post_id, self::POST_META_KEY, $meta ); } From e81783c6d6f2964d296223520744172314e4a673 Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Tue, 7 Oct 2025 17:44:55 +0800 Subject: [PATCH 04/10] PHPStan compat. --- admin/class-convertkit-admin-restrict-content.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/admin/class-convertkit-admin-restrict-content.php b/admin/class-convertkit-admin-restrict-content.php index 1205e505d..952e890d9 100644 --- a/admin/class-convertkit-admin-restrict-content.php +++ b/admin/class-convertkit-admin-restrict-content.php @@ -102,8 +102,6 @@ public function __construct() { * Page, Post or CPT is created, edited or deleted. * * @since 3.0.4 - * - * @return bool */ public function update_restrict_content_enabled() { @@ -138,9 +136,6 @@ public function update_restrict_content_enabled() { // Update option. update_option( $this->restrict_content_enabled_key, $restrict_content_enabled ); - // Return. - return $restrict_content_enabled; - } /** From 455c181115c6693225c1b147d85de9f79acb7faf Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Wed, 8 Oct 2025 19:48:51 +0800 Subject: [PATCH 05/10] Coding standards --- admin/class-convertkit-admin-restrict-content.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/class-convertkit-admin-restrict-content.php b/admin/class-convertkit-admin-restrict-content.php index 952e890d9..2626e82e5 100644 --- a/admin/class-convertkit-admin-restrict-content.php +++ b/admin/class-convertkit-admin-restrict-content.php @@ -126,7 +126,7 @@ public function update_restrict_content_enabled() { " . $wpdb->posts . ".post_type IN ('" . implode( "', '", convertkit_get_supported_post_types() ) . "') LIMIT 1;", // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.QuotedDynamicPlaceholderGeneration '%' . $wpdb->esc_like( '"restrict_content";' ) . '%', '%' . $wpdb->esc_like( '"restrict_content";s:0:' ) . '%', - '%' . $wpdb->esc_like( '"restrict_content";s:1:"0"' ) . '%', + '%' . $wpdb->esc_like( '"restrict_content";s:1:"0"' ) . '%' ) ); From 2d88f25069406591c08fe8d860f20a13d05e4eb8 Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Thu, 9 Oct 2025 14:32:40 +0800 Subject: [PATCH 06/10] Tests: Create pages via UI, to trigger notices --- .../general/RestrictContentCacheCest.php | 198 ++++++++++++++---- 1 file changed, 156 insertions(+), 42 deletions(-) diff --git a/tests/EndToEnd/restrict-content/general/RestrictContentCacheCest.php b/tests/EndToEnd/restrict-content/general/RestrictContentCacheCest.php index 9495df5a0..7968b035e 100644 --- a/tests/EndToEnd/restrict-content/general/RestrictContentCacheCest.php +++ b/tests/EndToEnd/restrict-content/general/RestrictContentCacheCest.php @@ -50,15 +50,34 @@ public function testRestrictContentLiteSpeedCache(EndToEndTester $I) $I->waitForElementVisible('body.index-php'); $I->dontSee('Kit: Member Content: Please add ck_subscriber_id to LiteSpeed Cache\'s "Do Not Cache Cookies" setting by clicking here.'); - // Create Restricted Content Page. - $pageID = $I->createRestrictedContentPage( + // Add a Page using the Gutenberg editor. + $I->addGutenbergPage( $I, - [ - 'post_title' => 'Kit: Restrict Content: Product: LiteSpeed Cache', - 'restrict_content_setting' => 'product_' . $_ENV['CONVERTKIT_API_PRODUCT_ID'], + title: 'Kit: Restrict Content: Product: LiteSpeed Cache' + ); + + // Configure metabox's Restrict Content setting = Product name. + $I->configureMetaboxSettings( + $I, + metabox: 'wp-convertkit-meta-box', + configuration: [ + 'form' => [ 'select2', 'None' ], + 'restrict_content' => [ 'select2', $_ENV['CONVERTKIT_API_PRODUCT_NAME'] ], ] ); + // Add blocks. + $I->addGutenbergParagraphBlock($I, 'Visible content.'); + $I->addGutenbergBlock( + $I, + blockName: 'More', + blockProgrammaticName: 'more' + ); + $I->addGutenbergParagraphBlock($I, 'Member-only content.'); + + // Publish Page. + $url = $I->publishGutenbergPage($I); + // Test that a notice is displayed in the WordPress Administration interface, as a Restrict Content // page is configured. $I->amOnAdminPage('index.php'); @@ -77,7 +96,7 @@ public function testRestrictContentLiteSpeedCache(EndToEndTester $I) $I->logOut(); // Navigate to the page. - $I->amOnPage('?p=' . $pageID); + $I->amOnUrl($url); // Test that the restricted content CTA displays when no valid signed subscriber ID is used, // to confirm caching does not show member-only content. @@ -88,7 +107,7 @@ public function testRestrictContentLiteSpeedCache(EndToEndTester $I) $I->setRestrictContentCookieAndReload( $I, subscriberID: $_ENV['CONVERTKIT_API_SIGNED_SUBSCRIBER_ID'], - urlOrPageID: $pageID + urlOrPageID: $url ); $I->testRestrictContentDisplaysContent($I); @@ -119,15 +138,34 @@ public function testRestrictContentW3TotalCache(EndToEndTester $I) $I->waitForElementVisible('body.index-php'); $I->dontSee('Kit: Member Content: Please add ck_subscriber_id to W3 Total Cache\'s "Rejected Cookies" setting by clicking here.'); - // Create Restricted Content Page. - $pageID = $I->createRestrictedContentPage( + // Add a Page using the Gutenberg editor. + $I->addGutenbergPage( $I, - [ - 'post_title' => 'Kit: Restrict Content: Product: W3 Total Cache', - 'restrict_content_setting' => 'product_' . $_ENV['CONVERTKIT_API_PRODUCT_ID'], + title: 'Kit: Restrict Content: Product: W3 Total Cache' + ); + + // Configure metabox's Restrict Content setting = Product name. + $I->configureMetaboxSettings( + $I, + metabox: 'wp-convertkit-meta-box', + configuration: [ + 'form' => [ 'select2', 'None' ], + 'restrict_content' => [ 'select2', $_ENV['CONVERTKIT_API_PRODUCT_NAME'] ], ] ); + // Add blocks. + $I->addGutenbergParagraphBlock($I, 'Visible content.'); + $I->addGutenbergBlock( + $I, + blockName: 'More', + blockProgrammaticName: 'more' + ); + $I->addGutenbergParagraphBlock($I, 'Member-only content.'); + + // Publish Page. + $url = $I->publishGutenbergPage($I); + // Test that a notice is displayed in the WordPress Administration interface, as a Restrict Content // page is configured. $I->amOnAdminPage('index.php'); @@ -146,7 +184,7 @@ public function testRestrictContentW3TotalCache(EndToEndTester $I) $I->logOut(); // Navigate to the page. - $I->amOnPage('?p=' . $pageID); + $I->amOnUrl($url); // Test that the restricted content CTA displays when no valid signed subscriber ID is used, // to confirm caching does not show member-only content. @@ -157,7 +195,7 @@ public function testRestrictContentW3TotalCache(EndToEndTester $I) $I->setRestrictContentCookieAndReload( $I, subscriberID: $_ENV['CONVERTKIT_API_SIGNED_SUBSCRIBER_ID'], - urlOrPageID: $pageID + urlOrPageID: $url ); $I->testRestrictContentDisplaysContent($I); @@ -194,15 +232,34 @@ public function testRestrictContentWPFastestCache(EndToEndTester $I) $I->grabOptionFromDatabase('WpFastestCacheExclude') ); - // Create Restricted Content Page. - $pageID = $I->createRestrictedContentPage( + // Add a Page using the Gutenberg editor. + $I->addGutenbergPage( + $I, + title: 'Kit: Restrict Content: Product: WP Fastest Cache' + ); + + // Configure metabox's Restrict Content setting = Product name. + $I->configureMetaboxSettings( $I, - [ - 'post_title' => 'Kit: Restrict Content: Product: WP Fastest Cache', - 'restrict_content_setting' => 'product_' . $_ENV['CONVERTKIT_API_PRODUCT_ID'], + metabox: 'wp-convertkit-meta-box', + configuration: [ + 'form' => [ 'select2', 'None' ], + 'restrict_content' => [ 'select2', $_ENV['CONVERTKIT_API_PRODUCT_NAME'] ], ] ); + // Add blocks. + $I->addGutenbergParagraphBlock($I, 'Visible content.'); + $I->addGutenbergBlock( + $I, + blockName: 'More', + blockProgrammaticName: 'more' + ); + $I->addGutenbergParagraphBlock($I, 'Member-only content.'); + + // Publish Page. + $url = $I->publishGutenbergPage($I); + // Load the Dashboard. $I->amOnAdminPage('index.php'); $I->waitForElementVisible('body.index-php'); @@ -224,7 +281,7 @@ public function testRestrictContentWPFastestCache(EndToEndTester $I) $I->logOut(); // Navigate to the page. - $I->amOnPage('?p=' . $pageID); + $I->amOnUrl($url); // Test that the restricted content CTA displays when no valid signed subscriber ID is used, // to confirm caching does not show member-only content. @@ -235,7 +292,7 @@ public function testRestrictContentWPFastestCache(EndToEndTester $I) $I->setRestrictContentCookieAndReload( $I, subscriberID: $_ENV['CONVERTKIT_API_SIGNED_SUBSCRIBER_ID'], - urlOrPageID: $pageID + urlOrPageID: $url ); $I->testRestrictContentDisplaysContent($I); @@ -268,15 +325,34 @@ public function testRestrictContentWPOptimize(EndToEndTester $I) $config['cache_exception_cookies'][0] ); - // Create Restricted Content Page. - $pageID = $I->createRestrictedContentPage( + // Add a Page using the Gutenberg editor. + $I->addGutenbergPage( $I, - [ - 'post_title' => 'Kit: Restrict Content: Product: WP-Optimize', - 'restrict_content_setting' => 'product_' . $_ENV['CONVERTKIT_API_PRODUCT_ID'], + title: 'Kit: Restrict Content: Product: WP-Optimize' + ); + + // Configure metabox's Restrict Content setting = Product name. + $I->configureMetaboxSettings( + $I, + metabox: 'wp-convertkit-meta-box', + configuration: [ + 'form' => [ 'select2', 'None' ], + 'restrict_content' => [ 'select2', $_ENV['CONVERTKIT_API_PRODUCT_NAME'] ], ] ); + // Add blocks. + $I->addGutenbergParagraphBlock($I, 'Visible content.'); + $I->addGutenbergBlock( + $I, + blockName: 'More', + blockProgrammaticName: 'more' + ); + $I->addGutenbergParagraphBlock($I, 'Member-only content.'); + + // Publish Page. + $url = $I->publishGutenbergPage($I); + // Load the Dashboard. $I->amOnAdminPage('index.php'); $I->waitForElementVisible('body.index-php'); @@ -293,7 +369,7 @@ public function testRestrictContentWPOptimize(EndToEndTester $I) $I->logOut(); // Navigate to the page. - $I->amOnPage('?p=' . $pageID); + $I->amOnUrl($url); // Test that the restricted content CTA displays when no valid signed subscriber ID is used, // to confirm caching does not show member-only content. @@ -304,7 +380,7 @@ public function testRestrictContentWPOptimize(EndToEndTester $I) $I->setRestrictContentCookieAndReload( $I, subscriberID: $_ENV['CONVERTKIT_API_SIGNED_SUBSCRIBER_ID'], - urlOrPageID: $pageID + urlOrPageID: $url ); $I->testRestrictContentDisplaysContent($I); @@ -335,15 +411,34 @@ public function testRestrictContentWPSuperCache(EndToEndTester $I) $I->waitForElementVisible('body.index-php'); $I->dontSee('Kit: Member Content: Please add ck_subscriber_id to WP Super Cache\'s "Rejected Cookies" setting by clicking here.'); - // Create Restricted Content Page. - $pageID = $I->createRestrictedContentPage( + // Add a Page using the Gutenberg editor. + $I->addGutenbergPage( $I, - [ - 'post_title' => 'Kit: Restrict Content: Product: WP Super Cache', - 'restrict_content_setting' => 'product_' . $_ENV['CONVERTKIT_API_PRODUCT_ID'], + title: 'Kit: Restrict Content: Product: WP Super Cache' + ); + + // Configure metabox's Restrict Content setting = Product name. + $I->configureMetaboxSettings( + $I, + metabox: 'wp-convertkit-meta-box', + configuration: [ + 'form' => [ 'select2', 'None' ], + 'restrict_content' => [ 'select2', $_ENV['CONVERTKIT_API_PRODUCT_NAME'] ], ] ); + // Add blocks. + $I->addGutenbergParagraphBlock($I, 'Visible content.'); + $I->addGutenbergBlock( + $I, + blockName: 'More', + blockProgrammaticName: 'more' + ); + $I->addGutenbergParagraphBlock($I, 'Member-only content.'); + + // Publish Page. + $url = $I->publishGutenbergPage($I); + // Test that a notice is displayed in the WordPress Administration interface, as a Restrict Content // page is configured. $I->amOnAdminPage('index.php'); @@ -362,7 +457,7 @@ public function testRestrictContentWPSuperCache(EndToEndTester $I) $I->logOut(); // Navigate to the page. - $I->amOnPage('?p=' . $pageID); + $I->amOnUrl($url); // Test that the restricted content CTA displays when no valid signed subscriber ID is used, // to confirm caching does not show member-only content. @@ -373,7 +468,7 @@ public function testRestrictContentWPSuperCache(EndToEndTester $I) $I->setRestrictContentCookieAndReload( $I, subscriberID: $_ENV['CONVERTKIT_API_SIGNED_SUBSCRIBER_ID'], - urlOrPageID: $pageID + urlOrPageID: $url ); $I->testRestrictContentDisplaysContent($I); @@ -400,15 +495,34 @@ public function testRestrictContentWPRocketCache(EndToEndTester $I) $config = $I->grabOptionFromDatabase('wp_rocket_settings'); $I->assertNotContains('ck_subscriber_id', $config['cache_reject_cookies']); - // Create Restricted Content Page. - $pageID = $I->createRestrictedContentPage( + // Add a Page using the Gutenberg editor. + $I->addGutenbergPage( + $I, + title: 'Kit: Restrict Content: Product: WP Rocket' + ); + + // Configure metabox's Restrict Content setting = Product name. + $I->configureMetaboxSettings( $I, - [ - 'post_title' => 'Kit: Restrict Content: Product: WP Rocket', - 'restrict_content_setting' => 'product_' . $_ENV['CONVERTKIT_API_PRODUCT_ID'], + metabox: 'wp-convertkit-meta-box', + configuration: [ + 'form' => [ 'select2', 'None' ], + 'restrict_content' => [ 'select2', $_ENV['CONVERTKIT_API_PRODUCT_NAME'] ], ] ); + // Add blocks. + $I->addGutenbergParagraphBlock($I, 'Visible content.'); + $I->addGutenbergBlock( + $I, + blockName: 'More', + blockProgrammaticName: 'more' + ); + $I->addGutenbergParagraphBlock($I, 'Member-only content.'); + + // Publish Page. + $url = $I->publishGutenbergPage($I); + // Load the Dashboard. $I->amOnAdminPage('index.php'); $I->waitForElementVisible('body.index-php'); @@ -422,7 +536,7 @@ public function testRestrictContentWPRocketCache(EndToEndTester $I) $I->logOut(); // Navigate to the page. - $I->amOnPage('?p=' . $pageID); + $I->amOnUrl($url); // Test that the restricted content CTA displays when no valid signed subscriber ID is used, // to confirm caching does not show member-only content. @@ -433,7 +547,7 @@ public function testRestrictContentWPRocketCache(EndToEndTester $I) $I->setRestrictContentCookieAndReload( $I, subscriberID: $_ENV['CONVERTKIT_API_SIGNED_SUBSCRIBER_ID'], - urlOrPageID: $pageID + urlOrPageID: $url ); $I->testRestrictContentDisplaysContent($I); From 2bbe520200bc47c9067e5353311d97b7a4d6d00e Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Fri, 10 Oct 2025 11:25:01 +0800 Subject: [PATCH 07/10] Clear Posts table between tests --- .../restrict-content/general/RestrictContentCacheCest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/EndToEnd/restrict-content/general/RestrictContentCacheCest.php b/tests/EndToEnd/restrict-content/general/RestrictContentCacheCest.php index 7968b035e..267222f2a 100644 --- a/tests/EndToEnd/restrict-content/general/RestrictContentCacheCest.php +++ b/tests/EndToEnd/restrict-content/general/RestrictContentCacheCest.php @@ -570,5 +570,7 @@ public function _passed(EndToEndTester $I) $I->clearRestrictContentCookie($I); $I->deactivateKitPlugin($I); $I->resetKitPlugin($I); + $I->truncateDbTable('wp_posts'); + $I->truncateDbTable('wp_postmeta'); } } From bb8474f77538469d7ef64d57db52e40c31827841 Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Fri, 10 Oct 2025 15:01:55 +0800 Subject: [PATCH 08/10] Reset cache options between tests --- tests/Support/Helper/KitPlugin.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Support/Helper/KitPlugin.php b/tests/Support/Helper/KitPlugin.php index 3b3a978ed..62ef8efce 100644 --- a/tests/Support/Helper/KitPlugin.php +++ b/tests/Support/Helper/KitPlugin.php @@ -537,6 +537,9 @@ public function resetKitPlugin($I) // Upgrades. $I->dontHaveOptionInDatabase('_wp_convertkit_upgrade_posts'); + + // Cache. + $I->dontHaveOptionInDatabase('convertkit_restrict_content_enabled'); } /** From a453d5c5c39ccdbd3261a51e93da939ded90872d Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Fri, 10 Oct 2025 17:19:29 +0800 Subject: [PATCH 09/10] Run Restrict Content tests in isolation --- .github/workflows/tests.yml | 20 ++----------------- .../general/RestrictContentCacheCest.php | 2 -- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 772909364..03c55f3bc 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -52,27 +52,11 @@ jobs: fail-fast: false matrix: wp-versions: [ 'latest' ] #[ '6.1.1', 'latest' ] - php-versions: [ '8.1', '8.2', '8.3', '8.4' ] #[ '7.4', '8.0', '8.1' ] + php-versions: [ '8.1' ] #[ '7.4', '8.0', '8.1' ] # Folder names within the 'tests' folder to run tests in parallel. test-groups: [ - 'EndToEnd/broadcasts/blocks-shortcodes', - 'EndToEnd/broadcasts/import-export', - 'EndToEnd/forms/blocks-shortcodes', - 'EndToEnd/forms/general', - 'EndToEnd/forms/post-types', - 'EndToEnd/general/other', - 'EndToEnd/general/plugin-screens', - 'EndToEnd/integrations/divi-builder', - 'EndToEnd/integrations/divi-theme', - 'EndToEnd/integrations/other', - 'EndToEnd/integrations/wlm', - 'EndToEnd/integrations/woocommerce', - 'EndToEnd/landing-pages', - 'EndToEnd/products', - 'EndToEnd/restrict-content/general', - 'EndToEnd/restrict-content/post-types', - 'EndToEnd/tags' + 'EndToEnd/restrict-content/general' ] # Steps to install, configure and run tests diff --git a/tests/EndToEnd/restrict-content/general/RestrictContentCacheCest.php b/tests/EndToEnd/restrict-content/general/RestrictContentCacheCest.php index 267222f2a..7968b035e 100644 --- a/tests/EndToEnd/restrict-content/general/RestrictContentCacheCest.php +++ b/tests/EndToEnd/restrict-content/general/RestrictContentCacheCest.php @@ -570,7 +570,5 @@ public function _passed(EndToEndTester $I) $I->clearRestrictContentCookie($I); $I->deactivateKitPlugin($I); $I->resetKitPlugin($I); - $I->truncateDbTable('wp_posts'); - $I->truncateDbTable('wp_postmeta'); } } From 40e797e9ba0532ca4999896bbb761936a3e1eedf Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Fri, 10 Oct 2025 17:42:22 +0800 Subject: [PATCH 10/10] Reinstate all tests --- .github/workflows/tests.yml | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 03c55f3bc..772909364 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -52,11 +52,27 @@ jobs: fail-fast: false matrix: wp-versions: [ 'latest' ] #[ '6.1.1', 'latest' ] - php-versions: [ '8.1' ] #[ '7.4', '8.0', '8.1' ] + php-versions: [ '8.1', '8.2', '8.3', '8.4' ] #[ '7.4', '8.0', '8.1' ] # Folder names within the 'tests' folder to run tests in parallel. test-groups: [ - 'EndToEnd/restrict-content/general' + 'EndToEnd/broadcasts/blocks-shortcodes', + 'EndToEnd/broadcasts/import-export', + 'EndToEnd/forms/blocks-shortcodes', + 'EndToEnd/forms/general', + 'EndToEnd/forms/post-types', + 'EndToEnd/general/other', + 'EndToEnd/general/plugin-screens', + 'EndToEnd/integrations/divi-builder', + 'EndToEnd/integrations/divi-theme', + 'EndToEnd/integrations/other', + 'EndToEnd/integrations/wlm', + 'EndToEnd/integrations/woocommerce', + 'EndToEnd/landing-pages', + 'EndToEnd/products', + 'EndToEnd/restrict-content/general', + 'EndToEnd/restrict-content/post-types', + 'EndToEnd/tags' ] # Steps to install, configure and run tests