first commit
This commit is contained in:
@@ -0,0 +1,381 @@
|
||||
<?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',
|
||||
'product-block-editor',
|
||||
'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,105 @@
|
||||
<?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';
|
||||
|
||||
/**
|
||||
* 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,158 @@
|
||||
<?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 = '';
|
||||
|
||||
/**
|
||||
* Snoozeable.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $is_snoozeable = false;
|
||||
|
||||
/**
|
||||
* Dismissable.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $is_dismissable = false;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Additonal 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,597 @@
|
||||
<?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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(),
|
||||
'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,494 @@
|
||||
<?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;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\TourInAppMarketplace;
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Array of default tasks.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
const DEFAULT_TASKS = array(
|
||||
'StoreDetails',
|
||||
'Purchase',
|
||||
'Products',
|
||||
'WooCommercePayments',
|
||||
'Payments',
|
||||
'Tax',
|
||||
'Shipping',
|
||||
'Marketing',
|
||||
'Appearance',
|
||||
'AdditionalPayments',
|
||||
'ReviewShippingOptions',
|
||||
'GetMobileApp',
|
||||
'TourInAppMarketplace',
|
||||
);
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
self::add_list(
|
||||
array(
|
||||
'id' => 'setup',
|
||||
'title' => __( 'Get ready to start selling', 'woocommerce' ),
|
||||
'tasks' => array(
|
||||
'StoreDetails',
|
||||
'Purchase',
|
||||
'Products',
|
||||
'WooCommercePayments',
|
||||
'Payments',
|
||||
'Tax',
|
||||
'Shipping',
|
||||
'Marketing',
|
||||
'Appearance',
|
||||
),
|
||||
'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',
|
||||
),
|
||||
)
|
||||
);
|
||||
self::add_list(
|
||||
array(
|
||||
'id' => 'setup_two_column',
|
||||
'hidden_id' => 'setup',
|
||||
'title' => __( 'Get ready to start selling', 'woocommerce' ),
|
||||
'tasks' => array(
|
||||
'Products',
|
||||
'WooCommercePayments',
|
||||
'Payments',
|
||||
'Tax',
|
||||
'Shipping',
|
||||
'Marketing',
|
||||
'Appearance',
|
||||
),
|
||||
'event_prefix' => 'tasklist_',
|
||||
)
|
||||
);
|
||||
self::add_list(
|
||||
array(
|
||||
'id' => 'extended_two_column',
|
||||
'hidden_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',
|
||||
),
|
||||
'event_prefix' => 'extended_tasklist_',
|
||||
)
|
||||
);
|
||||
|
||||
if ( Features::is_enabled( 'shipping-smart-defaults' ) ) {
|
||||
self::add_task(
|
||||
'extended',
|
||||
new ReviewShippingOptions(
|
||||
self::get_list( 'extended' )
|
||||
)
|
||||
);
|
||||
|
||||
self::add_task(
|
||||
'extended_two_column',
|
||||
new ReviewShippingOptions(
|
||||
self::get_list( 'extended_two_column' )
|
||||
)
|
||||
);
|
||||
|
||||
// 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 ( ! wp_is_mobile() ) { // Permit In-App Marketplace Tour on desktops only.
|
||||
$tour_task = new TourInAppMarketplace();
|
||||
self::add_task( 'extended', $tour_task );
|
||||
self::add_task( 'extended_two_column', $tour_task );
|
||||
}
|
||||
|
||||
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 neccessary.
|
||||
* 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 count-' . esc_attr( $tasks_count ) . '">' . number_format_i18n( $tasks_count ) . '</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,117 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Payments Task
|
||||
*/
|
||||
class AdditionalPayments extends Payments {
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
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 ( $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() {
|
||||
if ( ! Features::is_enabled( 'payment-gateway-suggestions' ) ) {
|
||||
// Hide task if feature not enabled.
|
||||
return false;
|
||||
}
|
||||
|
||||
$woocommerce_payments = new WooCommercePayments();
|
||||
|
||||
if ( ! $woocommerce_payments->is_requested() || ! $woocommerce_payments->is_supported() || ! $woocommerce_payments->is_connected() ) {
|
||||
// Hide task if WC Pay is not installed via OBW, or is not connected, or the store is located in a country that is not supported by WC Pay.
|
||||
return false;
|
||||
}
|
||||
if ( $this->get_parent_id() === 'extended_two_column' && WooCommercePayments::is_connected() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,136 @@
|
||||
<?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
|
||||
*
|
||||
* @param TaskList $task_list Parent task list.
|
||||
*/
|
||||
public function __construct( $task_list ) {
|
||||
parent::__construct( $task_list );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'add_media_scripts' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'possibly_add_return_notice_script' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return 'appearance';
|
||||
}
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
if ( $this->get_parent_option( 'use_completed_title' ) === true ) {
|
||||
if ( $this->is_complete() ) {
|
||||
return __( 'You personalized your store', 'woocommerce' );
|
||||
}
|
||||
return __( 'Personalize your store', 'woocommerce' );
|
||||
}
|
||||
return __( 'Personalize my store', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content() {
|
||||
return __(
|
||||
'Add your logo, create a homepage, and start designing your store.',
|
||||
'woocommerce'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_time() {
|
||||
return __( '2 minutes', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Addtional data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_additional_data() {
|
||||
return array(
|
||||
'has_homepage' => self::has_homepage(),
|
||||
'has_products' => Products::has_products(),
|
||||
'stylesheet' => get_option( 'stylesheet' ),
|
||||
'theme_mods' => get_theme_mods(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add media scripts for image uploader.
|
||||
*/
|
||||
public function add_media_scripts() {
|
||||
if ( ! PageController::is_admin_page() || ! $this->can_view() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_media();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a return to task list notice when completing the task.
|
||||
*
|
||||
* @param string $hook Page hook.
|
||||
*/
|
||||
public function possibly_add_return_notice_script( $hook ) {
|
||||
global $post;
|
||||
|
||||
if ( $hook !== 'post.php' || $post->post_type !== 'page' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $this->is_complete() || ! $this->is_active() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
WCAdminAssets::register_script( 'wp-admin-scripts', 'onboarding-homepage-notice', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the site has a homepage set up.
|
||||
*/
|
||||
public static function has_homepage() {
|
||||
if ( get_option( 'classic-editor-replace' ) === 'classic' ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$homepage_id = get_option( 'woocommerce_onboarding_homepage_post_id', false );
|
||||
|
||||
if ( ! $homepage_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$post = get_post( $homepage_id );
|
||||
$completed = $post && $post->post_status === 'publish';
|
||||
|
||||
return $completed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks;
|
||||
|
||||
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' ) &&
|
||||
PluginsHelper::is_plugin_active( 'jetpack' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the Jetpack is connected.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_jetpack_connected() {
|
||||
if ( class_exists( '\Jetpack' ) && is_callable( '\Jetpack::is_connection_ready' ) ) {
|
||||
return \Jetpack::is_connection_ready();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -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,76 @@
|
||||
<?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() {
|
||||
// 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,166 @@
|
||||
<?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 Services and Jetpack 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 ) {
|
||||
$this->is_complete_result = get_option( '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 where to find powerful store add-ons and integrations, with a WooCommerce Marketplace tour',
|
||||
'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-addons&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,159 @@
|
||||
<?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\Admin\Features\OnboardingTasks\TaskList;
|
||||
|
||||
/**
|
||||
* WooCommercePayments Task
|
||||
*/
|
||||
class WooCommercePayments extends Task {
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return 'woocommerce-payments';
|
||||
}
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return __( 'Set up WooCommerce Payments', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 WooCommerce Payments.",
|
||||
'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() {
|
||||
return __(
|
||||
'By using WooCommerce Payments 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() {
|
||||
return self::is_connected();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() &&
|
||||
( $this->get_parent_id() !== 'setup_two_column' || ! self::is_connected() );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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,105 @@
|
||||
<?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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,118 @@
|
||||
<?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 {
|
||||
/**
|
||||
* The directory where blocks are stored after build.
|
||||
*/
|
||||
const BLOCKS_DIR = 'product-editor/blocks';
|
||||
|
||||
/**
|
||||
* Array of all available product blocks.
|
||||
*/
|
||||
const PRODUCT_BLOCKS = [
|
||||
'woocommerce/product-name',
|
||||
'woocommerce/product-pricing',
|
||||
'woocommerce/product-section',
|
||||
'woocommerce/product-tab',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get a file path for a given block file.
|
||||
*
|
||||
* @param string $path File path.
|
||||
*/
|
||||
private function get_file_path( $path ) {
|
||||
return WC_ABSPATH . WCAdminAssets::get_path( 'js' ) . trailingslashit( self::BLOCKS_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_BLOCKS as $block_name ) {
|
||||
$this->register_block( $block_name );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a single block.
|
||||
*
|
||||
* @param string $block_name Block name.
|
||||
*
|
||||
* @return WP_Block_Type|false The registered block type on success, or false on failure.
|
||||
*/
|
||||
private function register_block( $block_name ) {
|
||||
$block_name = $this->remove_block_prefix( $block_name );
|
||||
$block_json_file = $this->get_file_path( $block_name . '/block.json' );
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,479 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Product Block Editor
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\ProductBlockEditor;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\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 product block editor.
|
||||
*/
|
||||
class Init {
|
||||
|
||||
const FEATURE_ID = 'product-block-editor';
|
||||
|
||||
/**
|
||||
* Option name used to toggle this feature.
|
||||
*/
|
||||
const TOGGLE_OPTION_NAME = 'woocommerce_' . self::FEATURE_ID . '_enabled';
|
||||
|
||||
/**
|
||||
* The context name used to identify the editor.
|
||||
*/
|
||||
const EDITOR_CONTEXT_NAME = 'woocommerce/edit-product';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
if ( ! Features::is_enabled( 'new-product-management-experience' ) && Features::is_enabled( self::FEATURE_ID ) ) {
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) );
|
||||
add_action( 'get_edit_post_link', array( $this, 'update_edit_product_link' ), 10, 2 );
|
||||
}
|
||||
if ( Features::is_enabled( self::FEATURE_ID ) ) {
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
||||
add_filter( 'woocommerce_register_post_type_product', array( $this, 'add_product_template' ) );
|
||||
$block_registry = new BlockRegistry();
|
||||
$block_registry->init();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts needed for the product form block editor.
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
if ( ! PageController::is_admin_or_embed_page() ) {
|
||||
return;
|
||||
}
|
||||
$post_type_object = get_post_type_object( 'product' );
|
||||
$block_editor_context = new WP_Block_Editor_Context( array( 'name' => self::EDITOR_CONTEXT_NAME ) );
|
||||
|
||||
$editor_settings = array();
|
||||
if ( ! empty( $post_type_object->template ) ) {
|
||||
$editor_settings['template'] = $post_type_object->template;
|
||||
$editor_settings['templateLock'] = ! empty( $post_type_object->template_lock ) ? $post_type_object->template_lock : false;
|
||||
}
|
||||
|
||||
$editor_settings = get_block_editor_settings( $editor_settings, $block_editor_context );
|
||||
|
||||
$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'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue styles needed for the rich text editor.
|
||||
*
|
||||
* @param array $args Array of post type arguments.
|
||||
* @return array Array of post type arguments.
|
||||
*/
|
||||
public function add_product_template( $args ) {
|
||||
if ( ! isset( $args['template'] ) ) {
|
||||
$args['template_lock'] = 'all';
|
||||
$args['template'] = array(
|
||||
array(
|
||||
'woocommerce/product-tab',
|
||||
array(
|
||||
'id' => 'general',
|
||||
'title' => __( 'General', 'woocommerce' ),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-section',
|
||||
array(
|
||||
'title' => __( 'Basic details', 'woocommerce' ),
|
||||
'description' => __( 'This info will be displayed on the product page, category pages, social media, and search results.', 'woocommerce' ),
|
||||
'icon' => array(
|
||||
'src' => '<svg xmlns="http://www.w3.org/2000/svg" view-box="0 0 24 24"><path fill-rule="evenodd" d="M5 5.5h14a.5.5 0 01.5.5v1.5a.5.5 0 01-.5.5H5a.5.5 0 01-.5-.5V6a.5.5 0 01.5-.5zM4 9.232A2 2 0 013 7.5V6a2 2 0 012-2h14a2 2 0 012 2v1.5a2 2 0 01-1 1.732V18a2 2 0 01-2 2H6a2 2 0 01-2-2V9.232zm1.5.268V18a.5.5 0 00.5.5h12a.5.5 0 00.5-.5V9.5h-13z" clip-rule="evenodd" /></svg>',
|
||||
),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-name',
|
||||
array(
|
||||
'name' => 'Product name',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-summary',
|
||||
),
|
||||
array(
|
||||
'core/columns',
|
||||
array(),
|
||||
array(
|
||||
array(
|
||||
'core/column',
|
||||
array(
|
||||
'templateLock' => 'all',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-pricing',
|
||||
array(
|
||||
'name' => 'regular_price',
|
||||
'label' => __( 'List price', 'woocommerce' ),
|
||||
'showPricingSection' => true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'core/column',
|
||||
array(
|
||||
'templateLock' => 'all',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-pricing',
|
||||
array(
|
||||
'name' => 'sale_price',
|
||||
'label' => __( 'Sale price', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-section',
|
||||
array(
|
||||
'title' => __( 'Images', 'woocommerce' ),
|
||||
'description' => sprintf(
|
||||
/* translators: %1$s: Images guide link opening tag. %2$s: Images guide link closing tag.*/
|
||||
__( 'Drag images, upload new ones or select files from your library. For best results, use JPEG files that are 1000 by 1000 pixels or larger. %1$sHow to prepare images?%2$s.', 'woocommerce' ),
|
||||
'<a href="http://woocommerce.com/#" target="_blank" rel="noreferrer">',
|
||||
'</a>'
|
||||
),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-images',
|
||||
array(
|
||||
'images' => array(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-tab',
|
||||
array(
|
||||
'id' => 'pricing',
|
||||
'title' => __( 'Pricing', 'woocommerce' ),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-section',
|
||||
array(
|
||||
'title' => __( 'Pricing', 'woocommerce' ),
|
||||
'description' => sprintf(
|
||||
/* translators: %1$s: Images guide link opening tag. %2$s: Images guide link closing tag.*/
|
||||
__( 'Set a competitive price, put the product on sale, and manage tax calculations. %1$sHow to price your product?%2$s', 'woocommerce' ),
|
||||
'<a href="https://woocommerce.com/posts/how-to-price-products-strategies-expert-tips/" target="_blank" rel="noreferrer">',
|
||||
'</a>'
|
||||
),
|
||||
'icon' => array(
|
||||
'src' => '<svg xmlns="http://www.w3.org/2000/svg" view-box="0 0 24 24"><path fill-rule="evenodd" d="M16.83 6.342l.602.3.625-.25.443-.176v12.569l-.443-.178-.625-.25-.603.301-1.444.723-2.41-.804-.475-.158-.474.158-2.41.803-1.445-.722-.603-.3-.625.25-.443.177V6.215l.443.178.625.25.603-.301 1.444-.722 2.41.803.475.158.474-.158 2.41-.803 1.445.722zM20 4l-1.5.6-1 .4-2-1-3 1-3-1-2 1-1-.4L5 4v17l1.5-.6 1-.4 2 1 3-1 3 1 2-1 1 .4 1.5.6V4zm-3.5 6.25v-1.5h-8v1.5h8zm0 3v-1.5h-8v1.5h8zm-8 3v-1.5h8v1.5h-8z" clip-rule="evenodd" /></svg>',
|
||||
),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'core/columns',
|
||||
array(),
|
||||
array(
|
||||
array(
|
||||
'core/column',
|
||||
array(
|
||||
'templateLock' => 'all',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-pricing',
|
||||
array(
|
||||
'name' => 'regular_price',
|
||||
'label' => __( 'List price', 'woocommerce' ),
|
||||
'showPricingSection' => true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'core/column',
|
||||
array(
|
||||
'templateLock' => 'all',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-pricing',
|
||||
array(
|
||||
'name' => 'sale_price',
|
||||
'label' => __( 'Sale price', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-schedule-sale-fields',
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-radio',
|
||||
array(
|
||||
'title' => __( 'Charge sales tax on', 'woocommerce' ),
|
||||
'property' => 'tax_status',
|
||||
'options' => array(
|
||||
array(
|
||||
'label' => __( 'Product and shipping', 'woocommerce' ),
|
||||
'value' => 'taxable',
|
||||
),
|
||||
array(
|
||||
'label' => __( 'Only shipping', 'woocommerce' ),
|
||||
'value' => 'shipping',
|
||||
),
|
||||
array(
|
||||
'label' => __( "Don't charge tax", 'woocommerce' ),
|
||||
'value' => 'none',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/collapsible',
|
||||
array(
|
||||
'toggleText' => __( 'Advanced', 'woocommerce' ),
|
||||
'initialCollapsed' => true,
|
||||
'persistRender' => true,
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-radio',
|
||||
array(
|
||||
'title' => __( 'Tax class', 'woocommerce' ),
|
||||
'description' => sprintf(
|
||||
/* translators: %1$s: Learn more link opening tag. %2$s: Learn more link closing tag.*/
|
||||
__( 'Apply a tax rate if this product qualifies for tax reduction or exemption. %1$sLearn more%2$s.', 'woocommerce' ),
|
||||
'<a href="https://woocommerce.com/document/setting-up-taxes-in-woocommerce/#shipping-tax-class" target="_blank" rel="noreferrer">',
|
||||
'</a>'
|
||||
),
|
||||
'property' => 'tax_class',
|
||||
'options' => array(
|
||||
array(
|
||||
'label' => __( 'Standard', 'woocommerce' ),
|
||||
'value' => '',
|
||||
),
|
||||
array(
|
||||
'label' => __( 'Reduced rate', 'woocommerce' ),
|
||||
'value' => 'reduced-rate',
|
||||
),
|
||||
array(
|
||||
'label' => __( 'Zero rate', 'woocommerce' ),
|
||||
'value' => 'zero-rate',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-tab',
|
||||
array(
|
||||
'id' => 'inventory',
|
||||
'title' => __( 'Inventory', 'woocommerce' ),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-section',
|
||||
array(
|
||||
'title' => __( 'Inventory', 'woocommerce' ),
|
||||
'description' => sprintf(
|
||||
/* translators: %1$s: Inventory settings link opening tag. %2$s: Inventory settings link closing tag.*/
|
||||
__( 'Set up and manage inventory for this product, including status and available quantity. %1$sManage store inventory settings%2$s', 'woocommerce' ),
|
||||
'<a href="' . admin_url( 'admin.php?page=wc-settings&tab=products§ion=inventory' ) . '" target="_blank" rel="noreferrer">',
|
||||
'</a>'
|
||||
),
|
||||
'icon' => array(
|
||||
'src' => '<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M2 2H14C14.2761 2 14.5 2.22386 14.5 2.5V9.5H11.5H10C10 10.6046 9.10457 11.5 8 11.5C6.89543 11.5 6 10.6046 6 9.5H4.5H1.5V2.5C1.5 2.22386 1.72386 2 2 2ZM1.5 11V14.5C1.5 14.7761 1.72386 15 2 15H14C14.2761 15 14.5 14.7761 14.5 14.5V11H11.1632C10.6015 12.1825 9.3962 13 8 13C6.6038 13 5.39855 12.1825 4.83682 11H1.5ZM0 9.5V2.5C0 1.39543 0.895431 0.5 2 0.5H14C15.1046 0.5 16 1.39543 16 2.5V9.5V11V14.5C16 15.6046 15.1046 16.5 14 16.5H2C0.895431 16.5 0 15.6046 0 14.5V11V9.5Z" fill="#1E1E1E"/></svg>',
|
||||
),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-sku',
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-track-inventory-fields',
|
||||
),
|
||||
array(
|
||||
'woocommerce/collapsible',
|
||||
array(
|
||||
'toggleText' => __( 'Advanced', 'woocommerce' ),
|
||||
'initialCollapsed' => true,
|
||||
'persistRender' => true,
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/conditional',
|
||||
array(
|
||||
'mustMatch' => array(
|
||||
'manage_stock' => array( true ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-radio',
|
||||
array(
|
||||
'title' => __( 'When out of stock', 'woocommerce' ),
|
||||
'property' => 'backorders',
|
||||
'options' => array(
|
||||
array(
|
||||
'label' => __( 'Allow purchases', 'woocommerce' ),
|
||||
'value' => 'yes',
|
||||
),
|
||||
array(
|
||||
'label' => __(
|
||||
'Allow purchases, but notify customers',
|
||||
'woocommerce'
|
||||
),
|
||||
'value' => 'notify',
|
||||
),
|
||||
array(
|
||||
'label' => __( "Don't allow purchases", 'woocommerce' ),
|
||||
'value' => 'no',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-inventory-email',
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-checkbox',
|
||||
array(
|
||||
'title' => __(
|
||||
'Restrictions',
|
||||
'woocommerce'
|
||||
),
|
||||
'label' => __(
|
||||
'Limit purchases to 1 item per order',
|
||||
'woocommerce'
|
||||
),
|
||||
'property' => 'sold_individually',
|
||||
'tooltip' => __(
|
||||
'When checked, customers will be able to purchase only 1 item in a single order. This is particularly useful for items that have limited quantity, like art or handmade goods.',
|
||||
'woocommerce'
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-tab',
|
||||
array(
|
||||
'id' => 'shipping',
|
||||
'title' => __( 'Shipping', 'woocommerce' ),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-section',
|
||||
array(
|
||||
'title' => __( 'Fees & dimensions', 'woocommerce' ),
|
||||
'description' => sprintf(
|
||||
/* translators: %1$s: How to get started? link opening tag. %2$s: How to get started? link closing tag.*/
|
||||
__( 'Set up shipping costs and enter dimensions used for accurate rate calculations. %1$sHow to get started?%2$s.', 'woocommerce' ),
|
||||
'<a href="https://woocommerce.com/posts/how-to-calculate-shipping-costs-for-your-woocommerce-store/" target="_blank" rel="noreferrer">',
|
||||
'</a>'
|
||||
),
|
||||
'icon' => array(
|
||||
'src' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M3.5 7.75C3.5 6.7835 4.2835 6 5.25 6H14.75H15.5V6.75V9H17.25H17.5607L17.7803 9.21967L20.7803 12.2197L21 12.4393V12.75V14.75C21 15.7165 20.2165 16.5 19.25 16.5H19.2377C19.2458 16.5822 19.25 16.6656 19.25 16.75C19.25 18.1307 18.1307 19.25 16.75 19.25C15.3693 19.25 14.25 18.1307 14.25 16.75C14.25 16.6656 14.2542 16.5822 14.2623 16.5H14H10.2377C10.2458 16.5822 10.25 16.6656 10.25 16.75C10.25 18.1307 9.13071 19.25 7.75 19.25C6.36929 19.25 5.25 18.1307 5.25 16.75C5.25 16.6656 5.25418 16.5822 5.26234 16.5H4.25H3.5V15.75V7.75ZM14 15V9.75V9V7.5H5.25C5.11193 7.5 5 7.61193 5 7.75V15H5.96464C6.41837 14.5372 7.05065 14.25 7.75 14.25C8.44935 14.25 9.08163 14.5372 9.53536 15H14ZM18.5354 15H19.25C19.3881 15 19.5 14.8881 19.5 14.75V13.0607L16.9393 10.5H15.5V14.5845C15.8677 14.3717 16.2946 14.25 16.75 14.25C17.4493 14.25 18.0816 14.5372 18.5354 15ZM6.7815 16.5C6.76094 16.5799 6.75 16.6637 6.75 16.75C6.75 17.3023 7.19772 17.75 7.75 17.75C8.30228 17.75 8.75 17.3023 8.75 16.75C8.75 16.6637 8.73906 16.5799 8.7185 16.5C8.60749 16.0687 8.21596 15.75 7.75 15.75C7.28404 15.75 6.89251 16.0687 6.7815 16.5ZM15.7815 16.5C15.7609 16.5799 15.75 16.6637 15.75 16.75C15.75 17.3023 16.1977 17.75 16.75 17.75C17.3023 17.75 17.75 17.3023 17.75 16.75C17.75 16.6637 17.7391 16.5799 17.7185 16.5C17.7144 16.4841 17.7099 16.4683 17.705 16.4526C17.5784 16.0456 17.1987 15.75 16.75 15.75C16.284 15.75 15.8925 16.0687 15.7815 16.5Z" fill="#1E1E1E"/></svg>',
|
||||
),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-shipping-fee-fields',
|
||||
array(
|
||||
'title' => __( 'Shipping fee', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-shipping-dimensions-fields',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return $args;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
<?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://woocommerce.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(
|
||||
'title' => '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' ),
|
||||
'layout_column' => array(
|
||||
'image' => $asset_base_url . 'wcs-column.svg',
|
||||
'features' => $column_layout_features,
|
||||
),
|
||||
'learn_more_link' => 'https://woocommerce.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