Merged in feature/from-pantheon (pull request #16)
code from pantheon * code from pantheon
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Async Product Editor Category Field.
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\AsyncProductEditorCategoryField;
|
||||
|
||||
use Automattic\Jetpack\Constants;
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;
|
||||
use Automattic\WooCommerce\Admin\PageController;
|
||||
|
||||
/**
|
||||
* Loads assets related to the async category field for the product editor.
|
||||
*/
|
||||
class Init {
|
||||
|
||||
const FEATURE_ID = 'async-product-editor-category-field';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
if ( Features::is_enabled( self::FEATURE_ID ) ) {
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
||||
add_filter( 'woocommerce_taxonomy_args_product_cat', array( $this, 'add_metabox_args' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds meta_box_cb callback arguments for custom metabox.
|
||||
*
|
||||
* @param array $args Category taxonomy args.
|
||||
* @return array $args category taxonomy args.
|
||||
*/
|
||||
public function add_metabox_args( $args ) {
|
||||
if ( ! isset( $args['meta_box_cb'] ) ) {
|
||||
$args['meta_box_cb'] = 'WC_Meta_Box_Product_Categories::output';
|
||||
$args['meta_box_sanitize_cb'] = 'taxonomy_meta_box_sanitize_cb_checkboxes';
|
||||
}
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts needed for the product form block editor.
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
if ( ! PageController::is_embed_page() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
WCAdminAssets::register_script( 'wp-admin-scripts', 'product-category-metabox', true );
|
||||
wp_localize_script(
|
||||
'wc-admin-product-category-metabox',
|
||||
'wc_product_category_metabox_params',
|
||||
array(
|
||||
'search_categories_nonce' => wp_create_nonce( 'search-categories' ),
|
||||
'search_taxonomy_terms_nonce' => wp_create_nonce( 'search-taxonomy-terms' ),
|
||||
)
|
||||
);
|
||||
wp_enqueue_script( 'product-category-metabox' );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue styles needed for the rich text editor.
|
||||
*/
|
||||
public function enqueue_styles() {
|
||||
if ( ! PageController::is_embed_page() ) {
|
||||
return;
|
||||
}
|
||||
$version = Constants::get_constant( 'WC_VERSION' );
|
||||
|
||||
wp_register_style(
|
||||
'woocommerce_admin_product_category_metabox_styles',
|
||||
WCAdminAssets::get_url( 'product-category-metabox/style', 'css' ),
|
||||
array(),
|
||||
$version
|
||||
);
|
||||
wp_style_add_data( 'woocommerce_admin_product_category_metabox_styles', 'rtl', 'replace' );
|
||||
|
||||
wp_enqueue_style( 'woocommerce_admin_product_category_metabox_styles' );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
<?php
|
||||
/**
|
||||
* Features loader for features developed in WooCommerce Admin.
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features;
|
||||
|
||||
use Automattic\WooCommerce\Admin\PageController;
|
||||
use Automattic\WooCommerce\Internal\Admin\Loader;
|
||||
use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;
|
||||
|
||||
/**
|
||||
* Features Class.
|
||||
*/
|
||||
class Features {
|
||||
/**
|
||||
* Class instance.
|
||||
*
|
||||
* @var Loader instance
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* Optional features
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $optional_features = array(
|
||||
'navigation' => array( 'default' => 'no' ),
|
||||
'settings' => array( 'default' => 'no' ),
|
||||
'analytics' => array( 'default' => 'yes' ),
|
||||
'remote-inbox-notifications' => array( 'default' => 'yes' ),
|
||||
);
|
||||
|
||||
/**
|
||||
* Beta features
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $beta_features = array(
|
||||
'navigation',
|
||||
'new-product-management-experience',
|
||||
'settings',
|
||||
);
|
||||
|
||||
/**
|
||||
* Get class instance.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->register_internal_class_aliases();
|
||||
// Load feature before WooCommerce update hooks.
|
||||
add_action( 'init', array( __CLASS__, 'load_features' ), 4 );
|
||||
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'maybe_load_beta_features_modal' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'load_scripts' ), 15 );
|
||||
add_filter( 'admin_body_class', array( __CLASS__, 'add_admin_body_classes' ) );
|
||||
add_filter( 'update_option_woocommerce_allow_tracking', array( __CLASS__, 'maybe_disable_features' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a build configured array of enabled WooCommerce Admin features/sections, but does not respect optionally disabled features.
|
||||
*
|
||||
* @return array Enabled Woocommerce Admin features/sections.
|
||||
*/
|
||||
public static function get_features() {
|
||||
return apply_filters( 'woocommerce_admin_features', array() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the optional feature options as an associative array that can be toggled on or off.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_optional_feature_options() {
|
||||
$features = [];
|
||||
|
||||
foreach ( array_keys( self::$optional_features ) as $optional_feature_key ) {
|
||||
$feature_class = self::get_feature_class( $optional_feature_key );
|
||||
|
||||
if ( $feature_class ) {
|
||||
$features[ $optional_feature_key ] = $feature_class::TOGGLE_OPTION_NAME;
|
||||
}
|
||||
}
|
||||
return $features;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if a specific wc-admin feature exists in the current environment.
|
||||
*
|
||||
* @param string $feature Feature slug.
|
||||
* @return bool Returns true if the feature exists.
|
||||
*/
|
||||
public static function exists( $feature ) {
|
||||
$features = self::get_features();
|
||||
return in_array( $feature, $features, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the feature class as a string.
|
||||
*
|
||||
* @param string $feature Feature name.
|
||||
* @return string|null
|
||||
*/
|
||||
public static function get_feature_class( $feature ) {
|
||||
$feature = str_replace( '-', '', ucwords( strtolower( $feature ), '-' ) );
|
||||
$feature_class = 'Automattic\\WooCommerce\\Admin\\Features\\' . $feature;
|
||||
|
||||
if ( class_exists( $feature_class ) ) {
|
||||
return $feature_class;
|
||||
}
|
||||
|
||||
// Handle features contained in subdirectory.
|
||||
if ( class_exists( $feature_class . '\\Init' ) ) {
|
||||
return $feature_class . '\\Init';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class loader for enabled WooCommerce Admin features/sections.
|
||||
*/
|
||||
public static function load_features() {
|
||||
$features = self::get_features();
|
||||
foreach ( $features as $feature ) {
|
||||
$feature_class = self::get_feature_class( $feature );
|
||||
|
||||
if ( $feature_class ) {
|
||||
new $feature_class();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a build configured array of enabled WooCommerce Admin respecting optionally disabled features.
|
||||
*
|
||||
* @return array Enabled Woocommerce Admin features/sections.
|
||||
*/
|
||||
public static function get_available_features() {
|
||||
$features = self::get_features();
|
||||
$optional_feature_keys = array_keys( self::$optional_features );
|
||||
$optional_features_unavailable = [];
|
||||
|
||||
/**
|
||||
* Filter allowing WooCommerce Admin optional features to be disabled.
|
||||
*
|
||||
* @param bool $disabled False.
|
||||
*/
|
||||
if ( apply_filters( 'woocommerce_admin_disabled', false ) ) {
|
||||
return array_values( array_diff( $features, $optional_feature_keys ) );
|
||||
}
|
||||
|
||||
foreach ( $optional_feature_keys as $optional_feature_key ) {
|
||||
$feature_class = self::get_feature_class( $optional_feature_key );
|
||||
|
||||
if ( $feature_class ) {
|
||||
$default = isset( self::$optional_features[ $optional_feature_key ]['default'] ) ?
|
||||
self::$optional_features[ $optional_feature_key ]['default'] :
|
||||
'no';
|
||||
|
||||
// Check if the feature is currently being enabled, if it is continue.
|
||||
/* phpcs:disable WordPress.Security.NonceVerification */
|
||||
$feature_option = $feature_class::TOGGLE_OPTION_NAME;
|
||||
if ( isset( $_POST[ $feature_option ] ) && '1' === $_POST[ $feature_option ] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( 'yes' !== get_option( $feature_class::TOGGLE_OPTION_NAME, $default ) ) {
|
||||
$optional_features_unavailable[] = $optional_feature_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_values( array_diff( $features, $optional_features_unavailable ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a feature is enabled.
|
||||
*
|
||||
* @param string $feature Feature slug.
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_enabled( $feature ) {
|
||||
$available_features = self::get_available_features();
|
||||
return in_array( $feature, $available_features, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable a toggleable optional feature.
|
||||
*
|
||||
* @param string $feature Feature name.
|
||||
* @return bool
|
||||
*/
|
||||
public static function enable( $feature ) {
|
||||
$features = self::get_optional_feature_options();
|
||||
|
||||
if ( isset( $features[ $feature ] ) ) {
|
||||
update_option( $features[ $feature ], 'yes' );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable a toggleable optional feature.
|
||||
*
|
||||
* @param string $feature Feature name.
|
||||
* @return bool
|
||||
*/
|
||||
public static function disable( $feature ) {
|
||||
$features = self::get_optional_feature_options();
|
||||
|
||||
if ( isset( $features[ $feature ] ) ) {
|
||||
update_option( $features[ $feature ], 'no' );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable features when opting out of tracking.
|
||||
*
|
||||
* @param string $old_value Old value.
|
||||
* @param string $value New value.
|
||||
*/
|
||||
public static function maybe_disable_features( $old_value, $value ) {
|
||||
if ( 'yes' === $value ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( self::$beta_features as $feature ) {
|
||||
self::disable( $feature );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the Features section to the advanced tab of WooCommerce Settings
|
||||
*
|
||||
* @deprecated 7.0 The WooCommerce Admin features are now handled by the WooCommerce features engine (see the FeaturesController class).
|
||||
*
|
||||
* @param array $sections Sections.
|
||||
* @return array
|
||||
*/
|
||||
public static function add_features_section( $sections ) {
|
||||
return $sections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the Features settings.
|
||||
*
|
||||
* @deprecated 7.0 The WooCommerce Admin features are now handled by the WooCommerce features engine (see the FeaturesController class).
|
||||
*
|
||||
* @param array $settings Settings.
|
||||
* @param string $current_section Current section slug.
|
||||
* @return array
|
||||
*/
|
||||
public static function add_features_settings( $settings, $current_section ) {
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conditionally loads the beta features tracking modal.
|
||||
*
|
||||
* @param string $hook Page hook.
|
||||
*/
|
||||
public static function maybe_load_beta_features_modal( $hook ) {
|
||||
if (
|
||||
'woocommerce_page_wc-settings' !== $hook ||
|
||||
! isset( $_GET['tab'] ) || 'advanced' !== $_GET['tab'] || // phpcs:ignore CSRF ok.
|
||||
! isset( $_GET['section'] ) || 'features' !== $_GET['section'] // phpcs:ignore CSRF ok.
|
||||
) {
|
||||
return;
|
||||
}
|
||||
$tracking_enabled = get_option( 'woocommerce_allow_tracking', 'no' );
|
||||
|
||||
if ( empty( self::$beta_features ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 'yes' === $tracking_enabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$rtl = is_rtl() ? '.rtl' : '';
|
||||
|
||||
wp_enqueue_style(
|
||||
'wc-admin-beta-features-tracking-modal',
|
||||
WCAdminAssets::get_url( "beta-features-tracking-modal/style{$rtl}", 'css' ),
|
||||
array( 'wp-components' ),
|
||||
WCAdminAssets::get_file_version( 'css' )
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'wc-admin-beta-features-tracking-modal',
|
||||
WCAdminAssets::get_url( 'wp-admin-scripts/beta-features-tracking-modal', 'js' ),
|
||||
array( 'wp-i18n', 'wp-element', WC_ADMIN_APP ),
|
||||
WCAdminAssets::get_file_version( 'js' ),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the required scripts on the correct pages.
|
||||
*/
|
||||
public static function load_scripts() {
|
||||
if ( ! PageController::is_admin_or_embed_page() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$features = self::get_features();
|
||||
$enabled_features = array();
|
||||
foreach ( $features as $key ) {
|
||||
$enabled_features[ $key ] = self::is_enabled( $key );
|
||||
}
|
||||
wp_add_inline_script( WC_ADMIN_APP, 'window.wcAdminFeatures = ' . wp_json_encode( $enabled_features ), 'before' );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds body classes to the main wp-admin wrapper, allowing us to better target elements in specific scenarios.
|
||||
*
|
||||
* @param string $admin_body_class Body class to add.
|
||||
*/
|
||||
public static function add_admin_body_classes( $admin_body_class = '' ) {
|
||||
if ( ! PageController::is_admin_or_embed_page() ) {
|
||||
return $admin_body_class;
|
||||
}
|
||||
|
||||
$classes = explode( ' ', trim( $admin_body_class ) );
|
||||
|
||||
$features = self::get_features();
|
||||
foreach ( $features as $feature_key ) {
|
||||
$classes[] = sanitize_html_class( 'woocommerce-feature-enabled-' . $feature_key );
|
||||
}
|
||||
|
||||
$admin_body_class = implode( ' ', array_unique( $classes ) );
|
||||
return " $admin_body_class ";
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias internal features classes to make them backward compatible.
|
||||
* We've moved our feature classes to src-internal as part of merging this
|
||||
* repository with WooCommerce Core to form a monorepo.
|
||||
* See https://wp.me/p90Yrv-2HY for details.
|
||||
*/
|
||||
private function register_internal_class_aliases() {
|
||||
$aliases = array(
|
||||
// new class => original class (this will be aliased).
|
||||
'Automattic\WooCommerce\Internal\Admin\WCPayPromotion\Init' => 'Automattic\WooCommerce\Admin\Features\WcPayPromotion\Init',
|
||||
'Automattic\WooCommerce\Internal\Admin\RemoteFreeExtensions\Init' => 'Automattic\WooCommerce\Admin\Features\RemoteFreeExtensions\Init',
|
||||
'Automattic\WooCommerce\Internal\Admin\ActivityPanels' => 'Automattic\WooCommerce\Admin\Features\ActivityPanels',
|
||||
'Automattic\WooCommerce\Internal\Admin\Analytics' => 'Automattic\WooCommerce\Admin\Features\Analytics',
|
||||
'Automattic\WooCommerce\Internal\Admin\Coupons' => 'Automattic\WooCommerce\Admin\Features\Coupons',
|
||||
'Automattic\WooCommerce\Internal\Admin\CouponsMovedTrait' => 'Automattic\WooCommerce\Admin\Features\CouponsMovedTrait',
|
||||
'Automattic\WooCommerce\Internal\Admin\CustomerEffortScoreTracks' => 'Automattic\WooCommerce\Admin\Features\CustomerEffortScoreTracks',
|
||||
'Automattic\WooCommerce\Internal\Admin\Homescreen' => 'Automattic\WooCommerce\Admin\Features\Homescreen',
|
||||
'Automattic\WooCommerce\Internal\Admin\Marketing' => 'Automattic\WooCommerce\Admin\Features\Marketing',
|
||||
'Automattic\WooCommerce\Internal\Admin\MobileAppBanner' => 'Automattic\WooCommerce\Admin\Features\MobileAppBanner',
|
||||
'Automattic\WooCommerce\Internal\Admin\RemoteInboxNotifications' => 'Automattic\WooCommerce\Admin\Features\RemoteInboxNotifications',
|
||||
'Automattic\WooCommerce\Internal\Admin\SettingsNavigationFeature' => 'Automattic\WooCommerce\Admin\Features\Settings',
|
||||
'Automattic\WooCommerce\Internal\Admin\ShippingLabelBanner' => 'Automattic\WooCommerce\Admin\Features\ShippingLabelBanner',
|
||||
'Automattic\WooCommerce\Internal\Admin\ShippingLabelBannerDisplayRules' => 'Automattic\WooCommerce\Admin\Features\ShippingLabelBannerDisplayRules',
|
||||
'Automattic\WooCommerce\Internal\Admin\WcPayWelcomePage' => 'Automattic\WooCommerce\Admin\Features\WcPayWelcomePage',
|
||||
);
|
||||
foreach ( $aliases as $new_class => $orig_class ) {
|
||||
class_alias( $new_class, $orig_class );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,463 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Navigation Core Menu
|
||||
*
|
||||
* @package Woocommerce Admin
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\Navigation;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
use Automattic\WooCommerce\Admin\Features\Navigation\Menu;
|
||||
use Automattic\WooCommerce\Admin\Features\Navigation\Screen;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\TaskLists;
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
|
||||
|
||||
/**
|
||||
* CoreMenu class. Handles registering Core menu items.
|
||||
*/
|
||||
class CoreMenu {
|
||||
/**
|
||||
* Class instance.
|
||||
*
|
||||
* @var Menu instance
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* Get class instance.
|
||||
*/
|
||||
final public static function instance() {
|
||||
if ( ! static::$instance ) {
|
||||
static::$instance = new static();
|
||||
}
|
||||
return static::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init.
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'admin_menu', array( $this, 'register_post_types' ) );
|
||||
// Add this after we've finished migrating menu items to avoid hiding these items.
|
||||
add_action( 'admin_menu', array( $this, 'add_dashboard_menu_items' ), PHP_INT_MAX );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add registered admin settings as menu items.
|
||||
*/
|
||||
public static function get_setting_items() {
|
||||
// Let the Settings feature add pages to the navigation if enabled.
|
||||
if ( Features::is_enabled( 'settings' ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Calling this method adds pages to the below tabs filter on non-settings pages.
|
||||
\WC_Admin_Settings::get_settings_pages();
|
||||
$tabs = apply_filters( 'woocommerce_settings_tabs_array', array() );
|
||||
|
||||
$menu_items = array();
|
||||
$order = 0;
|
||||
foreach ( $tabs as $key => $setting ) {
|
||||
$order += 10;
|
||||
$menu_items[] = (
|
||||
array(
|
||||
'parent' => 'woocommerce-settings',
|
||||
'title' => $setting,
|
||||
'capability' => 'manage_woocommerce',
|
||||
'id' => 'settings-' . $key,
|
||||
'url' => 'admin.php?page=wc-settings&tab=' . $key,
|
||||
'order' => $order,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $menu_items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get unfulfilled order count
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_shop_order_count() {
|
||||
$status_counts = array_map( 'wc_orders_count', array( 'processing', 'on-hold' ) );
|
||||
return array_sum( $status_counts );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all menu categories.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_categories() {
|
||||
$analytics_enabled = Features::is_enabled( 'analytics' );
|
||||
return array(
|
||||
array(
|
||||
'title' => __( 'Orders', 'woocommerce' ),
|
||||
'id' => 'woocommerce-orders',
|
||||
'badge' => self::get_shop_order_count(),
|
||||
'order' => 10,
|
||||
),
|
||||
array(
|
||||
'title' => __( 'Products', 'woocommerce' ),
|
||||
'id' => 'woocommerce-products',
|
||||
'order' => 20,
|
||||
),
|
||||
$analytics_enabled ?
|
||||
array(
|
||||
'title' => __( 'Analytics', 'woocommerce' ),
|
||||
'id' => 'woocommerce-analytics',
|
||||
'order' => 30,
|
||||
) : null,
|
||||
$analytics_enabled ?
|
||||
array(
|
||||
'title' => __( 'Reports', 'woocommerce' ),
|
||||
'id' => 'woocommerce-reports',
|
||||
'parent' => 'woocommerce-analytics',
|
||||
'order' => 200,
|
||||
) : null,
|
||||
array(
|
||||
'title' => __( 'Marketing', 'woocommerce' ),
|
||||
'id' => 'woocommerce-marketing',
|
||||
'order' => 40,
|
||||
),
|
||||
array(
|
||||
'title' => __( 'Settings', 'woocommerce' ),
|
||||
'id' => 'woocommerce-settings',
|
||||
'menuId' => 'secondary',
|
||||
'order' => 20,
|
||||
'url' => 'admin.php?page=wc-settings',
|
||||
),
|
||||
array(
|
||||
'title' => __( 'Tools', 'woocommerce' ),
|
||||
'id' => 'woocommerce-tools',
|
||||
'menuId' => 'secondary',
|
||||
'order' => 30,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all menu items.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_items() {
|
||||
$order_items = self::get_order_menu_items();
|
||||
$product_items = Menu::get_post_type_items( 'product', array( 'parent' => 'woocommerce-products' ) );
|
||||
$product_tag_items = Menu::get_taxonomy_items(
|
||||
'product_tag',
|
||||
array(
|
||||
'parent' => 'woocommerce-products',
|
||||
'order' => 30,
|
||||
)
|
||||
);
|
||||
$product_cat_items = Menu::get_taxonomy_items(
|
||||
'product_cat',
|
||||
array(
|
||||
'parent' => 'woocommerce-products',
|
||||
'order' => 20,
|
||||
)
|
||||
);
|
||||
|
||||
$coupon_items = Menu::get_post_type_items( 'shop_coupon', array( 'parent' => 'woocommerce-marketing' ) );
|
||||
$setting_items = self::get_setting_items();
|
||||
$wca_items = array();
|
||||
$wca_pages = \Automattic\WooCommerce\Admin\PageController::get_instance()->get_pages();
|
||||
|
||||
foreach ( $wca_pages as $page ) {
|
||||
if ( ! isset( $page['nav_args'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$path = isset( $page['path'] ) ? $page['path'] : null;
|
||||
$item = array_merge(
|
||||
array(
|
||||
'id' => $page['id'],
|
||||
'url' => $path,
|
||||
'title' => $page['title'][0],
|
||||
'capability' => isset( $page['capability'] ) ? $page['capability'] : 'manage_woocommerce',
|
||||
),
|
||||
$page['nav_args']
|
||||
);
|
||||
|
||||
// Don't allow top-level items to be added to the primary menu.
|
||||
if ( ! isset( $item['parent'] ) || 'woocommerce' === $item['parent'] ) {
|
||||
$item['menuId'] = 'plugins';
|
||||
}
|
||||
|
||||
$wca_items[] = $item;
|
||||
}
|
||||
|
||||
$home_item = array();
|
||||
$setup_tasks_remaining = TaskLists::setup_tasks_remaining();
|
||||
if ( defined( '\Automattic\WooCommerce\Internal\Admin\Homescreen::MENU_SLUG' ) ) {
|
||||
$home_item = array(
|
||||
'id' => 'woocommerce-home',
|
||||
'title' => __( 'Home', 'woocommerce' ),
|
||||
'url' => \Automattic\WooCommerce\Internal\Admin\Homescreen::MENU_SLUG,
|
||||
'order' => 0,
|
||||
'matchExpression' => 'page=wc-admin((?!path=).)*$',
|
||||
'badge' => $setup_tasks_remaining ? $setup_tasks_remaining : null,
|
||||
);
|
||||
}
|
||||
|
||||
$customers_item = array();
|
||||
if ( Features::is_enabled( 'analytics' ) ) {
|
||||
$customers_item = array(
|
||||
'id' => 'woocommerce-analytics-customers',
|
||||
'title' => __( 'Customers', 'woocommerce' ),
|
||||
'url' => 'wc-admin&path=/customers',
|
||||
'order' => 50,
|
||||
);
|
||||
}
|
||||
|
||||
$add_product_mvp = array();
|
||||
if ( Features::is_enabled( 'new-product-management-experience' ) ) {
|
||||
$add_product_mvp = array(
|
||||
'id' => 'woocommerce-add-product-mbp',
|
||||
'title' => __( 'Add New (MVP)', 'woocommerce' ),
|
||||
'url' => 'admin.php?page=wc-admin&path=/add-product',
|
||||
'parent' => 'woocommerce-products',
|
||||
'order' => 50,
|
||||
);
|
||||
}
|
||||
|
||||
return array_merge(
|
||||
array(
|
||||
$home_item,
|
||||
$customers_item,
|
||||
$order_items['all'],
|
||||
$order_items['new'],
|
||||
$product_items['all'],
|
||||
$product_cat_items['default'],
|
||||
$product_tag_items['default'],
|
||||
array(
|
||||
'id' => 'woocommerce-product-attributes',
|
||||
'title' => __( 'Attributes', 'woocommerce' ),
|
||||
'url' => 'edit.php?post_type=product&page=product_attributes',
|
||||
'capability' => 'manage_product_terms',
|
||||
'order' => 40,
|
||||
'parent' => 'woocommerce-products',
|
||||
'matchExpression' => 'edit.php(?=.*[?|&]page=product_attributes(&|$|#))|edit-tags.php(?=.*[?|&]taxonomy=pa_)(?=.*[?|&]post_type=product(&|$|#))',
|
||||
),
|
||||
array_merge( $product_items['new'], array( 'order' => 50 ) ),
|
||||
$coupon_items['default'],
|
||||
// Marketplace category.
|
||||
array(
|
||||
'title' => __( 'Marketplace', 'woocommerce' ),
|
||||
'capability' => 'manage_woocommerce',
|
||||
'id' => 'woocommerce-marketplace',
|
||||
'url' => 'wc-addons',
|
||||
'menuId' => 'secondary',
|
||||
'order' => 10,
|
||||
),
|
||||
$add_product_mvp,
|
||||
),
|
||||
// Tools category.
|
||||
self::get_tool_items(),
|
||||
// WooCommerce Admin items.
|
||||
$wca_items,
|
||||
// Settings category.
|
||||
$setting_items,
|
||||
// Legacy report items.
|
||||
self::get_legacy_report_items()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Supplies menu items for orders.
|
||||
*
|
||||
* This varies depending on whether we are actively using traditional post type-based orders or the new custom
|
||||
* table-based orders.
|
||||
*
|
||||
* @return ?array
|
||||
*/
|
||||
private static function get_order_menu_items(): ?array {
|
||||
if ( ! wc_get_container()->get( CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled() ) {
|
||||
return Menu::get_post_type_items( 'shop_order', array( 'parent' => 'woocommerce-orders' ) );
|
||||
}
|
||||
|
||||
$main_orders_menu = array(
|
||||
'title' => __( 'Orders', 'woocommerce' ),
|
||||
'capability' => 'edit_others_shop_orders',
|
||||
'id' => 'woocommerce-orders-default',
|
||||
'url' => 'admin.php?page=wc-orders',
|
||||
'parent' => 'woocommerce-orders',
|
||||
);
|
||||
|
||||
$all_orders_entry = $main_orders_menu;
|
||||
$all_orders_entry['id'] = 'woocommerce-orders-all-items';
|
||||
$all_orders_entry['order'] = 10;
|
||||
|
||||
$new_orders_entry = $main_orders_menu;
|
||||
$new_orders_entry['title'] = __( 'Add order', 'woocommerce' );
|
||||
$new_orders_entry['id'] = 'woocommerce-orders-add-item';
|
||||
$new_orders_entry['url'] = 'admin.php?page=TBD';
|
||||
$new_orders_entry['order'] = 20;
|
||||
|
||||
return array(
|
||||
'default' => $main_orders_menu,
|
||||
'all' => $all_orders_entry,
|
||||
'new' => $new_orders_entry,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get items for tools category.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_tool_items() {
|
||||
$tabs = array(
|
||||
'status' => __( 'System status', 'woocommerce' ),
|
||||
'tools' => __( 'Utilities', 'woocommerce' ),
|
||||
'logs' => __( 'Logs', 'woocommerce' ),
|
||||
);
|
||||
$tabs = apply_filters( 'woocommerce_admin_status_tabs', $tabs );
|
||||
|
||||
$order = 1;
|
||||
$items = array(
|
||||
array(
|
||||
'parent' => 'woocommerce-tools',
|
||||
'title' => __( 'Import / Export', 'woocommerce' ),
|
||||
'capability' => 'import',
|
||||
'id' => 'tools-import-export',
|
||||
'url' => 'import.php',
|
||||
'migrate' => false,
|
||||
'order' => 0,
|
||||
),
|
||||
);
|
||||
|
||||
foreach ( $tabs as $key => $tab ) {
|
||||
$items[] = array(
|
||||
'parent' => 'woocommerce-tools',
|
||||
'title' => $tab,
|
||||
'capability' => 'manage_woocommerce',
|
||||
'id' => 'tools-' . $key,
|
||||
'url' => 'wc-status&tab=' . $key,
|
||||
'order' => $order,
|
||||
);
|
||||
$order++;
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get legacy report items.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_legacy_report_items() {
|
||||
$reports = \WC_Admin_Reports::get_reports();
|
||||
$menu_items = array();
|
||||
|
||||
$order = 0;
|
||||
foreach ( $reports as $key => $report ) {
|
||||
$menu_items[] = array(
|
||||
'parent' => 'woocommerce-reports',
|
||||
'title' => $report['title'],
|
||||
'capability' => 'view_woocommerce_reports',
|
||||
'id' => $key,
|
||||
'url' => 'wc-reports&tab=' . $key,
|
||||
'order' => $order,
|
||||
);
|
||||
$order++;
|
||||
}
|
||||
|
||||
return $menu_items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all core post types.
|
||||
*/
|
||||
public function register_post_types() {
|
||||
Screen::register_post_type( 'shop_order' );
|
||||
Screen::register_post_type( 'product' );
|
||||
Screen::register_post_type( 'shop_coupon' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the dashboard items to the WP menu to create a quick-access flyout menu.
|
||||
*/
|
||||
public function add_dashboard_menu_items() {
|
||||
global $submenu, $menu;
|
||||
$mapped_items = Menu::get_mapped_menu_items();
|
||||
$top_level = $mapped_items['woocommerce'];
|
||||
|
||||
// phpcs:disable
|
||||
if ( ! isset( $submenu['woocommerce'] ) || empty( $top_level ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$menuIds = array(
|
||||
'primary',
|
||||
'secondary',
|
||||
'favorites',
|
||||
);
|
||||
|
||||
foreach ( $menuIds as $menuId ) {
|
||||
foreach( $top_level[ $menuId ] as $item ) {
|
||||
// Skip specific categories.
|
||||
if (
|
||||
in_array(
|
||||
$item['id'],
|
||||
array(
|
||||
'woocommerce-tools',
|
||||
),
|
||||
true
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Use the link from the first item if it's a category.
|
||||
if ( ! isset( $item['url'] ) ) {
|
||||
$categoryMenuId = $menuId === 'favorites' ? 'plugins' : $menuId;
|
||||
$category_items = $mapped_items[ $item['id'] ][ $categoryMenuId ];
|
||||
|
||||
if ( ! empty( $category_items ) ) {
|
||||
$first_item = $category_items[0];
|
||||
|
||||
|
||||
$submenu['woocommerce'][] = array(
|
||||
$item['title'],
|
||||
$first_item['capability'],
|
||||
isset( $first_item['url'] ) ? $first_item['url'] : null,
|
||||
$item['title'],
|
||||
);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Show top-level items.
|
||||
$submenu['woocommerce'][] = array(
|
||||
$item['title'],
|
||||
$item['capability'],
|
||||
isset( $item['url'] ) ? $item['url'] : null,
|
||||
$item['title'],
|
||||
);
|
||||
}
|
||||
}
|
||||
// phpcs:enable
|
||||
}
|
||||
|
||||
/**
|
||||
* Get items excluded from WooCommerce menu migration.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_excluded_items() {
|
||||
$excluded_items = array(
|
||||
'woocommerce',
|
||||
'wc-reports',
|
||||
'wc-settings',
|
||||
'wc-status',
|
||||
);
|
||||
|
||||
return apply_filters( 'woocommerce_navigation_core_excluded_items', $excluded_items );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Navigation Favorite
|
||||
*
|
||||
* @package Woocommerce Navigation
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\Navigation;
|
||||
|
||||
use Automattic\WooCommerce\Internal\Admin\WCAdminUser;
|
||||
|
||||
/**
|
||||
* Contains logic for the WooCommerce Navigation menu.
|
||||
*/
|
||||
class Favorites {
|
||||
|
||||
/**
|
||||
* Array index of menu capability.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const META_NAME = 'navigation_favorites';
|
||||
|
||||
/**
|
||||
* Favorites instance.
|
||||
*
|
||||
* @var Favorites|null
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* Get class instance.
|
||||
*/
|
||||
final public static function instance() {
|
||||
if ( ! static::$instance ) {
|
||||
static::$instance = new static();
|
||||
}
|
||||
return static::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set given favorites string to the user meta data.
|
||||
*
|
||||
* @param string|number $user_id User id.
|
||||
* @param array $favorites Array of favorite values to set.
|
||||
*/
|
||||
private static function set_meta_value( $user_id, $favorites ) {
|
||||
WCAdminUser::update_user_data_field( $user_id, self::META_NAME, wp_json_encode( (array) $favorites ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add item to favorites
|
||||
*
|
||||
* @param string $item_id Identifier of item to add.
|
||||
* @param string|number $user_id Identifier of user to add to.
|
||||
* @return WP_Error|Boolean Throws exception if item already exists.
|
||||
*/
|
||||
public static function add_item( $item_id, $user_id ) {
|
||||
|
||||
$all_favorites = self::get_all( $user_id );
|
||||
|
||||
if ( in_array( $item_id, $all_favorites, true ) ) {
|
||||
return new \WP_Error(
|
||||
'woocommerce_favorites_already_exists',
|
||||
__( 'Favorite already exists', 'woocommerce' )
|
||||
);
|
||||
}
|
||||
|
||||
$all_favorites[] = $item_id;
|
||||
|
||||
self::set_meta_value( $user_id, $all_favorites );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove item from favorites
|
||||
*
|
||||
* @param string $item_id Identifier of item to remove.
|
||||
* @param string|number $user_id Identifier of user to remove from.
|
||||
* @return \WP_Error|Boolean Throws exception if item does not exist.
|
||||
*/
|
||||
public static function remove_item( $item_id, $user_id ) {
|
||||
$all_favorites = self::get_all( $user_id );
|
||||
|
||||
if ( ! in_array( $item_id, $all_favorites, true ) ) {
|
||||
return new \WP_Error(
|
||||
'woocommerce_favorites_does_not_exist',
|
||||
__( 'Favorite item not found', 'woocommerce' )
|
||||
);
|
||||
}
|
||||
|
||||
$remaining = array_values( array_diff( $all_favorites, [ $item_id ] ) );
|
||||
|
||||
self::set_meta_value( $user_id, $remaining );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered favorites.
|
||||
*
|
||||
* @param string|number $user_id Identifier of user to query.
|
||||
* @return WP_Error|Array
|
||||
*/
|
||||
public static function get_all( $user_id ) {
|
||||
$response = WCAdminUser::get_user_data_field( $user_id, self::META_NAME );
|
||||
|
||||
return $response ? json_decode( $response, true ) : array();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
/**
|
||||
* Navigation Experience
|
||||
*
|
||||
* @package Woocommerce Admin
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\Navigation;
|
||||
|
||||
use Automattic\WooCommerce\Internal\Admin\Survey;
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
use Automattic\WooCommerce\Admin\Features\Navigation\Screen;
|
||||
use Automattic\WooCommerce\Admin\Features\Navigation\Menu;
|
||||
use Automattic\WooCommerce\Admin\Features\Navigation\CoreMenu;
|
||||
use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;
|
||||
|
||||
/**
|
||||
* Contains logic for the Navigation
|
||||
*/
|
||||
class Init {
|
||||
/**
|
||||
* Option name used to toggle this feature.
|
||||
*/
|
||||
const TOGGLE_OPTION_NAME = 'woocommerce_navigation_enabled';
|
||||
|
||||
/**
|
||||
* Determines if the feature has been toggled on or off.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected static $is_updated = false;
|
||||
|
||||
/**
|
||||
* Hook into WooCommerce.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'update_option_' . self::TOGGLE_OPTION_NAME, array( $this, 'reload_page_on_toggle' ), 10, 2 );
|
||||
add_action( 'woocommerce_settings_saved', array( $this, 'maybe_reload_page' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'maybe_enqueue_opt_out_scripts' ) );
|
||||
|
||||
if ( Features::is_enabled( 'navigation' ) ) {
|
||||
Menu::instance()->init();
|
||||
CoreMenu::instance()->init();
|
||||
Screen::instance()->init();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the feature toggle to the features settings.
|
||||
*
|
||||
* @deprecated 7.0 The WooCommerce Admin features are now handled by the WooCommerce features engine (see the FeaturesController class).
|
||||
*
|
||||
* @param array $features Feature sections.
|
||||
* @return array
|
||||
*/
|
||||
public static function add_feature_toggle( $features ) {
|
||||
return $features;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if sufficient versions are present to support Navigation feature
|
||||
*/
|
||||
public function is_nav_compatible() {
|
||||
include_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
|
||||
$gutenberg_minimum_version = '9.0.0'; // https://github.com/WordPress/gutenberg/releases/tag/v9.0.0.
|
||||
$wp_minimum_version = '5.6';
|
||||
$has_gutenberg = is_plugin_active( 'gutenberg/gutenberg.php' );
|
||||
$gutenberg_version = $has_gutenberg ? get_plugin_data( WP_PLUGIN_DIR . '/gutenberg/gutenberg.php' )['Version'] : false;
|
||||
|
||||
if ( $gutenberg_version && version_compare( $gutenberg_version, $gutenberg_minimum_version, '>=' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get unmodified $wp_version.
|
||||
include ABSPATH . WPINC . '/version.php';
|
||||
|
||||
// Strip '-src' from the version string. Messes up version_compare().
|
||||
$wp_version = str_replace( '-src', '', $wp_version );
|
||||
|
||||
if ( version_compare( $wp_version, $wp_minimum_version, '>=' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the page when the option is toggled to make sure all nav features are loaded.
|
||||
*
|
||||
* @param string $old_value Old value.
|
||||
* @param string $value New value.
|
||||
*/
|
||||
public static function reload_page_on_toggle( $old_value, $value ) {
|
||||
if ( $old_value === $value ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 'yes' !== $value ) {
|
||||
update_option( 'woocommerce_navigation_show_opt_out', 'yes' );
|
||||
}
|
||||
|
||||
self::$is_updated = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload the page if the setting has been updated.
|
||||
*/
|
||||
public static function maybe_reload_page() {
|
||||
if ( ! isset( $_SERVER['REQUEST_URI'] ) || ! self::$is_updated ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_safe_redirect( wp_unslash( $_SERVER['REQUEST_URI'] ) );
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue the opt out scripts.
|
||||
*/
|
||||
public function maybe_enqueue_opt_out_scripts() {
|
||||
if ( get_option( 'woocommerce_navigation_show_opt_out', 'no' ) !== 'yes' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$rtl = is_rtl() ? '.rtl' : '';
|
||||
wp_enqueue_style(
|
||||
'wc-admin-navigation-opt-out',
|
||||
WCAdminAssets::get_url( "navigation-opt-out/style{$rtl}", 'css' ),
|
||||
array( 'wp-components' ),
|
||||
WCAdminAssets::get_file_version( 'css' )
|
||||
);
|
||||
|
||||
WCAdminAssets::register_script( 'wp-admin-scripts', 'navigation-opt-out', true );
|
||||
wp_localize_script(
|
||||
'wc-admin-navigation-opt-out',
|
||||
'surveyData',
|
||||
array(
|
||||
'url' => Survey::get_url( '/new-navigation-opt-out' ),
|
||||
)
|
||||
);
|
||||
delete_option( 'woocommerce_navigation_show_opt_out' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,796 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Navigation Menu
|
||||
*
|
||||
* @package Woocommerce Navigation
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\Navigation;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\Navigation\Favorites;
|
||||
use Automattic\WooCommerce\Admin\Features\Navigation\Screen;
|
||||
use Automattic\WooCommerce\Admin\Features\Navigation\CoreMenu;
|
||||
|
||||
/**
|
||||
* Contains logic for the WooCommerce Navigation menu.
|
||||
*/
|
||||
class Menu {
|
||||
/**
|
||||
* Class instance.
|
||||
*
|
||||
* @var Menu instance
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* Array index of menu capability.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const CAPABILITY = 1;
|
||||
|
||||
/**
|
||||
* Array index of menu callback.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const CALLBACK = 2;
|
||||
|
||||
/**
|
||||
* Array index of menu callback.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const SLUG = 3;
|
||||
|
||||
/**
|
||||
* Array index of menu CSS class string.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const CSS_CLASSES = 4;
|
||||
|
||||
/**
|
||||
* Array of usable menu IDs.
|
||||
*/
|
||||
const MENU_IDS = array(
|
||||
'primary',
|
||||
'favorites',
|
||||
'plugins',
|
||||
'secondary',
|
||||
);
|
||||
|
||||
/**
|
||||
* Store menu items.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $menu_items = array();
|
||||
|
||||
/**
|
||||
* Store categories with menu item IDs.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $categories = array(
|
||||
'woocommerce' => array(),
|
||||
);
|
||||
|
||||
/**
|
||||
* Registered callbacks or URLs with migration boolean as key value pairs.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $callbacks = array();
|
||||
|
||||
/**
|
||||
* Get class instance.
|
||||
*/
|
||||
final public static function instance() {
|
||||
if ( ! static::$instance ) {
|
||||
static::$instance = new static();
|
||||
}
|
||||
return static::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init.
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'admin_menu', array( $this, 'add_core_items' ), 100 );
|
||||
add_filter( 'admin_enqueue_scripts', array( $this, 'enqueue_data' ), 20 );
|
||||
|
||||
add_filter( 'admin_menu', array( $this, 'migrate_core_child_items' ), PHP_INT_MAX - 1 );
|
||||
add_filter( 'admin_menu', array( $this, 'migrate_menu_items' ), PHP_INT_MAX - 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a WordPress menu callback to a URL.
|
||||
*
|
||||
* @param string $callback Menu callback.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_callback_url( $callback ) {
|
||||
// Return the full URL.
|
||||
if ( strpos( $callback, 'http' ) === 0 ) {
|
||||
return $callback;
|
||||
}
|
||||
|
||||
$pos = strpos( $callback, '?' );
|
||||
$file = $pos > 0 ? substr( $callback, 0, $pos ) : $callback;
|
||||
if ( file_exists( ABSPATH . "/wp-admin/$file" ) ) {
|
||||
return $callback;
|
||||
}
|
||||
return 'admin.php?page=' . $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent key if one exists.
|
||||
*
|
||||
* @param string $callback Callback or URL.
|
||||
* @return string|null
|
||||
*/
|
||||
public static function get_parent_key( $callback ) {
|
||||
global $submenu;
|
||||
|
||||
if ( ! $submenu ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// This is already a parent item.
|
||||
if ( isset( $submenu[ $callback ] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ( $submenu as $key => $menu ) {
|
||||
foreach ( $menu as $item ) {
|
||||
if ( $item[ self::CALLBACK ] === $callback ) {
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a top level menu item to the navigation.
|
||||
*
|
||||
* @param array $args Array containing the necessary arguments.
|
||||
* $args = array(
|
||||
* 'id' => (string) The unique ID of the menu item. Required.
|
||||
* 'title' => (string) Title of the menu item. Required.
|
||||
* 'url' => (string) URL or callback to be used. Required.
|
||||
* 'order' => (int) Menu item order.
|
||||
* 'migrate' => (bool) Whether or not to hide the item in the wp admin menu.
|
||||
* 'menuId' => (string) The ID of the menu to add the category to.
|
||||
* ).
|
||||
*/
|
||||
private static function add_category( $args ) {
|
||||
if ( ! isset( $args['id'] ) || isset( self::$menu_items[ $args['id'] ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$defaults = array(
|
||||
'id' => '',
|
||||
'title' => '',
|
||||
'order' => 100,
|
||||
'migrate' => true,
|
||||
'menuId' => 'primary',
|
||||
'isCategory' => true,
|
||||
);
|
||||
$menu_item = wp_parse_args( $args, $defaults );
|
||||
$menu_item['title'] = wp_strip_all_tags( wp_specialchars_decode( $menu_item['title'] ) );
|
||||
unset( $menu_item['url'] );
|
||||
unset( $menu_item['capability'] );
|
||||
|
||||
if ( ! isset( $menu_item['parent'] ) ) {
|
||||
$menu_item['parent'] = 'woocommerce';
|
||||
$menu_item['backButtonLabel'] = __(
|
||||
'WooCommerce Home',
|
||||
'woocommerce'
|
||||
);
|
||||
}
|
||||
|
||||
self::$menu_items[ $menu_item['id'] ] = $menu_item;
|
||||
self::$categories[ $menu_item['id'] ] = array();
|
||||
self::$categories[ $menu_item['parent'] ][] = $menu_item['id'];
|
||||
|
||||
if ( isset( $args['url'] ) ) {
|
||||
self::$callbacks[ $args['url'] ] = $menu_item['migrate'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a child menu item to the navigation.
|
||||
*
|
||||
* @param array $args Array containing the necessary arguments.
|
||||
* $args = array(
|
||||
* 'id' => (string) The unique ID of the menu item. Required.
|
||||
* 'title' => (string) Title of the menu item. Required.
|
||||
* 'parent' => (string) Parent menu item ID.
|
||||
* 'capability' => (string) Capability to view this menu item.
|
||||
* 'url' => (string) URL or callback to be used. Required.
|
||||
* 'order' => (int) Menu item order.
|
||||
* 'migrate' => (bool) Whether or not to hide the item in the wp admin menu.
|
||||
* 'menuId' => (string) The ID of the menu to add the item to.
|
||||
* 'matchExpression' => (string) A regular expression used to identify if the menu item is active.
|
||||
* ).
|
||||
*/
|
||||
private static function add_item( $args ) {
|
||||
if ( ! isset( $args['id'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( isset( self::$menu_items[ $args['id'] ] ) ) {
|
||||
error_log( // phpcs:ignore
|
||||
sprintf(
|
||||
/* translators: 1: Duplicate menu item path. */
|
||||
esc_html__( 'You have attempted to register a duplicate item with WooCommerce Navigation: %1$s', 'woocommerce' ),
|
||||
'`' . $args['id'] . '`'
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$defaults = array(
|
||||
'id' => '',
|
||||
'title' => '',
|
||||
'capability' => 'manage_woocommerce',
|
||||
'url' => '',
|
||||
'order' => 100,
|
||||
'migrate' => true,
|
||||
'menuId' => 'primary',
|
||||
);
|
||||
$menu_item = wp_parse_args( $args, $defaults );
|
||||
$menu_item['title'] = wp_strip_all_tags( wp_specialchars_decode( $menu_item['title'] ) );
|
||||
$menu_item['url'] = self::get_callback_url( $menu_item['url'] );
|
||||
|
||||
if ( ! isset( $menu_item['parent'] ) ) {
|
||||
$menu_item['parent'] = 'woocommerce';
|
||||
}
|
||||
|
||||
$menu_item['menuId'] = self::get_item_menu_id( $menu_item );
|
||||
|
||||
self::$menu_items[ $menu_item['id'] ] = $menu_item;
|
||||
self::$categories[ $menu_item['parent'] ][] = $menu_item['id'];
|
||||
|
||||
if ( isset( $args['url'] ) ) {
|
||||
self::$callbacks[ $args['url'] ] = $menu_item['migrate'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an item's menu ID from its parent.
|
||||
*
|
||||
* @param array $item Item args.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_item_menu_id( $item ) {
|
||||
$favorites = Favorites::get_all( get_current_user_id() );
|
||||
if ( is_array( $favorites ) && ! empty( $favorites ) && in_array( $item['id'], $favorites, true ) ) {
|
||||
return 'favorites';
|
||||
}
|
||||
|
||||
if ( isset( $item['parent'] ) && isset( self::$menu_items[ $item['parent'] ] ) ) {
|
||||
$menu_id = self::$menu_items[ $item['parent'] ]['menuId'];
|
||||
return 'favorites' === $menu_id
|
||||
? 'plugins'
|
||||
: $menu_id;
|
||||
}
|
||||
|
||||
return $item['menuId'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a plugin category.
|
||||
*
|
||||
* @param array $args Array containing the necessary arguments.
|
||||
* $args = array(
|
||||
* 'id' => (string) The unique ID of the menu item. Required.
|
||||
* 'title' => (string) Title of the menu item. Required.
|
||||
* 'url' => (string) URL or callback to be used. Required.
|
||||
* 'migrate' => (bool) Whether or not to hide the item in the wp admin menu.
|
||||
* 'order' => (int) Menu item order.
|
||||
* ).
|
||||
*/
|
||||
public static function add_plugin_category( $args ) {
|
||||
$category_args = array_merge(
|
||||
$args,
|
||||
array(
|
||||
'menuId' => 'plugins',
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! isset( $category_args['parent'] ) ) {
|
||||
unset( $category_args['order'] );
|
||||
}
|
||||
|
||||
$menu_id = self::get_item_menu_id( $category_args );
|
||||
if ( ! in_array( $menu_id, array( 'plugins', 'favorites' ), true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$category_args['menuId'] = $menu_id;
|
||||
|
||||
self::add_category( $category_args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a plugin item.
|
||||
*
|
||||
* @param array $args Array containing the necessary arguments.
|
||||
* $args = array(
|
||||
* 'id' => (string) The unique ID of the menu item. Required.
|
||||
* 'title' => (string) Title of the menu item. Required.
|
||||
* 'parent' => (string) Parent menu item ID.
|
||||
* 'capability' => (string) Capability to view this menu item.
|
||||
* 'url' => (string) URL or callback to be used. Required.
|
||||
* 'migrate' => (bool) Whether or not to hide the item in the wp admin menu.
|
||||
* 'order' => (int) Menu item order.
|
||||
* 'matchExpression' => (string) A regular expression used to identify if the menu item is active.
|
||||
* ).
|
||||
*/
|
||||
public static function add_plugin_item( $args ) {
|
||||
if ( ! isset( $args['parent'] ) ) {
|
||||
unset( $args['order'] );
|
||||
}
|
||||
|
||||
$item_args = array_merge(
|
||||
$args,
|
||||
array(
|
||||
'menuId' => 'plugins',
|
||||
)
|
||||
);
|
||||
|
||||
$menu_id = self::get_item_menu_id( $item_args );
|
||||
|
||||
if ( 'plugins' !== $menu_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::add_item( $item_args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a plugin setting item.
|
||||
*
|
||||
* @param array $args Array containing the necessary arguments.
|
||||
* $args = array(
|
||||
* 'id' => (string) The unique ID of the menu item. Required.
|
||||
* 'title' => (string) Title of the menu item. Required.
|
||||
* 'capability' => (string) Capability to view this menu item.
|
||||
* 'url' => (string) URL or callback to be used. Required.
|
||||
* 'migrate' => (bool) Whether or not to hide the item in the wp admin menu.
|
||||
* ).
|
||||
*/
|
||||
public static function add_setting_item( $args ) {
|
||||
unset( $args['order'] );
|
||||
|
||||
if ( isset( $args['parent'] ) || isset( $args['menuId'] ) ) {
|
||||
error_log( // phpcs:ignore
|
||||
sprintf(
|
||||
/* translators: 1: Duplicate menu item path. */
|
||||
esc_html__( 'The item ID %1$s attempted to register using an invalid option. The arguments `menuId` and `parent` are not allowed for add_setting_item()', 'woocommerce' ),
|
||||
'`' . $args['id'] . '`'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$item_args = array_merge(
|
||||
$args,
|
||||
array(
|
||||
'menuId' => 'secondary',
|
||||
'parent' => 'woocommerce-settings',
|
||||
)
|
||||
);
|
||||
|
||||
self::add_item( $item_args );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get menu item templates for a given post type.
|
||||
*
|
||||
* @param string $post_type Post type to add.
|
||||
* @param array $menu_args Arguments merged with the returned menu items.
|
||||
* @return array
|
||||
*/
|
||||
public static function get_post_type_items( $post_type, $menu_args = array() ) {
|
||||
$post_type_object = get_post_type_object( $post_type );
|
||||
|
||||
if ( ! $post_type_object || ! $post_type_object->show_in_menu ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parent = isset( $menu_args['parent'] ) ? $menu_args['parent'] . '-' : '';
|
||||
$match_expression = isset( $_GET['post'] ) && get_post_type( intval( $_GET['post'] ) ) === $post_type // phpcs:ignore WordPress.Security.NonceVerification
|
||||
? '(edit.php|post.php)'
|
||||
: null;
|
||||
|
||||
return array(
|
||||
'default' => array_merge(
|
||||
array(
|
||||
'title' => esc_attr( $post_type_object->labels->menu_name ),
|
||||
'capability' => $post_type_object->cap->edit_posts,
|
||||
'id' => $parent . $post_type,
|
||||
'url' => "edit.php?post_type={$post_type}",
|
||||
'matchExpression' => $match_expression,
|
||||
),
|
||||
$menu_args
|
||||
),
|
||||
'all' => array_merge(
|
||||
array(
|
||||
'title' => esc_attr( $post_type_object->labels->all_items ),
|
||||
'capability' => $post_type_object->cap->edit_posts,
|
||||
'id' => "{$parent}{$post_type}-all-items",
|
||||
'url' => "edit.php?post_type={$post_type}",
|
||||
'order' => 10,
|
||||
'matchExpression' => $match_expression,
|
||||
),
|
||||
$menu_args
|
||||
),
|
||||
'new' => array_merge(
|
||||
array(
|
||||
'title' => esc_attr( $post_type_object->labels->add_new ),
|
||||
'capability' => $post_type_object->cap->create_posts,
|
||||
'id' => "{$parent}{$post_type}-add-new",
|
||||
'url' => "post-new.php?post_type={$post_type}",
|
||||
'order' => 20,
|
||||
),
|
||||
$menu_args
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get menu item templates for a given taxonomy.
|
||||
*
|
||||
* @param string $taxonomy Taxonomy to add.
|
||||
* @param array $menu_args Arguments merged with the returned menu items.
|
||||
* @return array
|
||||
*/
|
||||
public static function get_taxonomy_items( $taxonomy, $menu_args = array() ) {
|
||||
$taxonomy_object = get_taxonomy( $taxonomy );
|
||||
|
||||
if ( ! $taxonomy_object || ! $taxonomy_object->show_in_menu ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parent = isset( $menu_args['parent'] ) ? $menu_args['parent'] . '-' : '';
|
||||
$product_type_query = ! empty( $taxonomy_object->object_type )
|
||||
? "&post_type={$taxonomy_object->object_type[0]}"
|
||||
: '';
|
||||
$match_expression = 'term.php'; // Match term.php pages.
|
||||
$match_expression .= "(?=.*[?|&]taxonomy={$taxonomy}(&|$|#))"; // Lookahead to match a taxonomy URL param.
|
||||
$match_expression .= '|'; // Or.
|
||||
$match_expression .= 'edit-tags.php'; // Match edit-tags.php pages.
|
||||
$match_expression .= "(?=.*[?|&]taxonomy={$taxonomy}(&|$|#))"; // Lookahead to match a taxonomy URL param.
|
||||
|
||||
return array(
|
||||
'default' => array_merge(
|
||||
array(
|
||||
'title' => esc_attr( $taxonomy_object->labels->menu_name ),
|
||||
'capability' => $taxonomy_object->cap->edit_terms,
|
||||
'id' => $parent . $taxonomy,
|
||||
'url' => "edit-tags.php?taxonomy={$taxonomy}{$product_type_query}",
|
||||
'matchExpression' => $match_expression,
|
||||
),
|
||||
$menu_args
|
||||
),
|
||||
'all' => array_merge(
|
||||
array(
|
||||
'title' => esc_attr( $taxonomy_object->labels->all_items ),
|
||||
'capability' => $taxonomy_object->cap->edit_terms,
|
||||
'id' => "{$parent}{$taxonomy}-all-items",
|
||||
'url' => "edit-tags.php?taxonomy={$taxonomy}{$product_type_query}",
|
||||
'matchExpression' => $match_expression,
|
||||
'order' => 10,
|
||||
),
|
||||
$menu_args
|
||||
),
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add core menu items.
|
||||
*/
|
||||
public function add_core_items() {
|
||||
$categories = CoreMenu::get_categories();
|
||||
foreach ( $categories as $category ) {
|
||||
self::add_category( $category );
|
||||
}
|
||||
|
||||
$items = CoreMenu::get_items();
|
||||
foreach ( $items as $item ) {
|
||||
if ( isset( $item['is_category'] ) && $item['is_category'] ) {
|
||||
self::add_category( $item );
|
||||
} else {
|
||||
self::add_item( $item );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an item or taxonomy.
|
||||
*
|
||||
* @param array $menu_item Menu item.
|
||||
*/
|
||||
public function add_item_and_taxonomy( $menu_item ) {
|
||||
if ( in_array( $menu_item[2], CoreMenu::get_excluded_items(), true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$menu_item[2] = htmlspecialchars_decode( $menu_item[2] );
|
||||
|
||||
// Don't add already added items.
|
||||
$callbacks = self::get_callbacks();
|
||||
if ( array_key_exists( $menu_item[2], $callbacks ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't add these Product submenus because they are added elsewhere.
|
||||
if ( in_array( $menu_item[2], array( 'product_importer', 'product_exporter', 'product_attributes' ), true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::add_plugin_item(
|
||||
array(
|
||||
'title' => $menu_item[0],
|
||||
'capability' => $menu_item[1],
|
||||
'id' => sanitize_title( $menu_item[0] ),
|
||||
'url' => $menu_item[2],
|
||||
)
|
||||
);
|
||||
|
||||
// Determine if migrated items are a taxonomy or post_type. If they are, register them.
|
||||
$parsed_url = wp_parse_url( $menu_item[2] );
|
||||
$query_string = isset( $parsed_url['query'] ) ? $parsed_url['query'] : false;
|
||||
|
||||
if ( $query_string ) {
|
||||
$query = array();
|
||||
parse_str( $query_string, $query );
|
||||
|
||||
if ( isset( $query['taxonomy'] ) ) {
|
||||
Screen::register_taxonomy( $query['taxonomy'] );
|
||||
} elseif ( isset( $query['post_type'] ) ) {
|
||||
Screen::register_post_type( $query['post_type'] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate any remaining WooCommerce child items.
|
||||
*
|
||||
* @param array $menu Menu items.
|
||||
* @return array
|
||||
*/
|
||||
public function migrate_core_child_items( $menu ) {
|
||||
global $submenu;
|
||||
|
||||
if ( ! isset( $submenu['woocommerce'] ) && ! isset( $submenu['edit.php?post_type=product'] ) ) {
|
||||
return $menu;
|
||||
}
|
||||
|
||||
$main_items = isset( $submenu['woocommerce'] ) ? $submenu['woocommerce'] : array();
|
||||
$product_items = isset( $submenu['edit.php?post_type=product'] ) ? $submenu['edit.php?post_type=product'] : array();
|
||||
|
||||
foreach ( $main_items as $key => $menu_item ) {
|
||||
self::add_item_and_taxonomy( $menu_item );
|
||||
// phpcs:disable
|
||||
if ( ! isset( $menu_item[ self::CSS_CLASSES ] ) ) {
|
||||
$submenu['woocommerce'][ $key ][] .= ' hide-if-js';
|
||||
} else if ( strpos( $submenu['woocommerce'][ $key ][ self::CSS_CLASSES ], 'hide-if-js' ) !== false ) {
|
||||
continue;
|
||||
} else {
|
||||
$submenu['woocommerce'][ $key ][ self::CSS_CLASSES ] .= ' hide-if-js';
|
||||
}
|
||||
// phpcs:enable
|
||||
}
|
||||
|
||||
foreach ( $product_items as $key => $menu_item ) {
|
||||
self::add_item_and_taxonomy( $menu_item );
|
||||
}
|
||||
|
||||
return $menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a menu item's callback is registered in the menu.
|
||||
*
|
||||
* @param array $menu_item Menu item args.
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_callback( $menu_item ) {
|
||||
if ( ! $menu_item || ! isset( $menu_item[ self::CALLBACK ] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$callback = $menu_item[ self::CALLBACK ];
|
||||
|
||||
if (
|
||||
isset( self::$callbacks[ $callback ] ) &&
|
||||
self::$callbacks[ $callback ]
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
isset( self::$callbacks[ self::get_callback_url( $callback ) ] ) &&
|
||||
self::$callbacks[ self::get_callback_url( $callback ) ]
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides all WP admin menus items and adds screen IDs to check for new items.
|
||||
*/
|
||||
public static function migrate_menu_items() {
|
||||
global $menu, $submenu;
|
||||
|
||||
foreach ( $menu as $key => $menu_item ) {
|
||||
if ( self::has_callback( $menu_item ) ) {
|
||||
// Disable phpcs since we need to override submenu classes.
|
||||
// Note that `phpcs:ignore WordPress.Variables.GlobalVariables.OverrideProhibited` does not work to disable this check.
|
||||
// phpcs:disable
|
||||
$menu[ $key ][ self::CSS_CLASSES ] .= ' hide-if-js';
|
||||
// phps:enable
|
||||
continue;
|
||||
}
|
||||
|
||||
// WordPress core menus make the parent item the same URL as the first child.
|
||||
$has_children = isset( $submenu[ $menu_item[ self::CALLBACK ] ] ) && isset( $submenu[ $menu_item[ self::CALLBACK ] ][0] );
|
||||
$first_child = $has_children ? $submenu[ $menu_item[ self::CALLBACK ] ][0] : null;
|
||||
if ( 'woocommerce' !== $menu_item[2] && self::has_callback( $first_child ) ) {
|
||||
// Disable phpcs since we need to override submenu classes.
|
||||
// Note that `phpcs:ignore WordPress.Variables.GlobalVariables.OverrideProhibited` does not work to disable this check.
|
||||
// phpcs:disable
|
||||
$menu[ $key ][ self::CSS_CLASSES ] .= ' hide-if-js';
|
||||
// phps:enable
|
||||
}
|
||||
}
|
||||
|
||||
// Remove excluded submenu items
|
||||
if ( isset( $submenu['woocommerce'] ) ) {
|
||||
foreach ( $submenu['woocommerce'] as $key => $submenu_item ) {
|
||||
if ( in_array( $submenu_item[ self::CALLBACK ], CoreMenu::get_excluded_items(), true ) ) {
|
||||
if ( isset( $submenu['woocommerce'][ $key ][ self::CSS_CLASSES ] ) ) {
|
||||
$submenu['woocommerce'][ $key ][ self::CSS_CLASSES ] .= ' hide-if-js';
|
||||
} else {
|
||||
$submenu['woocommerce'][ $key ][] = 'hide-if-js';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $submenu as $parent_key => $parent ) {
|
||||
foreach ( $parent as $key => $menu_item ) {
|
||||
if ( self::has_callback( $menu_item ) ) {
|
||||
// Disable phpcs since we need to override submenu classes.
|
||||
// Note that `phpcs:ignore WordPress.Variables.GlobalVariables.OverrideProhibited` does not work to disable this check.
|
||||
// phpcs:disable
|
||||
if ( ! isset( $menu_item[ self::SLUG ] ) ) {
|
||||
$submenu[ $parent_key ][ $key ][] = '';
|
||||
}
|
||||
if ( ! isset( $menu_item[ self::CSS_CLASSES ] ) ) {
|
||||
$submenu[ $parent_key ][ $key ][] .= ' hide-if-js';
|
||||
} else {
|
||||
$submenu[ $parent_key ][ $key ][ self::CSS_CLASSES ] .= ' hide-if-js';
|
||||
}
|
||||
// phps:enable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( array_keys( self::$callbacks ) as $callback ) {
|
||||
Screen::add_screen( $callback );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a callback to identify and hide pages in the WP menu.
|
||||
*/
|
||||
public static function hide_wp_menu_item( $callback ) {
|
||||
self::$callbacks[ $callback ] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get registered menu items.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_items() {
|
||||
return apply_filters( 'woocommerce_navigation_menu_items', self::$menu_items );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get registered menu items.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_category_items( $category ) {
|
||||
if ( ! isset( self::$categories[ $category ] ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$menu_item_ids = self::$categories[ $category ];
|
||||
|
||||
$category_menu_items = array();
|
||||
foreach ( $menu_item_ids as $id ) {
|
||||
if ( isset( self::$menu_items[ $id ] ) ) {
|
||||
$category_menu_items[] = self::$menu_items[ $id ];
|
||||
}
|
||||
}
|
||||
|
||||
return apply_filters( 'woocommerce_navigation_menu_category_items', $category_menu_items );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get registered callbacks.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_callbacks() {
|
||||
return apply_filters( 'woocommerce_navigation_callbacks', self::$callbacks );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the menu item data mapped by category and menu ID.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_mapped_menu_items() {
|
||||
$menu_items = self::get_items();
|
||||
$mapped_items = array();
|
||||
|
||||
// Sort the items by order and title.
|
||||
$order = array_column( $menu_items, 'order' );
|
||||
$title = array_column( $menu_items, 'title' );
|
||||
array_multisort( $order, SORT_ASC, $title, SORT_ASC, $menu_items );
|
||||
|
||||
foreach ( $menu_items as $id => $menu_item ) {
|
||||
$category_id = $menu_item[ 'parent' ];
|
||||
$menu_id = $menu_item[ 'menuId' ];
|
||||
if ( ! isset( $mapped_items[ $category_id ] ) ) {
|
||||
$mapped_items[ $category_id ] = array();
|
||||
foreach ( self::MENU_IDS as $available_menu_id ) {
|
||||
$mapped_items[ $category_id ][ $available_menu_id ] = array();
|
||||
}
|
||||
}
|
||||
|
||||
// Incorrect menu ID.
|
||||
if ( ! isset( $mapped_items[ $category_id ][ $menu_id ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove the item if the user cannot access it.
|
||||
if ( isset( $menu_item[ 'capability' ] ) && ! current_user_can( $menu_item[ 'capability' ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$mapped_items[ $category_id ][ $menu_id ][] = $menu_item;
|
||||
}
|
||||
|
||||
return $mapped_items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the menu to the page output.
|
||||
*
|
||||
* @param array $menu Menu items.
|
||||
* @return array
|
||||
*/
|
||||
public function enqueue_data( $menu ) {
|
||||
$data = array(
|
||||
'menuItems' => array_values( self::get_items() ),
|
||||
'rootBackUrl' => get_dashboard_url(),
|
||||
);
|
||||
|
||||
wp_add_inline_script( WC_ADMIN_APP, 'window.wcNavigation = ' . wp_json_encode( $data ), 'before' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Navigation Screen
|
||||
*
|
||||
* @package Woocommerce Navigation
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\Navigation;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\Navigation\Menu;
|
||||
|
||||
/**
|
||||
* Contains logic for the WooCommerce Navigation menu.
|
||||
*/
|
||||
class Screen {
|
||||
/**
|
||||
* Class instance.
|
||||
*
|
||||
* @var Screen instance
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* Screen IDs of registered pages.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $screen_ids = array();
|
||||
|
||||
/**
|
||||
* Registered post types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $post_types = array();
|
||||
|
||||
/**
|
||||
* Registered taxonomies.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $taxonomies = array();
|
||||
|
||||
/**
|
||||
* Get class instance.
|
||||
*/
|
||||
final public static function instance() {
|
||||
if ( ! static::$instance ) {
|
||||
static::$instance = new static();
|
||||
}
|
||||
return static::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init.
|
||||
*/
|
||||
public function init() {
|
||||
add_filter( 'admin_body_class', array( $this, 'add_body_class' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of filtered screen ids.
|
||||
*/
|
||||
public static function get_screen_ids() {
|
||||
return apply_filters( 'woocommerce_navigation_screen_ids', self::$screen_ids );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of registered post types.
|
||||
*/
|
||||
public static function get_post_types() {
|
||||
return apply_filters( 'woocommerce_navigation_post_types', self::$post_types );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of registered post types.
|
||||
*/
|
||||
public static function get_taxonomies() {
|
||||
return apply_filters( 'woocommerce_navigation_taxonomies', self::$taxonomies );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we're on a WooCommerce page
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_woocommerce_page() {
|
||||
global $pagenow;
|
||||
|
||||
// Get taxonomy if on a taxonomy screen.
|
||||
$taxonomy = '';
|
||||
if ( in_array( $pagenow, array( 'edit-tags.php', 'term.php' ), true ) ) {
|
||||
if ( isset( $_GET['taxonomy'] ) ) { // phpcs:ignore CSRF ok.
|
||||
$taxonomy = sanitize_text_field( wp_unslash( $_GET['taxonomy'] ) ); // phpcs:ignore CSRF ok.
|
||||
}
|
||||
}
|
||||
$taxonomies = self::get_taxonomies();
|
||||
|
||||
// Get post type if on a post screen.
|
||||
$post_type = '';
|
||||
if ( in_array( $pagenow, array( 'edit.php', 'post.php', 'post-new.php' ), true ) ) {
|
||||
if ( isset( $_GET['post'] ) ) { // phpcs:ignore CSRF ok.
|
||||
$post_type = get_post_type( (int) $_GET['post'] ); // phpcs:ignore CSRF ok.
|
||||
} elseif ( isset( $_GET['post_type'] ) ) { // phpcs:ignore CSRF ok.
|
||||
$post_type = sanitize_text_field( wp_unslash( $_GET['post_type'] ) ); // phpcs:ignore CSRF ok.
|
||||
}
|
||||
}
|
||||
$post_types = self::get_post_types();
|
||||
|
||||
// Get current screen ID.
|
||||
$current_screen = get_current_screen();
|
||||
$screen_ids = self::get_screen_ids();
|
||||
$current_screen_id = $current_screen ? $current_screen->id : null;
|
||||
|
||||
if (
|
||||
in_array( $post_type, $post_types, true ) ||
|
||||
in_array( $taxonomy, $taxonomies, true ) ||
|
||||
self::is_woocommerce_core_taxonomy( $taxonomy ) ||
|
||||
in_array( $current_screen_id, $screen_ids, true )
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given taxonomy is a WooCommerce core related taxonomy.
|
||||
*
|
||||
* @param string $taxonomy Taxonomy.
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_woocommerce_core_taxonomy( $taxonomy ) {
|
||||
if ( in_array( $taxonomy, array( 'product_cat', 'product_tag' ), true ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( 'pa_' === substr( $taxonomy, 0, 3 ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add navigation classes to body.
|
||||
*
|
||||
* @param string $classes Classes.
|
||||
* @return string
|
||||
*/
|
||||
public function add_body_class( $classes ) {
|
||||
if ( self::is_woocommerce_page() ) {
|
||||
$classes .= ' has-woocommerce-navigation';
|
||||
|
||||
/**
|
||||
* Adds the ability to skip disabling of the WP toolbar.
|
||||
*
|
||||
* @param boolean $bool WP Toolbar disabled.
|
||||
*/
|
||||
if ( apply_filters( 'woocommerce_navigation_wp_toolbar_disabled', true ) ) {
|
||||
$classes .= ' is-wp-toolbar-disabled';
|
||||
}
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a screen ID to the list of screens that use the navigtion.
|
||||
* Finds the parent if none is given to grab the correct screen ID.
|
||||
*
|
||||
* @param string $callback Callback or URL for page.
|
||||
* @param string|null $parent Parent screen ID.
|
||||
*/
|
||||
public static function add_screen( $callback, $parent = null ) {
|
||||
global $submenu;
|
||||
|
||||
$plugin_page = self::get_plugin_page( $callback );
|
||||
|
||||
if ( ! $parent ) {
|
||||
$parent = Menu::get_parent_key( $callback );
|
||||
}
|
||||
|
||||
$screen_id = get_plugin_page_hookname( $plugin_page, $parent );
|
||||
|
||||
// This screen has already been added.
|
||||
if ( in_array( $screen_id, self::$screen_ids, true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::$screen_ids[] = $screen_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin page slug.
|
||||
*
|
||||
* @param string $callback Callback.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_plugin_page( $callback ) {
|
||||
$url = Menu::get_callback_url( $callback );
|
||||
$parts = wp_parse_url( $url );
|
||||
|
||||
if ( ! isset( $parts['query'] ) ) {
|
||||
return $callback;
|
||||
}
|
||||
|
||||
parse_str( $parts['query'], $query );
|
||||
|
||||
if ( ! isset( $query['page'] ) ) {
|
||||
return $callback;
|
||||
}
|
||||
|
||||
$plugin_page = wp_unslash( $query['page'] );
|
||||
$plugin_page = plugin_basename( $plugin_page );
|
||||
return $plugin_page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register post type for use in WooCommerce Navigation screens.
|
||||
*
|
||||
* @param string $post_type Post type to add.
|
||||
*/
|
||||
public static function register_post_type( $post_type ) {
|
||||
if ( ! in_array( $post_type, self::$post_types, true ) ) {
|
||||
self::$post_types[] = $post_type;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register taxonomy for use in WooCommerce Navigation screens.
|
||||
*
|
||||
* @param string $taxonomy Taxonomy to add.
|
||||
*/
|
||||
public static function register_taxonomy( $taxonomy ) {
|
||||
if ( ! in_array( $taxonomy, self::$taxonomies, true ) ) {
|
||||
self::$taxonomies[] = $taxonomy;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce New Product Management Experience
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\TransientNotices;
|
||||
use Automattic\WooCommerce\Admin\PageController;
|
||||
use Automattic\WooCommerce\Internal\Admin\Loader;
|
||||
use WP_Block_Editor_Context;
|
||||
|
||||
/**
|
||||
* Loads assets related to the new product management experience page.
|
||||
*/
|
||||
class NewProductManagementExperience {
|
||||
|
||||
/**
|
||||
* Option name used to toggle this feature.
|
||||
*/
|
||||
const TOGGLE_OPTION_NAME = 'woocommerce_new_product_management_enabled';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->maybe_show_disabled_notice();
|
||||
if ( ! Features::is_enabled( 'new-product-management-experience' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) );
|
||||
add_action( 'get_edit_post_link', array( $this, 'update_edit_product_link' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe show disabled notice.
|
||||
*/
|
||||
public function maybe_show_disabled_notice() {
|
||||
$new_product_experience_param = 'new-product-experience-disabled';
|
||||
if ( isset( $_GET[ $new_product_experience_param ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
TransientNotices::add(
|
||||
array(
|
||||
'user_id' => get_current_user_id(),
|
||||
'id' => 'new-product-experience-disbled',
|
||||
'status' => 'success',
|
||||
'content' => __( '🌟 Thanks for the feedback. We’ll put it to good use!', 'woocommerce' ),
|
||||
)
|
||||
);
|
||||
|
||||
$url = isset( $_SERVER['REQUEST_URI'] ) ? wc_clean( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
|
||||
$url = remove_query_arg( 'new-product-experience-disabled', $url );
|
||||
wp_safe_redirect( $url );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue styles needed for the rich text editor.
|
||||
*/
|
||||
public function enqueue_styles() {
|
||||
if ( ! PageController::is_admin_or_embed_page() ) {
|
||||
return;
|
||||
}
|
||||
wp_enqueue_style( 'wp-edit-blocks' );
|
||||
wp_enqueue_style( 'wp-format-library' );
|
||||
wp_enqueue_editor();
|
||||
/**
|
||||
* Enqueue any block editor related assets.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*/
|
||||
do_action( 'enqueue_block_editor_assets' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the edit product links when the new experience is enabled.
|
||||
*
|
||||
* @param string $link The edit link.
|
||||
* @param int $post_id Post ID.
|
||||
* @return string
|
||||
*/
|
||||
public function update_edit_product_link( $link, $post_id ) {
|
||||
$product = wc_get_product( $post_id );
|
||||
|
||||
if ( ! $product ) {
|
||||
return $link;
|
||||
}
|
||||
|
||||
if ( $product->get_type() === 'simple' ) {
|
||||
return admin_url( 'admin.php?page=wc-admin&path=/product/' . $product->get_id() );
|
||||
}
|
||||
|
||||
return $link;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Onboarding
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features;
|
||||
|
||||
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
|
||||
|
||||
/**
|
||||
* Contains backend logic for the onboarding profile and checklist feature.
|
||||
*
|
||||
* @deprecated since 6.3.0, use WooCommerce\Internal\Admin\Onboarding.
|
||||
*/
|
||||
class Onboarding extends DeprecatedClassFacade {
|
||||
/**
|
||||
* The name of the non-deprecated class that this facade covers.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\Features\Onboarding';
|
||||
|
||||
/**
|
||||
* The version that this class was deprecated in.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $deprecated_in_version = '6.3.0';
|
||||
|
||||
/**
|
||||
* Hook into WooCommerce.
|
||||
*/
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of allowed industries for the onboarding wizard.
|
||||
*
|
||||
* @deprecated 6.3.0
|
||||
* @return array
|
||||
*/
|
||||
public static function get_allowed_industries() {
|
||||
wc_deprecated_function( 'get_allowed_industries', '6.3', '\Automattic\WooCommerce\Internal\Admin\OnboardingIndustries::get_allowed_industries()' );
|
||||
return \Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingIndustries::get_allowed_industries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of allowed product types for the onboarding wizard.
|
||||
*
|
||||
* @deprecated 6.3.0
|
||||
* @return array
|
||||
*/
|
||||
public static function get_allowed_product_types() {
|
||||
wc_deprecated_function( 'get_allowed_product_types', '6.3', '\Automattic\WooCommerce\Internal\Admin\OnboardingProducts::get_allowed_product_types()' );
|
||||
return \Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProducts::get_allowed_product_types();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of themes for the onboarding wizard.
|
||||
*
|
||||
* @deprecated 6.3.0
|
||||
* @return array
|
||||
*/
|
||||
public static function get_themes() {
|
||||
wc_deprecated_function( 'get_themes', '6.3', '\Automattic\WooCommerce\Internal\Admin\OnboardingThemes::get_themes()' );
|
||||
return \Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingThemes::get_themes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get theme data used in onboarding theme browser.
|
||||
*
|
||||
* @deprecated 6.3.0
|
||||
* @param WP_Theme $theme Theme to gather data from.
|
||||
* @return array
|
||||
*/
|
||||
public static function get_theme_data( $theme ) {
|
||||
wc_deprecated_function( 'get_theme_data', '6.3', '\Automattic\WooCommerce\Internal\Admin\OnboardingThemes::get_theme_data()' );
|
||||
return \Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingThemes::get_theme_data();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of themes that can be installed & activated via the onboarding wizard.
|
||||
*
|
||||
* @deprecated 6.3.0
|
||||
* @return array
|
||||
*/
|
||||
public static function get_allowed_themes() {
|
||||
wc_deprecated_function( 'get_allowed_themes', '6.3', '\Automattic\WooCommerce\Internal\Admin\OnboardingThemes::get_allowed_themes()' );
|
||||
return \Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingThemes::get_allowed_themes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dynamic product data from API.
|
||||
*
|
||||
* @deprecated 6.3.0
|
||||
* @param array $product_types Array of product types.
|
||||
* @return array
|
||||
*/
|
||||
public static function get_product_data( $product_types ) {
|
||||
wc_deprecated_function( 'get_product_data', '6.3', '\Automattic\WooCommerce\Internal\Admin\OnboardingProducts::get_product_data()' );
|
||||
return \Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProducts::get_product_data();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
/**
|
||||
* A temporary class for creating tasks on the fly from deprecated tasks.
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks;
|
||||
|
||||
/**
|
||||
* DeprecatedExtendedTask class.
|
||||
*/
|
||||
class DeprecatedExtendedTask extends Task {
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id = '';
|
||||
|
||||
/**
|
||||
* Additional info.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $additional_info = '';
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $content = '';
|
||||
|
||||
/**
|
||||
* Whether the task is complete or not.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $is_complete = false;
|
||||
|
||||
/**
|
||||
* Snoozeable.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $is_snoozeable = false;
|
||||
|
||||
/**
|
||||
* Dismissable.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $is_dismissable = false;
|
||||
|
||||
/**
|
||||
* Whether the store is capable of viewing the task.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $can_view = true;
|
||||
|
||||
/**
|
||||
* Level.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $level = 3;
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $time;
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $title = '';
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param TaskList $task_list Parent task list.
|
||||
* @param array $args Array of task args.
|
||||
*/
|
||||
public function __construct( $task_list, $args ) {
|
||||
parent::__construct( $task_list );
|
||||
$task_args = wp_parse_args(
|
||||
$args,
|
||||
array(
|
||||
'id' => null,
|
||||
'is_dismissable' => false,
|
||||
'is_snoozeable' => false,
|
||||
'can_view' => true,
|
||||
'level' => 3,
|
||||
'additional_info' => null,
|
||||
'content' => '',
|
||||
'title' => '',
|
||||
'is_complete' => false,
|
||||
'time' => null,
|
||||
)
|
||||
);
|
||||
|
||||
$this->id = $task_args['id'];
|
||||
$this->additional_info = $task_args['additional_info'];
|
||||
$this->content = $task_args['content'];
|
||||
$this->is_complete = $task_args['is_complete'];
|
||||
$this->is_dismissable = $task_args['is_dismissable'];
|
||||
$this->is_snoozeable = $task_args['is_snoozeable'];
|
||||
$this->can_view = $task_args['can_view'];
|
||||
$this->level = $task_args['level'];
|
||||
$this->time = $task_args['time'];
|
||||
$this->title = $task_args['title'];
|
||||
}
|
||||
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional info.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_additional_info() {
|
||||
return $this->additional_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content() {
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Level.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_level() {
|
||||
return $this->level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function get_time() {
|
||||
return $this->time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a task is snoozeable.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_snoozeable() {
|
||||
return $this->is_snoozeable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a task is dismissable.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_dismissable() {
|
||||
return $this->is_dismissable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a task is dismissable.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
return $this->is_complete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a task is dismissable.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_view() {
|
||||
return $this->can_view;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/**
|
||||
* Filters for maintaining backwards compatibility with deprecated options.
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\TaskList;
|
||||
use WC_Install;
|
||||
|
||||
/**
|
||||
* DeprecatedOptions class.
|
||||
*/
|
||||
class DeprecatedOptions {
|
||||
/**
|
||||
* Initialize.
|
||||
*/
|
||||
public static function init() {
|
||||
add_filter( 'pre_option_woocommerce_task_list_hidden', array( __CLASS__, 'get_deprecated_options' ), 10, 2 );
|
||||
add_filter( 'pre_option_woocommerce_extended_task_list_hidden', array( __CLASS__, 'get_deprecated_options' ), 10, 2 );
|
||||
add_action( 'pre_update_option_woocommerce_task_list_hidden', array( __CLASS__, 'update_deprecated_options' ), 10, 3 );
|
||||
add_action( 'pre_update_option_woocommerce_extended_task_list_hidden', array( __CLASS__, 'update_deprecated_options' ), 10, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the values from the correct source when attempting to retrieve deprecated options.
|
||||
*
|
||||
* @param string $pre_option Pre option value.
|
||||
* @param string $option Option name.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_deprecated_options( $pre_option, $option ) {
|
||||
if ( defined( 'WC_INSTALLING' ) && WC_INSTALLING === true ) {
|
||||
return $pre_option;
|
||||
}
|
||||
|
||||
$hidden = get_option( 'woocommerce_task_list_hidden_lists', array() );
|
||||
switch ( $option ) {
|
||||
case 'woocommerce_task_list_hidden':
|
||||
return in_array( 'setup', $hidden, true ) ? 'yes' : 'no';
|
||||
case 'woocommerce_extended_task_list_hidden':
|
||||
return in_array( 'extended', $hidden, true ) ? 'yes' : 'no';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the new option names when deprecated options are updated.
|
||||
* This is a temporary fallback until we can fully remove the old task list components.
|
||||
*
|
||||
* @param string $value New value.
|
||||
* @param string $old_value Old value.
|
||||
* @param string $option Option name.
|
||||
* @return string
|
||||
*/
|
||||
public static function update_deprecated_options( $value, $old_value, $option ) {
|
||||
switch ( $option ) {
|
||||
case 'woocommerce_task_list_hidden':
|
||||
$task_list = TaskLists::get_list( 'setup' );
|
||||
if ( ! $task_list ) {
|
||||
return;
|
||||
}
|
||||
$update = 'yes' === $value ? $task_list->hide() : $task_list->unhide();
|
||||
delete_option( 'woocommerce_task_list_hidden' );
|
||||
return false;
|
||||
case 'woocommerce_extended_task_list_hidden':
|
||||
$task_list = TaskLists::get_list( 'extended' );
|
||||
if ( ! $task_list ) {
|
||||
return;
|
||||
}
|
||||
$update = 'yes' === $value ? $task_list->hide() : $task_list->unhide();
|
||||
delete_option( 'woocommerce_extended_task_list_hidden' );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Onboarding Tasks
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\DeprecatedOptions;
|
||||
|
||||
/**
|
||||
* Contains the logic for completing onboarding tasks.
|
||||
*/
|
||||
class Init {
|
||||
/**
|
||||
* Class instance.
|
||||
*
|
||||
* @var OnboardingTasks instance
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* Get class instance.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
DeprecatedOptions::init();
|
||||
TaskLists::init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get task item data for settings filter.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_settings() {
|
||||
$settings = array();
|
||||
$wc_pay_is_connected = false;
|
||||
if ( class_exists( '\WC_Payments' ) ) {
|
||||
$wc_payments_gateway = \WC_Payments::get_gateway();
|
||||
$wc_pay_is_connected = method_exists( $wc_payments_gateway, 'is_connected' )
|
||||
? $wc_payments_gateway->is_connected()
|
||||
: false;
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,607 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles task related methods.
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks;
|
||||
|
||||
use Automattic\WooCommerce\Internal\Admin\WCAdminUser;
|
||||
|
||||
/**
|
||||
* Task class.
|
||||
*/
|
||||
abstract class Task {
|
||||
/**
|
||||
* Task traits.
|
||||
*/
|
||||
use TaskTraits;
|
||||
|
||||
/**
|
||||
* Name of the dismiss option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const DISMISSED_OPTION = 'woocommerce_task_list_dismissed_tasks';
|
||||
|
||||
/**
|
||||
* Name of the snooze option.
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
* @deprecated 7.2.0
|
||||
*/
|
||||
const SNOOZED_OPTION = 'woocommerce_task_list_remind_me_later_tasks';
|
||||
|
||||
/**
|
||||
* Name of the actioned option.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const ACTIONED_OPTION = 'woocommerce_task_list_tracked_completed_actions';
|
||||
|
||||
/**
|
||||
* Option name of completed tasks.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const COMPLETED_OPTION = 'woocommerce_task_list_tracked_completed_tasks';
|
||||
|
||||
/**
|
||||
* Name of the active task transient.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const ACTIVE_TASK_TRANSIENT = 'wc_onboarding_active_task';
|
||||
|
||||
/**
|
||||
* Parent task list.
|
||||
*
|
||||
* @var TaskList
|
||||
*/
|
||||
protected $task_list;
|
||||
|
||||
/**
|
||||
* Duration to milisecond mapping.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $duration_to_ms = array(
|
||||
'day' => DAY_IN_SECONDS * 1000,
|
||||
'hour' => HOUR_IN_SECONDS * 1000,
|
||||
'week' => WEEK_IN_SECONDS * 1000,
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param TaskList|null $task_list Parent task list.
|
||||
*/
|
||||
public function __construct( $task_list = null ) {
|
||||
$this->task_list = $task_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function get_id();
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function get_title();
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function get_content();
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function get_time();
|
||||
|
||||
/**
|
||||
* Parent ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_parent_id() {
|
||||
if ( ! $this->task_list ) {
|
||||
return '';
|
||||
}
|
||||
return $this->task_list->get_list_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get task list options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_parent_options() {
|
||||
if ( ! $this->task_list ) {
|
||||
return array();
|
||||
}
|
||||
return $this->task_list->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom option.
|
||||
*
|
||||
* @param string $option_name name of custom option.
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function get_parent_option( $option_name ) {
|
||||
if ( $this->task_list && isset( $this->task_list->options[ $option_name ] ) ) {
|
||||
return $this->task_list->options[ $option_name ];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prefix event for track event naming.
|
||||
*
|
||||
* @param string $event_name Event name.
|
||||
* @return string
|
||||
*/
|
||||
public function prefix_event( $event_name ) {
|
||||
if ( ! $this->task_list ) {
|
||||
return '';
|
||||
}
|
||||
return $this->task_list->prefix_event( $event_name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional info.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_additional_info() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional data.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_additional_data() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Badge.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_badge() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Level.
|
||||
*
|
||||
* @deprecated 7.2.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_level() {
|
||||
wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '7.2.0' );
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Action label.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_action_label() {
|
||||
return __( "Let's go", 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Action URL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_action_url() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a task is dismissable.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_dismissable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bool for task dismissal.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_dismissed() {
|
||||
if ( ! $this->is_dismissable() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$dismissed = get_option( self::DISMISSED_OPTION, array() );
|
||||
|
||||
return in_array( $this->get_id(), $dismissed, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss the task.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function dismiss() {
|
||||
if ( ! $this->is_dismissable() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$dismissed = get_option( self::DISMISSED_OPTION, array() );
|
||||
$dismissed[] = $this->get_id();
|
||||
$update = update_option( self::DISMISSED_OPTION, array_unique( $dismissed ) );
|
||||
|
||||
if ( $update ) {
|
||||
$this->record_tracks_event( 'dismiss_task', array( 'task_name' => $this->get_id() ) );
|
||||
}
|
||||
|
||||
return $update;
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo task dismissal.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function undo_dismiss() {
|
||||
$dismissed = get_option( self::DISMISSED_OPTION, array() );
|
||||
$dismissed = array_diff( $dismissed, array( $this->get_id() ) );
|
||||
$update = update_option( self::DISMISSED_OPTION, $dismissed );
|
||||
|
||||
if ( $update ) {
|
||||
$this->record_tracks_event( 'undo_dismiss_task', array( 'task_name' => $this->get_id() ) );
|
||||
}
|
||||
|
||||
return $update;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a task is snoozeable.
|
||||
*
|
||||
* @deprecated 7.2.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_snoozeable() {
|
||||
wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '7.2.0' );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the snoozed until datetime.
|
||||
*
|
||||
* @deprecated 7.2.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_snoozed_until() {
|
||||
wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '7.2.0' );
|
||||
|
||||
$snoozed_tasks = get_option( self::SNOOZED_OPTION, array() );
|
||||
if ( isset( $snoozed_tasks[ $this->get_id() ] ) ) {
|
||||
return $snoozed_tasks[ $this->get_id() ];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bool for task snoozed.
|
||||
*
|
||||
* @deprecated 7.2.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_snoozed() {
|
||||
wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '7.2.0' );
|
||||
|
||||
if ( ! $this->is_snoozeable() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$snoozed = get_option( self::SNOOZED_OPTION, array() );
|
||||
|
||||
return isset( $snoozed[ $this->get_id() ] ) && $snoozed[ $this->get_id() ] > ( time() * 1000 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Snooze the task.
|
||||
*
|
||||
* @param string $duration Duration to snooze. day|hour|week.
|
||||
*
|
||||
* @deprecated 7.2.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function snooze( $duration = 'day' ) {
|
||||
wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '7.2.0' );
|
||||
|
||||
if ( ! $this->is_snoozeable() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$snoozed = get_option( self::SNOOZED_OPTION, array() );
|
||||
$snoozed_until = $this->duration_to_ms[ $duration ] + ( time() * 1000 );
|
||||
$snoozed[ $this->get_id() ] = $snoozed_until;
|
||||
$update = update_option( self::SNOOZED_OPTION, $snoozed );
|
||||
|
||||
if ( $update ) {
|
||||
if ( $update ) {
|
||||
$this->record_tracks_event( 'remindmelater_task', array( 'task_name' => $this->get_id() ) );
|
||||
}
|
||||
}
|
||||
|
||||
return $update;
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo task snooze.
|
||||
*
|
||||
* @deprecated 7.2.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function undo_snooze() {
|
||||
wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '7.2.0' );
|
||||
|
||||
$snoozed = get_option( self::SNOOZED_OPTION, array() );
|
||||
unset( $snoozed[ $this->get_id() ] );
|
||||
$update = update_option( self::SNOOZED_OPTION, $snoozed );
|
||||
|
||||
if ( $update ) {
|
||||
$this->record_tracks_event( 'undo_remindmelater_task', array( 'task_name' => $this->get_id() ) );
|
||||
}
|
||||
|
||||
return $update;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a task list has previously been marked as complete.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has_previously_completed() {
|
||||
$complete = get_option( self::COMPLETED_OPTION, array() );
|
||||
return in_array( $this->get_id(), $complete, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Track task completion if task is viewable.
|
||||
*/
|
||||
public function possibly_track_completion() {
|
||||
if ( ! $this->is_complete() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $this->has_previously_completed() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$completed_tasks = get_option( self::COMPLETED_OPTION, array() );
|
||||
$completed_tasks[] = $this->get_id();
|
||||
update_option( self::COMPLETED_OPTION, $completed_tasks );
|
||||
$this->record_tracks_event( 'task_completed', array( 'task_name' => $this->get_id() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this as the active task across page loads.
|
||||
*/
|
||||
public function set_active() {
|
||||
if ( $this->is_complete() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
set_transient(
|
||||
self::ACTIVE_TASK_TRANSIENT,
|
||||
$this->get_id(),
|
||||
DAY_IN_SECONDS
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this is the active task.
|
||||
*/
|
||||
public function is_active() {
|
||||
return get_transient( self::ACTIVE_TASK_TRANSIENT ) === $this->get_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the store is capable of viewing the task.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_view() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if task is disabled.
|
||||
*
|
||||
* @deprecated 7.2.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_disabled() {
|
||||
wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '7.2.0' );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the task is complete.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
return self::is_actioned();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the task has been visited.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_visited() {
|
||||
$user_id = get_current_user_id();
|
||||
$response = WCAdminUser::get_user_data_field( $user_id, 'task_list_tracked_started_tasks' );
|
||||
$tracked_tasks = $response ? json_decode( $response, true ) : array();
|
||||
|
||||
return isset( $tracked_tasks[ $this->get_id() ] ) && $tracked_tasks[ $this->get_id() ] > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if should record event when task is viewed
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function get_record_view_event(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the task as JSON.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_json() {
|
||||
$this->possibly_track_completion();
|
||||
|
||||
return array(
|
||||
'id' => $this->get_id(),
|
||||
'parentId' => $this->get_parent_id(),
|
||||
'title' => $this->get_title(),
|
||||
'badge' => $this->get_badge(),
|
||||
'canView' => $this->can_view(),
|
||||
'content' => $this->get_content(),
|
||||
'additionalInfo' => $this->get_additional_info(),
|
||||
'actionLabel' => $this->get_action_label(),
|
||||
'actionUrl' => $this->get_action_url(),
|
||||
'isComplete' => $this->is_complete(),
|
||||
'time' => $this->get_time(),
|
||||
'level' => 3,
|
||||
'isActioned' => $this->is_actioned(),
|
||||
'isDismissed' => $this->is_dismissed(),
|
||||
'isDismissable' => $this->is_dismissable(),
|
||||
'isSnoozed' => false,
|
||||
'isSnoozeable' => false,
|
||||
'isVisited' => $this->is_visited(),
|
||||
'isDisabled' => false,
|
||||
'snoozedUntil' => null,
|
||||
'additionalData' => self::convert_object_to_camelcase( $this->get_additional_data() ),
|
||||
'eventPrefix' => $this->prefix_event( '' ),
|
||||
'recordViewEvent' => $this->get_record_view_event(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert object keys to camelcase.
|
||||
*
|
||||
* @param array $data Data to convert.
|
||||
* @return object
|
||||
*/
|
||||
public static function convert_object_to_camelcase( $data ) {
|
||||
if ( ! is_array( $data ) ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$new_object = (object) array();
|
||||
|
||||
foreach ( $data as $key => $value ) {
|
||||
$new_key = lcfirst( implode( '', array_map( 'ucfirst', explode( '_', $key ) ) ) );
|
||||
$new_object->$new_key = $value;
|
||||
}
|
||||
|
||||
return $new_object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a task as actioned. Used to verify an action has taken place in some tasks.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function mark_actioned() {
|
||||
$actioned = get_option( self::ACTIONED_OPTION, array() );
|
||||
|
||||
$actioned[] = $this->get_id();
|
||||
$update = update_option( self::ACTIONED_OPTION, array_unique( $actioned ) );
|
||||
|
||||
if ( $update ) {
|
||||
$this->record_tracks_event( 'actioned_task', array( 'task_name' => $this->get_id() ) );
|
||||
}
|
||||
|
||||
return $update;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a task has been actioned.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_actioned() {
|
||||
return self::is_task_actioned( $this->get_id() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a provided task ID has been actioned.
|
||||
*
|
||||
* @param string $id Task ID.
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_task_actioned( $id ) {
|
||||
$actioned = get_option( self::ACTIONED_OPTION, array() );
|
||||
return in_array( $id, $actioned, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorting function for tasks.
|
||||
*
|
||||
* @param Task $a Task a.
|
||||
* @param Task $b Task b.
|
||||
* @param array $sort_by list of columns with sort order.
|
||||
* @return int
|
||||
*/
|
||||
public static function sort( $a, $b, $sort_by = array() ) {
|
||||
$result = 0;
|
||||
foreach ( $sort_by as $data ) {
|
||||
$key = $data['key'];
|
||||
$a_val = $a->$key ?? false;
|
||||
$b_val = $b->$key ?? false;
|
||||
if ( 'asc' === $data['order'] ) {
|
||||
$result = $a_val <=> $b_val;
|
||||
} else {
|
||||
$result = $b_val <=> $a_val;
|
||||
}
|
||||
|
||||
if ( 0 !== $result ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles storage and retrieval of a task list
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
|
||||
use Automattic\WooCommerce\Admin\WCAdminHelper;
|
||||
|
||||
|
||||
/**
|
||||
* Task List class.
|
||||
*/
|
||||
class TaskList {
|
||||
/**
|
||||
* Task traits.
|
||||
*/
|
||||
use TaskTraits;
|
||||
|
||||
/**
|
||||
* Option name hidden task lists.
|
||||
*/
|
||||
const HIDDEN_OPTION = 'woocommerce_task_list_hidden_lists';
|
||||
|
||||
/**
|
||||
* Option name of completed task lists.
|
||||
*/
|
||||
const COMPLETED_OPTION = 'woocommerce_task_list_completed_lists';
|
||||
|
||||
/**
|
||||
* Option name of hidden reminder bar.
|
||||
*/
|
||||
const REMINDER_BAR_HIDDEN_OPTION = 'woocommerce_task_list_reminder_bar_hidden';
|
||||
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id = '';
|
||||
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $hidden_id = '';
|
||||
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $display_progress_header = false;
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $title = '';
|
||||
|
||||
/**
|
||||
* Tasks.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $tasks = array();
|
||||
|
||||
/**
|
||||
* Sort keys.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $sort_by = array();
|
||||
|
||||
/**
|
||||
* Event prefix.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $event_prefix = null;
|
||||
|
||||
/**
|
||||
* Task list visibility.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $visible = true;
|
||||
|
||||
/**
|
||||
* Array of custom options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $options = array();
|
||||
|
||||
/**
|
||||
* Array of TaskListSection.
|
||||
*
|
||||
* @deprecated 7.2.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $sections = array();
|
||||
|
||||
/**
|
||||
* Key value map of task class and id used for sections.
|
||||
*
|
||||
* @deprecated 7.2.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $task_class_id_map = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $data Task list data.
|
||||
*/
|
||||
public function __construct( $data = array() ) {
|
||||
$defaults = array(
|
||||
'id' => null,
|
||||
'hidden_id' => null,
|
||||
'title' => '',
|
||||
'tasks' => array(),
|
||||
'sort_by' => array(),
|
||||
'event_prefix' => null,
|
||||
'options' => array(),
|
||||
'visible' => true,
|
||||
'display_progress_header' => false,
|
||||
);
|
||||
|
||||
$data = wp_parse_args( $data, $defaults );
|
||||
|
||||
$this->id = $data['id'];
|
||||
$this->hidden_id = $data['hidden_id'];
|
||||
$this->title = $data['title'];
|
||||
$this->sort_by = $data['sort_by'];
|
||||
$this->event_prefix = $data['event_prefix'];
|
||||
$this->options = $data['options'];
|
||||
$this->visible = $data['visible'];
|
||||
$this->display_progress_header = $data['display_progress_header'];
|
||||
|
||||
foreach ( $data['tasks'] as $task_name ) {
|
||||
$class = 'Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\\' . $task_name;
|
||||
$task = new $class( $this );
|
||||
$this->add_task( $task );
|
||||
}
|
||||
|
||||
$this->possibly_remove_reminder_bar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the task list is hidden.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_hidden() {
|
||||
$hidden = get_option( self::HIDDEN_OPTION, array() );
|
||||
return in_array( $this->hidden_id ? $this->hidden_id : $this->id, $hidden, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the task list is visible.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_visible() {
|
||||
if ( ! $this->visible || ! count( $this->get_viewable_tasks() ) > 0 ) {
|
||||
return false;
|
||||
}
|
||||
return ! $this->is_hidden();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the task list.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hide() {
|
||||
if ( $this->is_hidden() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$viewable_tasks = $this->get_viewable_tasks();
|
||||
$completed_count = array_reduce(
|
||||
$viewable_tasks,
|
||||
function( $total, $task ) {
|
||||
return $task->is_complete() ? $total + 1 : $total;
|
||||
},
|
||||
0
|
||||
);
|
||||
|
||||
$this->record_tracks_event(
|
||||
'completed',
|
||||
array(
|
||||
'action' => 'remove_card',
|
||||
'completed_task_count' => $completed_count,
|
||||
'incomplete_task_count' => count( $viewable_tasks ) - $completed_count,
|
||||
)
|
||||
);
|
||||
|
||||
$hidden = get_option( self::HIDDEN_OPTION, array() );
|
||||
$hidden[] = $this->hidden_id ? $this->hidden_id : $this->id;
|
||||
$this->maybe_set_default_layout( $hidden );
|
||||
return update_option( self::HIDDEN_OPTION, array_unique( $hidden ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default homepage layout to two_columns if "setup" tasklist is completed or hidden.
|
||||
*
|
||||
* @param array $completed_or_hidden_tasklist_ids Array of tasklist ids.
|
||||
*/
|
||||
public function maybe_set_default_layout( $completed_or_hidden_tasklist_ids ) {
|
||||
if ( in_array( 'setup', $completed_or_hidden_tasklist_ids, true ) ) {
|
||||
update_option( 'woocommerce_default_homepage_layout', 'two_columns' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo hiding of the task list.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function unhide() {
|
||||
$hidden = get_option( self::HIDDEN_OPTION, array() );
|
||||
$hidden = array_diff( $hidden, array( $this->hidden_id ? $this->hidden_id : $this->id ) );
|
||||
return update_option( self::HIDDEN_OPTION, $hidden );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all viewable tasks are complete.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
foreach ( $this->get_viewable_tasks() as $viewable_task ) {
|
||||
if ( $viewable_task->is_complete() === false ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a task list has previously been marked as complete.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has_previously_completed() {
|
||||
$complete = get_option( self::COMPLETED_OPTION, array() );
|
||||
return in_array( $this->get_list_id(), $complete, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add task to the task list.
|
||||
*
|
||||
* @param Task $task Task class.
|
||||
*/
|
||||
public function add_task( $task ) {
|
||||
if ( ! is_subclass_of( $task, 'Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task' ) ) {
|
||||
return new \WP_Error(
|
||||
'woocommerce_task_list_invalid_task',
|
||||
__( 'Task is not a subclass of `Task`', 'woocommerce' )
|
||||
);
|
||||
}
|
||||
if ( array_search( $task, $this->tasks, true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->tasks[] = $task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get only visible tasks in list.
|
||||
*
|
||||
* @param string $task_id id of task.
|
||||
* @return Task
|
||||
*/
|
||||
public function get_task( $task_id ) {
|
||||
return current(
|
||||
array_filter(
|
||||
$this->tasks,
|
||||
function( $task ) use ( $task_id ) {
|
||||
return $task->get_id() === $task_id;
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get only visible tasks in list.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_viewable_tasks() {
|
||||
return array_values(
|
||||
array_filter(
|
||||
$this->tasks,
|
||||
function( $task ) {
|
||||
return $task->can_view();
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get task list sections.
|
||||
*
|
||||
* @deprecated 7.2.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_sections() {
|
||||
wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '7.2.0' );
|
||||
|
||||
return $this->sections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Track list completion of viewable tasks.
|
||||
*/
|
||||
public function possibly_track_completion() {
|
||||
if ( ! $this->is_complete() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $this->has_previously_completed() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$completed_lists = get_option( self::COMPLETED_OPTION, array() );
|
||||
$completed_lists[] = $this->get_list_id();
|
||||
update_option( self::COMPLETED_OPTION, $completed_lists );
|
||||
$this->maybe_set_default_layout( $completed_lists );
|
||||
$this->record_tracks_event( 'tasks_completed' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the attached tasks array.
|
||||
*
|
||||
* @param array $sort_by list of columns with sort order.
|
||||
* @return TaskList returns $this, for chaining.
|
||||
*/
|
||||
public function sort_tasks( $sort_by = array() ) {
|
||||
$sort_by = count( $sort_by ) > 0 ? $sort_by : $this->sort_by;
|
||||
if ( 0 !== count( $sort_by ) ) {
|
||||
usort(
|
||||
$this->tasks,
|
||||
function( $a, $b ) use ( $sort_by ) {
|
||||
return Task::sort( $a, $b, $sort_by );
|
||||
}
|
||||
);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefix event for track event naming.
|
||||
*
|
||||
* @param string $event_name Event name.
|
||||
* @return string
|
||||
*/
|
||||
public function prefix_event( $event_name ) {
|
||||
if ( null !== $this->event_prefix ) {
|
||||
return $this->event_prefix . $event_name;
|
||||
}
|
||||
return $this->get_list_id() . '_tasklist_' . $event_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns option to keep completed task list.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_keep_completed_task_list() {
|
||||
return get_option( 'woocommerce_task_list_keep_completed', 'no' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove reminder bar four weeks after store creation.
|
||||
*/
|
||||
public static function possibly_remove_reminder_bar() {
|
||||
$bar_hidden = get_option( self::REMINDER_BAR_HIDDEN_OPTION, 'no' );
|
||||
$active_for_four_weeks = WCAdminHelper::is_wc_admin_active_for( WEEK_IN_SECONDS * 4 );
|
||||
|
||||
if ( 'yes' === $bar_hidden || ! $active_for_four_weeks ) {
|
||||
return;
|
||||
}
|
||||
|
||||
update_option( self::REMINDER_BAR_HIDDEN_OPTION, 'yes' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list for use in JSON.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_json() {
|
||||
$this->possibly_track_completion();
|
||||
$tasks_json = array();
|
||||
foreach ( $this->tasks as $task ) {
|
||||
$json = $task->get_json();
|
||||
if ( $json['canView'] ) {
|
||||
$tasks_json[] = $json;
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'id' => $this->get_list_id(),
|
||||
'title' => $this->title,
|
||||
'isHidden' => $this->is_hidden(),
|
||||
'isVisible' => $this->is_visible(),
|
||||
'isComplete' => $this->is_complete(),
|
||||
'tasks' => $tasks_json,
|
||||
'eventPrefix' => $this->prefix_event( '' ),
|
||||
'displayProgressHeader' => $this->display_progress_header,
|
||||
'keepCompletedTaskList' => $this->get_keep_completed_task_list(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles storage and retrieval of a task list section
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks;
|
||||
|
||||
/**
|
||||
* Task List section class.
|
||||
*
|
||||
* @deprecated 7.2.0
|
||||
*/
|
||||
class TaskListSection {
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id = '';
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $title = '';
|
||||
|
||||
/**
|
||||
* Description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $description = '';
|
||||
|
||||
/**
|
||||
* Image.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $image = '';
|
||||
|
||||
/**
|
||||
* Tasks.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $task_names = array();
|
||||
|
||||
/**
|
||||
* Parent task list.
|
||||
*
|
||||
* @var TaskList
|
||||
*/
|
||||
protected $task_list;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $data Task list data.
|
||||
* @param TaskList|null $task_list Parent task list.
|
||||
*/
|
||||
public function __construct( $data = array(), $task_list = null ) {
|
||||
$defaults = array(
|
||||
'id' => '',
|
||||
'title' => '',
|
||||
'description' => '',
|
||||
'image' => '',
|
||||
'tasks' => array(),
|
||||
);
|
||||
|
||||
$data = wp_parse_args( $data, $defaults );
|
||||
|
||||
$this->task_list = $task_list;
|
||||
$this->id = $data['id'];
|
||||
$this->title = $data['title'];
|
||||
$this->description = $data['description'];
|
||||
$this->image = $data['image'];
|
||||
$this->task_names = $data['task_names'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if section is complete.
|
||||
*
|
||||
* @return boolean;
|
||||
*/
|
||||
private function is_complete() {
|
||||
$complete = true;
|
||||
foreach ( $this->task_names as $task_name ) {
|
||||
if ( null !== $this->task_list && isset( $this->task_list->task_class_id_map[ $task_name ] ) ) {
|
||||
$task = $this->task_list->get_task( $this->task_list->task_class_id_map[ $task_name ] );
|
||||
if ( $task->can_view() && ! $task->is_complete() ) {
|
||||
$complete = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $complete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list for use in JSON.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_json() {
|
||||
return array(
|
||||
'id' => $this->id,
|
||||
'title' => $this->title,
|
||||
'description' => $this->description,
|
||||
'image' => $this->image,
|
||||
'tasks' => array_map(
|
||||
function( $task_name ) {
|
||||
if ( null !== $this->task_list && isset( $this->task_list->task_class_id_map[ $task_name ] ) ) {
|
||||
return $this->task_list->task_class_id_map[ $task_name ];
|
||||
}
|
||||
return '';
|
||||
},
|
||||
$this->task_names
|
||||
),
|
||||
'isComplete' => $this->is_complete(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles storage and retrieval of task lists
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\DeprecatedExtendedTask;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\ReviewShippingOptions;
|
||||
/**
|
||||
* Task Lists class.
|
||||
*/
|
||||
class TaskLists {
|
||||
/**
|
||||
* Class instance.
|
||||
*
|
||||
* @var TaskLists instance
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* An array of all registered lists.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $lists = array();
|
||||
|
||||
/**
|
||||
* Boolean value to indicate if default tasks have been added.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected static $default_tasks_loaded = false;
|
||||
|
||||
/**
|
||||
* The contents of this array is used in init_tasks() to run their init() methods.
|
||||
* If the classes do not have an init() method then nothing is executed.
|
||||
* Beyond that, adding tasks to this list has no effect, see init_default_lists() for the list of tasks.
|
||||
* that are added for each task list.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
const DEFAULT_TASKS = array(
|
||||
'StoreDetails',
|
||||
'Products',
|
||||
'WooCommercePayments',
|
||||
'Payments',
|
||||
'Tax',
|
||||
'Shipping',
|
||||
'Marketing',
|
||||
'Appearance',
|
||||
'AdditionalPayments',
|
||||
'ReviewShippingOptions',
|
||||
'GetMobileApp',
|
||||
);
|
||||
|
||||
/**
|
||||
* Get class instance.
|
||||
*/
|
||||
final public static function instance() {
|
||||
if ( ! static::$instance ) {
|
||||
static::$instance = new static();
|
||||
}
|
||||
return static::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the task lists.
|
||||
*/
|
||||
public static function init() {
|
||||
self::init_default_lists();
|
||||
add_action( 'admin_init', array( __CLASS__, 'set_active_task' ), 5 );
|
||||
add_action( 'init', array( __CLASS__, 'init_tasks' ) );
|
||||
add_action( 'admin_menu', array( __CLASS__, 'menu_task_count' ) );
|
||||
add_filter( 'woocommerce_admin_shared_settings', array( __CLASS__, 'task_list_preloaded_settings' ), 20 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an experiment is the treatment or control.
|
||||
*
|
||||
* @param string $name Name prefix of experiment.
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_experiment_treatment( $name ) {
|
||||
$anon_id = isset( $_COOKIE['tk_ai'] ) ? sanitize_text_field( wp_unslash( $_COOKIE['tk_ai'] ) ) : '';
|
||||
$allow_tracking = 'yes' === get_option( 'woocommerce_allow_tracking' );
|
||||
$abtest = new \WooCommerce\Admin\Experimental_Abtest(
|
||||
$anon_id,
|
||||
'woocommerce',
|
||||
$allow_tracking
|
||||
);
|
||||
|
||||
$date = new \DateTime();
|
||||
$date->setTimeZone( new \DateTimeZone( 'UTC' ) );
|
||||
|
||||
$experiment_name = sprintf(
|
||||
'%s_%s_%s',
|
||||
$name,
|
||||
$date->format( 'Y' ),
|
||||
$date->format( 'm' )
|
||||
);
|
||||
return $abtest->get_variation( $experiment_name ) === 'treatment';
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize default lists.
|
||||
*/
|
||||
public static function init_default_lists() {
|
||||
$tasks = array(
|
||||
'CustomizeStore',
|
||||
'StoreDetails',
|
||||
'Products',
|
||||
'Appearance',
|
||||
'WooCommercePayments',
|
||||
'Payments',
|
||||
'Tax',
|
||||
'Shipping',
|
||||
'Marketing',
|
||||
);
|
||||
|
||||
if ( Features::is_enabled( 'core-profiler' ) ) {
|
||||
$key = array_search( 'StoreDetails', $tasks, true );
|
||||
if ( false !== $key ) {
|
||||
unset( $tasks[ $key ] );
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the old Personalize your store task if the new CustomizeStore is enabled.
|
||||
$task_to_remove = Features::is_enabled( 'customize-store' ) ? 'Appearance' : 'CustomizeStore';
|
||||
$store_customisation_task_index = array_search( $task_to_remove, $tasks, true );
|
||||
|
||||
if ( false !== $store_customisation_task_index ) {
|
||||
unset( $tasks[ $store_customisation_task_index ] );
|
||||
}
|
||||
|
||||
self::add_list(
|
||||
array(
|
||||
'id' => 'setup',
|
||||
'title' => __( 'Get ready to start selling', 'woocommerce' ),
|
||||
'tasks' => $tasks,
|
||||
'display_progress_header' => true,
|
||||
'event_prefix' => 'tasklist_',
|
||||
'options' => array(
|
||||
'use_completed_title' => true,
|
||||
),
|
||||
'visible' => true,
|
||||
)
|
||||
);
|
||||
|
||||
self::add_list(
|
||||
array(
|
||||
'id' => 'extended',
|
||||
'title' => __( 'Things to do next', 'woocommerce' ),
|
||||
'sort_by' => array(
|
||||
array(
|
||||
'key' => 'is_complete',
|
||||
'order' => 'asc',
|
||||
),
|
||||
array(
|
||||
'key' => 'level',
|
||||
'order' => 'asc',
|
||||
),
|
||||
),
|
||||
'tasks' => array(
|
||||
'AdditionalPayments',
|
||||
'GetMobileApp',
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
if ( Features::is_enabled( 'shipping-smart-defaults' ) ) {
|
||||
self::add_task(
|
||||
'extended',
|
||||
new ReviewShippingOptions(
|
||||
self::get_list( 'extended' )
|
||||
)
|
||||
);
|
||||
|
||||
// Tasklist that will never be shown in homescreen,
|
||||
// used for having tasks that are accessed by other means.
|
||||
self::add_list(
|
||||
array(
|
||||
'id' => 'secret_tasklist',
|
||||
'hidden_id' => 'setup',
|
||||
'tasks' => array(
|
||||
'ExperimentalShippingRecommendation',
|
||||
),
|
||||
'event_prefix' => 'secret_tasklist_',
|
||||
'visible' => false,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( has_filter( 'woocommerce_admin_experimental_onboarding_tasklists' ) ) {
|
||||
/**
|
||||
* Filter to override default task lists.
|
||||
*
|
||||
* @since 7.4
|
||||
* @param array $lists Array of tasklists.
|
||||
*/
|
||||
self::$lists = apply_filters( 'woocommerce_admin_experimental_onboarding_tasklists', self::$lists );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize tasks.
|
||||
*/
|
||||
public static function init_tasks() {
|
||||
foreach ( self::DEFAULT_TASKS as $task ) {
|
||||
$class = 'Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\\' . $task;
|
||||
if ( ! method_exists( $class, 'init' ) ) {
|
||||
continue;
|
||||
}
|
||||
$class::init();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporarily store the active task to persist across page loads when necessary.
|
||||
* Most tasks do not need this.
|
||||
*/
|
||||
public static function set_active_task() {
|
||||
if ( ! isset( $_GET[ Task::ACTIVE_TASK_TRANSIENT ] ) || ! current_user_can( 'manage_woocommerce' ) ) { // phpcs:ignore csrf ok.
|
||||
return;
|
||||
}
|
||||
$referer = wp_get_referer();
|
||||
if ( ! $referer || 0 !== strpos( $referer, wc_admin_url() ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$task_id = sanitize_title_with_dashes( wp_unslash( $_GET[ Task::ACTIVE_TASK_TRANSIENT ] ) ); // phpcs:ignore csrf ok.
|
||||
|
||||
$task = self::get_task( $task_id );
|
||||
|
||||
if ( ! $task ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$task->set_active();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a task list.
|
||||
*
|
||||
* @param array $args Task list properties.
|
||||
* @return \WP_Error|TaskList
|
||||
*/
|
||||
public static function add_list( $args ) {
|
||||
if ( isset( self::$lists[ $args['id'] ] ) ) {
|
||||
return new \WP_Error(
|
||||
'woocommerce_task_list_exists',
|
||||
__( 'Task list ID already exists', 'woocommerce' )
|
||||
);
|
||||
}
|
||||
|
||||
self::$lists[ $args['id'] ] = new TaskList( $args );
|
||||
return self::$lists[ $args['id'] ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add task to a given task list.
|
||||
*
|
||||
* @param string $list_id List ID to add the task to.
|
||||
* @param Task $task Task object.
|
||||
*
|
||||
* @return \WP_Error|Task
|
||||
*/
|
||||
public static function add_task( $list_id, $task ) {
|
||||
if ( ! isset( self::$lists[ $list_id ] ) ) {
|
||||
return new \WP_Error(
|
||||
'woocommerce_task_list_invalid_list',
|
||||
__( 'Task list ID does not exist', 'woocommerce' )
|
||||
);
|
||||
}
|
||||
|
||||
self::$lists[ $list_id ]->add_task( $task );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add default extended task lists.
|
||||
*
|
||||
* @param array $extended_tasks list of extended tasks.
|
||||
*/
|
||||
public static function maybe_add_extended_tasks( $extended_tasks ) {
|
||||
$tasks = $extended_tasks ?? array();
|
||||
|
||||
foreach ( self::$lists as $task_list ) {
|
||||
if ( 'extended' !== substr( $task_list->id, 0, 8 ) ) {
|
||||
continue;
|
||||
}
|
||||
foreach ( $tasks as $args ) {
|
||||
$task = new DeprecatedExtendedTask( $task_list, $args );
|
||||
$task_list->add_task( $task );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all task lists.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_lists() {
|
||||
return self::$lists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all task lists.
|
||||
*
|
||||
* @param array $ids list of task list ids.
|
||||
* @return array
|
||||
*/
|
||||
public static function get_lists_by_ids( $ids ) {
|
||||
return array_filter(
|
||||
self::$lists,
|
||||
function( $list ) use ( $ids ) {
|
||||
return in_array( $list->get_list_id(), $ids, true );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all task list ids.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_list_ids() {
|
||||
return array_keys( self::$lists );
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all task lists.
|
||||
*/
|
||||
public static function clear_lists() {
|
||||
self::$lists = array();
|
||||
return self::$lists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get visible task lists.
|
||||
*/
|
||||
public static function get_visible() {
|
||||
return array_filter(
|
||||
self::get_lists(),
|
||||
function ( $task_list ) {
|
||||
return $task_list->is_visible();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve a task list by ID.
|
||||
*
|
||||
* @param String $id Task list ID.
|
||||
*
|
||||
* @return TaskList|null
|
||||
*/
|
||||
public static function get_list( $id ) {
|
||||
if ( isset( self::$lists[ $id ] ) ) {
|
||||
return self::$lists[ $id ];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve single task.
|
||||
*
|
||||
* @param String $id Task ID.
|
||||
* @param String $task_list_id Task list ID.
|
||||
*
|
||||
* @return Object
|
||||
*/
|
||||
public static function get_task( $id, $task_list_id = null ) {
|
||||
$task_list = $task_list_id ? self::get_list( $task_list_id ) : null;
|
||||
|
||||
if ( $task_list_id && ! $task_list ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$tasks_to_search = $task_list ? $task_list->tasks : array_reduce(
|
||||
self::get_lists(),
|
||||
function ( $all, $curr ) {
|
||||
return array_merge( $all, $curr->tasks );
|
||||
},
|
||||
array()
|
||||
);
|
||||
|
||||
foreach ( $tasks_to_search as $task ) {
|
||||
if ( $id === $task->get_id() ) {
|
||||
return $task;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return number of setup tasks remaining
|
||||
*
|
||||
* @return number
|
||||
*/
|
||||
public static function setup_tasks_remaining() {
|
||||
$setup_list = self::get_list( 'setup' );
|
||||
|
||||
if ( ! $setup_list || $setup_list->is_hidden() || $setup_list->is_complete() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$remaining_tasks = array_values(
|
||||
array_filter(
|
||||
$setup_list->get_viewable_tasks(),
|
||||
function( $task ) {
|
||||
return ! $task->is_complete();
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
return count( $remaining_tasks );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add badge to homescreen menu item for remaining tasks
|
||||
*/
|
||||
public static function menu_task_count() {
|
||||
global $submenu;
|
||||
|
||||
$tasks_count = self::setup_tasks_remaining();
|
||||
|
||||
if ( ! $tasks_count || ! isset( $submenu['woocommerce'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $submenu['woocommerce'] as $key => $menu_item ) {
|
||||
if ( 0 === strpos( $menu_item[0], _x( 'Home', 'Admin menu name', 'woocommerce' ) ) ) {
|
||||
$submenu['woocommerce'][ $key ][0] .= ' <span class="awaiting-mod update-plugins remaining-tasks-badge woocommerce-task-list-remaining-tasks-badge"><span class="count-' . esc_attr( $tasks_count ) . '">' . absint( $tasks_count ) . '</span></span>'; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add visible list ids to component settings.
|
||||
*
|
||||
* @param array $settings Component settings.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function task_list_preloaded_settings( $settings ) {
|
||||
$settings['visibleTaskListIds'] = array_keys( self::get_visible() );
|
||||
|
||||
return $settings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* Task and TaskList Traits
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* TaskTraits class.
|
||||
*/
|
||||
trait TaskTraits {
|
||||
/**
|
||||
* Record a tracks event with the prefixed event name.
|
||||
*
|
||||
* @param string $event_name Event name.
|
||||
* @param array $args Array of tracks arguments.
|
||||
* @return string Prefixed event name.
|
||||
*/
|
||||
public function record_tracks_event( $event_name, $args = array() ) {
|
||||
if ( ! $this->get_list_id() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$prefixed_event_name = $this->prefix_event( $event_name );
|
||||
|
||||
wc_admin_record_tracks_event(
|
||||
$prefixed_event_name,
|
||||
$args
|
||||
);
|
||||
|
||||
return $prefixed_event_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the task list ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_list_id() {
|
||||
$namespaced_class = get_class( $this );
|
||||
return is_subclass_of( $namespaced_class, 'Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task' )
|
||||
? $this->get_parent_id()
|
||||
: $this->id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\Payments;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\WooCommercePayments;
|
||||
use Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions\Init;
|
||||
|
||||
/**
|
||||
* Payments Task
|
||||
*/
|
||||
class AdditionalPayments extends Payments {
|
||||
|
||||
/**
|
||||
* Used to cache is_complete() method result.
|
||||
*
|
||||
* @var null
|
||||
*/
|
||||
private $is_complete_result = null;
|
||||
|
||||
/**
|
||||
* Used to cache can_view() method result.
|
||||
*
|
||||
* @var null
|
||||
*/
|
||||
private $can_view_result = null;
|
||||
|
||||
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return 'payments';
|
||||
}
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return __(
|
||||
'Set up additional payment options',
|
||||
'woocommerce'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content() {
|
||||
return __(
|
||||
'Choose payment providers and enable payment methods at checkout.',
|
||||
'woocommerce'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_time() {
|
||||
return __( '2 minutes', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Task completion.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
if ( null === $this->is_complete_result ) {
|
||||
$this->is_complete_result = self::has_enabled_additional_gateways();
|
||||
}
|
||||
|
||||
return $this->is_complete_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Task visibility.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_view() {
|
||||
if ( ! Features::is_enabled( 'payment-gateway-suggestions' ) ) {
|
||||
// Hide task if feature not enabled.
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( null !== $this->can_view_result ) {
|
||||
return $this->can_view_result;
|
||||
}
|
||||
|
||||
// Show task if woocommerce-payments is connected or if there are any suggested gateways in other category enabled.
|
||||
$this->can_view_result = (
|
||||
WooCommercePayments::is_connected() ||
|
||||
self::has_enabled_other_category_gateways()
|
||||
);
|
||||
|
||||
// Early return if task is not visible.
|
||||
if ( ! $this->can_view_result ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Show task if there are any suggested gateways in additional category.
|
||||
$this->can_view_result = ! empty( self::get_suggestion_gateways( 'category_additional' ) );
|
||||
|
||||
return $this->can_view_result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the store has any enabled gateways in other category.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function has_enabled_other_category_gateways() {
|
||||
$other_gateways = self::get_suggestion_gateways( 'category_other' );
|
||||
$other_gateways_ids = wp_list_pluck( $other_gateways, 'id' );
|
||||
|
||||
return self::has_enabled_gateways(
|
||||
function( $gateway ) use ( $other_gateways_ids ) {
|
||||
return in_array( $gateway->id, $other_gateways_ids, true );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the store has any enabled gateways in additional category.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function has_enabled_additional_gateways() {
|
||||
$additional_gateways = self::get_suggestion_gateways( 'category_additional' );
|
||||
$additional_gateways_ids = wp_list_pluck( $additional_gateways, 'id' );
|
||||
|
||||
return self::has_enabled_gateways(
|
||||
function( $gateway ) use ( $additional_gateways_ids ) {
|
||||
return 'yes' === $gateway->enabled
|
||||
&& in_array( $gateway->id, $additional_gateways_ids, true );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the store has any enabled gateways based on the given criteria.
|
||||
*
|
||||
* @param callable|null $filter A callback function to filter the gateways.
|
||||
* @return bool
|
||||
*/
|
||||
private static function has_enabled_gateways( $filter = null ) {
|
||||
$gateways = WC()->payment_gateways->get_available_payment_gateways();
|
||||
$enabled_gateways = array_filter(
|
||||
$gateways,
|
||||
function( $gateway ) use ( $filter ) {
|
||||
if ( is_callable( $filter ) ) {
|
||||
return 'yes' === $gateway->enabled && call_user_func( $filter, $gateway );
|
||||
} else {
|
||||
return 'yes' === $gateway->enabled;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return ! empty( $enabled_gateways );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of gateways to suggest.
|
||||
*
|
||||
* @param string $filter_by Filter by category. "category_additional" or "category_other".
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function get_suggestion_gateways( $filter_by = 'category_additional' ) {
|
||||
$country = wc_get_base_location()['country'];
|
||||
$plugin_suggestions = Init::get_suggestions();
|
||||
$plugin_suggestions = array_filter(
|
||||
$plugin_suggestions,
|
||||
function( $plugin ) use ( $country, $filter_by ) {
|
||||
if ( ! isset( $plugin->{$filter_by} ) || ! isset( $plugin->plugins[0] ) ) {
|
||||
return false;
|
||||
}
|
||||
return in_array( $country, $plugin->{$filter_by}, true );
|
||||
}
|
||||
);
|
||||
return $plugin_suggestions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks;
|
||||
|
||||
use Automattic\WooCommerce\Admin\PageController;
|
||||
use Automattic\WooCommerce\Internal\Admin\Loader;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\Products;
|
||||
use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;
|
||||
|
||||
/**
|
||||
* Appearance Task
|
||||
*/
|
||||
class Appearance extends Task {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
if ( ! $this->is_complete() ) {
|
||||
add_action( 'load-theme-install.php', array( $this, 'mark_actioned' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return 'appearance';
|
||||
}
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return __( 'Choose your theme', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content() {
|
||||
return __(
|
||||
"Choose a theme that best fits your brand's look and feel, then make it your own. Change the colors, add your logo, and create pages.",
|
||||
'woocommerce'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_time() {
|
||||
return __( '2 minutes', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Action label.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_action_label() {
|
||||
return __( 'Choose theme', 'woocommerce' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
|
||||
use Jetpack_Gutenberg;
|
||||
|
||||
/**
|
||||
* Customize Your Store Task
|
||||
*/
|
||||
class CustomizeStore extends Task {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param TaskList $task_list Parent task list.
|
||||
*/
|
||||
public function __construct( $task_list ) {
|
||||
parent::__construct( $task_list );
|
||||
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'possibly_add_site_editor_scripts' ) );
|
||||
|
||||
add_action( 'show_admin_bar', array( $this, 'possibly_hide_wp_admin_bar' ) );
|
||||
|
||||
// Use "switch_theme" instead of "after_switch_theme" because the latter is fired after the next WP load and we don't want to trigger action when switching theme to TT3 via onboarding theme API.
|
||||
global $_GET;
|
||||
$theme_switch_via_cys_ai_loader = isset( $_GET['theme_switch_via_cys_ai_loader'] ) ? 1 === absint( $_GET['theme_switch_via_cys_ai_loader'] ) : false;
|
||||
if ( ! $theme_switch_via_cys_ai_loader ) {
|
||||
add_action( 'switch_theme', array( $this, 'mark_task_as_complete' ) );
|
||||
}
|
||||
|
||||
// Hook to remove unwanted UI elements when users are viewing with ?cys-hide-admin-bar=true.
|
||||
add_action( 'wp_head', array( $this, 'possibly_remove_unwanted_ui_elements' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return 'customize-store';
|
||||
}
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return __( 'Customize your store ', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_time() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Task completion.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
return get_option( 'woocommerce_admin_customize_store_completed' ) === 'yes';
|
||||
}
|
||||
|
||||
/**
|
||||
* Task visibility.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_view() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Action URL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_action_url() {
|
||||
return admin_url( 'wp-admin/admin.php?page=wc-admin&path=%2Fcustomize-store' );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Possibly add site editor scripts.
|
||||
*/
|
||||
public function possibly_add_site_editor_scripts() {
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Recommended
|
||||
$is_wc_admin_page = (
|
||||
isset( $_GET['page'] ) &&
|
||||
'wc-admin' === $_GET['page'] &&
|
||||
isset( $_GET['path'] )
|
||||
);
|
||||
|
||||
$is_assembler_hub = $is_wc_admin_page && str_starts_with( wc_clean( wp_unslash( $_GET['path'] ) ), '/customize-store/assembler-hub' );
|
||||
$is_transitional_page = $is_wc_admin_page && str_starts_with( wc_clean( wp_unslash( $_GET['path'] ) ), '/customize-store/transitional' );
|
||||
// phpcs:enable WordPress.Security.NonceVerification.Recommended
|
||||
|
||||
if ( ! ( $is_assembler_hub || $is_transitional_page ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// See: https://github.com/WordPress/WordPress/blob/master/wp-admin/site-editor.php.
|
||||
if ( ! wp_is_block_theme() ) {
|
||||
wp_die( esc_html__( 'The theme you are currently using is not compatible.', 'woocommerce' ) );
|
||||
}
|
||||
global $editor_styles;
|
||||
|
||||
// Flag that we're loading the block editor.
|
||||
$current_screen = get_current_screen();
|
||||
$current_screen->is_block_editor( true );
|
||||
|
||||
// Default to is-fullscreen-mode to avoid jumps in the UI.
|
||||
add_filter(
|
||||
'admin_body_class',
|
||||
static function( $classes ) {
|
||||
return "$classes is-fullscreen-mode";
|
||||
}
|
||||
);
|
||||
|
||||
$block_editor_context = new \WP_Block_Editor_Context( array( 'name' => 'core/edit-site' ) );
|
||||
$indexed_template_types = array();
|
||||
foreach ( get_default_block_template_types() as $slug => $template_type ) {
|
||||
$template_type['slug'] = (string) $slug;
|
||||
$indexed_template_types[] = $template_type;
|
||||
}
|
||||
|
||||
$custom_settings = array(
|
||||
'siteUrl' => site_url(),
|
||||
'postsPerPage' => get_option( 'posts_per_page' ),
|
||||
'styles' => get_block_editor_theme_styles(),
|
||||
'defaultTemplateTypes' => $indexed_template_types,
|
||||
'defaultTemplatePartAreas' => get_allowed_block_template_part_areas(),
|
||||
'supportsLayout' => wp_theme_has_theme_json(),
|
||||
'supportsTemplatePartsMode' => ! wp_is_block_theme() && current_theme_supports( 'block-template-parts' ),
|
||||
);
|
||||
|
||||
// Add additional back-compat patterns registered by `current_screen` et al.
|
||||
$custom_settings['__experimentalAdditionalBlockPatterns'] = \WP_Block_Patterns_Registry::get_instance()->get_all_registered( true );
|
||||
$custom_settings['__experimentalAdditionalBlockPatternCategories'] = \WP_Block_Pattern_Categories_Registry::get_instance()->get_all_registered( true );
|
||||
|
||||
$editor_settings = get_block_editor_settings( $custom_settings, $block_editor_context );
|
||||
$active_global_styles_id = \WP_Theme_JSON_Resolver::get_user_global_styles_post_id();
|
||||
$active_theme = get_stylesheet();
|
||||
$preload_paths = array(
|
||||
array( '/wp/v2/media', 'OPTIONS' ),
|
||||
'/wp/v2/types?context=view',
|
||||
'/wp/v2/types/wp_template?context=edit',
|
||||
'/wp/v2/types/wp_template-part?context=edit',
|
||||
'/wp/v2/templates?context=edit&per_page=-1',
|
||||
'/wp/v2/template-parts?context=edit&per_page=-1',
|
||||
'/wp/v2/themes?context=edit&status=active',
|
||||
'/wp/v2/global-styles/' . $active_global_styles_id . '?context=edit',
|
||||
'/wp/v2/global-styles/' . $active_global_styles_id,
|
||||
'/wp/v2/global-styles/themes/' . $active_theme,
|
||||
);
|
||||
|
||||
block_editor_rest_api_preload( $preload_paths, $block_editor_context );
|
||||
|
||||
wp_add_inline_script(
|
||||
'wp-blocks',
|
||||
sprintf(
|
||||
'window.wcBlockSettings = %s;',
|
||||
wp_json_encode( $editor_settings )
|
||||
)
|
||||
);
|
||||
|
||||
// Preload server-registered block schemas.
|
||||
wp_add_inline_script(
|
||||
'wp-blocks',
|
||||
'wp.blocks.unstable__bootstrapServerSideBlockDefinitions(' . wp_json_encode( get_block_editor_server_block_settings() ) . ');'
|
||||
);
|
||||
|
||||
wp_add_inline_script(
|
||||
'wp-blocks',
|
||||
sprintf( 'wp.blocks.setCategories( %s );', wp_json_encode( isset( $editor_settings['blockCategories'] ) ? $editor_settings['blockCategories'] : array() ) ),
|
||||
'after'
|
||||
);
|
||||
|
||||
wp_enqueue_script( 'wp-editor' );
|
||||
wp_enqueue_script( 'wp-format-library' ); // Not sure if this is needed.
|
||||
wp_enqueue_script( 'wp-router' );
|
||||
wp_enqueue_style( 'wp-editor' );
|
||||
wp_enqueue_style( 'wp-edit-site' );
|
||||
wp_enqueue_style( 'wp-format-library' );
|
||||
wp_enqueue_media();
|
||||
|
||||
if (
|
||||
current_theme_supports( 'wp-block-styles' ) &&
|
||||
( ! is_array( $editor_styles ) || count( $editor_styles ) === 0 )
|
||||
) {
|
||||
wp_enqueue_style( 'wp-block-library-theme' );
|
||||
}
|
||||
/** This action is documented in wp-admin/edit-form-blocks.php
|
||||
*
|
||||
* @since 8.0.3
|
||||
*/
|
||||
do_action( 'enqueue_block_editor_assets' );
|
||||
|
||||
// Load Jetpack's block editor assets because they are not enqueued by default.
|
||||
if ( class_exists( 'Jetpack_Gutenberg' ) ) {
|
||||
Jetpack_Gutenberg::enqueue_block_editor_assets();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark task as complete.
|
||||
*/
|
||||
public function mark_task_as_complete() {
|
||||
update_option( 'woocommerce_admin_customize_store_completed', 'yes' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a small style to hide admin bar
|
||||
*
|
||||
* @param bool $show Whether to show the admin bar.
|
||||
*/
|
||||
public function possibly_hide_wp_admin_bar( $show ) {
|
||||
if ( isset( $_GET['cys-hide-admin-bar'] ) ) { // @phpcs:ignore
|
||||
return false;
|
||||
}
|
||||
return $show;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs script and add styles to remove unwanted elements and hide scrollbar
|
||||
* when users are viewing with ?cys-hide-admin-bar=true.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function possibly_remove_unwanted_ui_elements() {
|
||||
if ( isset( $_GET['cys-hide-admin-bar'] ) ) { // @phpcs:ignore
|
||||
echo '
|
||||
<style type="text/css">
|
||||
body { overflow: hidden; }
|
||||
</style>';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks;
|
||||
|
||||
use Automattic\Jetpack\Connection\Manager;
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
|
||||
use Automattic\WooCommerce\Admin\PluginsHelper;
|
||||
|
||||
/**
|
||||
* Shipping Task
|
||||
*/
|
||||
class ExperimentalShippingRecommendation extends Task {
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return 'shipping-recommendation';
|
||||
}
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return __( 'Set up shipping', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_time() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Task completion.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
return self::has_plugins_active() && self::has_jetpack_connected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Task visibility.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_view() {
|
||||
return Features::is_enabled( 'shipping-smart-defaults' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Action URL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_action_url() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the store has any shipping zones.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_plugins_active() {
|
||||
return PluginsHelper::is_plugin_active( 'woocommerce-services' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the Jetpack is connected.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_jetpack_connected() {
|
||||
$jetpack_connection_manager = new Manager( 'woocommerce' );
|
||||
|
||||
return $jetpack_connection_manager->is_connected() && $jetpack_connection_manager->has_connected_owner();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
|
||||
use Automattic\Jetpack\Connection\Manager; // https://github.com/Automattic/jetpack/blob/trunk/projects/packages/connection/src/class-manager.php .
|
||||
|
||||
/**
|
||||
* Get Mobile App Task
|
||||
*/
|
||||
class GetMobileApp extends Task {
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return 'get-mobile-app';
|
||||
}
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return __( 'Get the free WooCommerce mobile app', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_time() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Task completion.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
return get_option( 'woocommerce_admin_dismissed_mobile_app_modal' ) === 'yes';
|
||||
}
|
||||
|
||||
/**
|
||||
* Task visibility.
|
||||
* Can view under these conditions:
|
||||
* - Jetpack is installed and connected && current site user has a wordpress.com account connected to jetpack
|
||||
* - Jetpack is not connected && current user is capable of installing plugins
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_view() {
|
||||
$jetpack_can_be_installed = current_user_can( 'manage_woocommerce' ) && current_user_can( 'install_plugins' ) && ! self::is_jetpack_connected();
|
||||
$jetpack_is_installed_and_current_user_connected = self::is_current_user_connected();
|
||||
|
||||
return $jetpack_can_be_installed || $jetpack_is_installed_and_current_user_connected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if site has any users connected to WordPress.com via JetPack
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function is_jetpack_connected() {
|
||||
if ( class_exists( '\Automattic\Jetpack\Connection\Manager' ) && method_exists( '\Automattic\Jetpack\Connection\Manager', 'is_active' ) ) {
|
||||
$connection = new Manager();
|
||||
return $connection->is_active();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the current user is connected to Jetpack.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function is_current_user_connected() {
|
||||
if ( class_exists( '\Automattic\Jetpack\Connection\Manager' ) && method_exists( '\Automattic\Jetpack\Connection\Manager', 'is_user_connected' ) ) {
|
||||
$connection = new Manager();
|
||||
return $connection->is_connection_owner();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Action URL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_action_url() {
|
||||
return admin_url( 'admin.php?page=wc-admin&mobileAppModal=true' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
|
||||
use Automattic\WooCommerce\Internal\Admin\RemoteFreeExtensions\Init as RemoteFreeExtensions;
|
||||
|
||||
/**
|
||||
* Marketing Task
|
||||
*/
|
||||
class Marketing extends Task {
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return 'marketing';
|
||||
}
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
if ( true === $this->get_parent_option( 'use_completed_title' ) ) {
|
||||
if ( $this->is_complete() ) {
|
||||
return __( 'You added sales channels', 'woocommerce' );
|
||||
}
|
||||
return __( 'Get more sales', 'woocommerce' );
|
||||
}
|
||||
return __( 'Set up marketing tools', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content() {
|
||||
return __(
|
||||
'Add recommended marketing tools to reach new customers and grow your business',
|
||||
'woocommerce'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_time() {
|
||||
return __( '2 minutes', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Task completion.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
return self::has_installed_extensions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Task visibility.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_view() {
|
||||
return Features::is_enabled( 'remote-free-extensions' ) && count( self::get_plugins() ) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the marketing plugins.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_plugins() {
|
||||
$bundles = RemoteFreeExtensions::get_extensions(
|
||||
array(
|
||||
'task-list/reach',
|
||||
'task-list/grow',
|
||||
)
|
||||
);
|
||||
|
||||
return array_reduce(
|
||||
$bundles,
|
||||
function( $plugins, $bundle ) {
|
||||
$visible = array();
|
||||
foreach ( $bundle['plugins'] as $plugin ) {
|
||||
if ( $plugin->is_visible ) {
|
||||
$visible[] = $plugin;
|
||||
}
|
||||
}
|
||||
return array_merge( $plugins, $visible );
|
||||
},
|
||||
array()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the store has installed marketing extensions.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_installed_extensions() {
|
||||
$plugins = self::get_plugins();
|
||||
$remaining = array();
|
||||
$installed = array();
|
||||
|
||||
foreach ( $plugins as $plugin ) {
|
||||
if ( ! $plugin->is_installed ) {
|
||||
$remaining[] = $plugin;
|
||||
} else {
|
||||
$installed[] = $plugin;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the task has been actioned and a marketing extension has been installed.
|
||||
if ( count( $installed ) > 0 && Task::is_task_actioned( 'marketing' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
|
||||
|
||||
/**
|
||||
* Payments Task
|
||||
*/
|
||||
class Payments extends Task {
|
||||
|
||||
/**
|
||||
* Used to cache is_complete() method result.
|
||||
* @var null
|
||||
*/
|
||||
private $is_complete_result = null;
|
||||
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return 'payments';
|
||||
}
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
if ( true === $this->get_parent_option( 'use_completed_title' ) ) {
|
||||
if ( $this->is_complete() ) {
|
||||
return __( 'You set up payments', 'woocommerce' );
|
||||
}
|
||||
return __( 'Set up payments', 'woocommerce' );
|
||||
}
|
||||
return __( 'Set up payments', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content() {
|
||||
return __(
|
||||
'Choose payment providers and enable payment methods at checkout.',
|
||||
'woocommerce'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_time() {
|
||||
return __( '2 minutes', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Task completion.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
if ( $this->is_complete_result === null ) {
|
||||
$this->is_complete_result = self::has_gateways();
|
||||
}
|
||||
|
||||
return $this->is_complete_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Task visibility.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_view() {
|
||||
$woocommerce_payments = $this->task_list->get_task( 'woocommerce-payments' );
|
||||
return Features::is_enabled( 'payment-gateway-suggestions' ) && ! $woocommerce_payments->can_view();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the store has any enabled gateways.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_gateways() {
|
||||
$gateways = WC()->payment_gateways->get_available_payment_gateways();
|
||||
$enabled_gateways = array_filter(
|
||||
$gateways,
|
||||
function( $gateway ) {
|
||||
return 'yes' === $gateway->enabled && 'woocommerce_payments' !== $gateway->id;
|
||||
}
|
||||
);
|
||||
|
||||
return ! empty( $enabled_gateways );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
|
||||
use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;
|
||||
|
||||
/**
|
||||
* Products Task
|
||||
*/
|
||||
class Products extends Task {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param TaskList $task_list Parent task list.
|
||||
*/
|
||||
public function __construct( $task_list ) {
|
||||
parent::__construct( $task_list );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'possibly_add_manual_return_notice_script' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'possibly_add_import_return_notice_script' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'possibly_add_load_sample_return_notice_script' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return 'products';
|
||||
}
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
if ( $this->get_parent_option( 'use_completed_title' ) === true ) {
|
||||
if ( $this->is_complete() ) {
|
||||
return __( 'You added products', 'woocommerce' );
|
||||
}
|
||||
return __( 'Add products', 'woocommerce' );
|
||||
}
|
||||
return __( 'Add my products', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content() {
|
||||
return __(
|
||||
'Start by adding the first product to your store. You can add your products manually, via CSV, or import them from another service.',
|
||||
'woocommerce'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_time() {
|
||||
return __( '1 minute per product', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Task completion.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
return self::has_products();
|
||||
}
|
||||
|
||||
/**
|
||||
* Addtional data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_additional_data() {
|
||||
return array(
|
||||
'has_products' => self::has_products(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a return to task list notice when completing the manual product task.
|
||||
*
|
||||
* @param string $hook Page hook.
|
||||
*/
|
||||
public function possibly_add_manual_return_notice_script( $hook ) {
|
||||
global $post;
|
||||
if ( $hook !== 'post.php' || $post->post_type !== 'product' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $this->is_active() || ! $this->is_complete() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
WCAdminAssets::register_script( 'wp-admin-scripts', 'onboarding-product-notice', true );
|
||||
|
||||
// Clear the active task transient to only show notice once per active session.
|
||||
delete_transient( self::ACTIVE_TASK_TRANSIENT );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a return to task list notice when completing the import product task.
|
||||
*
|
||||
* @param string $hook Page hook.
|
||||
*/
|
||||
public function possibly_add_import_return_notice_script( $hook ) {
|
||||
$step = isset( $_GET['step'] ) ? $_GET['step'] : ''; // phpcs:ignore csrf ok, sanitization ok.
|
||||
|
||||
if ( $hook !== 'product_page_product_importer' || $step !== 'done' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $this->is_active() || $this->is_complete() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
WCAdminAssets::register_script( 'wp-admin-scripts', 'onboarding-product-import-notice', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a return to task list notice when completing the loading sample products action.
|
||||
*
|
||||
* @param string $hook Page hook.
|
||||
*/
|
||||
public function possibly_add_load_sample_return_notice_script( $hook ) {
|
||||
if ( $hook !== 'edit.php' || get_query_var( 'post_type' ) !== 'product' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$referer = wp_get_referer();
|
||||
if ( ! $referer || strpos( $referer, wc_admin_url() ) !== 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! isset( $_GET[ Task::ACTIVE_TASK_TRANSIENT ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$task_id = sanitize_title_with_dashes( wp_unslash( $_GET[ Task::ACTIVE_TASK_TRANSIENT ] ) );
|
||||
if ( $task_id !== $this->get_id() || ! $this->is_complete() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
WCAdminAssets::register_script( 'wp-admin-scripts', 'onboarding-load-sample-products-notice', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the store has any published products.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_products() {
|
||||
$counts = wp_count_posts('product');
|
||||
return isset( $counts->publish ) && $counts->publish > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks;
|
||||
|
||||
use Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProducts;
|
||||
use Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingThemes;
|
||||
use Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProfile;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
|
||||
|
||||
/**
|
||||
* Purchase Task
|
||||
*/
|
||||
class Purchase extends Task {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param TaskList $task_list Parent task list.
|
||||
*/
|
||||
public function __construct( $task_list ) {
|
||||
parent::__construct( $task_list );
|
||||
add_action( 'update_option_woocommerce_onboarding_profile', array( $this, 'clear_dismissal' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear dismissal on onboarding product type changes.
|
||||
*
|
||||
* @param array $old_value Old value.
|
||||
* @param array $new_value New value.
|
||||
*/
|
||||
public function clear_dismissal( $old_value, $new_value ) {
|
||||
$product_types = isset( $new_value['product_types'] ) ? (array) $new_value['product_types'] : array();
|
||||
$previous_product_types = isset( $old_value['product_types'] ) ? (array) $old_value['product_types'] : array();
|
||||
|
||||
if ( empty( array_diff( $product_types, $previous_product_types ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->undo_dismiss();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the task arguments.
|
||||
* ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return 'purchase';
|
||||
}
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
$products = $this->get_paid_products_and_themes();
|
||||
$first_product = count( $products['purchaseable'] ) >= 1 ? $products['purchaseable'][0] : false;
|
||||
|
||||
if ( ! $first_product ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$product_label = isset( $first_product['label'] ) ? $first_product['label'] : $first_product['title'];
|
||||
$additional_count = count( $products['purchaseable'] ) - 1;
|
||||
|
||||
if ( $this->get_parent_option( 'use_completed_title' ) && $this->is_complete() ) {
|
||||
return count( $products['purchaseable'] ) === 1
|
||||
? sprintf(
|
||||
/* translators: %1$s: a purchased product name */
|
||||
__(
|
||||
'You added %1$s',
|
||||
'woocommerce'
|
||||
),
|
||||
$product_label
|
||||
)
|
||||
: sprintf(
|
||||
/* translators: %1$s: a purchased product name, %2$d the number of other products purchased */
|
||||
_n(
|
||||
'You added %1$s and %2$d other product',
|
||||
'You added %1$s and %2$d other products',
|
||||
$additional_count,
|
||||
'woocommerce'
|
||||
),
|
||||
$product_label,
|
||||
$additional_count
|
||||
);
|
||||
}
|
||||
|
||||
return count( $products['purchaseable'] ) === 1
|
||||
? sprintf(
|
||||
/* translators: %1$s: a purchaseable product name */
|
||||
__(
|
||||
'Add %s to my store',
|
||||
'woocommerce'
|
||||
),
|
||||
$product_label
|
||||
)
|
||||
: sprintf(
|
||||
/* translators: %1$s: a purchaseable product name, %2$d the number of other products to purchase */
|
||||
_n(
|
||||
'Add %1$s and %2$d more product to my store',
|
||||
'Add %1$s and %2$d more products to my store',
|
||||
$additional_count,
|
||||
'woocommerce'
|
||||
),
|
||||
$product_label,
|
||||
$additional_count
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content() {
|
||||
$products = $this->get_paid_products_and_themes();
|
||||
|
||||
if ( count( $products['remaining'] ) === 1 ) {
|
||||
return isset( $products['purchaseable'][0]['description'] ) ? $products['purchaseable'][0]['description'] : $products['purchaseable'][0]['excerpt'];
|
||||
}
|
||||
return sprintf(
|
||||
/* translators: %1$s: list of product names comma separated, %2%s the last product name */
|
||||
__(
|
||||
'Good choice! You chose to add %1$s and %2$s to your store.',
|
||||
'woocommerce'
|
||||
),
|
||||
implode( ', ', array_slice( $products['remaining'], 0, -1 ) ) . ( count( $products['remaining'] ) > 2 ? ',' : '' ),
|
||||
end( $products['remaining'] )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Action label.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_action_label() {
|
||||
return __( 'Purchase & install now', 'woocommerce' );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_time() {
|
||||
return __( '2 minutes', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Task completion.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
$products = $this->get_paid_products_and_themes();
|
||||
return count( $products['remaining'] ) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismissable.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_dismissable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Task visibility.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_view() {
|
||||
$products = $this->get_paid_products_and_themes();
|
||||
return count( $products['purchaseable'] ) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get purchaseable and remaining products.
|
||||
*
|
||||
* @return array purchaseable and remaining products and themes.
|
||||
*/
|
||||
public static function get_paid_products_and_themes() {
|
||||
$relevant_products = OnboardingProducts::get_relevant_products();
|
||||
|
||||
$profiler_data = get_option( OnboardingProfile::DATA_OPTION, array() );
|
||||
$theme = isset( $profiler_data['theme'] ) ? $profiler_data['theme'] : null;
|
||||
$paid_theme = $theme ? OnboardingThemes::get_paid_theme_by_slug( $theme ) : null;
|
||||
if ( $paid_theme ) {
|
||||
|
||||
$relevant_products['purchaseable'][] = $paid_theme;
|
||||
|
||||
if ( isset( $paid_theme['is_installed'] ) && false === $paid_theme['is_installed'] ) {
|
||||
$relevant_products['remaining'][] = $paid_theme['title'];
|
||||
}
|
||||
}
|
||||
return $relevant_products;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
|
||||
|
||||
/**
|
||||
* Review Shipping Options Task
|
||||
*/
|
||||
class ReviewShippingOptions extends Task {
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return 'review-shipping';
|
||||
}
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return __( 'Review shipping options', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_time() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Task completion.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
return get_option( 'woocommerce_admin_reviewed_default_shipping_zones' ) === 'yes';
|
||||
}
|
||||
|
||||
/**
|
||||
* Task visibility.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_view() {
|
||||
return get_option( 'woocommerce_admin_created_default_shipping_zones' ) === 'yes';
|
||||
}
|
||||
|
||||
/**
|
||||
* Action URL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_action_url() {
|
||||
return admin_url( 'admin.php?page=wc-settings&tab=shipping' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
use Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProfile;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
|
||||
use WC_Data_Store;
|
||||
|
||||
/**
|
||||
* Shipping Task
|
||||
*/
|
||||
class Shipping extends Task {
|
||||
|
||||
const ZONE_COUNT_TRANSIENT_NAME = 'woocommerce_shipping_task_zone_count_transient';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param TaskList $task_list Parent task list.
|
||||
*/
|
||||
public function __construct( $task_list = null ) {
|
||||
parent::__construct( $task_list );
|
||||
// wp_ajax_woocommerce_shipping_zone_methods_save_changes
|
||||
// and wp_ajax_woocommerce_shipping_zones_save_changes get fired
|
||||
// when a new zone is added or an existing one has been changed.
|
||||
add_action( 'wp_ajax_woocommerce_shipping_zones_save_changes', array( __CLASS__, 'delete_zone_count_transient' ), 9 );
|
||||
add_action( 'wp_ajax_woocommerce_shipping_zone_methods_save_changes', array( __CLASS__, 'delete_zone_count_transient' ), 9 );
|
||||
add_action( 'woocommerce_shipping_zone_method_added', array( __CLASS__, 'delete_zone_count_transient' ), 9 );
|
||||
add_action( 'woocommerce_after_shipping_zone_object_save', array( __CLASS__, 'delete_zone_count_transient' ), 9 );
|
||||
}
|
||||
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return 'shipping';
|
||||
}
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
if ( true === $this->get_parent_option( 'use_completed_title' ) ) {
|
||||
if ( $this->is_complete() ) {
|
||||
return __( 'You added shipping costs', 'woocommerce' );
|
||||
}
|
||||
return __( 'Add shipping costs', 'woocommerce' );
|
||||
}
|
||||
return __( 'Set up shipping', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content() {
|
||||
return __(
|
||||
"Set your store location and where you'll ship to.",
|
||||
'woocommerce'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_time() {
|
||||
return __( '1 minute', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Task completion.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
return self::has_shipping_zones();
|
||||
}
|
||||
|
||||
/**
|
||||
* Task visibility.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_view() {
|
||||
if ( Features::is_enabled( 'shipping-smart-defaults' ) ) {
|
||||
if ( 'yes' === get_option( 'woocommerce_admin_created_default_shipping_zones' ) ) {
|
||||
// If the user has already created a default shipping zone, we don't need to show the task.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not display the task when:
|
||||
* - The store sells digital products only
|
||||
* Display the task when:
|
||||
* - We don't know where the store's located
|
||||
* - The store is located in the UK, Australia or Canada
|
||||
*/
|
||||
|
||||
if ( self::is_selling_digital_type_only() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$default_store_country = wc_format_country_state_string( get_option( 'woocommerce_default_country', '' ) )['country'];
|
||||
|
||||
// Check if a store address is set so that we don't default to WooCommerce's default country US.
|
||||
// Similar logic: https://github.com/woocommerce/woocommerce/blob/059d542394b48468587f252dcb6941c6425cd8d3/plugins/woocommerce-admin/client/profile-wizard/steps/store-details/index.js#L511-L516.
|
||||
$store_country = '';
|
||||
if ( ! empty( get_option( 'woocommerce_store_address', '' ) ) || 'US' !== $default_store_country ) {
|
||||
$store_country = $default_store_country;
|
||||
}
|
||||
|
||||
// Unknown country.
|
||||
if ( empty( $store_country ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return in_array( $store_country, array( 'CA', 'AU', 'GB', 'ES', 'IT', 'DE', 'FR', 'MX', 'CO', 'CL', 'AR', 'PE', 'BR', 'UY', 'GT', 'NL', 'AT', 'BE' ), true );
|
||||
}
|
||||
|
||||
return self::has_physical_products();
|
||||
}
|
||||
|
||||
/**
|
||||
* Action URL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_action_url() {
|
||||
return self::has_shipping_zones()
|
||||
? admin_url( 'admin.php?page=wc-settings&tab=shipping' )
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the store has any shipping zones.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_shipping_zones() {
|
||||
$zone_count = get_transient( self::ZONE_COUNT_TRANSIENT_NAME );
|
||||
if ( false !== $zone_count ) {
|
||||
return (int) $zone_count > 0;
|
||||
}
|
||||
|
||||
$zone_count = count( WC_Data_Store::load( 'shipping-zone' )->get_zones() );
|
||||
set_transient( self::ZONE_COUNT_TRANSIENT_NAME, $zone_count );
|
||||
|
||||
return $zone_count > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the store has physical products.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_physical_products() {
|
||||
$profiler_data = get_option( OnboardingProfile::DATA_OPTION, array() );
|
||||
$product_types = isset( $profiler_data['product_types'] ) ? $profiler_data['product_types'] : array();
|
||||
|
||||
return in_array( 'physical', $product_types, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the zone count transient used in has_shipping_zones() method
|
||||
* to refresh the cache.
|
||||
*/
|
||||
public static function delete_zone_count_transient() {
|
||||
delete_transient( self::ZONE_COUNT_TRANSIENT_NAME );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the store sells digital products only.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function is_selling_digital_type_only() {
|
||||
$profiler_data = get_option( OnboardingProfile::DATA_OPTION, array() );
|
||||
$product_types = isset( $profiler_data['product_types'] ) ? $profiler_data['product_types'] : array();
|
||||
|
||||
return array( 'downloads' ) === $product_types;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\Onboarding;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
|
||||
|
||||
/**
|
||||
* Store Details Task
|
||||
*/
|
||||
class StoreCreation extends Task {
|
||||
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return 'store_creation';
|
||||
}
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
/* translators: Store name */
|
||||
return sprintf( __( 'You created %s', 'woocommerce' ), get_bloginfo( 'name' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_time() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_action_url() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Task completion.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if task is disabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_disabled() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks;
|
||||
|
||||
use Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProfile;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
|
||||
|
||||
/**
|
||||
* Store Details Task
|
||||
*/
|
||||
class StoreDetails extends Task {
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return 'store_details';
|
||||
}
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
if ( true === $this->get_parent_option( 'use_completed_title' ) ) {
|
||||
if ( $this->is_complete() ) {
|
||||
return __( 'You added store details', 'woocommerce' );
|
||||
}
|
||||
return __( 'Add store details', 'woocommerce' );
|
||||
}
|
||||
return __( 'Store details', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content() {
|
||||
return __(
|
||||
'Your store address is required to set the origin country for shipping, currencies, and payment options.',
|
||||
'woocommerce'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_time() {
|
||||
return __( '4 minutes', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_action_url() {
|
||||
return ! $this->is_complete() ? admin_url( 'admin.php?page=wc-settings&tab=general&tutorial=true' ) : admin_url( 'admin.php?page=wc-settings&tab=general' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Task completion.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
$country = WC()->countries->get_base_country();
|
||||
$country_locale = WC()->countries->get_country_locale();
|
||||
$locale = $country_locale[ $country ] ?? array();
|
||||
|
||||
$hide_postcode = $locale['postcode']['hidden'] ?? false;
|
||||
// If postcode is hidden, just check that the store address and city are set.
|
||||
if ( $hide_postcode ) {
|
||||
return get_option( 'woocommerce_store_address', '' ) !== '' && get_option( 'woocommerce_store_city', '' ) !== '';
|
||||
}
|
||||
|
||||
// Mark as completed if the store address, city and postcode are set. We don't need to check the country because it's set by default.
|
||||
return get_option( 'woocommerce_store_address', '' ) !== '' && get_option( 'woocommerce_store_city', '' ) !== '' &&
|
||||
get_option( 'woocommerce_store_postcode', '' ) !== '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks;
|
||||
|
||||
use Automattic\WooCommerce\Admin\API\Reports\Taxes\Stats\DataStore as TaxDataStore;
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
|
||||
use Automattic\WooCommerce\Admin\PluginsHelper;
|
||||
use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;
|
||||
|
||||
/**
|
||||
* Tax Task
|
||||
*/
|
||||
class Tax extends Task {
|
||||
|
||||
/**
|
||||
* Used to cache is_complete() method result.
|
||||
* @var null
|
||||
*/
|
||||
private $is_complete_result = null;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param TaskList $task_list Parent task list.
|
||||
*/
|
||||
public function __construct( $task_list ) {
|
||||
parent::__construct( $task_list );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'possibly_add_return_notice_script' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a return to task list notice when completing the task.
|
||||
*/
|
||||
public function possibly_add_return_notice_script() {
|
||||
$page = isset( $_GET['page'] ) ? $_GET['page'] : ''; // phpcs:ignore csrf ok, sanitization ok.
|
||||
$tab = isset( $_GET['tab'] ) ? $_GET['tab'] : ''; // phpcs:ignore csrf ok, sanitization ok.
|
||||
|
||||
if ( $page !== 'wc-settings' || $tab !== 'tax' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $this->is_active() || $this->is_complete() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
WCAdminAssets::register_script( 'wp-admin-scripts', 'onboarding-tax-notice', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return 'tax';
|
||||
}
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
if ( $this->get_parent_option( 'use_completed_title' ) === true ) {
|
||||
if ( $this->is_complete() ) {
|
||||
return __( 'You added tax rates', 'woocommerce' );
|
||||
}
|
||||
return __( 'Add tax rates', 'woocommerce' );
|
||||
}
|
||||
return __( 'Set up tax rates', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content() {
|
||||
return self::can_use_automated_taxes()
|
||||
? __(
|
||||
'Good news! WooCommerce Tax can automate your sales tax calculations for you.',
|
||||
'woocommerce'
|
||||
)
|
||||
: __(
|
||||
'Set your store location and configure tax rate settings.',
|
||||
'woocommerce'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_time() {
|
||||
return __( '1 minute', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Action label.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_action_label() {
|
||||
return self::can_use_automated_taxes()
|
||||
? __( 'Yes please', 'woocommerce' )
|
||||
: __( "Let's go", 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Task completion.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
if ( $this->is_complete_result === null ) {
|
||||
$wc_connect_taxes_enabled = get_option( 'wc_connect_taxes_enabled' );
|
||||
$is_wc_connect_taxes_enabled = ( $wc_connect_taxes_enabled === 'yes' ) || ( $wc_connect_taxes_enabled === true ); // seems that in some places boolean is used, and other places 'yes' | 'no' is used
|
||||
|
||||
$this->is_complete_result = $is_wc_connect_taxes_enabled ||
|
||||
count( TaxDataStore::get_taxes( array() ) ) > 0 ||
|
||||
get_option( 'woocommerce_no_sales_tax' ) !== false;
|
||||
}
|
||||
|
||||
return $this->is_complete_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Addtional data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_additional_data() {
|
||||
return array(
|
||||
'avalara_activated' => PluginsHelper::is_plugin_active( 'woocommerce-avatax' ),
|
||||
'tax_jar_activated' => class_exists( 'WC_Taxjar' ),
|
||||
'woocommerce_tax_countries' => self::get_automated_support_countries(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the store has any enabled gateways.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function can_use_automated_taxes() {
|
||||
if ( ! class_exists( 'WC_Taxjar' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return in_array( WC()->countries->get_base_country(), self::get_automated_support_countries(), true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of countries that support automated tax.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_automated_support_countries() {
|
||||
// https://developers.taxjar.com/api/reference/#countries .
|
||||
$tax_supported_countries = array_merge(
|
||||
array( 'US', 'CA', 'AU', 'GB' ),
|
||||
WC()->countries->get_european_union_countries()
|
||||
);
|
||||
|
||||
return $tax_supported_countries;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
|
||||
|
||||
/**
|
||||
* Tour In-App Marketplace task
|
||||
*/
|
||||
class TourInAppMarketplace extends Task {
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return 'tour-in-app-marketplace';
|
||||
}
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return __(
|
||||
'Discover ways of extending your store with a tour of the Woo Marketplace',
|
||||
'woocommerce'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_time() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Task completion.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
return get_option( 'woocommerce_admin_dismissed_in_app_marketplace_tour' ) === 'yes';
|
||||
}
|
||||
|
||||
/**
|
||||
* Action URL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_action_url() {
|
||||
return admin_url( 'admin.php?page=wc-admin&path=%2Fextensions&tutorial=true' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if should record event when task is viewed
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function get_record_view_event(): bool {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks;
|
||||
|
||||
use Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProfile;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
|
||||
use Automattic\WooCommerce\Admin\PluginsHelper;
|
||||
use Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions\Init as Suggestions;
|
||||
use Automattic\WooCommerce\Internal\Admin\WCPayPromotion\Init as WCPayPromotionInit;
|
||||
|
||||
/**
|
||||
* WooCommercePayments Task
|
||||
*/
|
||||
class WooCommercePayments extends Task {
|
||||
/**
|
||||
* Used to cache is_complete() method result.
|
||||
*
|
||||
* @var null
|
||||
*/
|
||||
private $is_complete_result = null;
|
||||
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return 'woocommerce-payments';
|
||||
}
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return __( 'Set up WooPayments', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Badge.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_badge() {
|
||||
/**
|
||||
* Filter WooPayments onboarding task badge.
|
||||
*
|
||||
* @param string $badge Badge content.
|
||||
* @since 8.2.0
|
||||
*/
|
||||
return apply_filters( 'woocommerce_admin_woopayments_onboarding_task_badge', '' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content() {
|
||||
return __(
|
||||
"You're only one step away from getting paid. Verify your business details to start managing transactions with WooPayments.",
|
||||
'woocommerce'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_time() {
|
||||
return __( '2 minutes', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Action label.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_action_label() {
|
||||
return __( 'Finish setup', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional info.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_additional_info() {
|
||||
if ( WCPayPromotionInit::is_woopay_eligible() ) {
|
||||
return __(
|
||||
'By using WooPayments you agree to be bound by our <a href="https://wordpress.com/tos/" target="_blank">Terms of Service</a> (including WooPay <a href="https://wordpress.com/tos/#more-woopay-specifically" target="_blank">merchant terms</a>) and acknowledge that you have read our <a href="https://automattic.com/privacy/" target="_blank">Privacy Policy</a>',
|
||||
'woocommerce'
|
||||
);
|
||||
}
|
||||
|
||||
return __(
|
||||
'By using WooPayments you agree to be bound by our <a href="https://wordpress.com/tos/" target="_blank">Terms of Service</a> and acknowledge that you have read our <a href="https://automattic.com/privacy/" target="_blank">Privacy Policy</a>',
|
||||
'woocommerce'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Task completion.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
if ( null === $this->is_complete_result ) {
|
||||
$this->is_complete_result = self::is_connected() && ! self::is_account_partially_onboarded();
|
||||
}
|
||||
|
||||
return $this->is_complete_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Task visibility.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_view() {
|
||||
$payments = $this->task_list->get_task( 'payments' );
|
||||
|
||||
return ! $payments->is_complete() && // Do not re-display the task if the "add payments" task has already been completed.
|
||||
self::is_installed() &&
|
||||
self::is_supported();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the plugin was requested during onboarding.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_requested() {
|
||||
$profiler_data = get_option( OnboardingProfile::DATA_OPTION, array() );
|
||||
$product_types = isset( $profiler_data['product_types'] ) ? $profiler_data['product_types'] : array();
|
||||
$business_extensions = isset( $profiler_data['business_extensions'] ) ? $profiler_data['business_extensions'] : array();
|
||||
|
||||
$subscriptions_and_us = in_array( 'subscriptions', $product_types, true ) && 'US' === WC()->countries->get_base_country();
|
||||
return in_array( 'woocommerce-payments', $business_extensions, true ) || $subscriptions_and_us;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the plugin is installed.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_installed() {
|
||||
$installed_plugins = PluginsHelper::get_installed_plugin_slugs();
|
||||
return in_array( 'woocommerce-payments', $installed_plugins, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if WooCommerce Payments is connected.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_connected() {
|
||||
if ( class_exists( '\WC_Payments' ) ) {
|
||||
$wc_payments_gateway = \WC_Payments::get_gateway();
|
||||
return method_exists( $wc_payments_gateway, 'is_connected' )
|
||||
? $wc_payments_gateway->is_connected()
|
||||
: false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if WooCommerce Payments needs setup.
|
||||
* Errored data or payments not enabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_account_partially_onboarded() {
|
||||
if ( class_exists( '\WC_Payments' ) ) {
|
||||
$wc_payments_gateway = \WC_Payments::get_gateway();
|
||||
return method_exists( $wc_payments_gateway, 'is_account_partially_onboarded' )
|
||||
? $wc_payments_gateway->is_account_partially_onboarded()
|
||||
: false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the store is in a supported country.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_supported() {
|
||||
$suggestions = Suggestions::get_suggestions();
|
||||
$suggestion_plugins = array_merge(
|
||||
...array_filter(
|
||||
array_column( $suggestions, 'plugins' ),
|
||||
function( $plugins ) {
|
||||
return is_array( $plugins );
|
||||
}
|
||||
)
|
||||
);
|
||||
$woocommerce_payments_ids = array_search( 'woocommerce-payments', $suggestion_plugins, true );
|
||||
if ( false !== $woocommerce_payments_ids ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* Evaluates the spec and returns a status.
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Admin\RemoteInboxNotifications\RuleEvaluator;
|
||||
|
||||
/**
|
||||
* Evaluates the spec and returns the evaluated suggestion.
|
||||
*/
|
||||
class EvaluateSuggestion {
|
||||
/**
|
||||
* Evaluates the spec and returns the suggestion.
|
||||
*
|
||||
* @param object|array $spec The suggestion to evaluate.
|
||||
* @return object The evaluated suggestion.
|
||||
*/
|
||||
public static function evaluate( $spec ) {
|
||||
$rule_evaluator = new RuleEvaluator();
|
||||
$suggestion = is_array( $spec ) ? (object) $spec : clone $spec;
|
||||
|
||||
if ( isset( $suggestion->is_visible ) ) {
|
||||
$is_visible = $rule_evaluator->evaluate( $suggestion->is_visible );
|
||||
$suggestion->is_visible = $is_visible;
|
||||
}
|
||||
|
||||
return $suggestion;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
/**
|
||||
* Handles running payment gateway suggestion specs
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions\DefaultPaymentGateways;
|
||||
use Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions\PaymentGatewaysController;
|
||||
|
||||
/**
|
||||
* Remote Payment Methods engine.
|
||||
* This goes through the specs and gets eligible payment gateways.
|
||||
*/
|
||||
class Init {
|
||||
/**
|
||||
* Option name for dismissed payment method suggestions.
|
||||
*/
|
||||
const RECOMMENDED_PAYMENT_PLUGINS_DISMISS_OPTION = 'woocommerce_setting_payments_recommendations_hidden';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
PaymentGatewaysController::init();
|
||||
add_action( 'update_option_woocommerce_default_country', array( $this, 'delete_specs_transient' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Go through the specs and run them.
|
||||
*
|
||||
* @param array|null $specs payment suggestion spec array.
|
||||
* @return array
|
||||
*/
|
||||
public static function get_suggestions( array $specs = null ) {
|
||||
$suggestions = array();
|
||||
if ( null === $specs ) {
|
||||
$specs = self::get_specs();
|
||||
}
|
||||
|
||||
foreach ( $specs as $spec ) {
|
||||
$suggestion = EvaluateSuggestion::evaluate( $spec );
|
||||
$suggestions[] = $suggestion;
|
||||
}
|
||||
|
||||
return array_values(
|
||||
array_filter(
|
||||
$suggestions,
|
||||
function( $suggestion ) {
|
||||
return ! property_exists( $suggestion, 'is_visible' ) || $suggestion->is_visible;
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the specs transient.
|
||||
*/
|
||||
public static function delete_specs_transient() {
|
||||
PaymentGatewaySuggestionsDataSourcePoller::get_instance()->delete_specs_transient();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get specs or fetch remotely if they don't exist.
|
||||
*/
|
||||
public static function get_specs() {
|
||||
if ( 'no' === get_option( 'woocommerce_show_marketplace_suggestions', 'yes' ) ) {
|
||||
return apply_filters( 'woocommerce_admin_payment_gateway_suggestion_specs', DefaultPaymentGateways::get_all() );
|
||||
}
|
||||
$specs = PaymentGatewaySuggestionsDataSourcePoller::get_instance()->get_specs_from_data_sources();
|
||||
|
||||
// Fetch specs if they don't yet exist.
|
||||
if ( false === $specs || ! is_array( $specs ) || 0 === count( $specs ) ) {
|
||||
return apply_filters( 'woocommerce_admin_payment_gateway_suggestion_specs', DefaultPaymentGateways::get_all() );
|
||||
}
|
||||
|
||||
return apply_filters( 'woocommerce_admin_payment_gateway_suggestion_specs', $specs );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if suggestions should be shown in the settings screen.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function should_display() {
|
||||
if ( 'yes' === get_option( self::RECOMMENDED_PAYMENT_PLUGINS_DISMISS_OPTION, 'no' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( 'no' === get_option( 'woocommerce_show_marketplace_suggestions', 'yes' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return apply_filters( 'woocommerce_allow_payment_recommendations', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss the suggestions.
|
||||
*/
|
||||
public static function dismiss() {
|
||||
return update_option( self::RECOMMENDED_PAYMENT_PLUGINS_DISMISS_OPTION, 'yes' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions;
|
||||
|
||||
use Automattic\WooCommerce\Admin\DataSourcePoller;
|
||||
|
||||
/**
|
||||
* Specs data source poller class for payment gateway suggestions.
|
||||
*/
|
||||
class PaymentGatewaySuggestionsDataSourcePoller extends DataSourcePoller {
|
||||
|
||||
/**
|
||||
* Data Source Poller ID.
|
||||
*/
|
||||
const ID = 'payment_gateway_suggestions';
|
||||
|
||||
/**
|
||||
* Default data sources array.
|
||||
*/
|
||||
const DATA_SOURCES = array(
|
||||
'https://woocommerce.com/wp-json/wccom/payment-gateway-suggestions/1.0/suggestions.json',
|
||||
);
|
||||
|
||||
/**
|
||||
* Class instance.
|
||||
*
|
||||
* @var Analytics instance
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* Get class instance.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
// Add country query param to data sources.
|
||||
$base_location = wc_get_base_location();
|
||||
$data_sources = array_map(
|
||||
function( $url ) use ( $base_location ) {
|
||||
return add_query_arg(
|
||||
'country',
|
||||
$base_location['country'],
|
||||
$url
|
||||
);
|
||||
},
|
||||
self::DATA_SOURCES
|
||||
);
|
||||
|
||||
self::$instance = new self( self::ID, $data_sources );
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
/**
|
||||
* Logic for extending WC_REST_Payment_Gateways_Controller.
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\TransientNotices;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* PaymentGateway class
|
||||
*/
|
||||
class PaymentGatewaysController {
|
||||
|
||||
/**
|
||||
* Initialize payment gateway changes.
|
||||
*/
|
||||
public static function init() {
|
||||
add_filter( 'woocommerce_rest_prepare_payment_gateway', array( __CLASS__, 'extend_response' ), 10, 3 );
|
||||
add_filter( 'admin_init', array( __CLASS__, 'possibly_do_connection_return_action' ) );
|
||||
add_action( 'woocommerce_admin_payment_gateway_connection_return', array( __CLASS__, 'handle_successfull_connection' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add necessary fields to REST API response.
|
||||
*
|
||||
* @param WP_REST_Response $response Response data.
|
||||
* @param WC_Payment_Gateway $gateway Payment gateway object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public static function extend_response( $response, $gateway, $request ) {
|
||||
$data = $response->get_data();
|
||||
|
||||
$data['needs_setup'] = $gateway->needs_setup();
|
||||
$data['post_install_scripts'] = self::get_post_install_scripts( $gateway );
|
||||
$data['settings_url'] = method_exists( $gateway, 'get_settings_url' )
|
||||
? $gateway->get_settings_url()
|
||||
: admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=' . strtolower( $gateway->id ) );
|
||||
|
||||
$return_url = wc_admin_url( '&task=payments&connection-return=' . strtolower( $gateway->id ) . '&_wpnonce=' . wp_create_nonce( 'connection-return' ) );
|
||||
$data['connection_url'] = method_exists( $gateway, 'get_connection_url' )
|
||||
? $gateway->get_connection_url( $return_url )
|
||||
: null;
|
||||
|
||||
$data['setup_help_text'] = method_exists( $gateway, 'get_setup_help_text' )
|
||||
? $gateway->get_setup_help_text()
|
||||
: null;
|
||||
|
||||
$data['required_settings_keys'] = method_exists( $gateway, 'get_required_settings_keys' )
|
||||
? $gateway->get_required_settings_keys()
|
||||
: array();
|
||||
|
||||
$response->set_data( $data );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get payment gateway scripts for post-install.
|
||||
*
|
||||
* @param WC_Payment_Gateway $gateway Payment gateway object.
|
||||
* @return array Install scripts.
|
||||
*/
|
||||
public static function get_post_install_scripts( $gateway ) {
|
||||
$scripts = array();
|
||||
$wp_scripts = wp_scripts();
|
||||
|
||||
$handles = method_exists( $gateway, 'get_post_install_script_handles' )
|
||||
? $gateway->get_post_install_script_handles()
|
||||
: array();
|
||||
|
||||
foreach ( $handles as $handle ) {
|
||||
if ( isset( $wp_scripts->registered[ $handle ] ) ) {
|
||||
$scripts[] = $wp_scripts->registered[ $handle ];
|
||||
}
|
||||
}
|
||||
|
||||
return $scripts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call an action after a gating has been successfully returned.
|
||||
*/
|
||||
public static function possibly_do_connection_return_action() {
|
||||
if (
|
||||
! isset( $_GET['page'] ) ||
|
||||
'wc-admin' !== $_GET['page'] ||
|
||||
! isset( $_GET['task'] ) ||
|
||||
'payments' !== $_GET['task'] ||
|
||||
! isset( $_GET['connection-return'] ) ||
|
||||
! isset( $_GET['_wpnonce'] ) ||
|
||||
! wp_verify_nonce( wc_clean( wp_unslash( $_GET['_wpnonce'] ) ), 'connection-return' )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$gateway_id = sanitize_text_field( wp_unslash( $_GET['connection-return'] ) );
|
||||
|
||||
do_action( 'woocommerce_admin_payment_gateway_connection_return', $gateway_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a successful gateway connection.
|
||||
*
|
||||
* @param string $gateway_id Gateway ID.
|
||||
*/
|
||||
public static function handle_successfull_connection( $gateway_id ) {
|
||||
// phpcs:disable WordPress.Security.NonceVerification
|
||||
if ( ! isset( $_GET['success'] ) || 1 !== intval( $_GET['success'] ) ) {
|
||||
return;
|
||||
}
|
||||
// phpcs:enable WordPress.Security.NonceVerification
|
||||
|
||||
$payment_gateways = WC()->payment_gateways()->payment_gateways();
|
||||
$payment_gateway = isset( $payment_gateways[ $gateway_id ] ) ? $payment_gateways[ $gateway_id ] : null;
|
||||
|
||||
if ( ! $payment_gateway ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$payment_gateway->update_option( 'enabled', 'yes' );
|
||||
|
||||
TransientNotices::add(
|
||||
array(
|
||||
'user_id' => get_current_user_id(),
|
||||
'id' => 'payment-gateway-connection-return-' . str_replace( ',', '-', $gateway_id ),
|
||||
'status' => 'success',
|
||||
'content' => sprintf(
|
||||
/* translators: the title of the payment gateway */
|
||||
__( '%s connected successfully', 'woocommerce' ),
|
||||
$payment_gateway->method_title
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
wc_admin_record_tracks_event(
|
||||
'tasklist_payment_connect_method',
|
||||
array(
|
||||
'payment_method' => $gateway_id,
|
||||
)
|
||||
);
|
||||
|
||||
wp_safe_redirect( wc_admin_url() );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Product Editor Block Registration
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\ProductBlockEditor;
|
||||
|
||||
use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;
|
||||
|
||||
/**
|
||||
* Product block registration and style registration functionality.
|
||||
*/
|
||||
class BlockRegistry {
|
||||
|
||||
/**
|
||||
* Generic blocks directory.
|
||||
*/
|
||||
const GENERIC_BLOCKS_DIR = 'product-editor/blocks/generic';
|
||||
/**
|
||||
* Product fields blocks directory.
|
||||
*/
|
||||
const PRODUCT_FIELDS_BLOCKS_DIR = 'product-editor/blocks/product-fields';
|
||||
/**
|
||||
* Array of all available generic blocks.
|
||||
*/
|
||||
const GENERIC_BLOCKS = array(
|
||||
'woocommerce/conditional',
|
||||
'woocommerce/product-checkbox-field',
|
||||
'woocommerce/product-collapsible',
|
||||
'woocommerce/product-radio-field',
|
||||
'woocommerce/product-pricing-field',
|
||||
'woocommerce/product-section',
|
||||
'woocommerce/product-tab',
|
||||
'woocommerce/product-toggle-field',
|
||||
'woocommerce/product-taxonomy-field',
|
||||
'woocommerce/product-text-field',
|
||||
'woocommerce/product-number-field',
|
||||
);
|
||||
|
||||
/**
|
||||
* Array of all available product fields blocks.
|
||||
*/
|
||||
const PRODUCT_FIELDS_BLOCKS = array(
|
||||
'woocommerce/product-catalog-visibility-field',
|
||||
'woocommerce/product-description-field',
|
||||
'woocommerce/product-downloads-field',
|
||||
'woocommerce/product-images-field',
|
||||
'woocommerce/product-inventory-email-field',
|
||||
'woocommerce/product-sku-field',
|
||||
'woocommerce/product-name-field',
|
||||
'woocommerce/product-regular-price-field',
|
||||
'woocommerce/product-sale-price-field',
|
||||
'woocommerce/product-schedule-sale-fields',
|
||||
'woocommerce/product-shipping-class-field',
|
||||
'woocommerce/product-shipping-dimensions-fields',
|
||||
'woocommerce/product-summary-field',
|
||||
'woocommerce/product-tag-field',
|
||||
'woocommerce/product-inventory-quantity-field',
|
||||
'woocommerce/product-variation-items-field',
|
||||
'woocommerce/product-variations-fields',
|
||||
'woocommerce/product-password-field',
|
||||
'woocommerce/product-has-variations-notice',
|
||||
'woocommerce/product-single-variation-notice',
|
||||
);
|
||||
|
||||
/**
|
||||
* Get a file path for a given block file.
|
||||
*
|
||||
* @param string $path File path.
|
||||
* @param string $dir File directory.
|
||||
*/
|
||||
private function get_file_path( $path, $dir ) {
|
||||
return WC_ABSPATH . WCAdminAssets::get_path( 'js' ) . trailingslashit( $dir ) . $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all blocks.
|
||||
*/
|
||||
public function init() {
|
||||
add_filter( 'block_categories_all', array( $this, 'register_categories' ), 10, 2 );
|
||||
$this->register_product_blocks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all the product blocks.
|
||||
*/
|
||||
private function register_product_blocks() {
|
||||
foreach ( self::PRODUCT_FIELDS_BLOCKS as $block_name ) {
|
||||
$this->register_block( $block_name, self::PRODUCT_FIELDS_BLOCKS_DIR );
|
||||
}
|
||||
foreach ( self::GENERIC_BLOCKS as $block_name ) {
|
||||
$this->register_block( $block_name, self::GENERIC_BLOCKS_DIR );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register product related block categories.
|
||||
*
|
||||
* @param array[] $block_categories Array of categories for block types.
|
||||
* @param WP_Block_Editor_Context $editor_context The current block editor context.
|
||||
*/
|
||||
public function register_categories( $block_categories, $editor_context ) {
|
||||
if ( INIT::EDITOR_CONTEXT_NAME === $editor_context->name ) {
|
||||
$block_categories[] = array(
|
||||
'slug' => 'woocommerce',
|
||||
'title' => __( 'WooCommerce', 'woocommerce' ),
|
||||
'icon' => null,
|
||||
);
|
||||
}
|
||||
|
||||
return $block_categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the block name without the "woocommerce/" prefix.
|
||||
*
|
||||
* @param string $block_name Block name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function remove_block_prefix( $block_name ) {
|
||||
if ( 0 === strpos( $block_name, 'woocommerce/' ) ) {
|
||||
return substr_replace( $block_name, '', 0, strlen( 'woocommerce/' ) );
|
||||
}
|
||||
|
||||
return $block_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Augment the attributes of a block by adding attributes that are used by the product editor.
|
||||
*
|
||||
* @param array $attributes Block attributes.
|
||||
*/
|
||||
private function augment_attributes( $attributes ) {
|
||||
// Note: If you modify this function, also update the client-side
|
||||
// registerWooBlockType function in @woocommerce/block-templates.
|
||||
return array_merge(
|
||||
$attributes,
|
||||
array(
|
||||
'_templateBlockId' => array(
|
||||
'type' => 'string',
|
||||
'__experimentalRole' => 'content',
|
||||
),
|
||||
'_templateBlockOrder' => array(
|
||||
'type' => 'integer',
|
||||
'__experimentalRole' => 'content',
|
||||
),
|
||||
'_templateBlockHideConditions' => array(
|
||||
'type' => 'array',
|
||||
'__experimentalRole' => 'content',
|
||||
),
|
||||
'_templateBlockDisableConditions' => array(
|
||||
'type' => 'array',
|
||||
'__experimentalRole' => 'content',
|
||||
),
|
||||
'disabled' => isset( $attributes['disabled'] ) ? $attributes['disabled'] : array(
|
||||
'type' => 'boolean',
|
||||
'__experimentalRole' => 'content',
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Augment the uses_context of a block by adding attributes that are used by the product editor.
|
||||
*
|
||||
* @param array $uses_context Block uses_context.
|
||||
*/
|
||||
private function augment_uses_context( $uses_context ) {
|
||||
// Note: If you modify this function, also update the client-side
|
||||
// registerProductEditorBlockType function in @woocommerce/product-editor.
|
||||
return array_merge(
|
||||
isset( $uses_context ) ? $uses_context : array(),
|
||||
array(
|
||||
'postType',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a single block.
|
||||
*
|
||||
* @param string $block_name Block name.
|
||||
* @param string $block_dir Block directory.
|
||||
*
|
||||
* @return WP_Block_Type|false The registered block type on success, or false on failure.
|
||||
*/
|
||||
private function register_block( $block_name, $block_dir ) {
|
||||
$block_name = $this->remove_block_prefix( $block_name );
|
||||
$block_json_file = $this->get_file_path( $block_name . '/block.json', $block_dir );
|
||||
|
||||
if ( ! file_exists( $block_json_file ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
|
||||
$metadata = json_decode( file_get_contents( $block_json_file ), true );
|
||||
if ( ! is_array( $metadata ) || ! $metadata['name'] ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$registry = \WP_Block_Type_Registry::get_instance();
|
||||
|
||||
if ( $registry->is_registered( $metadata['name'] ) ) {
|
||||
$registry->unregister( $metadata['name'] );
|
||||
}
|
||||
|
||||
return register_block_type_from_metadata(
|
||||
$block_json_file,
|
||||
array(
|
||||
'attributes' => $this->augment_attributes( isset( $metadata['attributes'] ) ? $metadata['attributes'] : array() ),
|
||||
'uses_context' => $this->augment_uses_context( isset( $metadata['usesContext'] ) ? $metadata['usesContext'] : array() ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Product Block Editor
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\ProductBlockEditor;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
use Automattic\WooCommerce\Internal\Admin\Features\ProductBlockEditor\ProductTemplates\SimpleProductTemplate;
|
||||
use Automattic\WooCommerce\Internal\Admin\Features\ProductBlockEditor\ProductTemplates\ProductVariationTemplate;
|
||||
use Automattic\WooCommerce\Admin\PageController;
|
||||
use Automattic\WooCommerce\Internal\Admin\BlockTemplateRegistry\BlockTemplateRegistry;
|
||||
use Automattic\WooCommerce\Internal\Admin\BlockTemplates\Block;
|
||||
use Automattic\WooCommerce\Internal\Admin\BlockTemplates\BlockTemplateLogger;
|
||||
use WP_Block_Editor_Context;
|
||||
|
||||
/**
|
||||
* Loads assets related to the product block editor.
|
||||
*/
|
||||
class Init {
|
||||
/**
|
||||
* The context name used to identify the editor.
|
||||
*/
|
||||
const EDITOR_CONTEXT_NAME = 'woocommerce/edit-product';
|
||||
|
||||
/**
|
||||
* Supported post types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $supported_post_types = array( 'simple' );
|
||||
|
||||
/**
|
||||
* Redirection controller.
|
||||
*
|
||||
* @var RedirectionController
|
||||
*/
|
||||
private $redirection_controller;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
if ( Features::is_enabled( 'product-variation-management' ) ) {
|
||||
array_push( $this->supported_post_types, 'variable' );
|
||||
}
|
||||
|
||||
if ( Features::is_enabled( 'product-external-affiliate' ) ) {
|
||||
array_push( $this->supported_post_types, 'external' );
|
||||
}
|
||||
|
||||
$this->redirection_controller = new RedirectionController( $this->supported_post_types );
|
||||
|
||||
if ( \Automattic\WooCommerce\Utilities\FeaturesUtil::feature_is_enabled( 'product_block_editor' ) ) {
|
||||
if ( ! Features::is_enabled( 'new-product-management-experience' ) ) {
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'dequeue_conflicting_styles' ), 100 );
|
||||
add_action( 'get_edit_post_link', array( $this, 'update_edit_product_link' ), 10, 2 );
|
||||
}
|
||||
add_filter( 'woocommerce_admin_get_user_data_fields', array( $this, 'add_user_data_fields' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
||||
add_filter( 'woocommerce_register_post_type_product_variation', array( $this, 'enable_rest_api_for_product_variation' ) );
|
||||
|
||||
add_action( 'current_screen', array( $this, 'set_current_screen_to_block_editor_if_wc_admin' ) );
|
||||
|
||||
$block_registry = new BlockRegistry();
|
||||
$block_registry->init();
|
||||
|
||||
$tracks = new Tracks();
|
||||
$tracks->init();
|
||||
|
||||
// Make sure the block template logger is initialized before any templates are created.
|
||||
BlockTemplateLogger::get_instance();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts needed for the product form block editor.
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
if ( ! PageController::is_admin_or_embed_page() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->register_product_editor_templates();
|
||||
$editor_settings = $this->get_product_editor_settings();
|
||||
|
||||
$script_handle = 'wc-admin-edit-product';
|
||||
wp_register_script( $script_handle, '', array(), '0.1.0', true );
|
||||
wp_enqueue_script( $script_handle );
|
||||
wp_add_inline_script(
|
||||
$script_handle,
|
||||
'var productBlockEditorSettings = productBlockEditorSettings || ' . wp_json_encode( $editor_settings ) . ';',
|
||||
'before'
|
||||
);
|
||||
wp_add_inline_script(
|
||||
$script_handle,
|
||||
sprintf( 'wp.blocks.setCategories( %s );', wp_json_encode( $editor_settings['blockCategories'] ) ),
|
||||
'before'
|
||||
);
|
||||
wp_tinymce_inline_scripts();
|
||||
wp_enqueue_media();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue styles needed for the rich text editor.
|
||||
*/
|
||||
public function enqueue_styles() {
|
||||
if ( ! PageController::is_admin_or_embed_page() ) {
|
||||
return;
|
||||
}
|
||||
wp_enqueue_style( 'wp-edit-blocks' );
|
||||
wp_enqueue_style( 'wp-format-library' );
|
||||
wp_enqueue_editor();
|
||||
/**
|
||||
* Enqueue any block editor related assets.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*/
|
||||
do_action( 'enqueue_block_editor_assets' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeue conflicting styles.
|
||||
*/
|
||||
public function dequeue_conflicting_styles() {
|
||||
if ( ! PageController::is_admin_or_embed_page() ) {
|
||||
return;
|
||||
}
|
||||
// Dequeing this to avoid conflicts, until we remove the 'woocommerce-page' class.
|
||||
wp_dequeue_style( 'woocommerce-blocktheme' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the edit product links when the new experience is enabled.
|
||||
*
|
||||
* @param string $link The edit link.
|
||||
* @param int $post_id Post ID.
|
||||
* @return string
|
||||
*/
|
||||
public function update_edit_product_link( $link, $post_id ) {
|
||||
$product = wc_get_product( $post_id );
|
||||
|
||||
if ( ! $product ) {
|
||||
return $link;
|
||||
}
|
||||
|
||||
if ( $product->get_type() === 'simple' ) {
|
||||
return admin_url( 'admin.php?page=wc-admin&path=/product/' . $product->get_id() );
|
||||
}
|
||||
|
||||
return $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables variation post type in REST API.
|
||||
*
|
||||
* @param array $args Array of post type arguments.
|
||||
* @return array Array of post type arguments.
|
||||
*/
|
||||
public function enable_rest_api_for_product_variation( $args ) {
|
||||
$args['show_in_rest'] = true;
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds fields so that we can store user preferences for the variations block.
|
||||
*
|
||||
* @param array $user_data_fields User data fields.
|
||||
* @return array
|
||||
*/
|
||||
public function add_user_data_fields( $user_data_fields ) {
|
||||
return array_merge(
|
||||
$user_data_fields,
|
||||
array(
|
||||
'variable_product_block_tour_shown',
|
||||
'product_block_variable_options_notice_dismissed',
|
||||
'variable_items_without_price_notice_dismissed',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current screen to the block editor if a wc-admin page.
|
||||
*/
|
||||
public function set_current_screen_to_block_editor_if_wc_admin() {
|
||||
$screen = get_current_screen();
|
||||
|
||||
// phpcs:ignore Squiz.PHP.CommentedOutCode.Found
|
||||
// (no idea why I need that phpcs:ignore above, but I'm tired trying to re-write this comment to get it to pass)
|
||||
// we can't check the 'path' query param because client-side routing is used within wc-admin,
|
||||
// so this action handler is only called on the initial page load from the server, which might
|
||||
// not be the product edit page (it mostly likely isn't).
|
||||
if ( PageController::is_admin_page() ) {
|
||||
$screen->is_block_editor( true );
|
||||
|
||||
wp_add_inline_script(
|
||||
'wp-blocks',
|
||||
'wp.blocks && wp.blocks.unstable__bootstrapServerSideBlockDefinitions && wp.blocks.unstable__bootstrapServerSideBlockDefinitions(' . wp_json_encode( get_block_editor_server_block_settings() ) . ');'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the product editor settings.
|
||||
*/
|
||||
private function get_product_editor_settings() {
|
||||
$editor_settings = array();
|
||||
|
||||
$template_registry = wc_get_container()->get( BlockTemplateRegistry::class );
|
||||
$block_template_logger = BlockTemplateLogger::get_instance();
|
||||
|
||||
$block_template_logger->log_template_events_to_file( 'simple-product' );
|
||||
$block_template_logger->log_template_events_to_file( 'product-variation' );
|
||||
|
||||
$editor_settings['templates'] = array(
|
||||
'product' => $template_registry->get_registered( 'simple-product' )->get_formatted_template(),
|
||||
'product_variation' => $template_registry->get_registered( 'product-variation' )->get_formatted_template(),
|
||||
);
|
||||
|
||||
$editor_settings['templateEvents'] = array(
|
||||
'product' => $block_template_logger->get_formatted_template_events( 'simple-product' ),
|
||||
'product_variation' => $block_template_logger->get_formatted_template_events( 'product-variation' ),
|
||||
);
|
||||
|
||||
$block_editor_context = new WP_Block_Editor_Context( array( 'name' => self::EDITOR_CONTEXT_NAME ) );
|
||||
|
||||
return get_block_editor_settings( $editor_settings, $block_editor_context );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register product editor templates.
|
||||
*/
|
||||
private function register_product_editor_templates() {
|
||||
$template_registry = wc_get_container()->get( BlockTemplateRegistry::class );
|
||||
|
||||
$template_registry->register( new SimpleProductTemplate() );
|
||||
$template_registry->register( new ProductVariationTemplate() );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\ProductBlockEditor\ProductTemplates;
|
||||
|
||||
use Automattic\WooCommerce\Admin\BlockTemplates\BlockContainerInterface;
|
||||
use Automattic\WooCommerce\Admin\BlockTemplates\BlockInterface;
|
||||
|
||||
/**
|
||||
* Interface for group containers, which contain sections and blocks.
|
||||
*/
|
||||
interface GroupInterface extends BlockContainerInterface {
|
||||
|
||||
/**
|
||||
* Adds a new section to the group
|
||||
*
|
||||
* @param array $block_config block config.
|
||||
* @return SectionInterface new block section.
|
||||
*/
|
||||
public function add_section( array $block_config ): SectionInterface;
|
||||
|
||||
/**
|
||||
* Adds a new block to the group.
|
||||
*
|
||||
* @param array $block_config block config.
|
||||
*/
|
||||
public function add_block( array $block_config ): BlockInterface;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\ProductBlockEditor\ProductTemplates;
|
||||
|
||||
use Automattic\WooCommerce\Admin\BlockTemplates\BlockInterface;
|
||||
use Automattic\WooCommerce\Admin\BlockTemplates\BlockTemplateInterface;
|
||||
|
||||
/**
|
||||
* Interface for block containers.
|
||||
*/
|
||||
interface ProductFormTemplateInterface extends BlockTemplateInterface {
|
||||
|
||||
/**
|
||||
* Adds a new group block.
|
||||
*
|
||||
* @param array $block_config block config.
|
||||
* @return GroupInterface new group block.
|
||||
*/
|
||||
public function add_group( array $block_config ): GroupInterface;
|
||||
|
||||
/**
|
||||
* Gets Group block by id.
|
||||
*
|
||||
* @param string $group_id group id.
|
||||
* @return GroupInterface|null
|
||||
*/
|
||||
public function get_group_by_id( string $group_id ): ?GroupInterface;
|
||||
|
||||
/**
|
||||
* Gets Section block by id.
|
||||
*
|
||||
* @param string $section_id section id.
|
||||
* @return SectionInterface|null
|
||||
*/
|
||||
public function get_section_by_id( string $section_id ): ?SectionInterface;
|
||||
|
||||
/**
|
||||
* Gets Block by id.
|
||||
*
|
||||
* @param string $block_id block id.
|
||||
* @return BlockInterface|null
|
||||
*/
|
||||
public function get_block_by_id( string $block_id ): ?BlockInterface;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\ProductBlockEditor\ProductTemplates;
|
||||
|
||||
use Automattic\WooCommerce\Admin\BlockTemplates\BlockContainerInterface;
|
||||
use Automattic\WooCommerce\Admin\BlockTemplates\BlockInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Interface for section containers, which contain sub-sections and blocks.
|
||||
*/
|
||||
interface SectionInterface extends BlockContainerInterface {
|
||||
|
||||
/**
|
||||
* Adds a new sub-section to the section.
|
||||
*
|
||||
* @param array $block_config block config.
|
||||
* @return SectionInterface new block section.
|
||||
*/
|
||||
public function add_section( array $block_config ): SectionInterface;
|
||||
|
||||
/**
|
||||
* Adds a new block to the section.
|
||||
*
|
||||
* @param array $block_config block config.
|
||||
*/
|
||||
public function add_block( array $block_config ): BlockInterface;
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Product Editor Redirection Controller
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\ProductBlockEditor;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;
|
||||
|
||||
/**
|
||||
* Handle redirecting to the old or new editor based on features and support.
|
||||
*/
|
||||
class RedirectionController {
|
||||
|
||||
/**
|
||||
* Supported post types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $supported_post_types;
|
||||
|
||||
/**
|
||||
* Set up the hooks used for redirection.
|
||||
*
|
||||
* @param array $supported_post_types Array of supported post types.
|
||||
*/
|
||||
public function __construct( $supported_post_types ) {
|
||||
$this->supported_post_types = $supported_post_types;
|
||||
|
||||
if ( \Automattic\WooCommerce\Utilities\FeaturesUtil::feature_is_enabled( 'product_block_editor' ) ) {
|
||||
add_action( 'current_screen', array( $this, 'maybe_redirect_to_new_editor' ), 30, 0 );
|
||||
add_action( 'current_screen', array( $this, 'redirect_non_supported_product_types' ), 30, 0 );
|
||||
} else {
|
||||
add_action( 'current_screen', array( $this, 'maybe_redirect_to_old_editor' ), 30, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current screen is the legacy add product screen.
|
||||
*/
|
||||
protected function is_legacy_add_new_screen(): bool {
|
||||
$screen = get_current_screen();
|
||||
return 'post' === $screen->base && 'product' === $screen->post_type && 'add' === $screen->action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current screen is the legacy edit product screen.
|
||||
*/
|
||||
protected function is_legacy_edit_screen(): bool {
|
||||
$screen = get_current_screen();
|
||||
return 'post' === $screen->base
|
||||
&& 'product' === $screen->post_type
|
||||
&& isset( $_GET['post'] )
|
||||
&& isset( $_GET['action'] )
|
||||
&& 'edit' === $_GET['action'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a product is supported by the new experience.
|
||||
*
|
||||
* @param integer $product_id Product ID.
|
||||
*/
|
||||
protected function is_product_supported( $product_id ): bool {
|
||||
$product = $product_id ? wc_get_product( $product_id ) : null;
|
||||
$digital_product = $product->is_downloadable() || $product->is_virtual();
|
||||
|
||||
if ( $product && in_array( $product->get_type(), $this->supported_post_types, true ) ) {
|
||||
if ( Features::is_enabled( 'product-virtual-downloadable' ) ) {
|
||||
return true;
|
||||
}
|
||||
return ! $digital_product;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects from old product form to the new product form if the
|
||||
* feature `product_block_editor` is enabled.
|
||||
*/
|
||||
public function maybe_redirect_to_new_editor(): void {
|
||||
if ( $this->is_legacy_add_new_screen() ) {
|
||||
wp_safe_redirect( admin_url( 'admin.php?page=wc-admin&path=/add-product' ) );
|
||||
exit();
|
||||
}
|
||||
|
||||
if ( $this->is_legacy_edit_screen() ) {
|
||||
$product_id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : null;
|
||||
if ( ! $this->is_product_supported( $product_id ) ) {
|
||||
return;
|
||||
}
|
||||
wp_safe_redirect( admin_url( 'admin.php?page=wc-admin&path=/product/' . $product_id ) );
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects from new product form to the old product form if the
|
||||
* feature `product_block_editor` is enabled.
|
||||
*/
|
||||
public function maybe_redirect_to_old_editor(): void {
|
||||
$route = $this->get_parsed_route();
|
||||
|
||||
if ( 'add-product' === $route['page'] ) {
|
||||
wp_safe_redirect( admin_url( 'post-new.php?post_type=product' ) );
|
||||
exit();
|
||||
}
|
||||
|
||||
if ( 'product' === $route['page'] ) {
|
||||
wp_safe_redirect( admin_url( 'post.php?post=' . $route['product_id'] . '&action=edit' ) );
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parsed WooCommerce Admin path.
|
||||
*/
|
||||
protected function get_parsed_route(): array {
|
||||
if ( ! \Automattic\WooCommerce\Admin\PageController::is_admin_page() || ! isset( $_GET['path'] ) ) {
|
||||
return array(
|
||||
'page' => null,
|
||||
'product_id' => null,
|
||||
);
|
||||
}
|
||||
|
||||
$path = esc_url_raw( wp_unslash( $_GET['path'] ) );
|
||||
$path_pieces = explode( '/', wp_parse_url( $path, PHP_URL_PATH ) );
|
||||
|
||||
return array(
|
||||
'page' => $path_pieces[1],
|
||||
'product_id' => 'product' === $path_pieces[1] ? absint( $path_pieces[2] ) : null,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Redirect non supported product types to legacy editor.
|
||||
*/
|
||||
public function redirect_non_supported_product_types(): void {
|
||||
$route = $this->get_parsed_route();
|
||||
$product_id = $route['product_id'];
|
||||
|
||||
if ( 'product' === $route['page'] && ! $this->is_product_supported( $product_id ) ) {
|
||||
wp_safe_redirect( admin_url( 'post.php?post=' . $route['product_id'] . '&action=edit' ) );
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Product Block Editor
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\ProductBlockEditor;
|
||||
|
||||
/**
|
||||
* Add tracks for the product block editor.
|
||||
*/
|
||||
class Tracks {
|
||||
|
||||
/**
|
||||
* Initialize the tracks.
|
||||
*/
|
||||
public function init() {
|
||||
add_filter( 'woocommerce_product_source', array( $this, 'add_product_source' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a URL is a product editor page.
|
||||
*
|
||||
* @param string $url Url to check.
|
||||
* @return boolean
|
||||
*/
|
||||
protected function is_product_editor_page( $url ) {
|
||||
$query_string = wp_parse_url( wp_get_referer(), PHP_URL_QUERY );
|
||||
parse_str( $query_string, $query );
|
||||
|
||||
if ( ! isset( $query['page'] ) || 'wc-admin' !== $query['page'] || ! isset( $query['path'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$path_pieces = explode( '/', $query['path'] );
|
||||
$route = $path_pieces[1];
|
||||
|
||||
return 'add-product' === $route || 'product' === $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the product source if we're on the product editor page.
|
||||
*
|
||||
* @param string $source Source of product.
|
||||
* @return string
|
||||
*/
|
||||
public function add_product_source( $source ) {
|
||||
if ( $this->is_product_editor_page( wp_get_referer() ) ) {
|
||||
return 'product-block-editor-v1';
|
||||
}
|
||||
|
||||
return $source;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\ShippingPartnerSuggestions;
|
||||
|
||||
/**
|
||||
* Default Shipping Partners
|
||||
*/
|
||||
class DefaultShippingPartners {
|
||||
|
||||
/**
|
||||
* Get default specs.
|
||||
*
|
||||
* @return array Default specs.
|
||||
*/
|
||||
public static function get_all() {
|
||||
$asset_base_url = WC()->plugin_url() . '/assets/images/shipping_partners/';
|
||||
$column_layout_features = array(
|
||||
array(
|
||||
'icon' => $asset_base_url . 'timer.svg',
|
||||
'title' => __( 'Save time', 'woocommerce' ),
|
||||
'description' => __(
|
||||
'Automatically import order information to quickly print your labels.',
|
||||
'woocommerce'
|
||||
),
|
||||
),
|
||||
array(
|
||||
'icon' => $asset_base_url . 'discount.svg',
|
||||
'title' => __( 'Save money', 'woocommerce' ),
|
||||
'description' => __(
|
||||
'Shop for the best shipping rates, and access pre-negotiated discounted rates.',
|
||||
'woocommerce'
|
||||
),
|
||||
),
|
||||
array(
|
||||
'icon' => $asset_base_url . 'star.svg',
|
||||
'title' => __( 'Wow your shoppers', 'woocommerce' ),
|
||||
'description' => __(
|
||||
'Keep your customers informed with tracking notifications.',
|
||||
'woocommerce'
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$check_icon = $asset_base_url . 'check.svg';
|
||||
|
||||
return array(
|
||||
array(
|
||||
'name' => 'ShipStation',
|
||||
'slug' => 'woocommerce-shipstation-integration',
|
||||
'description' => __( 'Powerful yet easy-to-use solution:', 'woocommerce' ),
|
||||
'layout_column' => array(
|
||||
'image' => $asset_base_url . 'shipstation-column.svg',
|
||||
'features' => $column_layout_features,
|
||||
),
|
||||
'layout_row' => array(
|
||||
'image' => $asset_base_url . 'shipstation-row.svg',
|
||||
'features' => array(
|
||||
array(
|
||||
'icon' => $check_icon,
|
||||
'description' => __(
|
||||
'Print labels from Royal Mail, Parcel Force, DPD, and many more',
|
||||
'woocommerce'
|
||||
),
|
||||
),
|
||||
array(
|
||||
'icon' => $check_icon,
|
||||
'description' => __(
|
||||
'Shop for the best rates, in real-time',
|
||||
'woocommerce'
|
||||
),
|
||||
),
|
||||
array(
|
||||
'icon' => $check_icon,
|
||||
'description' => __( 'Connect selling channels easily', 'woocommerce' ),
|
||||
),
|
||||
array(
|
||||
'icon' => $check_icon,
|
||||
'description' => __( 'Advance automated workflows', 'woocommerce' ),
|
||||
),
|
||||
array(
|
||||
'icon' => $check_icon,
|
||||
'description' => __( '30-days free trial', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
'learn_more_link' => 'https://wordpress.org/plugins/woocommerce-shipstation-integration/',
|
||||
'is_visible' => array(
|
||||
self::get_rules_for_countries( array( 'AU', 'CA', 'GB' ) ),
|
||||
),
|
||||
'available_layouts' => array( 'row', 'column' ),
|
||||
),
|
||||
array(
|
||||
'name' => 'Skydropx',
|
||||
'slug' => 'skydropx-cotizador-y-envios',
|
||||
'layout_column' => array(
|
||||
'image' => $asset_base_url . 'skydropx-column.svg',
|
||||
'features' => $column_layout_features,
|
||||
),
|
||||
'description' => '',
|
||||
'learn_more_link' => 'https://wordpress.org/plugins/skydropx-cotizador-y-envios/',
|
||||
'is_visible' => array(
|
||||
self::get_rules_for_countries( array( 'MX', 'CO' ) ),
|
||||
),
|
||||
'available_layouts' => array( 'column' ),
|
||||
),
|
||||
array(
|
||||
'name' => 'Envia',
|
||||
'slug' => '',
|
||||
'description' => '',
|
||||
'layout_column' => array(
|
||||
'image' => $asset_base_url . 'envia-column.svg',
|
||||
'features' => $column_layout_features,
|
||||
),
|
||||
'learn_more_link' => 'https://woo.com/products/envia-shipping-and-fulfillment/',
|
||||
'is_visible' => array(
|
||||
self::get_rules_for_countries( array( 'CL', 'AR', 'PE', 'BR', 'UY', 'GT' ) ),
|
||||
),
|
||||
'available_layouts' => array( 'column' ),
|
||||
),
|
||||
array(
|
||||
'name' => 'Sendcloud',
|
||||
'slug' => 'sendcloud-shipping',
|
||||
'description' => __( 'All-in-one shipping tool:', 'woocommerce' ),
|
||||
'layout_column' => array(
|
||||
'image' => $asset_base_url . 'sendcloud-column.svg',
|
||||
'features' => $column_layout_features,
|
||||
),
|
||||
'layout_row' => array(
|
||||
'image' => $asset_base_url . 'sendcloud-row.svg',
|
||||
'features' => array(
|
||||
array(
|
||||
'icon' => $check_icon,
|
||||
'description' => __( 'Print labels from 80+ carriers', 'woocommerce' ),
|
||||
),
|
||||
array(
|
||||
'icon' => $check_icon,
|
||||
'description' => __(
|
||||
'Process orders in just a few clicks',
|
||||
'woocommerce'
|
||||
),
|
||||
),
|
||||
array(
|
||||
'icon' => $check_icon,
|
||||
'description' => __( 'Customize checkout options', 'woocommerce' ),
|
||||
),
|
||||
|
||||
array(
|
||||
'icon' => $check_icon,
|
||||
'description' => __( 'Self-service tracking & returns', 'woocommerce' ),
|
||||
),
|
||||
array(
|
||||
'icon' => $check_icon,
|
||||
'description' => __( 'Start with a free plan', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
'learn_more_link' => 'https://wordpress.org/plugins/sendcloud-shipping/',
|
||||
'is_visible' => array(
|
||||
self::get_rules_for_countries( array( 'NL', 'AT', 'BE', 'FR', 'DE', 'ES', 'GB', 'IT' ) ),
|
||||
),
|
||||
'available_layouts' => array( 'row', 'column' ),
|
||||
),
|
||||
array(
|
||||
'name' => 'Packlink',
|
||||
'slug' => 'packlink-pro-shipping',
|
||||
'description' => __( 'Optimize your full shipping process:', 'woocommerce' ),
|
||||
'layout_column' => array(
|
||||
'image' => $asset_base_url . 'packlink-column.svg',
|
||||
'features' => $column_layout_features,
|
||||
),
|
||||
'layout_row' => array(
|
||||
'image' => $asset_base_url . 'packlink-row.svg',
|
||||
'features' => array(
|
||||
array(
|
||||
'icon' => $check_icon,
|
||||
'description' => __(
|
||||
'Automated, real-time order import',
|
||||
'woocommerce'
|
||||
),
|
||||
),
|
||||
array(
|
||||
'icon' => $check_icon,
|
||||
'description' => __(
|
||||
'Direct access to leading carriers',
|
||||
'woocommerce'
|
||||
),
|
||||
),
|
||||
array(
|
||||
'icon' => $check_icon,
|
||||
'description' => __(
|
||||
'Access competitive shipping prices',
|
||||
'woocommerce'
|
||||
),
|
||||
),
|
||||
array(
|
||||
'icon' => $check_icon,
|
||||
'description' => __( 'Quickly bulk print labels', 'woocommerce' ),
|
||||
),
|
||||
array(
|
||||
'icon' => $check_icon,
|
||||
'description' => __( 'Free shipping platform', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
'learn_more_link' => 'https://wordpress.org/plugins/packlink-pro-shipping/',
|
||||
'is_visible' => array(
|
||||
self::get_rules_for_countries( array( 'FR', 'DE', 'ES', 'IT' ) ),
|
||||
),
|
||||
'available_layouts' => array( 'row', 'column' ),
|
||||
),
|
||||
array(
|
||||
'name' => 'WooCommerce Shipping',
|
||||
'slug' => 'woocommerce-services',
|
||||
'description' => __( 'Save time and money by printing your shipping labels right from your computer with WooCommerce Shipping. Try WooCommerce Shipping for free.', 'woocommerce' ),
|
||||
'dependencies' => array( 'jetpack' ),
|
||||
'layout_column' => array(
|
||||
'image' => $asset_base_url . 'wcs-column.svg',
|
||||
'features' => array(
|
||||
array(
|
||||
'icon' => $asset_base_url . 'printer.svg',
|
||||
'title' => __( 'Buy postage when you need it', 'woocommerce' ),
|
||||
'description' => __( 'No need to wonder where that stampbook went.', 'woocommerce' ),
|
||||
),
|
||||
array(
|
||||
'icon' => $asset_base_url . 'paper.svg',
|
||||
'title' => __( 'Print at home', 'woocommerce' ),
|
||||
'description' => __( 'Pick up an order, then just pay, print, package and post.', 'woocommerce' ),
|
||||
),
|
||||
array(
|
||||
'icon' => $asset_base_url . 'discount.svg',
|
||||
'title' => __( 'Discounted rates', 'woocommerce' ),
|
||||
'description' => __( 'Access discounted shipping rates with DHL and USPS.', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
'learn_more_link' => 'https://woo.com/products/shipping/',
|
||||
'is_visible' => array(
|
||||
self::get_rules_for_countries( array( 'US' ) ),
|
||||
),
|
||||
'available_layouts' => array( 'column' ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rules that match the store base location to one of the provided countries.
|
||||
*
|
||||
* @param array $countries Array of countries to match.
|
||||
* @return object Rules to match.
|
||||
*/
|
||||
public static function get_rules_for_countries( $countries ) {
|
||||
$rules = array();
|
||||
|
||||
foreach ( $countries as $country ) {
|
||||
$rules[] = (object) array(
|
||||
'type' => 'base_location_country',
|
||||
'value' => $country,
|
||||
'operation' => '=',
|
||||
);
|
||||
}
|
||||
|
||||
return (object) array(
|
||||
'type' => 'or',
|
||||
'operands' => $rules,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\ShippingPartnerSuggestions;
|
||||
|
||||
use Automattic\WooCommerce\Admin\RemoteInboxNotifications\RuleEvaluator;
|
||||
|
||||
/**
|
||||
* Class ShippingPartnerSuggestions
|
||||
*/
|
||||
class ShippingPartnerSuggestions {
|
||||
|
||||
/**
|
||||
* Go through the specs and run them.
|
||||
*
|
||||
* @param array|null $specs shipping partner suggestion spec array.
|
||||
* @return array
|
||||
*/
|
||||
public static function get_suggestions( $specs = null ) {
|
||||
$suggestions = array();
|
||||
if ( null === $specs ) {
|
||||
$specs = self::get_specs_from_datasource();
|
||||
}
|
||||
|
||||
$rule_evaluator = new RuleEvaluator();
|
||||
foreach ( $specs as &$spec ) {
|
||||
$spec = is_array( $spec ) ? (object) $spec : $spec;
|
||||
if ( isset( $spec->is_visible ) ) {
|
||||
$is_visible = $rule_evaluator->evaluate( $spec->is_visible );
|
||||
if ( $is_visible ) {
|
||||
$spec->is_visible = true;
|
||||
$suggestions[] = $spec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $suggestions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get specs or fetch remotely if they don't exist.
|
||||
*/
|
||||
public static function get_specs_from_datasource() {
|
||||
if ( 'no' === get_option( 'woocommerce_show_marketplace_suggestions', 'yes' ) ) {
|
||||
/**
|
||||
* It can be used to modify shipping partner suggestions spec.
|
||||
*
|
||||
* @since 7.4.1
|
||||
*/
|
||||
return apply_filters( 'woocommerce_admin_shipping_partner_suggestions_specs', DefaultShippingPartners::get_all() );
|
||||
}
|
||||
$specs = ShippingPartnerSuggestionsDataSourcePoller::get_instance()->get_specs_from_data_sources();
|
||||
|
||||
// Fetch specs if they don't yet exist.
|
||||
if ( false === $specs || ! is_array( $specs ) || 0 === count( $specs ) ) {
|
||||
/**
|
||||
* It can be used to modify shipping partner suggestions spec.
|
||||
*
|
||||
* @since 7.4.1
|
||||
*/
|
||||
return apply_filters( 'woocommerce_admin_shipping_partner_suggestions_specs', DefaultShippingPartners::get_all() );
|
||||
}
|
||||
|
||||
/**
|
||||
* It can be used to modify shipping partner suggestions spec.
|
||||
*
|
||||
* @since 7.4.1
|
||||
*/
|
||||
return apply_filters( 'woocommerce_admin_shipping_partner_suggestions_specs', $specs );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\ShippingPartnerSuggestions;
|
||||
|
||||
use Automattic\WooCommerce\Admin\DataSourcePoller;
|
||||
|
||||
/**
|
||||
* Specs data source poller class for shipping partner suggestions.
|
||||
*/
|
||||
class ShippingPartnerSuggestionsDataSourcePoller extends DataSourcePoller {
|
||||
|
||||
/**
|
||||
* Data Source Poller ID.
|
||||
*/
|
||||
const ID = 'shipping_partner_suggestions';
|
||||
|
||||
/**
|
||||
* Default data sources array.
|
||||
*/
|
||||
const DATA_SOURCES = array(
|
||||
'https://woocommerce.com/wp-json/wccom/shipping-partner-suggestions/1.0/suggestions.json',
|
||||
);
|
||||
|
||||
/**
|
||||
* Class instance.
|
||||
*
|
||||
* @var ShippingPartnerSuggestionsDataSourcePoller instance
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* Get class instance.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
self::$instance = new self( self::ID, self::DATA_SOURCES );
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Transient Notices
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features;
|
||||
|
||||
use Automattic\WooCommerce\Internal\Admin\Loader;
|
||||
|
||||
/**
|
||||
* Shows print shipping label banner on edit order page.
|
||||
*/
|
||||
class TransientNotices {
|
||||
|
||||
/**
|
||||
* Option name for the queue.
|
||||
*/
|
||||
const QUEUE_OPTION = 'woocommerce_admin_transient_notices_queue';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter( 'woocommerce_admin_preload_options', array( $this, 'preload_options' ) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all notices in the queue.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_queue() {
|
||||
return get_option( self::QUEUE_OPTION, array() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all notices in the queue by a given user ID.
|
||||
*
|
||||
* @param int $user_id User ID.
|
||||
* @return array
|
||||
*/
|
||||
public static function get_queue_by_user( $user_id ) {
|
||||
$notices = self::get_queue();
|
||||
|
||||
return array_filter(
|
||||
$notices,
|
||||
function( $notice ) use ( $user_id ) {
|
||||
return ! isset( $notice['user_id'] ) ||
|
||||
null === $notice['user_id'] ||
|
||||
$user_id === $notice['user_id'];
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a notice by ID.
|
||||
*
|
||||
* @param array $notice_id Notice of ID to get.
|
||||
* @return array|null
|
||||
*/
|
||||
public static function get( $notice_id ) {
|
||||
$queue = self::get_queue();
|
||||
|
||||
if ( isset( $queue[ $notice_id ] ) ) {
|
||||
return $queue[ $notice_id ];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a notice to be shown.
|
||||
*
|
||||
* @param array $notice Notice.
|
||||
* $notice = array(
|
||||
* 'id' => (string) Unique ID for the notice. Required.
|
||||
* 'user_id' => (int|null) User ID to show the notice to.
|
||||
* 'status' => (string) info|error|success
|
||||
* 'content' => (string) Content to be shown for the notice. Required.
|
||||
* 'options' => (array) Array of options to be passed to the notice component.
|
||||
* See https://developer.wordpress.org/block-editor/reference-guides/data/data-core-notices/#createNotice for available options.
|
||||
* ).
|
||||
*/
|
||||
public static function add( $notice ) {
|
||||
$queue = self::get_queue();
|
||||
|
||||
$defaults = array(
|
||||
'user_id' => null,
|
||||
'status' => 'info',
|
||||
'options' => array(),
|
||||
);
|
||||
$notice_data = array_merge( $defaults, $notice );
|
||||
$notice_data['options'] = (object) $notice_data['options'];
|
||||
|
||||
$queue[ $notice['id'] ] = $notice_data;
|
||||
update_option( self::QUEUE_OPTION, $queue );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a notice by ID.
|
||||
*
|
||||
* @param array $notice_id Notice of ID to remove.
|
||||
*/
|
||||
public static function remove( $notice_id ) {
|
||||
$queue = self::get_queue();
|
||||
unset( $queue[ $notice_id ] );
|
||||
update_option( self::QUEUE_OPTION, $queue );
|
||||
}
|
||||
|
||||
/**
|
||||
* Preload options to prime state of the application.
|
||||
*
|
||||
* @param array $options Array of options to preload.
|
||||
* @return array
|
||||
*/
|
||||
public function preload_options( $options ) {
|
||||
$options[] = self::QUEUE_OPTION;
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user