diff --git a/includes/Data/Options.php b/includes/Data/Options.php
index 9cb9abe37..a19fb9fc6 100644
--- a/includes/Data/Options.php
+++ b/includes/Data/Options.php
@@ -30,6 +30,7 @@ final class Options {
'settings_initialized' => 'settings_initialized',
'plugins_init_status' => 'plugins_init_status',
'plugin_install_queue' => 'plugin_install_queue',
+ 'plugin_uninstall_queue' => 'plugin_uninstall_queue',
'flow' => 'flow',
'theme_init_status' => 'theme_init_status',
'theme_install_queue' => 'theme_install_queue',
diff --git a/includes/Data/Plugins.php b/includes/Data/Plugins.php
index 1cbe923e9..711c1a4eb 100644
--- a/includes/Data/Plugins.php
+++ b/includes/Data/Plugins.php
@@ -41,6 +41,10 @@ final class Plugins {
'approved' => true,
'path' => 'yith-woocommerce-ajax-search/init.php',
),
+ 'creative-mail-by-constant-contact' => array(
+ 'approved' => true,
+ 'path' => 'creative-mail-by-constant-contact/creative-mail-plugin.php',
+ ),
);
/**
@@ -229,6 +233,18 @@ public static function get() {
);
}
+ /**
+ * Use this for finding the path for installed plugins.
+ *
+ * @return array
+ */
+ public static function get_squashed() {
+ return array_merge(
+ array_filter( self::$wp_slugs, array( __CLASS__, 'check_approved' ) ) ,
+ array_filter( self::$nfd_slugs, array( __CLASS__, 'check_approved' ) ) ,
+ );
+ }
+
/**
* Get approved slugs/urls/domains
*
diff --git a/includes/Data/SiteFeatures.php b/includes/Data/SiteFeatures.php
new file mode 100644
index 000000000..b16d2fc1c
--- /dev/null
+++ b/includes/Data/SiteFeatures.php
@@ -0,0 +1,115 @@
+ array(),
+ 'ecommerce' => array(
+ 'jetpack' => array(
+ 'slug' => 'jetpack',
+ 'icon' => '--site-features-security',
+ 'title' => 'Security, Speed & Growth',
+ 'subtitle' => 'Powered by Jetpack',
+ 'desc' => 'Jetpack',
+ 'selected' => false,
+ ),
+ 'wpforms-lite' => array(
+ 'slug' => 'wpforms-lite',
+ 'icon' => '--site-features-form',
+ 'title' => 'Forms',
+ 'subtitle' => 'Powered by WP Forms',
+ 'desc' => 'WP Forms',
+ 'selected' => false,
+ ),
+ 'google-analytics-for-wordpress' => array(
+ 'slug' => 'google-analytics-for-wordpress',
+ 'icon' => '--site-features-analytics',
+ 'title' => 'Site Traffic',
+ 'subtitle' => 'Powered by MonsterInsights',
+ 'desc' => 'MonsterInsights',
+ 'selected' => false,
+ ),
+ 'wordpress-seo' => array(
+ 'slug' => 'wordpress-seo',
+ 'icon' => '--site-features-share',
+ 'title' => 'Search Engine Optimization',
+ 'subtitle' => 'Powered by Yoast',
+ 'desc' => 'Yoast',
+ 'selected' => false,
+ ),
+ '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' => 'Creative Email',
+ 'selected' => false,
+ ),
+ 'yith-woocommerce-ajax-search' => array(
+ 'slug' => 'yith-woocommerce-ajax-search',
+ 'icon' => '--site-features-search',
+ 'title' => 'Enhanced Product Search',
+ 'subtitle' => 'Powered by YITH',
+ 'desc' => 'YITH',
+ 'selected' => false,
+ ),
+ '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' => 'YITH',
+ 'selected' => false,
+ ),
+ 'nfd_slug_yith_woocommerce_booking' => array(
+ 'slug' => 'nfd_slug_yith_woocommerce_booking',
+ 'icon' => '--site-features-bookingcalendar',
+ 'title' => 'Bookings & Appointments',
+ 'subtitle' => 'Powered by YITH',
+ 'desc' => 'YITH',
+ 'selected' => false,
+ ),
+ 'nfd_slug_yith_woocommerce_wishlist' => array(
+ 'slug' => 'nfd_slug_yith_woocommerce_wishlist',
+ 'icon' => '--site-features-wishlist',
+ 'title' => 'Product Wishlists',
+ 'subtitle' => 'Powered by YITH',
+ 'desc' => 'YITH',
+ 'selected' => false,
+ ),
+ 'optinmonster' => array(
+ 'slug' => 'optinmonster',
+ 'icon' => '--site-features-lead',
+ 'title' => 'Lead Generation',
+ 'subtitle' => 'Powered by Optin Monster',
+ 'desc' => 'Optin Monster',
+ 'selected' => false,
+ ),
+ ),
+ );
+
+ public static function mark_initial_plugins() {
+ $flow = Data::current_flow();
+ $installed_plugins = Plugins::get_init();
+
+ // Get a Copy of the list for alteration
+ $site_features_marked = self::$site_features[ $flow ];
+
+ foreach ( $installed_plugins as $installed_plugin ) {
+ if ( isset( $site_features_marked[ $installed_plugin['slug'] ] ) ) {
+ $site_features_marked[ $installed_plugin['slug'] ]['selected'] = true;
+ }
+ }
+
+ return $site_features_marked;
+ }
+
+ public static function get() {
+ return self::mark_initial_plugins();
+ }
+
+}
diff --git a/includes/RestApi/PluginsController.php b/includes/RestApi/PluginsController.php
index b2a9197f3..039c4dfb8 100644
--- a/includes/RestApi/PluginsController.php
+++ b/includes/RestApi/PluginsController.php
@@ -3,10 +3,12 @@
use NewfoldLabs\WP\Module\Onboarding\Permissions;
use NewfoldLabs\WP\Module\Onboarding\Data\Plugins;
-use NewfoldLabs\WP\Module\Onboarding\Data\Options;
+use NewfoldLabs\WP\Module\Onboarding\Data\SiteFeatures;
use NewfoldLabs\WP\Module\Onboarding\Services\PluginInstaller;
use NewfoldLabs\WP\Module\Onboarding\Tasks\PluginInstallTask;
use NewfoldLabs\WP\Module\Onboarding\TaskManagers\PluginInstallTaskManager;
+use NewfoldLabs\WP\Module\Onboarding\Tasks\PluginUninstallTask;
+use NewfoldLabs\WP\Module\Onboarding\TaskManagers\PluginUninstallTaskManager;
/**
* Class PluginsController
@@ -71,13 +73,31 @@ public function register_routes() {
$this->rest_base . '/status',
array(
array(
- 'methods' => \WP_REST_Server::READABLE,
- 'callback' => array( $this, 'get_status' ),
- 'args' => $this->get_status_args(),
+ 'methods' => \WP_REST_Server::READABLE,
+ 'callback' => array( $this, 'get_status' ),
+ 'args' => $this->get_status_args(),
'permission_callback' => array( Permissions::class, 'rest_is_authorized_admin' ),
),
)
);
+
+ \register_rest_route(
+ $this->namespace,
+ $this->rest_base . '/site-features',
+ array(
+ array(
+ 'methods' => \WP_REST_Server::READABLE,
+ 'callback' => array( $this, 'get_site_features' ),
+ 'permission_callback' => array( Permissions::class, 'rest_is_authorized_admin' ),
+ ),
+ array(
+ 'methods' => \WP_REST_Server::CREATABLE,
+ 'callback' => array( $this, 'set_site_features' ),
+ 'args' => $this->set_site_features_args(),
+ 'permission_callback' => array( $this, 'check_install_permissions' ),
+ ),
+ )
+ );
}
/**
@@ -132,6 +152,15 @@ public function get_status_args() {
);
}
+ public function set_site_features_args() {
+ return array(
+ 'plugins' => array(
+ 'type' => 'object',
+ 'required' => true,
+ ),
+ );
+ }
+
/**
* Verify caller has permissions to install plugins.
*
@@ -242,4 +271,57 @@ public function get_status( \WP_REST_Request $request ) {
);
}
+
+ /**
+ * Retrieves the Customized list of Plugins for the user.
+ *
+ * @return array|\WP_Error
+ */
+ public function get_site_features() {
+ return SiteFeatures::get();
+ }
+
+ /**
+ * Installs/Uninstalls the requested plugins.
+ *
+ * @param \WP_REST_Request $request
+ *
+ * @return \WP_REST_Response|\WP_Error
+ */
+ public function set_site_features( \WP_REST_Request $request ) {
+
+ $plugin_body = json_decode( $request->get_body(), true );
+ $plugins = isset( $plugin_body['plugins'] ) ? $plugin_body['plugins'] : false;
+
+ if ( ! $plugins ) {
+ return new \WP_Error(
+ 'plugin_list_not_provided',
+ 'Plugins List Not Provided',
+ array( 'status' => 404 )
+ );
+ }
+
+ foreach ( $plugins as $plugin => $decision ) {
+ if ( $decision ) {
+ PluginInstallTaskManager::add_to_queue(
+ new PluginInstallTask(
+ $plugin,
+ true,
+ )
+ );
+ } else {
+ PluginUninstallTaskManager::add_to_queue(
+ new PluginUninstallTask(
+ $plugin,
+ )
+ );
+ }
+ }
+
+ return new \WP_REST_Response(
+ array(),
+ 202
+ );
+ }
}
+
diff --git a/includes/Services/PluginUninstaller.php b/includes/Services/PluginUninstaller.php
new file mode 100644
index 000000000..9b5c4406e
--- /dev/null
+++ b/includes/Services/PluginUninstaller.php
@@ -0,0 +1,158 @@
+ 500 )
+ );
+ }
+
+ // Removes directory and files of a plugin
+ $deleted = \delete_plugins( array( $plugin_path ) );
+ if ( ! $deleted || is_wp_error( $deleted ) ) {
+ return new \WP_Error(
+ 'nfd_onboarding_error',
+ 'Unable to Delete the Plugin',
+ array( 'status' => 500 )
+ );
+ }
+
+ return true;
+ }
+
+}
diff --git a/includes/TaskManagers/PluginInstallTaskManager.php b/includes/TaskManagers/PluginInstallTaskManager.php
index 283c191b6..c03014281 100644
--- a/includes/TaskManagers/PluginInstallTaskManager.php
+++ b/includes/TaskManagers/PluginInstallTaskManager.php
@@ -95,6 +95,9 @@ public function install() {
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 );
+
// Recreate the PluginInstall task from the associative array.
$plugin_install_task = new PluginInstallTask(
$plugin_to_install['slug'],
@@ -113,21 +116,26 @@ public function install() {
// 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() );
+
/*
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() );
+ array_push( $plugins, $plugin_install_task->to_array() );
+
+ // Update the plugin install queue.
+ \update_option( Options::get_option_name( self::$queue_name ), $plugins );
}
}
// If there are no more plugins to be installed then change the status to completed.
if ( empty( $plugins ) ) {
- \update_option( Options::get_option_name( 'plugins_init_status' ), 'completed' );
+ return \update_option( Options::get_option_name( 'plugins_init_status' ), 'completed' );
}
- // Update the plugin install queue.
- return \update_option( Options::get_option_name( self::$queue_name ), $plugins );
+ return true;
}
/**
@@ -166,6 +174,24 @@ public static function add_to_queue( PluginInstallTask $plugin_install_task ) {
return \update_option( Options::get_option_name( self::$queue_name ), $queue->to_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. */
+ $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 ( $queued_plugin['slug'] !== $plugin ) {
+ $queue->insert( $queued_plugin, $queued_plugin['priority'] );
+ }
+ }
+
+ return \update_option( Options::get_option_name( self::$queue_name ), $queue->to_array() );
+ }
+
public static function status( $plugin ) {
$plugins = \get_option( Options::get_option_name( self::$queue_name ), array() );
return array_search( $plugin, array_column( $plugins, 'slug' ) );
diff --git a/includes/TaskManagers/PluginUninstallTaskManager.php b/includes/TaskManagers/PluginUninstallTaskManager.php
new file mode 100644
index 000000000..74ea563fd
--- /dev/null
+++ b/includes/TaskManagers/PluginUninstallTaskManager.php
@@ -0,0 +1,137 @@
+ 10,
+ 'display' => __( 'Once Every Ten Seconds' ),
+ );
+ }
+
+ return $schedules;
+ }
+
+ /**
+
+ * Queue out a PluginUninstallTask with the highest priority in the plugin uninstall queue and execute it.
+ *
+ * @return array|false
+ */
+ public function uninstall() {
+ /*
+ Get the plugins queued up to be uninstalled, the PluginUninstall 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 PluginUninstallTask with the highest
+ priority at the beginning of the array */
+ $plugin_to_uninstall = array_shift( $plugins );
+
+ // Update the plugin uninstall queue.
+ \update_option( Options::get_option_name( self::$queue_name ), $plugins );
+
+ // Recreate the PluginInstall task from the associative array.
+ $plugin_uninstall_task = new PluginUninstallTask(
+ $plugin_to_uninstall['slug'],
+ $plugin_to_uninstall['priority'],
+ $plugin_to_uninstall['retries']
+ );
+
+ // Execute the PluginUninstall Task.
+ $status = $plugin_uninstall_task->execute();
+ if ( \is_wp_error( $status ) ) {
+
+ // If there is an error, then increase the retry count for the task.
+ $plugin_uninstall_task->increment_retries();
+
+ /*
+ 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_uninstall_task->get_retries() <= self::$retry_limit ) {
+ array_push( $plugins, $plugin_uninstall_task->to_array() );
+
+ // Update the plugin install queue.
+ \update_option( Options::get_option_name( self::$queue_name ), $plugins );
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @param PluginUninstallTask $plugin_uninstall_task
+ *
+ * Adds a new PluginUninstallTask to the Plugin Uninstall queue.
+ * The Task will be inserted at an appropriate position in the queue based on it's priority.
+ *
+ * @return array|false
+ */
+ public static function add_to_queue( PluginUninstallTask $plugin_uninstall_task ) {
+ /*
+ Get the plugins queued up to be uninstalled, the PluginUninstall 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 PluginUninstallTask in the queue
+ for a given slug. */
+ if ( $queued_plugin['slug'] === $plugin_uninstall_task->get_slug() ) {
+ return false;
+ }
+ $queue->insert( $queued_plugin, $queued_plugin['priority'] );
+ }
+
+ // Insert a new PluginUninstallTask at the appropriate position in the queue.
+ $queue->insert(
+ $plugin_uninstall_task->to_array(),
+ $plugin_uninstall_task->get_priority()
+ );
+
+ return \update_option( Options::get_option_name( self::$queue_name ), $queue->to_array() );
+ }
+
+ public static function status( $plugin ) {
+ $plugins = \get_option( Options::get_option_name( self::$queue_name ), array() );
+ return array_search( $plugin, array_column( $plugins, 'slug' ) );
+ }
+}
diff --git a/includes/TaskManagers/TaskManager.php b/includes/TaskManagers/TaskManager.php
index 5b394a941..e4b816b6c 100644
--- a/includes/TaskManagers/TaskManager.php
+++ b/includes/TaskManagers/TaskManager.php
@@ -7,6 +7,7 @@ final class TaskManager {
protected $task_managers = array(
'NewfoldLabs\\WP\Module\\Onboarding\\TaskManagers\\PluginInstallTaskManager',
+ 'NewfoldLabs\\WP\Module\\Onboarding\\TaskManagers\\PluginUninstallTaskManager',
'NewfoldLabs\\WP\Module\\Onboarding\\TaskManagers\\ThemeInstallTaskManager',
);
diff --git a/includes/Tasks/PluginUninstallTask.php b/includes/Tasks/PluginUninstallTask.php
new file mode 100644
index 000000000..a0780cdfb
--- /dev/null
+++ b/includes/Tasks/PluginUninstallTask.php
@@ -0,0 +1,62 @@
+slug = $slug;
+ $this->priority = $priority;
+ $this->retries = $retries;
+ }
+
+ public function get_slug() {
+ return $this->slug;
+ }
+
+ public function get_priority() {
+ return $this->priority;
+ }
+
+ public function get_retries() {
+ return $this->retries;
+ }
+
+ public function increment_retries() {
+ $this->retries++;
+ }
+
+ /**
+ * Uninstalls the Plugin using the PluginUninstaller Service.
+ *
+ * @return \WP_REST_Response|\WP_Error
+ */
+ public function execute() {
+ return PluginUninstaller::uninstall( $this->get_slug() );
+ }
+
+ /**
+ * Convert the PluginUninstallTask into an associative array.
+ *
+ * @return array
+ */
+ public function to_array() {
+ return array(
+ 'slug' => $this->slug,
+ 'priority' => $this->priority,
+ 'retries' => $this->retries,
+ );
+ }
+
+}
diff --git a/src/OnboardingSPA/components/CheckboxTemplate/CheckboxItem/index.js b/src/OnboardingSPA/components/CheckboxTemplate/CheckboxItem/index.js
new file mode 100644
index 000000000..7681fb09e
--- /dev/null
+++ b/src/OnboardingSPA/components/CheckboxTemplate/CheckboxItem/index.js
@@ -0,0 +1,111 @@
+import { useState } from '@wordpress/element';
+import { Icon, help, box } from '@wordpress/icons';
+
+import { CheckboxControl } from '@wordpress/components';
+
+/**
+ * Checkbox Item Component
+ * This returns a Single Element with a toggable description
+ *
+ * @param {string} icon - The icon name of the Item
+ * @param {string} title - The Main Title of the Item
+ * @param {string} subtitle - The Sub Title of the Item
+ * @param {string} desc - The Description of the Item
+ *
+ * @return CheckboxItem
+ */
+
+const CheckboxItem = ( {
+ name,
+ icon,
+ title,
+ desc,
+ subtitle,
+ callback,
+ isSelectedDefault,
+ className = 'checkbox-item',
+} ) => {
+ const [ showDescription, setShowDescription ] = useState( false );
+ const [ isSelected, setIsSelected ] = useState( isSelectedDefault );
+
+ const handleCheck = () => {
+ setIsSelected( ! isSelected );
+ callback( name, ! isSelected );
+ };
+
+ const handleShowDesc = () => {
+ setShowDescription( ! showDescription );
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ {title}
+
+
+ {subtitle}
+
+
+
+
+
+
+
+
+ {
+ showDescription && (
+
{desc}
+ )
+ }
+
+ );
+};
+
+export default CheckboxItem;
diff --git a/src/OnboardingSPA/components/CheckboxTemplate/CheckboxItem/stylesheet.scss b/src/OnboardingSPA/components/CheckboxTemplate/CheckboxItem/stylesheet.scss
new file mode 100644
index 000000000..54fdfd435
--- /dev/null
+++ b/src/OnboardingSPA/components/CheckboxTemplate/CheckboxItem/stylesheet.scss
@@ -0,0 +1,122 @@
+/*COLOR VARIABLES*/
+$white-offset: rgb(224, 224, 224);
+$main-color-dark: var(--wp-admin-theme-color);
+$main-color-light: var(--nfd-onboarding-white);
+$main-color: var(--nfd-onboarding-highlighted--rgb);
+$main-border-main: var(--nfd-onboarding-primary-alt);
+$box-shadow: var(--nfd-onboarding-light-gray-highlighted);
+
+.checkbox-item {
+ margin: 12px;
+ padding: 16px;
+ margin-top: 20px;
+ background: $main-color-light;
+ border: 1px solid $white-offset;
+ width: clamp(15rem, 25vw, 35rem);
+ box-shadow: 0px 2px 8px 2px rgba(204, 204, 204, 0.175295);
+
+ &-container{
+ display: flex;
+ align-items: center;
+ justify-content: flex-start;
+ }
+
+ &-checkbox{
+ padding: 6px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ &__contents{
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ &-icon{
+ width: 45px;
+ height: 45px;
+ display: flex;
+ margin-right: 16px;
+ border-radius: 50%;
+ align-items: center;
+ background: #F0F0F0;
+ justify-content: center;
+
+ &--selected {
+ background: $main-color-dark !important;
+ }
+
+ &--shown {
+ background: #F0F0F0;
+ }
+ }
+
+ &-text {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ justify-content: center;
+
+ &-title{
+ color: #0D0D0D;
+ line-height: 18px;
+ margin-bottom: 6px;
+ font-size: clamp(0.9rem, 2vw, 1rem);
+
+ &--selected {
+ color: $main-color-dark;
+ }
+ }
+
+ &-subtitle {
+ color: #343434;
+ font-weight: 200;
+ line-height: 18px;
+ font-size: clamp(0.82rem, 2vw, 0.9rem);
+ }
+ }
+
+ &-help {
+ cursor: pointer;
+ }
+
+ }
+
+ &--selected {
+ background: rgba($main-color, 0.2);
+ border: 1px solid rgba($main-color, 0.6);
+ box-shadow: 0px 2px 8px 2px rgba($white-offset, 0.8);
+ }
+
+ &--shown {
+ border-bottom: none;
+ background: $box-shadow;
+ border-radius: 2px 2px 0px 0px;
+ border-top: 1px solid rgba($main-color, 0.1);
+ border-left: 1px solid rgba($main-color, 0.1);
+ border-right: 1px solid rgba($main-color, 0.1);
+ }
+
+ &__desc {
+ z-index: 2;
+ padding: 16px;
+ border-top: none;
+ margin-left: 12px;
+ margin-top: -12px;
+ position: absolute;
+ font-style: italic;
+ background: $box-shadow;
+ border-radius: 0px 0px 2px 2px;
+ transform-origin: top center;
+ width: clamp(15rem, 25vw, 35rem);
+ font-size: clamp(0.82rem, 2vw, 0.9rem);
+ border-left: 1px solid rgba($main-color, 0.1);
+ border-right: 1px solid rgba($main-color, 0.1);
+ border-bottom: 1px solid rgba($main-color, 0.1);
+ animation: dropdown 400ms ease-in-out forwards;
+ box-shadow: 0px 11px 8px -3px rgba($main-color, 0.20);
+ }
+}
\ No newline at end of file
diff --git a/src/OnboardingSPA/components/CheckboxTemplate/CheckboxList/index.js b/src/OnboardingSPA/components/CheckboxTemplate/CheckboxList/index.js
new file mode 100644
index 000000000..51a8808e6
--- /dev/null
+++ b/src/OnboardingSPA/components/CheckboxTemplate/CheckboxList/index.js
@@ -0,0 +1,58 @@
+import { CheckboxItem } from '../index';
+
+/**
+ * Checkbox List Component
+ * This returns a List of Checkbox Items to be placed dynamically on screen
+ *
+ * @param customItemsList.callback
+ * @param {Object} customItemsList - The List to be shown with a Title, Subtitle and a Description
+ *
+ * @param customItemsList.selectedItems
+ * @param customItemsList.customItemsList
+ * @return CheckboxList
+ */
+const CheckboxList = ( { callback, selectedItems, customItemsList } ) => {
+
+ const length = Object.keys(customItemsList).length;
+
+ const buildCheckboxItems = () => {
+ var customItems = [];
+
+ for (const key in customItemsList) {
+ var item = customItemsList[key];
+ const isSelectedDefault = selectedItems[item.slug];
+ customItems.push(
+
+ );
+ }
+
+ return customItems;
+ };
+
+ return (
+
+
+ { buildCheckboxItems().slice(
+ 0,
+ Math.floor( length / 2 )
+ ) }
+
+
+ { buildCheckboxItems().slice(
+ Math.floor( length / 2 ),
+ length
+ ) }
+
+
+ );
+};
+
+export default CheckboxList;
diff --git a/src/OnboardingSPA/components/CheckboxTemplate/CheckboxList/stylesheet.scss b/src/OnboardingSPA/components/CheckboxTemplate/CheckboxList/stylesheet.scss
new file mode 100644
index 000000000..564491c4a
--- /dev/null
+++ b/src/OnboardingSPA/components/CheckboxTemplate/CheckboxList/stylesheet.scss
@@ -0,0 +1,16 @@
+.checkbox-list {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ @media (max-width: #{ ($break-xlarge) }) {
+ flex-direction: column;
+ }
+
+ &-col {
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+ justify-content: center;
+ }
+}
\ No newline at end of file
diff --git a/src/OnboardingSPA/components/CheckboxTemplate/index.js b/src/OnboardingSPA/components/CheckboxTemplate/index.js
new file mode 100644
index 000000000..024215455
--- /dev/null
+++ b/src/OnboardingSPA/components/CheckboxTemplate/index.js
@@ -0,0 +1,2 @@
+export { default as CheckboxItem } from './CheckboxItem';
+export { default as CheckboxList } from './CheckboxList';
diff --git a/src/OnboardingSPA/data/routes/default-flow.js b/src/OnboardingSPA/data/routes/default-flow.js
index c8f6bf744..e84456aa2 100644
--- a/src/OnboardingSPA/data/routes/default-flow.js
+++ b/src/OnboardingSPA/data/routes/default-flow.js
@@ -518,9 +518,12 @@ export const steps = [
{
path: '/wp-setup/step/site-features',
title: __( 'Features', 'wp-module-onboarding' ),
- heading: __( 'Our toolbox is your toolbox', 'wp-module-onboarding' ),
+ heading: __(
+ 'Key features to supercharge your site',
+ 'wp-module-onboarding'
+ ),
subheading: __(
- "We've learned a lot in 16 years of WordPress! Now that expertise is yours.",
+ 'Our toolbox of Plugins & Services is your toolbox.',
'wp-module-onboarding'
),
description: __(
diff --git a/src/OnboardingSPA/pages/Steps/SiteFeatures/index.js b/src/OnboardingSPA/pages/Steps/SiteFeatures/index.js
index 843fb52d5..6af7fc52f 100644
--- a/src/OnboardingSPA/pages/Steps/SiteFeatures/index.js
+++ b/src/OnboardingSPA/pages/Steps/SiteFeatures/index.js
@@ -1,21 +1,105 @@
+import { isEmpty } from 'lodash';
+import { useViewportMatch } from '@wordpress/compose';
+import { useEffect, useState } from '@wordpress/element';
+import { useSelect, useDispatch } from '@wordpress/data';
import { store as nfdOnboardingStore } from '../../../store';
-import { useDispatch } from '@wordpress/data';
-import { useEffect } from '@wordpress/element';
+import { VIEW_NAV_PRIMARY } from '../../../../constants';
import CommonLayout from '../../../components/Layouts/Common';
-import StepOverview from '../../../components/StepOverview';
+import { getSiteFeatures } from '../../../utils/api/plugins';
+import HeadingWithSubHeading from '../../../components/HeadingWithSubHeading';
+import CheckboxList from '../../../components/CheckboxTemplate/CheckboxList';
const StepSiteFeatures = () => {
- const { setIsSidebarOpened, setIsHeaderNavigationEnabled } = useDispatch( nfdOnboardingStore );
+ const isLargeViewport = useViewportMatch( 'medium' );
+
+ const [ isLoaded, setisLoaded ] = useState( false );
+ const [ selectedPlugins, setSelectedPlugins ] = useState();
+ const [ customPluginsList, setCustomPluginsList ] = useState();
+
+ const {
+ setIsDrawerOpened,
+ setDrawerActiveView,
+ setIsSidebarOpened,
+ setCurrentOnboardingData,
+ setIsDrawerSuppressed,
+ setIsHeaderNavigationEnabled,
+ } = useDispatch( nfdOnboardingStore );
+
+ const { currentStep, currentData } = useSelect( ( select ) => {
+ return {
+ currentStep: select( nfdOnboardingStore ).getCurrentStep(),
+ currentData:
+ select( nfdOnboardingStore ).getCurrentOnboardingData(),
+ };
+ }, [] );
+
+ async function selectPlugin( slug, choice ) {
+ const selectedPluginsCopy = { ...selectedPlugins };
+ selectedPluginsCopy[ slug ] = choice;
+ setSelectedPlugins( selectedPluginsCopy );
+
+ currentData.data.siteFeatures = { ...selectedPluginsCopy };
+ setCurrentOnboardingData( currentData );
+ }
+
+ async function changeToStoreSchema(
+ customPluginsList,
+ saveToStore = false
+ ) {
+ const selectedPlugins = {};
+
+ for (const key in customPluginsList) {
+ var plugin = customPluginsList[key];
+ selectedPlugins[plugin.slug] = plugin.selected;
+ }
+ setSelectedPlugins( selectedPlugins );
+
+ if ( saveToStore ) {
+ currentData.data.siteFeatures = { ...selectedPlugins };
+ setCurrentOnboardingData( currentData );
+ }
+ }
+
+ async function getCustomPlugins() {
+ const customPluginsList = await getSiteFeatures();
+ if ( isEmpty( currentData?.data?.siteFeatures ) )
+ changeToStoreSchema( customPluginsList.body, true );
+ else setSelectedPlugins( { ...currentData?.data?.siteFeatures } );
+
+ setCustomPluginsList( customPluginsList.body );
+ setisLoaded( true );
+ }
+
+ useEffect( () => {
+ if ( ! isLoaded ) {
+ getCustomPlugins();
+ }
+ }, [ isLoaded ] );
useEffect( () => {
+ if ( isLargeViewport ) {
+ setIsDrawerOpened( false );
+ }
setIsSidebarOpened( false );
+ setIsDrawerSuppressed( false );
+ setDrawerActiveView( VIEW_NAV_PRIMARY );
setIsHeaderNavigationEnabled( true );
}, [] );
return (
-
-
+
+
+ { customPluginsList && (
+
+ ) }
);
};
diff --git a/src/OnboardingSPA/static/icons/site-features/analytics.svg b/src/OnboardingSPA/static/icons/site-features/analytics.svg
new file mode 100644
index 000000000..303e3c4b7
--- /dev/null
+++ b/src/OnboardingSPA/static/icons/site-features/analytics.svg
@@ -0,0 +1,18 @@
+
diff --git a/src/OnboardingSPA/static/icons/site-features/analytics_light.svg b/src/OnboardingSPA/static/icons/site-features/analytics_light.svg
new file mode 100644
index 000000000..28b5002c6
--- /dev/null
+++ b/src/OnboardingSPA/static/icons/site-features/analytics_light.svg
@@ -0,0 +1,18 @@
+
diff --git a/src/OnboardingSPA/static/icons/site-features/bookingcalendar.svg b/src/OnboardingSPA/static/icons/site-features/bookingcalendar.svg
new file mode 100644
index 000000000..8c3b47bf9
--- /dev/null
+++ b/src/OnboardingSPA/static/icons/site-features/bookingcalendar.svg
@@ -0,0 +1,23 @@
+
diff --git a/src/OnboardingSPA/static/icons/site-features/bookingcalendar_light.svg b/src/OnboardingSPA/static/icons/site-features/bookingcalendar_light.svg
new file mode 100644
index 000000000..95cb715db
--- /dev/null
+++ b/src/OnboardingSPA/static/icons/site-features/bookingcalendar_light.svg
@@ -0,0 +1,23 @@
+
diff --git a/src/OnboardingSPA/static/icons/site-features/email.svg b/src/OnboardingSPA/static/icons/site-features/email.svg
new file mode 100644
index 000000000..4016aa347
--- /dev/null
+++ b/src/OnboardingSPA/static/icons/site-features/email.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/OnboardingSPA/static/icons/site-features/email_light.svg b/src/OnboardingSPA/static/icons/site-features/email_light.svg
new file mode 100644
index 000000000..7a7207337
--- /dev/null
+++ b/src/OnboardingSPA/static/icons/site-features/email_light.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/OnboardingSPA/static/icons/site-features/filter.svg b/src/OnboardingSPA/static/icons/site-features/filter.svg
new file mode 100644
index 000000000..0c91ce13f
--- /dev/null
+++ b/src/OnboardingSPA/static/icons/site-features/filter.svg
@@ -0,0 +1,11 @@
+
diff --git a/src/OnboardingSPA/static/icons/site-features/filter_light.svg b/src/OnboardingSPA/static/icons/site-features/filter_light.svg
new file mode 100644
index 000000000..9d6ba62ff
--- /dev/null
+++ b/src/OnboardingSPA/static/icons/site-features/filter_light.svg
@@ -0,0 +1,11 @@
+
diff --git a/src/OnboardingSPA/static/icons/site-features/form.svg b/src/OnboardingSPA/static/icons/site-features/form.svg
new file mode 100644
index 000000000..d0d897472
--- /dev/null
+++ b/src/OnboardingSPA/static/icons/site-features/form.svg
@@ -0,0 +1,18 @@
+
diff --git a/src/OnboardingSPA/static/icons/site-features/form_light.svg b/src/OnboardingSPA/static/icons/site-features/form_light.svg
new file mode 100644
index 000000000..dc81d3598
--- /dev/null
+++ b/src/OnboardingSPA/static/icons/site-features/form_light.svg
@@ -0,0 +1,18 @@
+
diff --git a/src/OnboardingSPA/static/icons/site-features/lead.svg b/src/OnboardingSPA/static/icons/site-features/lead.svg
new file mode 100644
index 000000000..ea59e7e8e
--- /dev/null
+++ b/src/OnboardingSPA/static/icons/site-features/lead.svg
@@ -0,0 +1,18 @@
+
diff --git a/src/OnboardingSPA/static/icons/site-features/lead_light.svg b/src/OnboardingSPA/static/icons/site-features/lead_light.svg
new file mode 100644
index 000000000..08e883fa8
--- /dev/null
+++ b/src/OnboardingSPA/static/icons/site-features/lead_light.svg
@@ -0,0 +1,18 @@
+
diff --git a/src/OnboardingSPA/static/icons/site-features/search.svg b/src/OnboardingSPA/static/icons/site-features/search.svg
new file mode 100644
index 000000000..220fb5167
--- /dev/null
+++ b/src/OnboardingSPA/static/icons/site-features/search.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/OnboardingSPA/static/icons/site-features/search_light.svg b/src/OnboardingSPA/static/icons/site-features/search_light.svg
new file mode 100644
index 000000000..69ccf0539
--- /dev/null
+++ b/src/OnboardingSPA/static/icons/site-features/search_light.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/OnboardingSPA/static/icons/site-features/security.svg b/src/OnboardingSPA/static/icons/site-features/security.svg
new file mode 100644
index 000000000..089ff3def
--- /dev/null
+++ b/src/OnboardingSPA/static/icons/site-features/security.svg
@@ -0,0 +1,18 @@
+
diff --git a/src/OnboardingSPA/static/icons/site-features/security_light.svg b/src/OnboardingSPA/static/icons/site-features/security_light.svg
new file mode 100644
index 000000000..5f028d1f4
--- /dev/null
+++ b/src/OnboardingSPA/static/icons/site-features/security_light.svg
@@ -0,0 +1,18 @@
+
diff --git a/src/OnboardingSPA/static/icons/site-features/share.svg b/src/OnboardingSPA/static/icons/site-features/share.svg
new file mode 100644
index 000000000..746564332
--- /dev/null
+++ b/src/OnboardingSPA/static/icons/site-features/share.svg
@@ -0,0 +1,18 @@
+
diff --git a/src/OnboardingSPA/static/icons/site-features/share_light.svg b/src/OnboardingSPA/static/icons/site-features/share_light.svg
new file mode 100644
index 000000000..65252b90a
--- /dev/null
+++ b/src/OnboardingSPA/static/icons/site-features/share_light.svg
@@ -0,0 +1,18 @@
+
diff --git a/src/OnboardingSPA/static/icons/site-features/wishlist.svg b/src/OnboardingSPA/static/icons/site-features/wishlist.svg
new file mode 100644
index 000000000..58d756018
--- /dev/null
+++ b/src/OnboardingSPA/static/icons/site-features/wishlist.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/OnboardingSPA/static/icons/site-features/wishlist_light.svg b/src/OnboardingSPA/static/icons/site-features/wishlist_light.svg
new file mode 100644
index 000000000..661edd9f7
--- /dev/null
+++ b/src/OnboardingSPA/static/icons/site-features/wishlist_light.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/OnboardingSPA/styles/_icons.scss b/src/OnboardingSPA/styles/_icons.scss
index 2be397c74..0e02f9160 100644
--- a/src/OnboardingSPA/styles/_icons.scss
+++ b/src/OnboardingSPA/styles/_icons.scss
@@ -50,6 +50,27 @@ body {
--nfd-onboarding-sidebar-learn-more-pages-illustration: url("../static/icons/learn-more-pages.svg");
--nfd-onboarding-sidebar-learn-more-site-features-illustration: url("../static/icons/learn-more-site-features.svg");
--nfd-onboarding-sidebar-learn-more-what-next-illustration: url("../static/icons/learn-more-what-next.svg");
+
+ --site-features-analytics: url("../static/icons/site-features/analytics.svg");
+ --site-features-analytics--light: url("../static/icons/site-features/analytics_light.svg");
+ --site-features-bookingcalendar: url("../static/icons/site-features/bookingcalendar.svg");
+ --site-features-bookingcalendar--light: url("../static/icons/site-features/bookingcalendar_light.svg");
+ --site-features-email: url("../static/icons/site-features/email.svg");
+ --site-features-email--light: url("../static/icons/site-features/email_light.svg");
+ --site-features-filter: url("../static/icons/site-features/filter.svg");
+ --site-features-filter--light: url("../static/icons/site-features/filter_light.svg");
+ --site-features-form: url("../static/icons/site-features/form.svg");
+ --site-features-form--light: url("../static/icons/site-features/form_light.svg");
+ --site-features-lead: url("../static/icons/site-features/lead.svg");
+ --site-features-lead--light: url("../static/icons/site-features/lead_light.svg");
+ --site-features-search: url("../static/icons/site-features/search.svg");
+ --site-features-search--light: url("../static/icons/site-features/search_light.svg");
+ --site-features-security: url("../static/icons/site-features/security.svg");
+ --site-features-security--light: url("../static/icons/site-features/security_light.svg");
+ --site-features-share: url("../static/icons/site-features/share.svg");
+ --site-features-share--light: url("../static/icons/site-features/share_light.svg");
+ --site-features-wishlist: url("../static/icons/site-features/wishlist.svg");
+ --site-features-wishlist--light: url("../static/icons/site-features/wishlist_light.svg");
/*
* Below Icons are commented because they get added to the CSS bundle and
diff --git a/src/OnboardingSPA/styles/app.scss b/src/OnboardingSPA/styles/app.scss
index f43d4a388..445893673 100644
--- a/src/OnboardingSPA/styles/app.scss
+++ b/src/OnboardingSPA/styles/app.scss
@@ -34,6 +34,8 @@
@import "../components/Button/NavCardButton/stylesheet";
@import "../pages/Steps/Ecommerce/stylesheet";
@import "../components/ErrorState/stylesheet";
+@import "../components/CheckboxTemplate/CheckboxItem/stylesheet";
+@import "../components/CheckboxTemplate/CheckboxList/stylesheet";
@import "../components/Sidebar/components/LearnMore/Skeleton/stylesheet";
// CSS for Pages
diff --git a/src/OnboardingSPA/utils/api/plugins.js b/src/OnboardingSPA/utils/api/plugins.js
index af7b8b8e8..7716ad275 100644
--- a/src/OnboardingSPA/utils/api/plugins.js
+++ b/src/OnboardingSPA/utils/api/plugins.js
@@ -30,3 +30,11 @@ export const getPluginStatus = async ( plugin ) => {
} )
);
};
+
+export const getSiteFeatures = async () => {
+ return await resolve(
+ apiFetch( {
+ url: onboardingRestURL( 'plugins/site-features' ),
+ } )
+ );
+};