diff --git a/composer.json b/composer.json index bf7f5c1e7..0b96213b8 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "type": "project", "license": "GPLv3", "require": { - "convertkit/convertkit-wordpress-libraries": "2.1.5" + "convertkit/convertkit-wordpress-libraries": "dev-ignore-invalid-resource-arrays" }, "require-dev": { "php-webdriver/webdriver": "^1.0", diff --git a/includes/class-convertkit-setup.php b/includes/class-convertkit-setup.php index 6cb68da5b..6d7ccbe97 100644 --- a/includes/class-convertkit-setup.php +++ b/includes/class-convertkit-setup.php @@ -154,6 +154,7 @@ public function update() { // Actions that should run regardless of the version number // whenever the Plugin is updated. $this->remove_v3_api_secret_from_settings(); + $this->maybe_delete_old_cached_resource_structure(); // Update the installed version number in the options table. update_option( 'convertkit_version', CONVERTKIT_PLUGIN_VERSION ); @@ -176,6 +177,46 @@ private function remove_v3_api_secret_from_settings() { } + /** + * Deletes any cached resources stored in the options table if they are a flat array + * that originates from between 1.6.0 and 1.9.5.2 of the Plugin. + * i.e. [id => name], rather than the expected [id => [id => '...', name => '...', ...]]. + * + * This will permit the Resources classes to fetch and cache the Form resources correctly + * from the API, and prevent fatal errors when a user activates the latest version of the Plugin + * on a site that previously had a very old Plugin version installed. + * + * @since 3.2.5 + */ + private function maybe_delete_old_cached_resource_structure() { + + // Define the resource options to check. + $resource_options = array( + 'convertkit_forms', + 'convertkit_landing_pages', + 'convertkit_tags', + ); + + foreach ( $resource_options as $resource_option ) { + // Get the resource option. + $resource = get_option( $resource_option, false ); + + // If the resource option does not exist, there's nothing to delete. + if ( false === $resource ) { + return; + } + + // If the resource structure is correct e.g. [id => [name => '...']], there's nothing to delete. + if ( is_array( reset( $resource ) ) ) { + return; + } + + // Delete the cached resource. + delete_option( $resource_option ); + } + + } + /** * Adds form_id column and key to form entries database table. * diff --git a/tests/EndToEnd/general/other/UpgradePathsCest.php b/tests/EndToEnd/general/other/UpgradePathsCest.php index 65cabca4c..99f4e8fd8 100644 --- a/tests/EndToEnd/general/other/UpgradePathsCest.php +++ b/tests/EndToEnd/general/other/UpgradePathsCest.php @@ -289,6 +289,102 @@ public function testV3APISecretRemovedFromSettings(EndToEndTester $I) $I->assertEquals($settings['api_secret'], ''); } + /** + * Tests that cached resources are deleted when they originate from between 1.6.0 and 1.9.5.2 of the Plugin + * i.e. [id => name], rather than the expected [id => [id => '...', name => '...', ...]]. + * + * @since 3.2.5 + * + * @param EndToEndTester $I Tester. + */ + public function testFlatArrayResourcesDeleted(EndToEndTester $I) + { + // Setup the Kit Plugin as if it's cached resources were created with 1.6.0 to 1.9.5.2 of the Plugin. + $I->haveOptionInDatabase('convertkit_version', '1.6.0'); + $I->haveOptionInDatabase( + 'convertkit_forms', + [ + 330786 => 'Form 1', + ] + ); + $I->haveOptionInDatabase( + 'convertkit_landing_pages', + [ + 330787 => 'Landing Page 1', + ] + ); + $I->haveOptionInDatabase( + 'convertkit_tags', + [ + 330788 => 'Tag 1', + ] + ); + + // Activate the Plugin. + $I->activateKitPlugin($I); + + // Confirm the invalid resource options are deleted. + $I->dontSeeOptionInDatabase('convertkit_forms'); + $I->dontSeeOptionInDatabase('convertkit_landing_pages'); + $I->dontSeeOptionInDatabase('convertkit_tags'); + } + + /** + * Tests that cached resources are deleted when they originate from between 1.6.0 and 1.9.5.2 of the Plugin + * i.e. [id => name], rather than the expected [id => [id => '...', name => '...', ...]], and + * the credentials are defined. + * + * @since 3.2.5 + * + * @param EndToEndTester $I Tester. + */ + public function testFlatArrayResourcesDeletedWhenCredentialsAreDefined(EndToEndTester $I) + { + // Setup the Kit Plugin with API credentials. + $I->setupKitPlugin($I); + + // Setup the Kit Plugin as if it's cached resources were created with 1.6.0 to 1.9.5.2 of the Plugin. + $I->haveOptionInDatabase('convertkit_version', '1.6.0'); + $I->haveOptionInDatabase( + 'convertkit_forms', + [ + 330786 => 'Form 1', + ] + ); + $I->haveOptionInDatabase( + 'convertkit_landing_pages', + [ + 330787 => 'Landing Page 1', + ] + ); + $I->haveOptionInDatabase( + 'convertkit_tags', + [ + 330788 => 'Tag 1', + ] + ); + + // Activate the Plugin. + $I->activateKitPlugin($I, false); + + // Confirm the invalid resource options are deleted. + $I->dontSeeOptionInDatabase('convertkit_forms'); + $I->dontSeeOptionInDatabase('convertkit_landing_pages'); + $I->dontSeeOptionInDatabase('convertkit_tags'); + + // Navigate to the Plugin's Settings Screen. + $I->loadKitSettingsGeneralScreen($I); + + // Confirm the Plugin is authorized by checking for a Disconnect button. + $I->see('Kit WordPress'); + $I->see('Disconnect'); + + // Confirm the resources are cached. + $I->seeOptionInDatabase('convertkit_forms'); + $I->seeOptionInDatabase('convertkit_landing_pages'); + $I->seeOptionInDatabase('convertkit_tags'); + } + /** * Deactivate and reset Plugin(s) after each test, if the test passes. * We don't use _after, as this would provide a screenshot of the Plugin diff --git a/tests/Integration/ResourceFormsTest.php b/tests/Integration/ResourceFormsTest.php index 3089f5272..d22d6d786 100644 --- a/tests/Integration/ResourceFormsTest.php +++ b/tests/Integration/ResourceFormsTest.php @@ -334,6 +334,28 @@ public function testNonInlineExist() $this->assertSame($result, true); } + /** + * Test that the exist() function returns false when invalid data is stored in the options table. + * + * @since 3.2.5 + */ + public function testExistWithInvalidData() + { + // Define invalid form data. + update_option( + $this->resource->settings_name, + [ + 12345 => 'Form Name', + ] + ); + + // Initialize the resource class we want to test. + $resource = new \ConvertKit_Resource_Forms(); + + // Confirm that the function returns false, because resources are invalid. + $this->assertFalse($resource->exist()); + } + /** * Test that the get_html() function returns the expected data. * diff --git a/tests/Integration/ResourceLandingPagesTest.php b/tests/Integration/ResourceLandingPagesTest.php index 93a6a42ac..bb1624fc0 100644 --- a/tests/Integration/ResourceLandingPagesTest.php +++ b/tests/Integration/ResourceLandingPagesTest.php @@ -234,6 +234,28 @@ public function testExist() $this->assertSame($result, true); } + /** + * Test that the exist() function returns false when invalid data is stored in the options table. + * + * @since 3.2.5 + */ + public function testExistWithInvalidData() + { + // Define invalid landing page data. + update_option( + $this->resource->settings_name, + [ + 12345 => 'Landing Page Name', + ] + ); + + // Initialize the resource class we want to test. + $resource = new \ConvertKit_Resource_Landing_Pages(); + + // Confirm that the function returns false, because resources are invalid. + $this->assertFalse($resource->exist()); + } + /** * Test that the get_html() function returns the expected data. * diff --git a/tests/Integration/ResourceTagsTest.php b/tests/Integration/ResourceTagsTest.php index de8802dc9..a8609365b 100644 --- a/tests/Integration/ResourceTagsTest.php +++ b/tests/Integration/ResourceTagsTest.php @@ -233,4 +233,26 @@ public function testExist() $result = $this->resource->exist(); $this->assertSame($result, true); } + + /** + * Test that the exist() function returns false when invalid data is stored in the options table. + * + * @since 3.2.5 + */ + public function testExistWithInvalidData() + { + // Define invalid tag data. + update_option( + $this->resource->settings_name, + [ + 12345 => 'Tag Name', + ] + ); + + // Initialize the resource class we want to test. + $resource = new \ConvertKit_Resource_Tags(); + + // Confirm that the function returns false, because resources are invalid. + $this->assertFalse($resource->exist()); + } }