diff --git a/includes/Data/Brands.php b/includes/Data/Brands.php index d6518789a..d77c4754e 100644 --- a/includes/Data/Brands.php +++ b/includes/Data/Brands.php @@ -13,10 +13,10 @@ final class Brands { */ public static function get_default_brand() { $default_brand_data = array( - 'brand' => 'wordpress', - 'name' => __( 'your web host', 'wp-module-onboarding' ), - 'pluginDashboardPage' => \admin_url(), - 'hireExpertsInfo' => array( + 'brand' => 'wordpress', + 'name' => __( 'your web host', 'wp-module-onboarding' ), + 'pluginDashboardPage' => \admin_url(), + 'hireExpertsInfo' => array( 'defaultLink' => 'https://www.bluehost.com/wp-live', 'queryParameters' => array( 'page' => 'bluehost', @@ -92,7 +92,11 @@ public static function get_brands() { ), ), 'config' => array( - 'net_new_signup_date_threshold' => '2022-08-18T15:30:00.000Z', + 'net_new_signup_date_threshold' => '2023-05-04T00:00:00.000Z', + 'enabled_flows' => array( + 'ecommerce' => true, + 'wp-setup' => true, + ), ), ), 'bluehost-india' => array( @@ -146,6 +150,10 @@ public static function get_brands() { ), 'config' => array( 'net_new_signup_date_threshold' => '2022-08-18T15:30:00.000Z', + 'enabled_flows' => array( + 'ecommerce' => true, + 'wp-setup' => false, + ), ), ), 'webcom' => array( @@ -200,6 +208,10 @@ public static function get_brands() { ), 'config' => array( 'net_new_signup_date_threshold' => '2022-08-18T15:30:00.000Z', + 'enabled_flows' => array( + 'ecommerce' => false, + 'wp-setup' => false, + ), ), ), 'crazy-domains' => array( @@ -253,7 +265,11 @@ public static function get_brands() { ), ), 'config' => array( - 'net_new_signup_date_threshold' => '2022-08-18T15:30:00.000Z', + 'net_new_signup_date_threshold' => '2023-05-04T00:00:00.000Z', + 'enabled_flows' => array( + 'ecommerce' => true, + 'wp-setup' => true, + ), 'views' => array( 'sidebar' => array( 'illustration' => array( diff --git a/includes/Data/Data.php b/includes/Data/Data.php index 8616e1343..49f63e57a 100644 --- a/includes/Data/Data.php +++ b/includes/Data/Data.php @@ -80,6 +80,15 @@ public static function current_plan() { } } + $current_flow = Flows::get_flow_from_top_priority(); + if ( false !== $current_flow ) { + return array( + 'flow' => 'ecommerce', + 'subtype' => 'wc_priority', + 'type' => null, + ); + } + return array( 'flow' => Flows::get_default_flow(), 'subtype' => null, @@ -93,24 +102,8 @@ public static function current_plan() { * @return string */ public static function current_flow() { - - $current_flow = Flows::get_flow_from_params(); - if ( false !== $current_flow ) { - return $current_flow; - } - - $current_flow = Flows::get_flow_from_plugins(); - if ( false !== $current_flow ) { - return $current_flow; - } - - $customer_data = self::customer_data(); - $current_flow = Flows::get_flow_from_customer_data( $customer_data ); - if ( false !== $current_flow ) { - return $current_flow; - } - - return Flows::get_default_flow(); + $current_plan = self::current_plan(); + return $current_plan['flow']; } /** diff --git a/includes/Data/Flows.php b/includes/Data/Flows.php index 982cbda8e..a04f56778 100644 --- a/includes/Data/Flows.php +++ b/includes/Data/Flows.php @@ -1,6 +1,7 @@ true, - 'ecommerce' => true, + $current_brand = Data::current_brand(); + return isset( $current_brand['config']['enabled_flows'] ) + ? $current_brand['config']['enabled_flows'] : array( + 'wp-setup' => false, + 'ecommerce' => false, ); } @@ -212,15 +215,15 @@ public static function get_flow_from_params() { $flows = self::get_flows(); if ( isset( $_GET['flow'] ) ) { - $current_flow_type = \sanitize_text_field( $_GET['flow'] ); + $current_flow_type = \sanitize_text_field( $_GET['flow'] ); } - if ( ! empty( $current_flow_type ) && isset( $flows[ $current_flow_type ] ) ) { + if ( ! empty( $current_flow_type ) && true === $flows[ $current_flow_type ] ) { return $current_flow_type; } $current_flow_type = \get_option( Options::get_option_name( 'flow_preset' ), false ); - if ( $current_flow_type && isset( $flows[ $current_flow_type ] ) ) { + if ( $current_flow_type && true === $flows[ $current_flow_type ] ) { return $current_flow_type; } @@ -234,7 +237,7 @@ public static function get_flow_from_params() { */ public static function get_flow_from_plugins() { if ( PluginInstaller::exists( 'woocommerce', true ) ) { - return 'ecommerce'; + return true === self::get_flows()['ecommerce'] ? 'ecommerce' : false; } return false; } @@ -260,8 +263,20 @@ public static function get_flow_from_customer_data( $customer_data = array() ) { */ public static function get_flow_from_plan_subtype( $plan_subtype ) { if ( self::is_ecommerce_plan( $plan_subtype ) ) { - return isset( self::get_flows()['ecommerce'] ) ? 'ecommerce' : false; + return true === self::get_flows()['ecommerce'] ? 'ecommerce' : false; } - return false; + return false; + } + /** + * Get the corresponding flow from the top priority in flow data. + * + * @return string|boolean + */ + public static function get_flow_from_top_priority() { + $flow_data = FlowService::read_data_from_wp_option(); + if ( $flow_data && isset( $flow_data['data']['topPriority']['priority1'] ) && 'selling' === $flow_data['data']['topPriority']['priority1'] ) { + return true === self::get_flows()['ecommerce'] ? 'ecommerce' : false; + } + return false; } } diff --git a/includes/Data/Plugins.php b/includes/Data/Plugins.php index 3b759f5b7..68b2082ef 100644 --- a/includes/Data/Plugins.php +++ b/includes/Data/Plugins.php @@ -18,8 +18,9 @@ final class Plugins { 'path' => 'jetpack/jetpack.php', ), 'woocommerce' => array( - 'approved' => true, - 'path' => 'woocommerce/woocommerce.php', + 'approved' => true, + 'path' => 'woocommerce/woocommerce.php', + 'post_install_callback' => array( __CLASS__, 'wc_prevent_redirect_on_activation' ), ), 'wordpress-seo' => array( 'approved' => true, @@ -420,4 +421,13 @@ public static function get_init() { return $init_list; } + /** + * Prevent redirect to woo wizard after activation of woocommerce. + * + * @return void + */ + public static function wc_prevent_redirect_on_activation() { + \delete_transient( '_wc_activation_redirect' ); + } + } diff --git a/includes/Data/Preview.php b/includes/Data/Preview.php index c4f7930b8..886994f35 100644 --- a/includes/Data/Preview.php +++ b/includes/Data/Preview.php @@ -4,12 +4,25 @@ use NewfoldLabs\WP\Module\Onboarding\Services\PluginInstaller; use NewfoldLabs\WP\Module\Onboarding\Services\ThemeInstaller; +/** + * Class Preview + */ final class Preview { - + /** + * Convert boolean to plugin/theme status. + * + * @param boolean $boolean The boolean value. + * @return string + */ private static function boolean_to_status( $boolean ) { - return $boolean ? 'activated' : 'init'; + return $boolean ? 'activated' : 'init'; } + /** + * Map of pre requisites to show the live preview successfully for a flow. + * + * @return array + */ private static function pre_requisites() { $theme_map = Themes::get(); return array( @@ -30,21 +43,35 @@ private static function pre_requisites() { ); } - public static function get_pre_requisites() { - $pre_requisites = self::pre_requisites(); - return isset( $pre_requisites[ Data::current_flow() ] ) ? $pre_requisites[ Data::current_flow() ] : array(); + /** + * Get the pre requisites for a given flow. + * + * @param string $flow A valid Onboarding flow. + * @return array + */ + public static function get_pre_requisites( $flow = null ) { + $pre_requisites = self::pre_requisites(); + if ( ! isset( $flow ) ) { + $flow = Data::current_flow(); + } + return isset( $pre_requisites[ $flow ] ) ? $pre_requisites[ $flow ] : array(); } + /** + * Get all the settings necessary to load the live preview + * + * @return array + */ public static function get_settings() { - $block_editor_context = new \WP_Block_Editor_Context( array( 'name' => 'core/edit-site' ) ); - $custom_settings = array( - 'siteUrl' => \site_url(), - ); + $block_editor_context = new \WP_Block_Editor_Context( array( 'name' => 'core/edit-site' ) ); + $custom_settings = array( + 'siteUrl' => \site_url(), + ); - return array( - 'settings' => \get_block_editor_settings( $custom_settings, $block_editor_context ), - 'globalStyles' => \wp_get_global_styles(), - 'preRequisites' => self::get_pre_requisites(), - ); + return array( + 'settings' => \get_block_editor_settings( $custom_settings, $block_editor_context ), + 'globalStyles' => \wp_get_global_styles(), + 'preRequisites' => self::get_pre_requisites(), + ); } } diff --git a/includes/Data/SiteFeatures.php b/includes/Data/SiteFeatures.php index d0f2c5092..727a167a9 100644 --- a/includes/Data/SiteFeatures.php +++ b/includes/Data/SiteFeatures.php @@ -11,144 +11,203 @@ final class SiteFeatures { * * @var array */ - protected static $site_features_flow_plan_map = array( - 'wp-setup' => array(), - 'ecommerce' => array( - 'default' => array( - 'jetpack' => array( - 'slug' => 'jetpack', - 'icon' => '--site-features-security', - 'title' => 'Security, Speed & Growth', - 'subtitle' => 'Powered by Jetpack', - 'desc' => 'Jetpack includes dozens of powerful, unique capabilities for your WordPress sites from Automattic.', - 'selected' => false, - 'shown' => true, - ), - 'wpforms-lite' => array( - 'slug' => 'wpforms-lite', - 'icon' => '--site-features-form', - 'title' => 'Forms', - 'subtitle' => 'Powered by WP Forms', - 'desc' => 'Five million people build smarter forms and surveys with WPForms from Awesome Motive.', - 'selected' => false, - 'shown' => true, - ), - 'google-analytics-for-wordpress' => array( - 'slug' => 'google-analytics-for-wordpress', - 'icon' => '--site-features-analytics', - 'title' => 'Site Traffic', - 'subtitle' => 'Powered by MonsterInsights', - 'desc' => 'See the opportunities in your website analytics traffic data using MonsterInsights from Awesome Motive.', - 'selected' => false, - 'shown' => true, - ), - 'wordpress-seo' => array( - 'slug' => 'wordpress-seo', - 'icon' => '--site-features-share', - 'title' => 'Search Engine Optimization', - 'subtitle' => 'Powered by Yoast', - 'desc' => 'Get more traffic to your WordPress site with powerful analysis and tools from our colleagues at Yoast.', - 'selected' => false, - 'shown' => true, - ), - 'creative-mail-by-constant-contact' => array( - 'slug' => 'creative-mail-by-constant-contact', - 'icon' => '--site-features-email', - 'title' => 'Email Newsletters', - 'subtitle' => 'Powered by Creative Email', - 'desc' => 'A professional logo builder, marketing automations with WooCommerce and social management -- CreativeMail is a whole lot more than mail from Constant Contact.', - 'selected' => false, - 'shown' => true, - ), - 'optinmonster' => array( - 'slug' => 'optinmonster', - 'icon' => '--site-features-lead', - 'title' => 'Lead Generation', - 'subtitle' => 'Powered by Optin Monster', - 'desc' => 'Connect with website visitors using a proven kit of tools for growth using this offering from Awesome Motive.', - 'selected' => false, - 'shown' => true, - ), - ), - 'wc_standard' => array( - 'yith-woocommerce-ajax-search' => array( - 'slug' => 'yith-woocommerce-ajax-search', - 'icon' => '--site-features-search', - 'title' => 'Enhanced Product Search', - 'subtitle' => 'Powered by YITH', - 'desc' => 'Give your visitors great search experiences with this exclusive offering from our colleagues at YITH.', - 'selected' => false, - 'shown' => true, - ), - 'nfd_slug_yith_woocommerce_ajax_product_filter' => array( - 'slug' => 'nfd_slug_yith_woocommerce_ajax_product_filter', - 'icon' => '--site-features-filter', - 'title' => 'Enhanced Product Filters', - 'subtitle' => 'Powered by YITH', - 'desc' => 'Give your visitors powerful tools to discover your great products with this exclusive offering from our colleagues at YITH.', - 'selected' => false, - 'shown' => true, - ), - 'nfd_slug_yith_woocommerce_booking' => array( - 'slug' => 'nfd_slug_yith_woocommerce_booking', - 'icon' => '--site-features-bookingcalendar', - 'title' => 'Bookings & Appointments', - 'subtitle' => 'Powered by YITH', - 'desc' => 'Have visitors book meetings and services with you, accepting payment and more using this exclusive offering from our colleagues at YITH.', - 'selected' => false, - 'shown' => true, - ), - 'nfd_slug_yith_woocommerce_wishlist' => array( - 'slug' => 'nfd_slug_yith_woocommerce_wishlist', - 'icon' => '--site-features-wishlist', - 'title' => 'Product Wishlists', - 'subtitle' => 'Powered by YITH', - 'desc' => 'Let discerning shoppers curate their selections with a system of favorites using this exclusive offering from our colleagues at YITH.', - 'selected' => false, - 'shown' => true, + protected static function get_site_features_flow_plan_map() { + return array( + 'wp-setup' => array( + 'default' => array( + 'jetpack' => array( + 'slug' => 'jetpack', + 'icon' => '--site-features-security', + 'title' => __( 'Security, Speed & Growth', 'wp-module-onboarding' ), + 'subtitle' => __( 'Powered by Jetpack', 'wp-module-onboarding' ), + 'desc' => __( 'Jetpack includes dozens of powerful, unique capabilities for your WordPress sites from Automattic.', 'wp-module-onboarding' ), + 'selected' => false, + 'shown' => true, + ), + 'wpforms-lite' => array( + 'slug' => 'wpforms-lite', + 'icon' => '--site-features-form', + 'title' => __( 'Forms', 'wp-module-onboarding' ), + 'subtitle' => __( 'Powered by WP Forms', 'wp-module-onboarding' ), + 'desc' => __( 'Five million people build smarter forms and surveys with WPForms from Awesome Motive.', 'wp-module-onboarding' ), + 'selected' => false, + 'shown' => true, + ), + 'google-analytics-for-wordpress' => array( + 'slug' => 'google-analytics-for-wordpress', + 'icon' => '--site-features-analytics', + 'title' => __( 'Site Traffic', 'wp-module-onboarding' ), + 'subtitle' => __( 'Powered by MonsterInsights', 'wp-module-onboarding' ), + 'desc' => __( 'See the opportunities in your website analytics traffic data using MonsterInsights from Awesome Motive.', 'wp-module-onboarding' ), + 'selected' => false, + 'shown' => true, + ), + 'wordpress-seo' => array( + 'slug' => 'wordpress-seo', + 'icon' => '--site-features-share', + 'title' => __( 'Search Engine Optimization', 'wp-module-onboarding' ), + 'subtitle' => __( 'Powered by Yoast', 'wp-module-onboarding' ), + 'desc' => __( 'Get more traffic to your WordPress site with powerful analysis and tools from our colleagues at Yoast.', 'wp-module-onboarding' ), + 'selected' => false, + 'shown' => true, + ), + 'creative-mail-by-constant-contact' => array( + 'slug' => 'creative-mail-by-constant-contact', + 'icon' => '--site-features-email', + 'title' => __( 'Email Newsletters', 'wp-module-onboarding' ), + 'subtitle' => __( 'Powered by Creative Email', 'wp-module-onboarding' ), + 'desc' => __( 'A professional logo builder, marketing automations with WooCommerce and social management -- CreativeMail is a whole lot more than mail from Constant Contact.', 'wp-module-onboarding' ), + 'selected' => false, + 'shown' => true, + ), + 'optinmonster' => array( + 'slug' => 'optinmonster', + 'icon' => '--site-features-lead', + 'title' => __( 'Lead Generation', 'wp-module-onboarding' ), + 'subtitle' => __( 'Powered by Optin Monster', 'wp-module-onboarding' ), + 'desc' => __( 'Connect with website visitors using a proven kit of tools for growth using this offering from Awesome Motive.', 'wp-module-onboarding' ), + 'selected' => false, + 'shown' => true, + ), ), ), - 'wc_premium' => array( - 'yith-woocommerce-ajax-search' => array( - 'slug' => 'yith-woocommerce-ajax-search', - 'icon' => '--site-features-search', - 'title' => 'Enhanced Product Search', - 'subtitle' => 'Powered by YITH', - 'desc' => 'Give your visitors great search experiences with this exclusive offering from our colleagues at YITH.', - 'selected' => false, - 'shown' => true, - ), - 'nfd_slug_yith_woocommerce_ajax_product_filter' => array( - 'slug' => 'nfd_slug_yith_woocommerce_ajax_product_filter', - 'icon' => '--site-features-filter', - 'title' => 'Enhanced Product Filters', - 'subtitle' => 'Powered by YITH', - 'desc' => 'Give your visitors powerful tools to discover your great products with this exclusive offering from our colleagues at YITH.', - 'selected' => false, - 'shown' => true, + 'ecommerce' => array( + 'default' => array( + 'jetpack' => array( + 'slug' => 'jetpack', + 'icon' => '--site-features-security', + 'title' => __( 'Security, Speed & Growth', 'wp-module-onboarding' ), + 'subtitle' => __( 'Powered by Jetpack', 'wp-module-onboarding' ), + 'desc' => __( 'Jetpack includes dozens of powerful, unique capabilities for your WordPress sites from Automattic.', 'wp-module-onboarding' ), + 'selected' => false, + 'shown' => true, + ), + 'wpforms-lite' => array( + 'slug' => 'wpforms-lite', + 'icon' => '--site-features-form', + 'title' => __( 'Forms', 'wp-module-onboarding' ), + 'subtitle' => __( 'Powered by WP Forms', 'wp-module-onboarding' ), + 'desc' => __( 'Five million people build smarter forms and surveys with WPForms from Awesome Motive.', 'wp-module-onboarding' ), + 'selected' => false, + 'shown' => true, + ), + 'google-analytics-for-wordpress' => array( + 'slug' => 'google-analytics-for-wordpress', + 'icon' => '--site-features-analytics', + 'title' => __( 'Site Traffic', 'wp-module-onboarding' ), + 'subtitle' => __( 'Powered by MonsterInsights', 'wp-module-onboarding' ), + 'desc' => __( 'See the opportunities in your website analytics traffic data using MonsterInsights from Awesome Motive.', 'wp-module-onboarding' ), + 'selected' => false, + 'shown' => true, + ), + 'wordpress-seo' => array( + 'slug' => 'wordpress-seo', + 'icon' => '--site-features-share', + 'title' => __( 'Search Engine Optimization', 'wp-module-onboarding' ), + 'subtitle' => __( 'Powered by Yoast', 'wp-module-onboarding' ), + 'desc' => __( 'Get more traffic to your WordPress site with powerful analysis and tools from our colleagues at Yoast.', 'wp-module-onboarding' ), + 'selected' => false, + 'shown' => true, + ), + 'creative-mail-by-constant-contact' => array( + 'slug' => 'creative-mail-by-constant-contact', + 'icon' => '--site-features-email', + 'title' => __( 'Email Newsletters', 'wp-module-onboarding' ), + 'subtitle' => __( 'Powered by Creative Email', 'wp-module-onboarding' ), + 'desc' => __( 'A professional logo builder, marketing automations with WooCommerce and social management -- CreativeMail is a whole lot more than mail from Constant Contact.', 'wp-module-onboarding' ), + 'selected' => false, + 'shown' => true, + ), + 'optinmonster' => array( + 'slug' => 'optinmonster', + 'icon' => '--site-features-lead', + 'title' => __( 'Lead Generation', 'wp-module-onboarding' ), + 'subtitle' => __( 'Powered by Optin Monster', 'wp-module-onboarding' ), + 'desc' => __( 'Connect with website visitors using a proven kit of tools for growth using this offering from Awesome Motive.', 'wp-module-onboarding' ), + 'selected' => false, + 'shown' => true, + ), ), - 'nfd_slug_yith_woocommerce_booking' => array( - 'slug' => 'nfd_slug_yith_woocommerce_booking', - 'icon' => '--site-features-bookingcalendar', - 'title' => 'Bookings & Appointments', - 'subtitle' => 'Powered by YITH', - 'desc' => 'Have visitors book meetings and services with you, accepting payment and more using this exclusive offering from our colleagues at YITH.', - 'selected' => false, - 'shown' => true, + 'wc_standard' => array( + 'yith-woocommerce-ajax-search' => array( + 'slug' => 'yith-woocommerce-ajax-search', + 'icon' => '--site-features-search', + 'title' => __( 'Enhanced Product Search', 'wp-module-onboarding' ), + 'subtitle' => __( 'Powered by YITH', 'wp-module-onboarding' ), + 'desc' => __( 'Give your visitors great search experiences with this exclusive offering from our colleagues at YITH.', 'wp-module-onboarding' ), + 'selected' => false, + 'shown' => true, + ), + 'nfd_slug_yith_woocommerce_ajax_product_filter' => array( + 'slug' => 'nfd_slug_yith_woocommerce_ajax_product_filter', + 'icon' => '--site-features-filter', + 'title' => __( 'Enhanced Product Filters', 'wp-module-onboarding' ), + 'subtitle' => __( 'Powered by YITH', 'wp-module-onboarding' ), + 'desc' => __( 'Give your visitors powerful tools to discover your great products with this exclusive offering from our colleagues at YITH.', 'wp-module-onboarding' ), + 'selected' => false, + 'shown' => true, + ), + 'nfd_slug_yith_woocommerce_booking' => array( + 'slug' => 'nfd_slug_yith_woocommerce_booking', + 'icon' => '--site-features-bookingcalendar', + 'title' => __( 'Bookings & Appointments', 'wp-module-onboarding' ), + 'subtitle' => __( 'Powered by YITH', 'wp-module-onboarding' ), + 'desc' => __( 'Have visitors book meetings and services with you, accepting payment and more using this exclusive offering from our colleagues at YITH.', 'wp-module-onboarding' ), + 'selected' => false, + 'shown' => true, + ), + 'nfd_slug_yith_woocommerce_wishlist' => array( + 'slug' => 'nfd_slug_yith_woocommerce_wishlist', + 'icon' => '--site-features-wishlist', + 'title' => __( 'Product Wishlists', 'wp-module-onboarding' ), + 'subtitle' => __( 'Powered by YITH', 'wp-module-onboarding' ), + 'desc' => __( 'Let discerning shoppers curate their selections with a system of favorites using this exclusive offering from our colleagues at YITH.', 'wp-module-onboarding' ), + 'selected' => false, + 'shown' => true, + ), ), - 'nfd_slug_yith_woocommerce_wishlist' => array( - 'slug' => 'nfd_slug_yith_woocommerce_wishlist', - 'icon' => '--site-features-wishlist', - 'title' => 'Product Wishlists', - 'subtitle' => 'Powered by YITH', - 'desc' => 'Let discerning shoppers curate their selections with a system of favorites using this exclusive offering from our colleagues at YITH.', - 'selected' => false, - 'shown' => true, + 'wc_premium' => array( + 'yith-woocommerce-ajax-search' => array( + 'slug' => 'yith-woocommerce-ajax-search', + 'icon' => '--site-features-search', + 'title' => __( 'Enhanced Product Search', 'wp-module-onboarding' ), + 'subtitle' => __( 'Powered by YITH', 'wp-module-onboarding' ), + 'desc' => __( 'Give your visitors great search experiences with this exclusive offering from our colleagues at YITH.', 'wp-module-onboarding' ), + 'selected' => false, + 'shown' => true, + ), + 'nfd_slug_yith_woocommerce_ajax_product_filter' => array( + 'slug' => 'nfd_slug_yith_woocommerce_ajax_product_filter', + 'icon' => '--site-features-filter', + 'title' => __( 'Enhanced Product Filters', 'wp-module-onboarding' ), + 'subtitle' => __( 'Powered by YITH', 'wp-module-onboarding' ), + 'desc' => __( 'Give your visitors powerful tools to discover your great products with this exclusive offering from our colleagues at YITH.', 'wp-module-onboarding' ), + 'selected' => false, + 'shown' => true, + ), + 'nfd_slug_yith_woocommerce_booking' => array( + 'slug' => 'nfd_slug_yith_woocommerce_booking', + 'icon' => '--site-features-bookingcalendar', + 'title' => __( 'Bookings & Appointments', 'wp-module-onboarding' ), + 'subtitle' => __( 'Powered by YITH', 'wp-module-onboarding' ), + 'desc' => __( 'Have visitors book meetings and services with you, accepting payment and more using this exclusive offering from our colleagues at YITH.', 'wp-module-onboarding' ), + 'selected' => false, + 'shown' => true, + ), + 'nfd_slug_yith_woocommerce_wishlist' => array( + 'slug' => 'nfd_slug_yith_woocommerce_wishlist', + 'icon' => '--site-features-wishlist', + 'title' => __( 'Product Wishlists', 'wp-module-onboarding' ), + 'subtitle' => __( 'Powered by YITH', 'wp-module-onboarding' ), + 'desc' => __( 'Let discerning shoppers curate their selections with a system of favorites using this exclusive offering from our colleagues at YITH.', 'wp-module-onboarding' ), + 'selected' => false, + 'shown' => true, + ), ), + 'wc_priority' => array(), ), - 'wc_priority' => array(), - ), - ); + ); + } /** * Retrieves all the site features for a particular flow and plan. @@ -156,17 +215,18 @@ final class SiteFeatures { * @return array */ public static function get() { - $plan_data = Data::current_plan(); - $plan_flow = $plan_data['flow']; - $plan_subtype = $plan_data['subtype']; + $plan_data = Data::current_plan(); + $plan_flow = $plan_data['flow']; + $plan_subtype = $plan_data['subtype']; + $site_features_flow_plan_map = self::get_site_features_flow_plan_map(); $site_features = array(); - if ( $plan_flow && isset( self::$site_features_flow_plan_map[ $plan_flow ] ) ) { - if ( isset( self::$site_features_flow_plan_map[ $plan_flow ]['default'] ) ) { - $site_features = array_merge( $site_features, self::$site_features_flow_plan_map[ $plan_flow ]['default'] ); + if ( $plan_flow && isset( $site_features_flow_plan_map[ $plan_flow ] ) ) { + if ( isset( $site_features_flow_plan_map[ $plan_flow ]['default'] ) ) { + $site_features = array_merge( $site_features, $site_features_flow_plan_map[ $plan_flow ]['default'] ); } - if ( 'default' !== $plan_subtype && isset( self::$site_features_flow_plan_map[ $plan_flow ][ $plan_subtype ] ) ) { - $site_features = array_merge( $site_features, self::$site_features_flow_plan_map[ $plan_flow ][ $plan_subtype ] ); + if ( 'default' !== $plan_subtype && isset( $site_features_flow_plan_map[ $plan_flow ][ $plan_subtype ] ) ) { + $site_features = array_merge( $site_features, $site_features_flow_plan_map[ $plan_flow ][ $plan_subtype ] ); } } return $site_features; diff --git a/includes/Data/Themes.php b/includes/Data/Themes.php index cb9a0b7b8..96ad22a56 100644 --- a/includes/Data/Themes.php +++ b/includes/Data/Themes.php @@ -28,24 +28,33 @@ final class Themes { * @var array */ protected static $flow_default_theme_slugs = array( - 'wp-setup' => 'twentytwentythree', + 'wp-setup' => 'yith-wonder', 'ecommerce' => 'yith-wonder', ); - /** - * Key 'default' contains a list of default themes to be installed irrespective of the plan. - * Key contains a Key 'default' and a list of Key 's. - * Key => 'default' contains a list of default theme installs for . - * Key => contains a list of themes to be installed for a particular . - * - * The final queue of themes to be installed makes use of a max heap and hence the greater the number the earlier - * a theme will be placed for install in the queue. This will also allow us to - * prevent entering negative numbers when queueing a theme for earlier installs. - * - * @var array Initial themes to be installed classified based on the hosting plan. - */ + /** + * Key 'default' contains a list of default themes to be installed irrespective of the plan. + * Key contains a Key 'default' and a list of Key 's. + * Key => 'default' contains a list of default theme installs for . + * Key => contains a list of themes to be installed for a particular . + * + * The final queue of themes to be installed makes use of a max heap and hence the greater the number the earlier + * a theme will be placed for install in the queue. This will also allow us to + * prevent entering negative numbers when queueing a theme for earlier installs. + * + * @var array Initial themes to be installed classified based on the hosting plan. + */ protected static $init_list = array( 'default' => array(), + 'wp-setup' => array( + 'default' => array( + array( + 'slug' => 'nfd_slug_yith_wonder', + 'activate' => true, + 'priority' => 20, + ), + ), + ), 'ecommerce' => array( 'default' => array( array( @@ -57,26 +66,26 @@ final class Themes { ), ); - /** - * Returns the list of themes in a data structure that is faster to search. - * - * @return array - */ + /** + * Returns the list of themes in a data structure that is faster to search. + * + * @return array + */ public static function get() { - return array( - 'nfd_slugs' => self::$nfd_slugs, - ); + return array( + 'nfd_slugs' => self::$nfd_slugs, + ); } - /** - * Get all the approved theme slugs. - * - * @return array - */ + /** + * Get all the approved theme slugs. + * + * @return array + */ public static function get_approved() { - return array( - 'nfd_slugs' => array_keys( array_filter( self::$nfd_slugs, array( __CLASS__, 'check_approved' ) ) ), - ); + return array( + 'nfd_slugs' => array_keys( array_filter( self::$nfd_slugs, array( __CLASS__, 'check_approved' ) ) ), + ); } /** @@ -89,12 +98,12 @@ private static function check_approved( $value ) { return true === $value['approved']; } - /** - * Get the number of previews that will be fetched in each step. - * This helps us show the number of necessary skeletons in the front end. - * - * @return array - */ + /** + * Get the number of previews that will be fetched in each step. + * This helps us show the number of necessary skeletons in the front end. + * + * @return array + */ public static function step_preview_data() { $theme_step_data = Patterns::get_count_of_patterns(); $site_features = count( SiteFeatures::get() ); @@ -102,11 +111,11 @@ public static function step_preview_data() { return $theme_step_data; } - /** - * Get a list of initial themes to be installed for a particular hosting plan. - * - * @return array - */ + /** + * Get a list of initial themes to be installed for a particular hosting plan. + * + * @return array + */ public static function get_init() { $plan_data = Data::current_plan(); $plan_flow = $plan_data['flow']; diff --git a/includes/ModuleController.php b/includes/ModuleController.php index 8df4b8afc..6de4298b2 100644 --- a/includes/ModuleController.php +++ b/includes/ModuleController.php @@ -35,8 +35,10 @@ public static function module_switcher() { Brands::set_current_brand( container() ); $customer_data = Data::customer_data(); + $enable_onboarding = self::verify_onboarding_criteria( $customer_data ); + // Check if he is a Non-Ecom Cust and Disable Redirect and Module - if ( ! self::is_new_commerce_signup( $customer_data ) ) { + if ( ! $enable_onboarding ) { // Check if the Module Does Exist if ( ModuleRegistry::get( $module_name ) ) { @@ -56,6 +58,37 @@ public static function module_switcher() { } + /** + * Verify all the necessary criteria to enable Onboarding for the site. + * + * @param array $customer_data The brand customer data. + * @return boolean + */ + public static function verify_onboarding_criteria( $customer_data ) { + $brand_enabled_flows = Flows::get_flows(); + + foreach ( $brand_enabled_flows as $flow => $enabled ) { + if ( ! $enabled ) { + continue; + } + + switch ( $flow ) { + case 'ecommerce': + if ( self::is_new_commerce_signup( $customer_data ) ) { + return true; + } + break; + case 'wp-setup': + if ( self::is_net_new_signup( $customer_data ) ) { + return true; + } + break; + } + } + + return false; + } + /** * Get signup date of the install. * @@ -82,6 +115,31 @@ public static function get_signup_date( $customer_data ) { return false; } + /** + * Determines if the signup data is after the brand's net_new_signup_date_threshold. + * + * @param array $customer_data The brand customer data. + * @return boolean + */ + public static function is_net_new_signup( $customer_data ) { + $current_brand = Data::current_brand(); + if ( ! isset( $current_brand['config']['net_new_signup_date_threshold'] ) ) { + return false; + } + $net_new_signup_date_threshold = gmdate( 'Y-m-d H:i:s', strtotime( $current_brand['config']['net_new_signup_date_threshold'] ) ); + + // Get the actual signup date of the install. + $signup_date = self::get_signup_date( $customer_data ); + + // As a safety measure, return false if a signup date cannot be determined. + if ( false === $signup_date ) { + return false; + } + + // Determine whether the commerce install is a net new signup. + return $signup_date >= $net_new_signup_date_threshold; + } + /** * Determine if the install is a new commerce signup * @@ -106,26 +164,8 @@ public static function is_new_commerce_signup( $customer_data ) { return false; } - /* - Get the net new signup date threshold from the brand configuration. - As a safety measure, return false if a threshold is not set for a particular brand. - */ - $current_brand = Data::current_brand(); - if ( ! isset( $current_brand['config']['net_new_signup_date_threshold'] ) ) { - return false; - } - $net_new_signup_date_threshold = gmdate( 'Y-m-d H:i:s', strtotime( $current_brand['config']['net_new_signup_date_threshold'] ) ); - - // Get the actual signup date of the install. - $signup_date = self::get_signup_date( $customer_data ); - - // As a safety measure, return false if a signup date cannot be determined. - if ( false === $signup_date ) { - return false; - } - // Determine whether the commerce install is a net new signup. - $is_net_new_signup = $signup_date >= $net_new_signup_date_threshold; + $is_net_new_signup = self::is_net_new_signup( $customer_data ); if ( ! $is_net_new_signup ) { return false; } diff --git a/includes/RestApi/FlowController.php b/includes/RestApi/FlowController.php index d749b9512..34fed044b 100644 --- a/includes/RestApi/FlowController.php +++ b/includes/RestApi/FlowController.php @@ -1,13 +1,8 @@ namespace, + $this->rest_base . '/switch', + array( + array( + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'switch' ), + 'permission_callback' => array( Permissions::class, 'rest_is_authorized_admin' ), + 'args' => $this->get_switch_args(), + ), + ) + ); } /** - * Fetch onboarding flow details from database. + * Get the valid request params for the switch endpoint. * - * @param \WP_REST_Request $request Request model. + * @return array + */ + public function get_switch_args() { + return array( + 'flow' => array( + 'required' => true, + 'type' => 'string', + 'sanitize_callback' => 'sanitize_text_field', + ), + ); + } + + /** + * Get Onboarding flow data. * * @return \WP_REST_Response */ - public function get_onboarding_flow_data( \WP_REST_Request $request ) { - // check if data is available in the database if not then fetch the default dataset - $result = $this->read_details_from_wp_options(); - if ( ! $result ) { - $result = Flows::get_data(); - $result['createdAt'] = time(); - // update default data if flow type is ecommerce - $result = $this->update_default_data_for_ecommerce( $result ); - $this->save_details_to_wp_options( $result ); - } - + public function get_onboarding_flow_data() { return new \WP_REST_Response( - $result, + FlowService::get_flow_data(), 200 ); } /** - * Save / Update onboarding flow details to database. + * Update the Onboarding flow data. * - * @param \WP_REST_Request $request Request model. + * @param \WP_REST_Request $request The incoming request. * - * @return \WP_REST_Response|\WP_Error + * @return \WP_Error|\WP_REST_Response */ public function save_onboarding_flow_data( \WP_REST_Request $request ) { - $flow_data = array(); - $params = json_decode( $request->get_body(), true ); - - if ( is_null( $params ) ) { - return new \WP_Error( - 'no_post_data', - 'No Data Provided', - array( 'status' => 404 ) - ); - } + $params = json_decode( $request->get_body(), true ); - $flow_data = $this->read_details_from_wp_options(); - if ( ! $flow_data ) { - $flow_data = Flows::get_data(); - $flow_data['createdAt'] = time(); - // update default data if flow type is ecommerce - $flow_data = $this->update_default_data_for_ecommerce( $flow_data ); - $this->save_details_to_wp_options( $flow_data ); - } - - foreach ( $params as $key => $param ) { - $value = $this->array_search_key( $key, $flow_data ); - if ( false === $value ) { - return new \WP_Error( - 'wrong_param_provided', - "Wrong Parameter Provided : $key", - array( 'status' => 404 ) - ); - } - } - - /* - [TODO] Handle this and some of the site name, logo, description logic in a cleaner way. - At least the primary and secondary update does not run on every flow data request. - */ - if ( ! empty( $params['data']['siteType']['primary']['refers'] ) && - ( empty( $flow_data['data']['siteType']['primary']['value'] ) || $flow_data['data']['siteType']['primary']['value'] !== $params['data']['siteType']['primary']['value'] ) ) { - if ( class_exists( 'NewfoldLabs\WP\Module\Data\SiteClassification\PrimaryType' ) ) { - $primary_type = new PrimaryType( $params['data']['siteType']['primary']['refers'], $params['data']['siteType']['primary']['value'] ); - if ( ! $primary_type->save() ) { - return new \WP_Error( - 'wrong_param_provided', - __( 'Wrong Parameter Provided : primary => value', 'wp-module-onboarding' ), - array( 'status' => 404 ) - ); - } - } - } - - if ( ! empty( $params['data']['siteType']['secondary']['refers'] ) && - ( empty( $flow_data['data']['siteType']['secondary']['value'] ) || $flow_data['data']['siteType']['secondary']['value'] !== $params['data']['siteType']['secondary']['value'] ) ) { - if ( class_exists( 'NewfoldLabs\WP\Module\Data\SiteClassification\SecondaryType' ) ) { - $secondary_type = new SecondaryType( $params['data']['siteType']['secondary']['refers'], $params['data']['siteType']['secondary']['value'] ); - if ( ! $secondary_type->save() ) { - return new \WP_Error( - 'wrong_param_provided', - __( 'Wrong Parameter Provided : secondary => value', 'wp-module-onboarding' ), - array( 'status' => 404 ) - ); - } - } - } - - $flow_data = array_replace_recursive( $flow_data, $params ); - - // update timestamp once data is updated - $flow_data['updatedAt'] = time(); - - // Update Blog Information from Basic Info - if ( ( ! empty( $flow_data['data']['blogName'] ) ) ) { - \update_option( Options::get_option_name( 'blog_name', false ), $flow_data['data']['blogName'] ); - } - - if ( ( ! empty( $flow_data['data']['blogDescription'] ) ) ) { - \update_option( Options::get_option_name( 'blog_description', false ), $flow_data['data']['blogDescription'] ); - } - - if ( ( ! empty( $flow_data['data']['siteLogo'] ) ) && ! empty( $flow_data['data']['siteLogo']['id'] ) ) { - \update_option( Options::get_option_name( 'site_icon', false ), $flow_data['data']['siteLogo']['id'] ); - \update_option( Options::get_option_name( 'site_logo', false ), $flow_data['data']['siteLogo']['id'] ); - } else { - \update_option( Options::get_option_name( 'site_icon', false ), 0 ); - \delete_option( Options::get_option_name( 'site_logo', false ) ); - } - - // save data to database - if ( ! $this->update_wp_options_data_in_database( $flow_data ) ) { - return new \WP_Error( - 'database_update_failed', - 'There was an error saving the data', - array( 'status' => 404 ) - ); + $flow_data = FlowService::update_flow_data( $params ); + if ( \is_wp_error( $flow_data ) ) { + return $flow_data; } return new \WP_REST_Response( @@ -196,78 +120,7 @@ public function save_onboarding_flow_data( \WP_REST_Request $request ) { } /** - * Check the current flow type and update default data if flowtype is ecommerce. - * - * @param array $data default blueprint flow data. - * - * @return array - */ - private function update_default_data_for_ecommerce( $data ) { - // get current flow type - $flow_type = Data::current_flow(); - if ( 'ecommerce' === $flow_type ) { - // update default data with ecommerce data - $data['data']['topPriority']['priority1'] = 'selling'; - } - return $data; - } - - /** - * Read onboarding flow options from database - * - * @return array - */ - public function read_details_from_wp_options() { - return \get_option( Options::get_option_name( 'flow' ) ); - } - - /** - * Add onboarding flow options - * - * @param array $data default blueprint flow data. - * - * @return array - */ - private function save_details_to_wp_options( $data ) { - return \add_option( Options::get_option_name( 'flow' ), $data ); - } - - /** - * Update onboarding flow options - * - * @param array $data default blueprint flow data. - * - * @return array - */ - private function update_wp_options_data_in_database( $data ) { - return \update_option( Options::get_option_name( 'flow' ), $data ); - } - - /** - * Function to search for key in array recursively with case sensitive exact match - * - * @param array $needle_key specific key in flow data. - * @param array $array WP Options Data. - * - * @return boolean - */ - private function array_search_key( $needle_key, $array ) { - foreach ( $array as $key => $value ) { - if ( strcmp( $key, $needle_key ) === 0 ) { - return true; - } - if ( is_array( $value ) ) { - $result = $this->array_search_key( $needle_key, $value ); - if ( false !== $result ) { - return $result; - } - } - } - return false; - } - - /** - * Flow completion API for child theme generation, verify child theme and publish site pages + * Flow completion API for child theme generation, verify child theme and publish site pages. * * @return \WP_REST_Response */ @@ -294,4 +147,23 @@ public function complete() { 201 ); } + + /** + * Switch the Onboarding flow. + * + * @param \WP_REST_Request $request The incoming switch request. + * @return \WP_Error|\WP_REST_Response + */ + public function switch( \WP_REST_Request $request ) { + $flow = $request->get_param( 'flow' ); + $status = FlowService::switch_flow( $flow ); + if ( \is_wp_error( $status ) ) { + return $status; + } + + return new \WP_REST_Response( + array(), + 200 + ); + } } diff --git a/includes/Services/FlowService.php b/includes/Services/FlowService.php new file mode 100644 index 000000000..dab38ca00 --- /dev/null +++ b/includes/Services/FlowService.php @@ -0,0 +1,232 @@ + 404 ) + ); + } + + $flow_data = self::get_flow_data(); + + foreach ( $params as $key => $param ) { + $value = self::array_search_key( $key, $flow_data ); + if ( false === $value ) { + return new \WP_Error( + 'wrong_param_provided', + "Wrong Parameter Provided : $key", + array( 'status' => 404 ) + ); + } + } + + /* + [TODO] Handle this and some of the site name, logo, description logic in a cleaner way. + At least the primary and secondary update does not run on every flow data request. + */ + if ( ! empty( $params['data']['siteType']['primary']['refers'] ) && + ( empty( $flow_data['data']['siteType']['primary']['value'] ) || $flow_data['data']['siteType']['primary']['value'] !== $params['data']['siteType']['primary']['value'] ) ) { + if ( class_exists( 'NewfoldLabs\WP\Module\Data\SiteClassification\PrimaryType' ) ) { + $primary_type = new PrimaryType( $params['data']['siteType']['primary']['refers'], $params['data']['siteType']['primary']['value'] ); + if ( ! $primary_type->save() ) { + return new \WP_Error( + 'wrong_param_provided', + __( 'Wrong Parameter Provided : primary => value', 'wp-module-onboarding' ), + array( 'status' => 404 ) + ); + } + } + } + + if ( ! empty( $params['data']['siteType']['secondary']['refers'] ) && + ( empty( $flow_data['data']['siteType']['secondary']['value'] ) || $flow_data['data']['siteType']['secondary']['value'] !== $params['data']['siteType']['secondary']['value'] ) ) { + if ( class_exists( 'NewfoldLabs\WP\Module\Data\SiteClassification\SecondaryType' ) ) { + $secondary_type = new SecondaryType( $params['data']['siteType']['secondary']['refers'], $params['data']['siteType']['secondary']['value'] ); + if ( ! $secondary_type->save() ) { + return new \WP_Error( + 'wrong_param_provided', + __( 'Wrong Parameter Provided : secondary => value', 'wp-module-onboarding' ), + array( 'status' => 404 ) + ); + } + } + } + + $flow_data = array_replace_recursive( $flow_data, $params ); + + // Update timestamp everytime the Onboarding flow data is updated. + $flow_data['updatedAt'] = time(); + + // Update Blog Information from Basic Info + if ( ( ! empty( $flow_data['data']['blogName'] ) ) ) { + \update_option( Options::get_option_name( 'blog_name', false ), $flow_data['data']['blogName'] ); + } + + if ( ( ! empty( $flow_data['data']['blogDescription'] ) ) ) { + \update_option( Options::get_option_name( 'blog_description', false ), $flow_data['data']['blogDescription'] ); + } + + if ( ( ! empty( $flow_data['data']['siteLogo'] ) ) && ! empty( $flow_data['data']['siteLogo']['id'] ) ) { + \update_option( Options::get_option_name( 'site_icon', false ), $flow_data['data']['siteLogo']['id'] ); + \update_option( Options::get_option_name( 'site_logo', false ), $flow_data['data']['siteLogo']['id'] ); + } else { + \update_option( Options::get_option_name( 'site_icon', false ), 0 ); + \delete_option( Options::get_option_name( 'site_logo', false ) ); + } + + if ( ! self::update_data_in_wp_option( $flow_data ) ) { + return new \WP_Error( + 'database_update_failed', + 'There was an error saving the data', + array( 'status' => 404 ) + ); + } + + return $flow_data; + } + + /** + * Switch the Onboarding flow. + * + * @param string $flow A valid Onboarding flow for a brand. + * @return \WP_Error|boolean + */ + public static function switch_flow( $flow ) { + // Get all the enabled flows for a brand. + $enabled_flows = Flows::get_flows(); + // If the request flow does not exist or is not enabled then return an error. + if ( ! isset( $enabled_flows[ $flow ] ) || true !== $enabled_flows[ $flow ] ) { + return new \WP_Error( + 'nfd_onboarding_error', + 'Flow not enabled.', + array( 'status' => 400 ) + ); + } + + // Reset the Plugin Install Status and Queue. + PluginInstallTaskManager::reset_install_status(); + + // Get the pre requisites for a flow. + $pre_requisites = Preview::get_pre_requisites( $flow ); + if ( ! isset( $pre_requisites ) || ! isset( $pre_requisites['plugins'] ) ) { + return true; + } + + // Install and activate all the required plugins. + foreach ( $pre_requisites['plugins'] as $plugin => $active ) { + // Skip if the plugin installation if it is already active. + if ( 'activated' === $active ) { + continue; + } + + $plugin_install_task = new PluginInstallTask( $plugin, true ); + $status = $plugin_install_task->execute(); + + if ( \is_wp_error( $status ) ) { + return $status; + } + } + + return true; + } + + /** + * Read Onboarding flow data from the wp_option. + * + * @return array + */ + public static function read_data_from_wp_option() { + return \get_option( Options::get_option_name( 'flow' ), false ); + } + + /** + * Update flow data params if the current flow is ecommerce. + * + * @param array $data The flow data. + * + * @return array + */ + private static function update_data_for_ecommerce( $data ) { + // get current flow type + $flow_type = Data::current_flow(); + if ( 'ecommerce' === $flow_type ) { + // update default data with ecommerce data + $data['data']['topPriority']['priority1'] = 'selling'; + } + return $data; + } + + /** + * Update Onboarding flow data in the wp_option. + * + * @param array $data default blueprint flow data. + * + * @return array + */ + private static function update_data_in_wp_option( $data ) { + return \update_option( Options::get_option_name( 'flow' ), $data ); + } + + /** + * Search for $needle_key in $array recursively. + * + * @param string $needle_key The key to be searched for. + * @param array $array The array in which the search occurs. + * + * @return boolean + */ + private static function array_search_key( $needle_key, $array ) { + foreach ( $array as $key => $value ) { + if ( strcmp( $key, $needle_key ) === 0 ) { + return true; + } + if ( is_array( $value ) ) { + $result = self::array_search_key( $needle_key, $value ); + if ( false !== $result ) { + return $result; + } + } + } + return false; + } + +} diff --git a/includes/Services/PluginInstaller.php b/includes/Services/PluginInstaller.php index 5dc155e39..89d2eb497 100644 --- a/includes/Services/PluginInstaller.php +++ b/includes/Services/PluginInstaller.php @@ -2,10 +2,20 @@ namespace NewfoldLabs\WP\Module\Onboarding\Services; use NewfoldLabs\WP\Module\Onboarding\Data\Plugins; -use NewfoldLabs\WP\Module\Onboarding\Data\Options; +/** + * Class PluginInstaller + */ class PluginInstaller { + /** + * Install a whitelisted plugin. + * + * @param string $plugin The plugin slug from Plugins.php. + * @param boolean $activate Whether to activate the plugin after install. + * + * @return \WP_Error|\WP_REST_Response + */ public static function install( $plugin, $activate ) { $plugins_list = Plugins::get(); @@ -36,23 +46,23 @@ public static function install( $plugin, $activate ) { // If it is not a zip URL then check if it is an approved slug. $plugin = \sanitize_text_field( $plugin ); if ( self::is_nfd_slug( $plugin ) ) { - // [TODO] Better handle mu-plugins and direct file downloads. - if ( $plugin === 'nfd_slug_endurance_page_cache' ) { + // [TODO] Better handle mu-plugins and direct file downloads. + if ( 'nfd_slug_endurance_page_cache' === $plugin ) { return self::install_endurance_page_cache(); } - $plugin_path = $plugins_list['nfd_slugs'][ $plugin ]['path']; + $plugin_path = $plugins_list['nfd_slugs'][ $plugin ]['path']; if ( ! self::is_plugin_installed( $plugin_path ) ) { - $status = self::install_from_zip( $plugins_list['nfd_slugs'][ $plugin ]['url'], $activate ); + $status = self::install_from_zip( $plugins_list['nfd_slugs'][ $plugin ]['url'], $activate ); if ( \is_wp_error( $status ) ) { return $status; } } if ( $activate && ! \is_plugin_active( $plugin_path ) ) { - $status = \activate_plugin( $plugin_path ); + $status = \activate_plugin( $plugin_path ); if ( \is_wp_error( $status ) ) { - $status->add_data( array( 'status' => 500 ) ); + $status->add_data( array( 'status' => 500 ) ); - return $status; + return $status; } } return new \WP_REST_Response( @@ -69,20 +79,26 @@ public static function install( $plugin, $activate ) { ); } - $plugin_path = $plugins_list['wp_slugs'][ $plugin ]['path']; + $plugin_path = $plugins_list['wp_slugs'][ $plugin ]['path']; + $plugin_post_install_callback = isset( $plugins_list['wp_slugs'][ $plugin ]['post_install_callback'] ) + ? $plugins_list['wp_slugs'][ $plugin ]['post_install_callback'] + : false; if ( ! self::is_plugin_installed( $plugin_path ) ) { - $status = self::install_from_wordpress( $plugin, $activate ); + $status = self::install_from_wordpress( $plugin, $activate ); if ( \is_wp_error( $status ) ) { - return $status; + return $status; + } + if ( is_callable( $plugin_post_install_callback ) ) { + $plugin_post_install_callback(); } } if ( $activate && ! \is_plugin_active( $plugin_path ) ) { - $status = \activate_plugin( $plugin_path ); + $status = \activate_plugin( $plugin_path ); if ( \is_wp_error( $status ) ) { - $status->add_data( array( 'status' => 500 ) ); + $status->add_data( array( 'status' => 500 ) ); - return $status; + return $status; } } @@ -93,12 +109,14 @@ public static function install( $plugin, $activate ) { } /** - * @param string $slug Representing the wordpress.org slug. + * Install a plugin from wordpress.org. * + * @param string $plugin The wp_slug to install. + * @param boolean $activate Whether to activate the plugin after install. * @return \WP_REST_Response|\WP_Error */ public static function install_from_wordpress( $plugin, $activate ) { - require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; + require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; $api = \plugins_api( 'plugin_information', @@ -121,9 +139,9 @@ public static function install_from_wordpress( $plugin, $activate ) { return $api; } - $status = self::install_from_zip( $api->download_link, $activate ); + $status = self::install_from_zip( $api->download_link, $activate ); if ( \is_wp_error( $status ) ) { - return $status; + return $status; } return new \WP_REST_Response( @@ -133,8 +151,10 @@ public static function install_from_wordpress( $plugin, $activate ) { } /** - * @param string $url URL to the zip for the plugin. + * Install the plugin from a custom ZIP. * + * @param string $url The ZIP URL to install from. + * @param boolean $activate Whether to activate the plugin after install. * @return \WP_REST_Response|\WP_Error */ public static function install_from_zip( $url, $activate ) { @@ -192,11 +212,11 @@ public static function install_from_zip( $url, $activate ) { } if ( $activate && ! \is_plugin_active( $plugin_file ) ) { - $status = \activate_plugin( $plugin_file ); + $status = \activate_plugin( $plugin_file ); if ( \is_wp_error( $status ) ) { - $status->add_data( array( 'status' => 500 ) ); + $status->add_data( array( 'status' => 500 ) ); - return $status; + return $status; } } @@ -207,25 +227,23 @@ public static function install_from_zip( $url, $activate ) { } /** - * @param string $plugin Slug of the plugin. - * * Checks if a given slug is a valid nfd_slug. Ref: includes/Data/Plugins.php for nfd_slug. * + * @param string $plugin Slug of the plugin. * @return boolean */ public static function is_nfd_slug( $plugin ) { - $plugins_list = Plugins::get(); + $plugins_list = Plugins::get(); if ( isset( $plugins_list['nfd_slugs'][ $plugin ]['approved'] ) ) { - return true; + return true; } - return false; + return false; } /** - * @param string $plugin_path Path to the plugin's header file. - * * Determines if a plugin has already been installed. * + * @param string $plugin_path Path to the plugin's header file. * @return boolean */ public static function is_plugin_installed( $plugin_path ) { @@ -241,50 +259,51 @@ public static function is_plugin_installed( $plugin_path ) { } /** - * @param string $plugin + * Get the type of plugin slug. Ref: includes/Data/Plugins.php for the different types. * - * @return string Type of plugin. Ref: includes/Data/Plugins.php for the different types. + * @param string $plugin The plugin slug to retrieve the type. + * @return string */ public static function get_plugin_type( $plugin ) { if ( \wp_http_validate_url( $plugin ) ) { - return 'urls'; + return 'urls'; } if ( self::is_nfd_slug( $plugin ) ) { - return 'nfd_slugs'; + return 'nfd_slugs'; } - return 'wp_slugs'; + return 'wp_slugs'; } /** - * @param string $plugin - * @param string $plugin_type + * Get the path to the Plugin's header file. * - * @return string Path to the Plugin's header file. + * @param string $plugin The slug of the plugin. + * @param string $plugin_type The type of plugin. + * @return string */ public static function get_plugin_path( $plugin, $plugin_type ) { - $plugin_list = Plugins::get(); - return $plugin_list[ $plugin_type ][ $plugin ]['path']; + $plugin_list = Plugins::get(); + return $plugin_list[ $plugin_type ][ $plugin ]['path']; } /** - * @param string $plugin - * @param string $activate - * * Checks if a plugin with the given slug and activation criteria already exists. * + * @param string $plugin The slug of the plugin to check for + * @param boolean $activate The activation criteria. * @return boolean */ public static function exists( $plugin, $activate ) { - $plugin_type = self::get_plugin_type( $plugin ); - $plugin_path = self::get_plugin_path( $plugin, $plugin_type ); + $plugin_type = self::get_plugin_type( $plugin ); + $plugin_path = self::get_plugin_path( $plugin, $plugin_type ); if ( ! self::is_plugin_installed( $plugin_path ) ) { - return false; + return false; } if ( $activate && ! \is_plugin_active( $plugin_path ) ) { - return false; + return false; } - return true; + return true; } /** @@ -303,11 +322,11 @@ public static function install_endurance_page_cache() { ); } - global $wp_filesystem; + global $wp_filesystem; - $plugin_list = Plugins::get(); - $plugin_url = $plugin_list['nfd_slugs']['nfd_slug_endurance_page_cache']['url']; - $plugin_path = $plugin_list['nfd_slugs']['nfd_slug_endurance_page_cache']['path']; + $plugin_list = Plugins::get(); + $plugin_url = $plugin_list['nfd_slugs']['nfd_slug_endurance_page_cache']['url']; + $plugin_path = $plugin_list['nfd_slugs']['nfd_slug_endurance_page_cache']['path']; if ( $wp_filesystem->exists( $plugin_path ) ) { return new \WP_REST_Response( @@ -317,15 +336,15 @@ public static function install_endurance_page_cache() { } if ( ! $wp_filesystem->is_dir( WP_CONTENT_DIR . '/mu-plugins' ) ) { - $wp_filesystem->mkdir( WP_CONTENT_DIR . '/mu-plugins' ); + $wp_filesystem->mkdir( WP_CONTENT_DIR . '/mu-plugins' ); } - $request = \wp_remote_get( $plugin_url ); + $request = \wp_remote_get( $plugin_url ); if ( \is_wp_error( $request ) ) { - return $request; + return $request; } - $wp_filesystem->put_contents( $plugin_path, $request['body'], FS_CHMOD_FILE ); + $wp_filesystem->put_contents( $plugin_path, $request['body'], FS_CHMOD_FILE ); return new \WP_REST_Response( array(), @@ -343,7 +362,7 @@ protected static function connect_to_filesystem() { // We want to ensure that the user has direct access to the filesystem. $access_type = \get_filesystem_method(); - if ( $access_type !== 'direct' ) { + if ( 'direct' !== $access_type ) { return false; } diff --git a/includes/TaskManagers/PluginInstallTaskManager.php b/includes/TaskManagers/PluginInstallTaskManager.php index c03014281..f5e1d7e4f 100644 --- a/includes/TaskManagers/PluginInstallTaskManager.php +++ b/includes/TaskManagers/PluginInstallTaskManager.php @@ -12,16 +12,24 @@ */ class PluginInstallTaskManager { - /** - * The number of times a PluginInstallTask can be retried. - * - * @var int - */ + /** + * The number of times a PluginInstallTask can be retried. + * + * @var int + */ private static $retry_limit = 1; + /** + * The name of the queue, might be prefixed. + * + * @var string + */ private static $queue_name = 'plugin_install_queue'; - function __construct() { + /** + * Schedules the crons. + */ + public function __construct() { // Ensure there is a thirty second option in the cron schedules add_filter( 'cron_schedules', array( $this, 'add_thirty_seconds_schedule' ) ); @@ -34,10 +42,21 @@ function __construct() { } } + /** + * Returns the queue name, might be prefixed. + * + * @return string + */ public static function get_queue_name() { - return self::$queue_name; + return self::$queue_name; } + /** + * Adds a 30 second cron schedule. + * + * @param array $schedules The existing cron schedule. + * @return array + */ public function add_thirty_seconds_schedule( $schedules ) { if ( ! array_key_exists( 'thirty_seconds', $schedules ) || 30 !== $schedules['thirty_seconds']['interval'] ) { $schedules['thirty_seconds'] = array( @@ -46,9 +65,14 @@ public function add_thirty_seconds_schedule( $schedules ) { ); } - return $schedules; + return $schedules; } + /** + * Queues the initial list of Plugin Installs for a flow. + * + * @return boolean + */ public static function queue_initial_installs() { // Checks if the init_list of plugins have already been queued. @@ -57,7 +81,7 @@ public static function queue_initial_installs() { } // Set option to installing to prevent re-queueing the init_list again on page load. - \update_option( Options::get_option_name( 'plugins_init_status' ), 'installing' ); + \update_option( Options::get_option_name( 'plugins_init_status' ), 'installing' ); // Get the initial list of plugins to be installed based on the plan. $init_plugins = Plugins::get_init(); @@ -86,17 +110,19 @@ public static function queue_initial_installs() { */ public function install() { /* - Get the plugins queued up to be installed, the PluginInstall task gets - converted to an associative array before storing it in the option. */ + Get the plugins queued up to be installed, the PluginInstall task gets + converted to an associative array before storing it in the option. + */ $plugins = \get_option( Options::get_option_name( self::$queue_name ), array() ); /* - Conversion of the max heap to an array will always place the PluginInstallTask with the highest - priority at the beginning of the array */ + Conversion of the max heap to an array will always place the PluginInstallTask with the highest + priority at the beginning of the array + */ $plugin_to_install = array_shift( $plugins ); // Update the plugin install queue. - \update_option( Options::get_option_name( self::$queue_name ), $plugins ); + \update_option( Options::get_option_name( self::$queue_name ), $plugins ); // Recreate the PluginInstall task from the associative array. $plugin_install_task = new PluginInstallTask( @@ -113,15 +139,16 @@ public function install() { $status = $plugin_install_task->execute(); if ( \is_wp_error( $status ) ) { - // If there is an error, then increase the retry count for the task. - $plugin_install_task->increment_retries(); + // If there is an error, then increase the retry count for the task. + $plugin_install_task->increment_retries(); - // Get Latest Value of the install queue - $plugins = \get_option( Options::get_option_name( self::$queue_name ), array() ); + // Get Latest Value of the install queue + $plugins = \get_option( Options::get_option_name( self::$queue_name ), array() ); - /* - If the number of retries have not exceeded the limit - then re-queue the task at the end of the queue to be retried. */ + /* + If the number of retries have not exceeded the limit + then re-queue the task at the end of the queue to be retried. + */ if ( $plugin_install_task->get_retries() <= self::$retry_limit ) { array_push( $plugins, $plugin_install_task->to_array() ); @@ -139,30 +166,30 @@ public function install() { } /** - * @param PluginInstallTask $plugin_install_task - * * Adds a new PluginInstallTask to the Plugin Install queue. * The Task will be inserted at an appropriate position in the queue based on it's priority. * + * @param PluginInstallTask $plugin_install_task The task to be inserted. * @return array|false */ public static function add_to_queue( PluginInstallTask $plugin_install_task ) { /* - Get the plugins queued up to be installed, the PluginInstall task gets - converted to an associative array before storing it in the option. */ + Get the plugins queued up to be installed, the PluginInstall task gets + converted to an associative array before storing it in the option. + */ $plugins = \get_option( Options::get_option_name( self::$queue_name ), array() ); $queue = new PriorityQueue(); foreach ( $plugins as $queued_plugin ) { - /* - Check if there is an already existing PluginInstallTask in the queue - for a given slug and activation criteria. */ + Check if there is an already existing PluginInstallTask in the queue + for a given slug and activation criteria. + */ if ( $queued_plugin['slug'] === $plugin_install_task->get_slug() - && $queued_plugin['activate'] === $plugin_install_task->get_activate() ) { - return false; + && $queued_plugin['activate'] === $plugin_install_task->get_activate() ) { + return false; } - $queue->insert( $queued_plugin, $queued_plugin['priority'] ); + $queue->insert( $queued_plugin, $queued_plugin['priority'] ); } // Insert a new PluginInstallTask at the appropriate position in the queue. @@ -171,29 +198,53 @@ public static function add_to_queue( PluginInstallTask $plugin_install_task ) { $plugin_install_task->get_priority() ); - return \update_option( Options::get_option_name( self::$queue_name ), $queue->to_array() ); + return \update_option( Options::get_option_name( self::$queue_name ), $queue->to_array() ); } + /** + * Removes a PluginInstallTask from the queue. + * + * @param string $plugin The slug of the task to remove. + * @return array + */ public static function remove_from_queue( $plugin ) { /* - Get the plugins queued up to be installed, the PluginInstall task gets - converted to an associative array before storing it in the option. */ + Get the plugins queued up to be installed, the PluginInstall task gets + converted to an associative array before storing it in the option. + */ $plugins = \get_option( Options::get_option_name( self::$queue_name ), array() ); $queue = new PriorityQueue(); foreach ( $plugins as $queued_plugin ) { /* - If the Plugin slug does not match add it back to the queue. */ + If the Plugin slug does not match add it back to the queue. + */ if ( $queued_plugin['slug'] !== $plugin ) { - $queue->insert( $queued_plugin, $queued_plugin['priority'] ); + $queue->insert( $queued_plugin, $queued_plugin['priority'] ); } } - return \update_option( Options::get_option_name( self::$queue_name ), $queue->to_array() ); + return \update_option( Options::get_option_name( self::$queue_name ), $queue->to_array() ); } + /** + * Get the status of a given plugin slug from the queue. + * + * @param string $plugin The slug of the plugin. + * @return boolean + */ public static function status( $plugin ) { $plugins = \get_option( Options::get_option_name( self::$queue_name ), array() ); - return array_search( $plugin, array_column( $plugins, 'slug' ) ); + return array_search( $plugin, array_column( $plugins, 'slug' ), true ); + } + + /** + * Reset the Plugin install status and the queue. + * + * @return void + */ + public static function reset_install_status() { + \delete_option( Options::get_option_name( 'plugins_init_status' ) ); + \delete_option( Options::get_option_name( 'plugin_install_queue' ) ); } } diff --git a/src/OnboardingSPA/components/Content/index.js b/src/OnboardingSPA/components/Content/index.js index f111868d9..46a050b79 100644 --- a/src/OnboardingSPA/components/Content/index.js +++ b/src/OnboardingSPA/components/Content/index.js @@ -3,6 +3,7 @@ import { Fragment, memo, Suspense, useCallback } from '@wordpress/element'; import { store as nfdOnboardingStore } from '../../store'; import { useSelect } from '@wordpress/data'; +import FlowStateHandler from '../StateHandlers/Flow'; /** * Primary content area within the . @@ -17,24 +18,23 @@ const Content = () => { }; } ); - const getMappedPages = useCallback( - ( routes ) => { - return routes?.map( ( route ) => ( - } - /> - ) ); - }, - [ routes ] - ); + const getMappedPages = useCallback( () => { + return routes?.map( ( route ) => ( + } + /> + ) ); + }, [ routes ] ); return (
}> - { getMappedPages( routes ) } + + { getMappedPages( routes ) } +
); diff --git a/src/OnboardingSPA/components/Loaders/Step/Ecommerce/contents.js b/src/OnboardingSPA/components/Loaders/Step/Ecommerce/contents.js new file mode 100644 index 000000000..a9feb7449 --- /dev/null +++ b/src/OnboardingSPA/components/Loaders/Step/Ecommerce/contents.js @@ -0,0 +1,22 @@ +import { __, sprintf } from '@wordpress/i18n'; +import { translations } from '../../../../utils/locales/translations'; + +const getContents = ( brandName ) => { + return { + title: sprintf( + /* translators: 1: Brand 2: Site */ + __( + 'Making the keys to your %1$s Online %2$s', + 'wp-module-onboarding' + ), + brandName, + translations( 'Site' ) + ), + subtitle: __( + 'We’re installing WooCommerce for you to fill with your amazing products & services!', + 'wp-module-onboarding' + ), + }; +}; + +export default getContents; diff --git a/src/OnboardingSPA/components/Loaders/Step/Ecommerce/index.js b/src/OnboardingSPA/components/Loaders/Step/Ecommerce/index.js new file mode 100644 index 000000000..71a7dd320 --- /dev/null +++ b/src/OnboardingSPA/components/Loaders/Step/Ecommerce/index.js @@ -0,0 +1,19 @@ +import StepLoader from '..'; +import getContents from './contents'; +import { useSelect } from '@wordpress/data'; + +import { store as nfdOnboardingStore } from '../../../../store'; + +const EcommerceStepLoader = () => { + const { brandName } = useSelect( ( select ) => { + return { + brandName: select( nfdOnboardingStore ).getNewfoldBrandName(), + }; + }, [] ); + const contents = getContents( brandName ); + return ( + + ); +}; + +export default EcommerceStepLoader; diff --git a/src/OnboardingSPA/components/Sidebar/components/LearnMore/Menu.js b/src/OnboardingSPA/components/Sidebar/components/LearnMore/Menu.js index f95b9b86f..355a41cc1 100644 --- a/src/OnboardingSPA/components/Sidebar/components/LearnMore/Menu.js +++ b/src/OnboardingSPA/components/Sidebar/components/LearnMore/Menu.js @@ -33,7 +33,7 @@ const LearnMoreMenu = () => { return ( <> - { currentStep?.sidebars?.LearnMore && ( + { sideBarView && currentStep?.sidebars?.LearnMore && ( diff --git a/src/OnboardingSPA/components/SkipButton/index.js b/src/OnboardingSPA/components/SkipButton/index.js index 96a475971..d47cb3636 100644 --- a/src/OnboardingSPA/components/SkipButton/index.js +++ b/src/OnboardingSPA/components/SkipButton/index.js @@ -9,12 +9,7 @@ import { store as nfdOnboardingStore } from '../../store'; import { getSettings, setSettings } from '../../utils/api/settings'; import { wpAdminPage, pluginDashboardPage } from '../../../constants'; -/** - * Interface Text Inputs with standard design. - * - * @return {WPComponent} SkipButton Component - */ -const SkipButton = () => { +const SkipButton = ( { callback = false } ) => { const navigate = useNavigate(); const location = useLocation(); const { nextStep, currentData, socialData } = useSelect( ( select ) => { @@ -75,7 +70,12 @@ const SkipButton = () => { return ( diff --git a/src/OnboardingSPA/components/StateHandlers/Ecommerce/contents.js b/src/OnboardingSPA/components/StateHandlers/Ecommerce/contents.js index 6090ca529..e9d1b5707 100644 --- a/src/OnboardingSPA/components/StateHandlers/Ecommerce/contents.js +++ b/src/OnboardingSPA/components/StateHandlers/Ecommerce/contents.js @@ -3,26 +3,11 @@ import { translations } from '../../../utils/locales/translations'; const getContents = ( brandName ) => { return { - loader: { - title: sprintf( - /* translators: 1: Brand 2: Site */ - __( - 'Making the keys to your %s Online %s', - 'wp-module-onboarding' - ), - brandName, - translations( 'Site' ) - ), - subtitle: __( - 'We’re installing WooCommerce for you to fill with your amazing products & services!', - 'wp-module-onboarding' - ), - }, errorState: { title: sprintf( /* translators: 1: Brand 2: Site */ __( - 'Making the keys to your %s Online %s', + 'Making the keys to your %1$s Online %2$s', 'wp-module-onboarding' ), brandName, diff --git a/src/OnboardingSPA/components/StateHandlers/Ecommerce/index.js b/src/OnboardingSPA/components/StateHandlers/Ecommerce/index.js index bf3d7af1a..af582f672 100644 --- a/src/OnboardingSPA/components/StateHandlers/Ecommerce/index.js +++ b/src/OnboardingSPA/components/StateHandlers/Ecommerce/index.js @@ -2,7 +2,6 @@ import { useViewportMatch } from '@wordpress/compose'; import { useSelect, useDispatch } from '@wordpress/data'; import { useEffect, useState } from '@wordpress/element'; -import { StepLoader } from '../../Loaders'; import { store as nfdOnboardingStore } from '../../../store'; import { getPluginStatus } from '../../../utils/api/plugins'; import { @@ -15,6 +14,7 @@ import { } from '../../../../constants'; import { StepErrorState } from '../../ErrorState'; import getContents from './contents'; +import EcommerceStepLoader from '../../Loaders/Step/Ecommerce'; const EcommerceStateHandler = ( { children, @@ -110,8 +110,7 @@ const EcommerceStateHandler = ( { window.location.reload(); break; default: - pluginsStatus[ ECOMMERCE_STEPS_PLUGIN ] = - pluginStatus; + pluginsStatus[ ECOMMERCE_STEPS_PLUGIN ] = pluginStatus; setWoocommerceStatus( pluginStatus ); updatePluginsStatus( pluginsStatus ); } @@ -139,12 +138,7 @@ const EcommerceStateHandler = ( { case PLUGIN_STATUS_ACTIVE: return children; default: - return ( - - ); + return ; } }; diff --git a/src/OnboardingSPA/components/StateHandlers/Flow/index.js b/src/OnboardingSPA/components/StateHandlers/Flow/index.js new file mode 100644 index 000000000..d562e3845 --- /dev/null +++ b/src/OnboardingSPA/components/StateHandlers/Flow/index.js @@ -0,0 +1,93 @@ +import { useEffect, useState } from '@wordpress/element'; +import { useLocation } from 'react-router-dom'; +import { useSelect, useDispatch } from '@wordpress/data'; + +import { store as nfdOnboardingStore } from '../../../store'; +import { getFirstEcommerceStep } from '../../../data/routes/ecommerce-flow'; +import EcommerceStepLoader from '../../Loaders/Step/Ecommerce'; +import { switchFlow } from '../../../utils/api/flow'; +import { removeQueryParam } from '../../../utils'; +import { MAX_RETRIES_FLOW_SWITCH } from '../../../../constants'; +import { getFragment } from '@wordpress/url'; + +const FlowStateHandler = ( { children } ) => { + const location = useLocation(); + const [ newFlow, setNewFlow ] = useState( false ); + + const { brandConfig } = useSelect( ( select ) => { + return { + brandName: select( nfdOnboardingStore ).getNewfoldBrandName(), + brandConfig: select( nfdOnboardingStore ).getNewfoldBrandConfig(), + }; + }, [] ); + + const { + setIsDrawerOpened, + setIsDrawerSuppressed, + setIsHeaderNavigationEnabled, + setSidebarActiveView, + } = useDispatch( nfdOnboardingStore ); + + const disableNavigation = () => { + setIsDrawerOpened( false ); + setIsDrawerSuppressed( true ); + setIsHeaderNavigationEnabled( false ); + setSidebarActiveView( false ); + }; + + const handleCommerceFlow = async ( flow, retries = 0 ) => { + if ( retries >= MAX_RETRIES_FLOW_SWITCH ) { + return setNewFlow( false ); + } + const response = await switchFlow( flow ); + if ( response?.error ) { + retries = retries + 1; + return handleCommerceFlow( flow, retries ); + } + const firstEcommerceStep = getFirstEcommerceStep(); + const fragment = getFragment( window.location.href ); + const redirect = removeQueryParam( + window.location.href, + 'flow' + ).replace( fragment, '' ); + window.location.replace( `${ redirect }#${ firstEcommerceStep.path }` ); + window.location.reload(); + }; + + const switchToNewFlow = async ( flow ) => { + const enabledFlows = brandConfig?.enabled_flows ?? {}; + if ( ! ( flow in enabledFlows ) || enabledFlows[ flow ] !== true ) { + return setNewFlow( false ); + } + + switch ( flow ) { + case 'ecommerce': + handleCommerceFlow( flow ); + break; + default: + setNewFlow( false ); + } + }; + + useEffect( () => { + if ( window.nfdOnboarding?.newFlow ) { + const flow = window.nfdOnboarding.newFlow; + disableNavigation(); + setNewFlow( flow ); + switchToNewFlow( flow ); + window.nfdOnboarding.newFlow = undefined; + } + }, [ location.pathname ] ); + + const handleRender = () => { + switch ( newFlow ) { + case 'ecommerce': + return ; + default: + return children; + } + }; + return <>{ handleRender() }; +}; + +export default FlowStateHandler; diff --git a/src/OnboardingSPA/data/routes/ecommerce-flow.js b/src/OnboardingSPA/data/routes/ecommerce-flow.js index 014c2cd85..1a23100a9 100644 --- a/src/OnboardingSPA/data/routes/ecommerce-flow.js +++ b/src/OnboardingSPA/data/routes/ecommerce-flow.js @@ -1,6 +1,7 @@ import { __ } from '@wordpress/i18n'; import { store, institution, shipping } from '@wordpress/icons'; import { lazy } from '@wordpress/element'; +// eslint-disable-next-line import/no-extraneous-dependencies import { orderBy, filter } from 'lodash'; import { @@ -37,11 +38,11 @@ export const ecommerceSteps = [ title: __( 'Street Address', 'wp-module-onboarding' ), heading: __( 'Street Address', 'wp-module-onboarding' ), subheading: __( - 'In this step you confirm the business address of your store. Simply confirm the one you provided during your initial Bluehost account setup or provide a new one.', + 'In this step you confirm the business address of your store. Simply confirm the one you provided during your initial Bluehost account setup or provide a new one.', 'wp-module-onboarding' ), description: __( - 'In this step you confirm the business address of your store. Simply confirm the one you provided during your initial Bluehost account setup or provide a new one.', + 'In this step you confirm the business address of your store. Simply confirm the one you provided during your initial Bluehost account setup or provide a new one.', 'wp-module-onboarding' ), Component: StepAddress, @@ -156,3 +157,7 @@ export const ecommerceGetStartedSteps = () => { ( step ) => ! step.path.includes( '/step/get-started/site-primary' ) ); }; + +export const getFirstEcommerceStep = () => { + return ecommerceSteps[ 0 ]; +}; diff --git a/src/OnboardingSPA/pages/Steps/TopPriority/index.js b/src/OnboardingSPA/pages/Steps/TopPriority/index.js index 186f01b1b..ffeb3ce32 100644 --- a/src/OnboardingSPA/pages/Steps/TopPriority/index.js +++ b/src/OnboardingSPA/pages/Steps/TopPriority/index.js @@ -2,7 +2,7 @@ import { __ } from '@wordpress/i18n'; import { useViewportMatch } from '@wordpress/compose'; import { useEffect, useState } from '@wordpress/element'; import { useDispatch, useSelect } from '@wordpress/data'; -import { SIDEBAR_LEARN_MORE, VIEW_NAV_PRIMARY } from '../../../../constants'; +import { VIEW_NAV_PRIMARY } from '../../../../constants'; import SkipButton from '../../../components/SkipButton'; import { store as nfdOnboardingStore } from '../../../store'; @@ -48,7 +48,7 @@ const StepTopPriority = () => { if ( isLargeViewport ) { setIsDrawerOpened( true ); } - setSidebarActiveView( SIDEBAR_LEARN_MORE ); + setSidebarActiveView( false ); setIsDrawerSuppressed( false ); setDrawerActiveView( VIEW_NAV_PRIMARY ); setIsHeaderNavigationEnabled( true ); @@ -58,8 +58,9 @@ const StepTopPriority = () => { async function setInitialData() { if ( currentData ) { const val = await currentData?.data.topPriority.priority1; - if ( val !== '' ) setSelected( parseInt( getKey( val ) ) ); - else { + if ( val !== '' ) { + setSelected( parseInt( getKey( val ) ) ); + } else { currentData.data.topPriority.priority1 = priorityTypes[ selected ]; setCurrentOnboardingData( currentData ); @@ -67,16 +68,34 @@ const StepTopPriority = () => { } setisLoaded( true ); } - if ( ! isLoaded ) setInitialData(); + if ( ! isLoaded ) { + setInitialData(); + } }, [ isLoaded ] ); + const handleSelling = () => { + if ( 'ecommerce' !== window.nfdOnboarding.currentFlow ) { + window.nfdOnboarding.newFlow = 'ecommerce'; + } + }; + useEffect( () => { - if ( isLoaded ) { - currentData.data.topPriority.priority1 = priorityTypes[ selected ]; - setCurrentOnboardingData( currentData ); + const selectedPriorityType = priorityTypes[ selected ]; + currentData.data.topPriority.priority1 = selectedPriorityType; + setCurrentOnboardingData( currentData ); + if ( 'selling' === selectedPriorityType ) { + handleSelling(); + } else { + window.nfdOnboarding.newFlow = undefined; } }, [ selected ] ); + const handleSkip = () => { + window.nfdOnboarding.newFlow = undefined; + currentData.data.topPriority.priority1 = priorityTypes[ 0 ]; + setCurrentOnboardingData( currentData ); + }; + const content = getContents(); return ( @@ -101,7 +120,7 @@ const StepTopPriority = () => { 'wp-module-onboarding' ) }

- + ); diff --git a/src/OnboardingSPA/utils/api/flow.js b/src/OnboardingSPA/utils/api/flow.js index 3610d0b5c..cf0366213 100644 --- a/src/OnboardingSPA/utils/api/flow.js +++ b/src/OnboardingSPA/utils/api/flow.js @@ -37,3 +37,15 @@ export async function completeFlow() { } ).then() ); } + +export async function switchFlow( flow ) { + return await resolve( + apiFetch( { + url: onboardingRestURL( 'flow/switch' ), + method: 'POST', + data: { + flow, + }, + } ).then() + ); +} diff --git a/src/OnboardingSPA/utils/index.js b/src/OnboardingSPA/utils/index.js index 6202713d3..5abb1f9f2 100644 --- a/src/OnboardingSPA/utils/index.js +++ b/src/OnboardingSPA/utils/index.js @@ -1,18 +1,13 @@ -import { findIndex } from 'lodash'; - -export const insertBeforeStep = (steps, path, newStep) => {}; - -export const insertAfterStep = (steps, path, newStep) => {}; - -export const insertStepAtIndex = (steps, index, newStep) => {}; - -export const findStepIndex = ( steps, path ) => { - const index = findIndex( steps, { path } ); - - return -1 !== index ? index : false; -}; +import { removeQueryArgs, hasQueryArg } from '@wordpress/url'; export const getQueryParam = ( paramName ) => { - const urlParams = new URLSearchParams( location.search ); + const urlParams = new URLSearchParams( window.location.search ); return urlParams.get( paramName ); }; + +export const removeQueryParam = ( url, paramName ) => { + if ( hasQueryArg( url, paramName ) ) { + return removeQueryArgs( url, paramName ); + } + return url; +}; diff --git a/src/constants.js b/src/constants.js index 0d74c016b..eed2f4c2b 100644 --- a/src/constants.js +++ b/src/constants.js @@ -32,6 +32,7 @@ export const SIDEBAR_LEARN_MORE = 'LearnMore'; export const MAX_RETRIES_API_QUEUER = 2; export const MAX_RETRIES_SETTINGS_INIT = 2; +export const MAX_RETRIES_FLOW_SWITCH = 2; export const NFD_PLUGINS_QUERY_PARAM = 'nfd_plugins'; export const NFD_THEMES_QUERY_PARAM = 'nfd_themes';