Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions admin/importers/class-convertkit-admin-importer.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ public function import( $mappings ) {

// Iterate through the mappings, replacing the third party form shortcodes and blocks with the Kit form shortcodes and blocks.
foreach ( $mappings as $third_party_form_id => $kit_form_id ) {
// Skip empty Kit Form IDs i.e. no mapping was provided for this third party form.
if ( empty( $kit_form_id ) ) {
continue;
}

$this->replace_blocks_in_posts( (int) $third_party_form_id, (int) $kit_form_id );
$this->replace_shortcodes_in_posts( (int) $third_party_form_id, (int) $kit_form_id );
}
Expand Down
177 changes: 119 additions & 58 deletions admin/setup-wizard/class-convertkit-admin-setup-wizard-plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,24 @@ class ConvertKit_Admin_Setup_Wizard_Plugin extends ConvertKit_Admin_Setup_Wizard
*/
public $exit_url = 'options-general.php?page=_wp_convertkit_settings';

/**
* If the Form Importer step will be displayed.
*
* @since 3.1.7
*
* @var bool
*/
public $show_form_importer_step = false;

/**
* Holds the form importers.
*
* @since 3.1.7
*
* @var array
*/
public $form_importers = array();

/**
* Registers action and filter hooks.
*
Expand All @@ -103,6 +121,8 @@ public function __construct() {
$this->api = new ConvertKit_API_V4( CONVERTKIT_OAUTH_CLIENT_ID, CONVERTKIT_OAUTH_CLIENT_REDIRECT_URI, false, false, false, 'setup_wizard' );
$this->settings = new ConvertKit_Settings();

$this->show_form_importer_step = count( convertkit_get_form_importers() ) > 0 ? true : false;

// Define details for each step in the setup process.
$this->steps = array(
'start' => array(
Expand All @@ -115,12 +135,24 @@ public function __construct() {
'configuration' => array(
'name' => __( 'Configuration', 'convertkit' ),
'next_button' => array(
'label' => __( 'Finish Setup', 'convertkit' ),
'label' => $this->show_form_importer_step ? __( 'Next', 'convertkit' ) : __( 'Finish Setup', 'convertkit' ),
),
),
'finish' => array(
'name' => __( 'Done', 'convertkit' ),
),
);

// If the Form Importer step will be displayed, add it to the steps.
if ( $this->show_form_importer_step ) {
$this->steps['form-importer'] = array(
'name' => __( 'Form Importer', 'convertkit' ),
'next_button' => array(
'label' => __( 'Finish Setup', 'convertkit' ),
),
);
}

// Add the finish step.
$this->steps['finish'] = array(
'name' => __( 'Done', 'convertkit' ),
);

// Register link to Setup Wizard below Plugin Name at Plugins > Installed Plugins.
Expand Down Expand Up @@ -209,69 +241,92 @@ public function maybe_redirect_to_setup_screen() {
public function process_form( $step ) {

// Depending on the step, process the form data.
switch ( $step ) {
case 'configuration':
// If an error occured from OAuth i.e. the user did not authorize, show it now.
if ( array_key_exists( 'error', $_REQUEST ) && array_key_exists( 'error_description', $_REQUEST ) ) {
// Decrement the step.
$this->step = 'start';
$this->error = sanitize_text_field( wp_unslash( $_REQUEST['error_description'] ) );
return;
}
if ( array_key_exists( 'code', $_REQUEST ) || array_key_exists( 'error', $_REQUEST ) || array_key_exists( 'error_description', $_REQUEST ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended - no nonce is sent back from OAuth.
$this->save_oauth( map_deep( wp_unslash( $_REQUEST ), 'sanitize_text_field' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended - no nonce is sent back from OAuth.
return;
}

// Bail if no authorization code is included in the request.
if ( ! array_key_exists( 'code', $_REQUEST ) ) {
return;
}
// Run security checks.
if ( ! isset( $_REQUEST['_wpnonce'] ) ) {
return;
}
if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), $this->page_name ) ) {
// Decrement the step.
$this->step = 'configuration';
$this->error = __( 'Invalid nonce specified.', 'convertkit' );
return;
}

// Sanitize token.
$authorization_code = sanitize_text_field( wp_unslash( $_REQUEST['code'] ) );
// Configuration.
if ( array_key_exists( 'post_form', $_REQUEST ) || array_key_exists( 'page_form', $_REQUEST ) || array_key_exists( 'usage_tracking', $_REQUEST ) ) {
$settings = new ConvertKit_Settings();
$settings->save(
array(
'post_form' => isset( $_REQUEST['post_form'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['post_form'] ) ) : '0',
'page_form' => isset( $_REQUEST['page_form'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['page_form'] ) ) : '0',
'usage_tracking' => isset( $_REQUEST['usage_tracking'] ) ? 'on' : '',
)
);
return;
}

// Exchange the authorization code and verifier for an access token.
$result = $this->api->get_access_token( $authorization_code );
// Form Importer.
if ( array_key_exists( 'form_importer', $_REQUEST ) ) {
// Replace third party form shortcodes and blocks with Kit form shortcodes and blocks.
foreach ( map_deep( wp_unslash( $_REQUEST['form_importer'] ), 'sanitize_text_field' ) as $form_importer_name => $mappings ) {
// Sanitize mappings.
$mappings = array_map( 'sanitize_text_field', wp_unslash( $mappings ) );

// Show an error message if we could not fetch the access token.
if ( is_wp_error( $result ) ) {
// Decrement the step.
$this->step = 'start';
$this->error = $result->get_error_message();
return;
}
// Replace third party form shortcodes and blocks with Kit form shortcodes and blocks.
WP_ConvertKit()->get_class( 'admin_importer_' . $form_importer_name )->import( $mappings );
}

// Store Access Token, Refresh Token and expiry.
$this->settings->save(
array(
'access_token' => $result['access_token'],
'refresh_token' => $result['refresh_token'],
'token_expires' => ( time() + $result['expires_in'] ),
)
);
break;
return;
}

case 'finish':
// Run security checks.
if ( ! isset( $_REQUEST['_wpnonce'] ) ) {
return;
}
if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), $this->page_name ) ) {
// Decrement the step.
$this->step = 'configuration';
$this->error = __( 'Invalid nonce specified.', 'convertkit' );
return;
}
}

// Save Default Page and Post Form settings.
$settings = new ConvertKit_Settings();
$settings->save(
array(
'post_form' => ( isset( $_POST['post_form'] ) ? sanitize_text_field( wp_unslash( $_POST['post_form'] ) ) : '0' ),
'page_form' => ( isset( $_POST['page_form'] ) ? sanitize_text_field( wp_unslash( $_POST['page_form'] ) ) : '0' ),
'usage_tracking' => ( isset( $_POST['usage_tracking'] ) ? 'on' : '' ),
)
);
break;
/**
* Save the OAuth credentials.
*
* @since 3.1.7
*
* @param array $request Request.
* @return void
*/
private function save_oauth( $request ) {

// If an error occured from OAuth i.e. the user did not authorize, show it now.
if ( array_key_exists( 'error', $request ) || array_key_exists( 'error_description', $request ) ) {
// Decrement the step.
$this->step = 'start';
$this->error = sanitize_text_field( wp_unslash( $request['error_description'] ) );
return;
}

// Sanitize token.
$authorization_code = sanitize_text_field( wp_unslash( $request['code'] ) );

// Exchange the authorization code and verifier for an access token.
$result = $this->api->get_access_token( $authorization_code );

// Show an error message if we could not fetch the access token.
if ( is_wp_error( $result ) ) {
// Decrement the step.
$this->step = 'start';
$this->error = $result->get_error_message();
return;
}

// Store Access Token, Refresh Token and expiry.
$this->settings->save(
array(
'access_token' => $result['access_token'],
'refresh_token' => $result['refresh_token'],
'token_expires' => ( time() + $result['expires_in'] ),
)
);

}

/**
Expand Down Expand Up @@ -356,6 +411,12 @@ function ( $hosts ) {
$this->preview_post_url = WP_ConvertKit()->get_class( 'preview_output' )->get_preview_form_url( 'post' );
$this->preview_page_url = WP_ConvertKit()->get_class( 'preview_output' )->get_preview_form_url( 'page' );
break;

case 'form-importer':
// Fetch form importers and Kit Forms.
$this->form_importers = convertkit_get_form_importers();
$this->forms = new ConvertKit_Resource_Forms( 'setup_wizard' );
break;
}

}
Expand Down
114 changes: 112 additions & 2 deletions tests/EndToEnd/general/plugin-screens/PluginSetupWizardCest.php
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,115 @@ public function testSetupWizardFormConfigurationScreenWhenNoPostsOrPagesExist(En
$I->dontSeeElementInDOM('a#convertkit-preview-form-page');
}

/**
* Test that the Setup Wizard > Form Importer screen works as expected
* when Active Campaign Forms exist.
*
* @since 3.1.7
*
* @param EndToEndTester $I Tester.
*/
public function testSetupWizardFormImporterScreen(EndToEndTester $I)
{
// Activate Plugin.
$this->_activatePlugin($I);

// Wait for the Plugin Setup Wizard screen to load.
$I->waitForElementVisible('body.convertkit');

// Define Plugin settings and resources.
$I->setupKitPluginResources($I);
$I->setupKitPluginNoDefaultForms($I);

// Create Active Campaign Forms.
$I->haveOptionInDatabase(
'settings_activecampaign',
[
'forms' => [
1 => [
'id' => '1',
'name' => 'ActiveCampaign Form #1',
],
2 => [
'id' => '2',
'name' => 'ActiveCampaign Form #2',
],
],
]
);

// Create Pages with ActiveCampaign Form Shortcode and Block.
$pageIDs = [
$I->havePostInDatabase(
[
'post_type' => 'page',
'post_status' => 'publish',
'post_title' => 'Page with ActiveCampaign Form',
'post_content' => '[activecampaign form="1"]',
'meta_input' => [
'_wp_convertkit_post_meta' => [
'form' => '0',
'landing_page' => '',
'tag' => '',
],
],
]
),
$I->havePostInDatabase(
[
'post_type' => 'page',
'post_status' => 'publish',
'post_title' => 'Page with ActiveCampaign Block',
'post_content' => '<!-- wp:activecampaign-form/activecampaign-form-block {"formId":2} /--><!-- wp:html --><div class="wp-block-core-html">Some content with characters !@£$%^&amp;*()_+~!@£$%^&amp;*()_+\\\</div><!-- /wp:html -->',

// Configure Kit Plugin to not display a default Form, so we test against the Kit Form in the content.
'meta_input' => [
'_wp_convertkit_post_meta' => [
'form' => '0',
'landing_page' => '',
'tag' => '',
],
],
]
),
];

// Load Form Importer screen of Setup Wizard.
$I->amOnAdminPage('options.php?page=convertkit-setup&step=form-importer');

// Select the Kit Forms to replace the ActiveCampaign Forms.
$I->fillSelect2Field(
$I,
container: '#select2-form-importer-activecampaign-1-container',
value: $_ENV['CONVERTKIT_API_FORM_NAME']
);
$I->fillSelect2Field(
$I,
container: '#select2-form-importer-activecampaign-2-container',
value: $_ENV['CONVERTKIT_API_FORM_NAME']
);

// Click the Finish Setup button.
$I->click('Finish Setup');

// Confirm expected setup wizard screen is displayed.
$this->_seeExpectedSetupWizardScreen(
$I,
step: 'finish',
stepCount: 4,
totalSteps: 4,
title: 'Setup complete'
);

// Test each Page.
foreach ($pageIDs as $pageID) {
$I->amOnPage('?p=' . $pageID);

// Check Kit Forms are displayed.
$I->seeElementInDOM('form[data-sv-form]');
}
}

/**
* Tests that a link to the Setup Wizard exists on the Plugins screen, and works when clicked.
*
Expand Down Expand Up @@ -606,8 +715,9 @@ private function _activatePlugin(EndToEndTester $I)
* @param int $stepCount Current step count.
* @param string $title Expected title.
* @param bool $nextButtonIsLink Check that next button is a link (false = must be a <button> element).
* @param int $totalSteps Total steps in Setup Wizard.
*/
private function _seeExpectedSetupWizardScreen(EndToEndTester $I, $step, $stepCount, $title, $nextButtonIsLink = false)
private function _seeExpectedSetupWizardScreen(EndToEndTester $I, $step, $stepCount, $title, $nextButtonIsLink = false, $totalSteps = 3)
{
// Wait for the Plugin Setup Wizard screen to load.
$I->waitForElementVisible('body.convertkit');
Expand All @@ -625,7 +735,7 @@ private function _seeExpectedSetupWizardScreen(EndToEndTester $I, $step, $stepCo
$I->seeNumberOfElements('ol li.done', $stepCount);

// Confirm Step text is correct.
$I->see('Step ' . $stepCount . ' of 3');
$I->see('Step ' . $stepCount . ' of ' . $totalSteps);

// Depending on the step, confirm previous/next buttons exist / do not exist.
switch ($step) {
Expand Down
Loading