Σ(゚Д゚;≡;゚д゚)Σ(゚Д゚;≡;゚д゚)Σ(゚Д゚;≡;゚д゚)始,故人唐宰相鲁公,🆚开府南服,余以布衣从戎。明年,别公漳水湄。后明年,公以事过张睢阳庙及颜杲卿所尝往来处,悲歌慷慨,卒不负其言而从之游。今其诗具在,可考也。😭
PaymentGatewaySuggestions/Init.php 0000644 00000005517 15233351333 0013363 0 ustar 00 <?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' );
}
}
PaymentGatewaySuggestions/PaymentGatewaysController.php 0000644 00000010671 15233351333 0017643 0 ustar 00 <?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() );
}
}
PaymentGatewaySuggestions/EvaluateSuggestion.php 0000644 00000001534 15233351333 0016271 0 ustar 00 <?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;
}
}
PaymentGatewaySuggestions/DefaultPaymentGateways.php 0000644 00000055425 15233351333 0017112 0 ustar 00 <?php
/**
* Gets a list of fallback methods if remote fetching is disabled.
*/
namespace Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Init as OnboardingTasks;
/**
* Default Payment Gateways
*/
class DefaultPaymentGateways {
/**
* Get default specs.
*
* @return array Default specs.
*/
public static function get_all() {
return array(
array(
'id' => 'payfast',
'title' => __( 'PayFast', 'woocommerce' ),
'content' => __( 'The PayFast extension for WooCommerce enables you to accept payments by Credit Card and EFT via one of South Africa’s most popular payment gateways. No setup fees or monthly subscription costs. Selecting this extension will configure your store to use South African rands as the selected currency.', 'woocommerce' ),
'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/payfast.png',
'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/payfast.png',
'plugins' => array( 'woocommerce-payfast-gateway' ),
'is_visible' => array(
self::get_rules_for_countries( array( 'ZA', 'GH', 'NG' ) ),
self::get_rules_for_cbd( false ),
),
'category_other' => array( 'ZA', 'GH', 'NG' ),
'category_additional' => array(),
),
array(
'id' => 'stripe',
'title' => __( ' Stripe', 'woocommerce' ),
'content' => __( 'Accept debit and credit cards in 135+ currencies, methods such as Alipay, and one-touch checkout with Apple Pay.', 'woocommerce' ),
'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/stripe.png',
'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/stripe.png',
'plugins' => array( 'woocommerce-gateway-stripe' ),
'is_visible' => array(
// https://stripe.com/global.
self::get_rules_for_countries(
array( 'AU', 'AT', 'BE', 'BG', 'BR', 'CA', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HK', 'IN', 'IE', 'IT', 'JP', 'LV', 'LT', 'LU', 'MY', 'MT', 'MX', 'NL', 'NZ', 'NO', 'PL', 'PT', 'RO', 'SG', 'SK', 'SI', 'ES', 'SE', 'CH', 'GB', 'US', 'PR', 'HU', 'SL', 'ID', 'MY', 'SI', 'PR' )
),
self::get_rules_for_cbd( false ),
),
'category_other' => array( 'AU', 'AT', 'BE', 'BG', 'BR', 'CA', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HK', 'IN', 'IE', 'IT', 'JP', 'LV', 'LT', 'LU', 'MY', 'MT', 'MX', 'NL', 'NZ', 'NO', 'PL', 'PT', 'RO', 'SG', 'SK', 'SI', 'ES', 'SE', 'CH', 'GB', 'US', 'PR', 'HU', 'SL', 'ID', 'MY', 'SI', 'PR' ),
'category_additional' => array(),
'recommendation_priority' => 3,
),
array(
'id' => 'paystack',
'title' => __( 'Paystack', 'woocommerce' ),
'content' => __( 'Paystack helps African merchants accept one-time and recurring payments online with a modern, safe, and secure payment gateway.', 'woocommerce' ),
'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/paystack.png',
'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/paystack.png',
'plugins' => array( 'woo-paystack' ),
'is_visible' => array(
self::get_rules_for_countries( array( 'ZA', 'GH', 'NG' ) ),
self::get_rules_for_cbd( false ),
),
'category_other' => array( 'ZA', 'GH', 'NG' ),
'category_additional' => array(),
),
array(
'id' => 'kco',
'title' => __( 'Klarna Checkout', 'woocommerce' ),
'content' => __( 'Choose the payment that you want, pay now, pay later or slice it. No credit card numbers, no passwords, no worries.', 'woocommerce' ),
'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/klarna-black.png',
'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/klarna.png',
'plugins' => array( 'klarna-checkout-for-woocommerce' ),
'is_visible' => array(
self::get_rules_for_countries( array( 'SE', 'FI', 'NO' ) ),
self::get_rules_for_cbd( false ),
),
'category_other' => array( 'SE', 'FI', 'NO' ),
'category_additional' => array(),
),
array(
'id' => 'klarna_payments',
'title' => __( 'Klarna Payments', 'woocommerce' ),
'content' => __( 'Choose the payment that you want, pay now, pay later or slice it. No credit card numbers, no passwords, no worries.', 'woocommerce' ),
'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/klarna-black.png',
'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/klarna.png',
'plugins' => array( 'klarna-payments-for-woocommerce' ),
'is_visible' => array(
self::get_rules_for_countries(
array( 'US', 'CA', 'DK', 'DE', 'AT', 'NL', 'CH', 'BE', 'SP', 'PL', 'FR', 'IT', 'GB', 'ES', 'FI', 'NO', 'SE', 'ES', 'FI', 'NO', 'SE' )
),
self::get_rules_for_cbd( false ),
),
'category_other' => array(),
'category_additional' => array( 'US', 'CA', 'DK', 'DE', 'AT', 'NL', 'CH', 'BE', 'SP', 'PL', 'FR', 'IT', 'GB', 'ES', 'FI', 'NO', 'SE', 'ES', 'FI', 'NO', 'SE' ),
),
array(
'id' => 'mollie_wc_gateway_banktransfer',
'title' => __( 'Mollie', 'woocommerce' ),
'content' => __( 'Effortless payments by Mollie: Offer global and local payment methods, get onboarded in minutes, and supported in your language.', 'woocommerce' ),
'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/mollie.svg',
'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/mollie.png',
'plugins' => array( 'mollie-payments-for-woocommerce' ),
'is_visible' => array(
self::get_rules_for_countries(
array( 'FR', 'DE', 'GB', 'AT', 'CH', 'ES', 'IT', 'PL', 'FI', 'NL', 'BE' )
),
),
'category_other' => array( 'FR', 'DE', 'GB', 'AT', 'CH', 'ES', 'IT', 'PL', 'FI', 'NL', 'BE' ),
'category_additional' => array(),
),
array(
'id' => 'woo-mercado-pago-custom',
'title' => __( 'Mercado Pago Checkout Pro & Custom', 'woocommerce' ),
'content' => __( 'Accept credit and debit cards, offline (cash or bank transfer) and logged-in payments with money in Mercado Pago. Safe and secure payments with the leading payment processor in LATAM.', 'woocommerce' ),
'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/mercadopago.png',
'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/mercadopago.png',
'plugins' => array( 'woocommerce-mercadopago' ),
'is_visible' => array(
self::get_rules_for_countries( array( 'AR', 'BR', 'CL', 'CO', 'MX', 'PE', 'UY' ) ),
),
'recommendation_priority' => 2,
'is_local_partner' => true,
'category_other' => array( 'AR', 'BR', 'CL', 'CO', 'MX', 'PE', 'UY' ),
'category_additional' => array(),
),
array(
'id' => 'ppcp-gateway',
'title' => __( 'PayPal Payments', 'woocommerce' ),
'content' => __( "Safe and secure payments using credit cards or your customer's PayPal account.", 'woocommerce' ),
'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/paypal.png',
'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/paypal.png',
'plugins' => array( 'woocommerce-paypal-payments' ),
'is_visible' => array(
(object) array(
'type' => 'base_location_country',
'value' => 'IN',
'operation' => '!=',
),
self::get_rules_for_cbd( false ),
),
'category_other' => array( 'US', 'CA', 'AT', 'BE', 'BG', 'HR', 'CH', 'CY', 'CZ', 'DK', 'EE', 'ES', 'FI', 'FR', 'DE', 'GB', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'SK', 'SL', 'SE', 'MX', 'BR', 'AR', 'CL', 'CO', 'EC', 'PE', 'UY', 'VE', 'AU', 'NZ', 'HK', 'JP', 'SG', 'CN', 'ID', 'ZA', 'NG', 'GH' ),
'category_additional' => array( 'US', 'CA', 'AT', 'BE', 'BG', 'HR', 'CH', 'CY', 'CZ', 'DK', 'EE', 'ES', 'FI', 'FR', 'DE', 'GB', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'SK', 'SL', 'SE', 'MX', 'BR', 'AR', 'CL', 'CO', 'EC', 'PE', 'UY', 'VE', 'AU', 'NZ', 'HK', 'JP', 'SG', 'CN', 'ID', 'IN', 'ZA', 'NG', 'GH' ),
),
array(
'id' => 'cod',
'title' => __( 'Cash on delivery', 'woocommerce' ),
'content' => __( 'Take payments in cash upon delivery.', 'woocommerce' ),
'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/cod.svg',
'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/cod.png',
'is_visible' => array(
self::get_rules_for_cbd( false ),
),
'is_offline' => true,
),
array(
'id' => 'bacs',
'title' => __( 'Direct bank transfer', 'woocommerce' ),
'content' => __( 'Take payments via bank transfer.', 'woocommerce' ),
'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/bacs.svg',
'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/bacs.png',
'is_visible' => array(
self::get_rules_for_cbd( false ),
),
'is_offline' => true,
),
// This is for backwards compatibility only (WC < 5.10.0-dev or WCA < 2.9.0-dev).
array(
'id' => 'woocommerce_payments',
'title' => __( 'WooCommerce Payments', 'woocommerce' ),
'content' => __(
'Manage transactions without leaving your WordPress Dashboard. Only with WooCommerce Payments.',
'woocommerce'
),
'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/wcpay.svg',
'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/wcpay.svg',
'plugins' => array( 'woocommerce-payments' ),
'description' => __( 'With WooCommerce Payments, you can securely accept major cards, Apple Pay, and payments in over 100 currencies. Track cash flow and manage recurring revenue directly from your store’s dashboard - with no setup costs or monthly fees.', 'woocommerce' ),
'is_visible' => array(
self::get_rules_for_cbd( false ),
self::get_rules_for_countries( self::get_wcpay_countries() ),
(object) array(
'type' => 'plugin_version',
'plugin' => 'woocommerce',
'version' => '5.10.0-dev',
'operator' => '<',
),
(object) array(
'type' => 'or',
'operands' => (object) array(
(object) array(
'type' => 'not',
'operand' => [
(object) array(
'type' => 'plugins_activated',
'plugins' => [ 'woocommerce-admin' ],
),
],
),
(object) array(
'type' => 'plugin_version',
'plugin' => 'woocommerce-admin',
'version' => '2.9.0-dev',
'operator' => '<',
),
),
),
),
'recommendation_priority' => 1,
),
array(
'id' => 'woocommerce_payments:without-in-person-payments',
'title' => __( 'WooCommerce Payments', 'woocommerce' ),
'content' => __(
'Manage transactions without leaving your WordPress Dashboard. Only with WooCommerce Payments.',
'woocommerce'
),
'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/wcpay.svg',
'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/wcpay.svg',
'plugins' => array( 'woocommerce-payments' ),
'description' => __( 'With WooCommerce Payments, you can securely accept major cards, Apple Pay, and payments in over 100 currencies. Track cash flow and manage recurring revenue directly from your store’s dashboard - with no setup costs or monthly fees.', 'woocommerce' ),
'is_visible' => array(
self::get_rules_for_cbd( false ),
self::get_rules_for_countries( array_diff( self::get_wcpay_countries(), array( 'US', 'CA' ) ) ),
(object) array(
'type' => 'or',
// Older versions of WooCommerce Admin require the ID to be `woocommerce-payments` to show the suggestion card.
'operands' => (object) array(
(object) array(
'type' => 'plugin_version',
'plugin' => 'woocommerce-admin',
'version' => '2.9.0-dev',
'operator' => '>=',
),
(object) array(
'type' => 'plugin_version',
'plugin' => 'woocommerce',
'version' => '5.10.0-dev',
'operator' => '>=',
),
),
),
),
'recommendation_priority' => 1,
),
// This is the same as the above, but with a different description for countries that support in-person payments such as US and CA.
array(
'id' => 'woocommerce_payments:with-in-person-payments',
'title' => __( 'WooCommerce Payments', 'woocommerce' ),
'content' => __(
'Manage transactions without leaving your WordPress Dashboard. Only with WooCommerce Payments.',
'woocommerce'
),
'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/wcpay.svg',
'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/wcpay.svg',
'plugins' => array( 'woocommerce-payments' ),
'description' => __( 'With WooCommerce Payments, you can securely accept major cards, Apple Pay, and payments in over 100 currencies – with no setup costs or monthly fees – and you can now accept in-person payments with the Woo mobile app.', 'woocommerce' ),
'is_visible' => array(
self::get_rules_for_cbd( false ),
self::get_rules_for_countries( array( 'US', 'CA' ) ),
(object) array(
'type' => 'or',
// Older versions of WooCommerce Admin require the ID to be `woocommerce-payments` to show the suggestion card.
'operands' => (object) array(
(object) array(
'type' => 'plugin_version',
'plugin' => 'woocommerce-admin',
'version' => '2.9.0-dev',
'operator' => '>=',
),
(object) array(
'type' => 'plugin_version',
'plugin' => 'woocommerce',
'version' => '5.10.0-dev',
'operator' => '>=',
),
),
),
),
'recommendation_priority' => 1,
),
array(
'id' => 'razorpay',
'title' => __( 'Razorpay', 'woocommerce' ),
'content' => __( 'The official Razorpay extension for WooCommerce allows you to accept credit cards, debit cards, netbanking, wallet, and UPI payments.', 'woocommerce' ),
'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/razorpay.svg',
'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/razorpay.png',
'plugins' => array( 'woo-razorpay' ),
'is_visible' => array(
(object) array(
'type' => 'base_location_country',
'value' => 'IN',
'operation' => '=',
),
self::get_rules_for_cbd( false ),
),
'category_other' => array( 'IN' ),
'category_additional' => array(),
),
array(
'id' => 'payubiz',
'title' => __( 'PayU for WooCommerce', 'woocommerce' ),
'content' => __( 'Enable PayU’s exclusive plugin for WooCommerce to start accepting payments in 100+ payment methods available in India including credit cards, debit cards, UPI, & more!', 'woocommerce' ),
'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/payu.svg',
'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/payu.png',
'plugins' => array( 'payu-india' ),
'is_visible' => array(
(object) array(
'type' => 'base_location_country',
'value' => 'IN',
'operation' => '=',
),
self::get_rules_for_cbd( false ),
),
'category_other' => array( 'IN' ),
'category_additional' => array(),
),
array(
'id' => 'eway',
'title' => __( 'Eway', 'woocommerce' ),
'content' => __( 'The Eway extension for WooCommerce allows you to take credit card payments directly on your store without redirecting your customers to a third party site to make payment.', 'woocommerce' ),
'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/eway.png',
'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/eway.png',
'plugins' => array( 'woocommerce-gateway-eway' ),
'is_visible' => array(
self::get_rules_for_countries( array( 'AU', 'NZ' ) ),
self::get_rules_for_cbd( false ),
),
'category_other' => array( 'AU', 'NZ' ),
'category_additional' => array(),
),
array(
'id' => 'square_credit_card',
'title' => __( 'Square', 'woocommerce' ),
'content' => __( 'Securely accept credit and debit cards with one low rate, no surprise fees (custom rates available). Sell online and in store and track sales and inventory in one place.', 'woocommerce' ),
'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/square-black.png',
'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/square.png',
'plugins' => array( 'woocommerce-square' ),
'is_visible' => array(
(object) array(
'type' => 'or',
'operands' => (object) array(
array(
self::get_rules_for_countries( array( 'US' ) ),
self::get_rules_for_cbd( true ),
),
array(
self::get_rules_for_countries( array( 'US', 'CA', 'JP', 'GB', 'AU', 'IE', 'FR', 'ES', 'FI' ) ),
self::get_rules_for_selling_venues( array( 'brick-mortar', 'brick-mortar-other' ) ),
),
),
),
),
'category_other' => array( 'US', 'CA', 'JP', 'GB', 'AU', 'IE', 'FR', 'ES', 'FI' ),
'category_additional' => array(),
),
array(
'id' => 'afterpay',
'title' => __( 'Afterpay', 'woocommerce' ),
'content' => __( 'Afterpay allows customers to receive products immediately and pay for purchases over four installments, always interest-free.', 'woocommerce' ),
'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/afterpay.png',
'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/afterpay.png',
'plugins' => array( 'afterpay-gateway-for-woocommerce' ),
'is_visible' => array(
self::get_rules_for_countries( array( 'US', 'CA' ) ),
),
'category_other' => array(),
'category_additional' => array( 'US', 'CA' ),
),
array(
'id' => 'amazon_payments_advanced',
'title' => __( 'Amazon Pay', 'woocommerce' ),
'content' => __( 'Enable a familiar, fast checkout for hundreds of millions of active Amazon customers globally.', 'woocommerce' ),
'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/amazonpay.png',
'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/amazonpay.png',
'plugins' => array( 'woocommerce-gateway-amazon-payments-advanced' ),
'is_visible' => array(
self::get_rules_for_countries( array( 'US', 'CA' ) ),
),
'category_other' => array(),
'category_additional' => array( 'US', 'CA' ),
),
array(
'id' => 'affirm',
'title' => __( 'Affirm', 'woocommerce' ),
'content' => __( 'Affirm’s tailored Buy Now Pay Later programs remove price as a barrier, turning browsers into buyers, increasing average order value, and expanding your customer base.', 'woocommerce' ),
'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/affirm.png',
'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/affirm.png',
'plugins' => array(),
'external_link' => 'https://woocommerce.com/products/woocommerce-gateway-affirm',
'is_visible' => array(
self::get_rules_for_countries( array( 'US', 'CA' ) ),
),
'category_other' => array(),
'category_additional' => array( 'US', 'CA' ),
),
);
}
/**
* Get array of countries supported by WCPay depending on feature flag.
*
* @return array Array of countries.
*/
public static function get_wcpay_countries() {
return array( 'US', 'PR', 'AU', 'CA', 'DE', 'ES', 'FR', 'GB', 'IE', 'IT', 'NZ', 'AT', 'BE', 'NL', 'PL', 'PT', 'CH', 'HK', 'SG' );
}
/**
* 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,
);
}
/**
* Get rules that match the store's selling venues.
*
* @param array $selling_venues Array of venues to match.
* @return object Rules to match.
*/
public static function get_rules_for_selling_venues( $selling_venues ) {
$rules = array();
foreach ( $selling_venues as $venue ) {
$rules[] = (object) array(
'type' => 'option',
'transformers' => array(
(object) array(
'use' => 'dot_notation',
'arguments' => (object) array(
'path' => 'selling_venues',
),
),
),
'option_name' => 'woocommerce_onboarding_profile',
'operation' => '=',
'value' => $venue,
'default' => array(),
);
}
return (object) array(
'type' => 'or',
'operands' => $rules,
);
}
/**
* Get default rules for CBD based on given argument.
*
* @param bool $should_have Whether or not the store should have CBD as an industry (true) or not (false).
* @return array Rules to match.
*/
public static function get_rules_for_cbd( $should_have ) {
return (object) array(
'type' => 'option',
'transformers' => array(
(object) array(
'use' => 'dot_notation',
'arguments' => (object) array(
'path' => 'industry',
),
),
(object) array(
'use' => 'array_column',
'arguments' => (object) array(
'key' => 'slug',
),
),
),
'option_name' => 'woocommerce_onboarding_profile',
'operation' => $should_have ? 'contains' : '!contains',
'value' => 'cbd-other-hemp-derived-products',
'default' => array(),
);
}
}
PaymentGatewaySuggestions/PaymentGatewaySuggestionsDataSourcePoller.php 0000644 00000001504 15233351333 0022773 0 ustar 00 <?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 ) {
self::$instance = new self( self::ID, self::DATA_SOURCES );
}
return self::$instance;
}
}
TransientNotices.php 0000644 00000005451 15233351333 0010557 0 ustar 00 <?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;
}
}
MultichannelMarketing/Init.php 0000644 00000004153 15233351333 0012451 0 ustar 00 <?php
/**
* Multichannel Marketing Experience
*
* @package Woocommerce Admin
*/
namespace Automattic\WooCommerce\Admin\Features\MultichannelMarketing;
use Automattic\WooCommerce\Internal\Admin\Survey;
use Automattic\WooCommerce\Admin\Features\Features;
use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;
/**
* Contains logic for Multichannel Marketing.
*/
class Init {
/**
* Option name used to toggle this feature.
*/
const TOGGLE_OPTION_NAME = 'woocommerce_multichannel_marketing_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_filter( 'woocommerce_settings_features', array( $this, 'add_feature_toggle' ) );
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 the feature toggle to the features settings.
*
* @param array $features Feature sections.
* @return array
*/
public static function add_feature_toggle( $features ) {
$description = __(
'Enables the new WooCommerce Multichannel Marketing experience in the Marketing page',
'woocommerce'
);
$features[] = array(
'title' => __( 'Marketing', 'woocommerce' ),
'desc' => $description,
'id' => self::TOGGLE_OPTION_NAME,
'type' => 'checkbox',
'class' => '',
);
return $features;
}
/**
* Reloads the page when the option is toggled to make sure all Multichannel Marketing 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;
}
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();
}
}
Navigation/Favorites.php 0000644 00000005026 15233351333 0011322 0 ustar 00 <?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();
}
}
Navigation/Menu.php 0000644 00000054435 15233351333 0010274 0 ustar 00 <?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' );
}
}
Navigation/Screen.php 0000644 00000013106 15233351333 0010575 0 ustar 00 <?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;
}
}
}
Navigation/CoreMenu.php 0000644 00000030141 15233351333 0011071 0 ustar 00 <?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 );
}
}
Navigation/Init.php 0000644 00000007714 15233351333 0010271 0 ustar 00 <?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' );
}
}
NewProductManagementExperience.php 0000644 00000001556 15233351333 0013364 0 ustar 00 <?php
/**
* WooCommerce New Product Management Experience
*/
namespace Automattic\WooCommerce\Admin\Features;
use \Automattic\WooCommerce\Admin\PageController;
use \Automattic\WooCommerce\Internal\Admin\Loader;
/**
* Loads assets related to the new product management experience page.
*/
class NewProductManagementExperience {
/**
* Constructor
*/
public function __construct() {
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) );
}
/**
* 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' );
}
}
Features.php 0000644 00000027357 15233351333 0007052 0 ustar 00 <?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(
'multichannel-marketing' => array( 'default' => 'no' ),
'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(
'multichannel-marketing',
'navigation',
'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 );
}
}
}
OnboardingTasks/TaskTraits.php 0000644 00000001713 15233351333 0012441 0 ustar 00 <?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;
}
}
OnboardingTasks/Task.php 0000644 00000027701 15233351333 0011257 0 ustar 00 <?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;
}
}
OnboardingTasks/DeprecatedExtendedTask.php 0000644 00000005252 15233351333 0014716 0 ustar 00 <?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;
}
}
OnboardingTasks/Tasks/Payments.php 0000644 00000003746 15233351333 0013245 0 ustar 00 <?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 );
}
}
OnboardingTasks/Tasks/AdditionalPayments.php 0000644 00000004513 15233351333 0015227 0 ustar 00 <?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 );
}
}
OnboardingTasks/Tasks/GetMobileApp.php 0000644 00000005012 15233351333 0013741 0 ustar 00 <?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_user_connected();
}
return false;
}
/**
* Action URL.
*
* @return string
*/
public function get_action_url() {
return admin_url( 'admin.php?page=wc-admin&mobileAppModal=true' );
}
}
OnboardingTasks/Tasks/Appearance.php 0000644 00000005544 15233351333 0013502 0 ustar 00 <?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;
}
}
OnboardingTasks/Tasks/Products.php 0000644 00000007634 15233351333 0013250 0 ustar 00 <?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;
}
}
OnboardingTasks/Tasks/ReviewShippingOptions.php 0000644 00000002177 15233351333 0015761 0 ustar 00 <?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' );
}
}
OnboardingTasks/Tasks/Marketing.php 0000644 00000004736 15233351333 0013366 0 ustar 00 <?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;
}
}
OnboardingTasks/Tasks/Purchase.php 0000644 00000012127 15233351333 0013210 0 ustar 00 <?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;
}
}
OnboardingTasks/Tasks/Tax.php 0000644 00000007436 15233351333 0012201 0 ustar 00 <?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;
}
}
OnboardingTasks/Tasks/TourInAppMarketplace.php 0000644 00000002175 15233351333 0015472 0 ustar 00 <?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 __( 'Tour the WooCommerce Marketplace', 'woocommerce' );
}
/**
* Content.
*
* @return string
*/
public function get_content() {
return '';
}
/**
* Time.
*
* @return string
*/
public function get_time() {
return '';
}
/**
* Task completion.
*
* @return bool
*/
public function is_complete() {
return get_option( 'woocommerce_admin_dismissed_in_app_marketplace_tour' ) === 'yes';
}
/**
* Action URL.
*
* @return string
*/
public function get_action_url() {
return admin_url( 'admin.php?page=wc-addons&tutorial=true' );
}
/**
* Check if should record event when task is viewed
*
* @return bool
*/
public function get_record_view_event(): bool {
return true;
}
}
OnboardingTasks/Tasks/ExperimentalShippingRecommendation.php 0000644 00000003216 15233351333 0020461 0 ustar 00 <?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;
}
}
OnboardingTasks/Tasks/WooCommercePayments.php 0000644 00000007651 15233351333 0015404 0 ustar 00 <?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;
}
}
OnboardingTasks/Tasks/StoreCreation.php 0000644 00000002044 15233351333 0014214 0 ustar 00 <?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;
}
}
OnboardingTasks/Tasks/StoreDetails.php 0000644 00000003267 15233351333 0014045 0 ustar 00 <?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', '' ) !== '';
}
}
OnboardingTasks/Tasks/Shipping.php 0000644 00000011545 15233351333 0013222 0 ustar 00 <?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 );
}
/**
* 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( 'AU', 'CA', 'GB' ), 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;
}
}
OnboardingTasks/DeprecatedOptions.php 0000644 00000005000 15233351333 0013755 0 ustar 00 <?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;
}
}
}
OnboardingTasks/TaskListSection.php 0000644 00000004466 15233351333 0013443 0 ustar 00 <?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(),
);
}
}
OnboardingTasks/Init.php 0000644 00000002116 15233351333 0011251 0 ustar 00 <?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;
}
}
OnboardingTasks/TaskList.php 0000644 00000022162 15233351333 0012107 0 ustar 00 <?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(),
);
}
}
OnboardingTasks/TaskLists.php 0000644 00000025416 15233351333 0012277 0 ustar 00 <?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 );
}
}
/**
* 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 array $args Task properties.
* @return \WP_Error|Task
*/
public static function add_task( $list_id, $args ) {
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( $args );
}
/**
* 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;
}
}
Onboarding.php 0000644 00000006254 15233351333 0007347 0 ustar 00 <?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\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\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\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\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\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\OnboardingProducts::get_product_data();
}
}