Merged in feature/280-dev-dev01 (pull request #21)
auto-patch 280-dev-dev01-2024-01-19T16_41_58 * auto-patch 280-dev-dev01-2024-01-19T16_41_58
This commit is contained in:
@@ -0,0 +1,514 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Domain;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Assets\Api as AssetApi;
|
||||
use Automattic\WooCommerce\Blocks\Assets\AssetDataRegistry;
|
||||
use Automattic\WooCommerce\Blocks\AssetsController;
|
||||
use Automattic\WooCommerce\Blocks\BlockPatterns;
|
||||
use Automattic\WooCommerce\Blocks\BlockTemplatesController;
|
||||
use Automattic\WooCommerce\Blocks\BlockTypesController;
|
||||
use Automattic\WooCommerce\Blocks\Domain\Services\CreateAccount;
|
||||
use Automattic\WooCommerce\Blocks\Domain\Services\Notices;
|
||||
use Automattic\WooCommerce\Blocks\Domain\Services\DraftOrders;
|
||||
use Automattic\WooCommerce\Blocks\Domain\Services\FeatureGating;
|
||||
use Automattic\WooCommerce\Blocks\Domain\Services\GoogleAnalytics;
|
||||
use Automattic\WooCommerce\Blocks\Domain\Services\Hydration;
|
||||
use Automattic\WooCommerce\Blocks\Domain\Services\CheckoutFields;
|
||||
use Automattic\WooCommerce\Blocks\InboxNotifications;
|
||||
use Automattic\WooCommerce\Blocks\Installer;
|
||||
use Automattic\WooCommerce\Blocks\Migration;
|
||||
use Automattic\WooCommerce\Blocks\Payments\Api as PaymentsApi;
|
||||
use Automattic\WooCommerce\Blocks\Payments\Integrations\BankTransfer;
|
||||
use Automattic\WooCommerce\Blocks\Payments\Integrations\CashOnDelivery;
|
||||
use Automattic\WooCommerce\Blocks\Payments\Integrations\Cheque;
|
||||
use Automattic\WooCommerce\Blocks\Payments\Integrations\PayPal;
|
||||
use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry;
|
||||
use Automattic\WooCommerce\Blocks\Registry\Container;
|
||||
use Automattic\WooCommerce\Blocks\Templates\CartTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\CheckoutHeaderTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\CheckoutTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ClassicTemplatesCompatibility;
|
||||
use Automattic\WooCommerce\Blocks\Templates\OrderConfirmationTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductAttributeTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductSearchResultsTemplate;
|
||||
use Automattic\WooCommerce\StoreApi\RoutesController;
|
||||
use Automattic\WooCommerce\StoreApi\SchemaController;
|
||||
use Automattic\WooCommerce\StoreApi\StoreApi;
|
||||
use Automattic\WooCommerce\Blocks\Shipping\ShippingController;
|
||||
use Automattic\WooCommerce\Blocks\Templates\SingleProductTemplateCompatibility;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ArchiveProductTemplatesCompatibility;
|
||||
use Automattic\WooCommerce\Blocks\Domain\Services\OnboardingTasks\TasksController;
|
||||
|
||||
/**
|
||||
* Takes care of bootstrapping the plugin.
|
||||
*
|
||||
* @since 2.5.0
|
||||
*/
|
||||
class Bootstrap {
|
||||
|
||||
/**
|
||||
* Holds the Dependency Injection Container
|
||||
*
|
||||
* @var Container
|
||||
*/
|
||||
private $container;
|
||||
|
||||
/**
|
||||
* Holds the Package instance
|
||||
*
|
||||
* @var Package
|
||||
*/
|
||||
private $package;
|
||||
|
||||
|
||||
/**
|
||||
* Holds the Migration instance
|
||||
*
|
||||
* @var Migration
|
||||
*/
|
||||
private $migration;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Container $container The Dependency Injection Container.
|
||||
*/
|
||||
public function __construct( Container $container ) {
|
||||
$this->container = $container;
|
||||
$this->package = $container->get( Package::class );
|
||||
$this->migration = $container->get( Migration::class );
|
||||
|
||||
$this->init();
|
||||
/**
|
||||
* Fires when the woocommerce blocks are loaded and ready to use.
|
||||
*
|
||||
* This hook is intended to be used as a safe event hook for when the plugin
|
||||
* has been loaded, and all dependency requirements have been met.
|
||||
*
|
||||
* To ensure blocks are initialized, you must use the `woocommerce_blocks_loaded`
|
||||
* hook instead of the `plugins_loaded` hook. This is because the functions
|
||||
* hooked into plugins_loaded on the same priority load in an inconsistent and unpredictable manner.
|
||||
*
|
||||
* @since 2.5.0
|
||||
*/
|
||||
do_action( 'woocommerce_blocks_loaded' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the package - load the blocks library and define constants.
|
||||
*/
|
||||
protected function init() {
|
||||
$this->register_dependencies();
|
||||
$this->register_payment_methods();
|
||||
$this->load_interactivity_api();
|
||||
|
||||
// This is just a temporary solution to make sure the migrations are run. We have to refactor this. More details: https://github.com/woocommerce/woocommerce-blocks/issues/10196.
|
||||
if ( $this->package->get_version() !== $this->package->get_version_stored_on_db() ) {
|
||||
$this->migration->run_migrations();
|
||||
$this->package->set_version_stored_on_db();
|
||||
}
|
||||
|
||||
add_action(
|
||||
'admin_init',
|
||||
function() {
|
||||
// Delete this notification because the blocks are included in WC Core now. This will handle any sites
|
||||
// with lingering notices.
|
||||
InboxNotifications::delete_surface_cart_checkout_blocks_notification();
|
||||
},
|
||||
10,
|
||||
0
|
||||
);
|
||||
|
||||
$is_rest = wc()->is_rest_api_request();
|
||||
// phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
$is_store_api_request = $is_rest && ! empty( $_SERVER['REQUEST_URI'] ) && ( false !== strpos( $_SERVER['REQUEST_URI'], trailingslashit( rest_get_url_prefix() ) . 'wc/store/' ) );
|
||||
|
||||
// Load and init assets.
|
||||
$this->container->get( StoreApi::class )->init();
|
||||
$this->container->get( PaymentsApi::class )->init();
|
||||
$this->container->get( DraftOrders::class )->init();
|
||||
$this->container->get( CreateAccount::class )->init();
|
||||
$this->container->get( ShippingController::class )->init();
|
||||
$this->container->get( TasksController::class )->init();
|
||||
$this->container->get( CheckoutFields::class );
|
||||
|
||||
// Load assets in admin and on the frontend.
|
||||
if ( ! $is_rest ) {
|
||||
$this->add_build_notice();
|
||||
$this->container->get( AssetDataRegistry::class );
|
||||
$this->container->get( AssetsController::class );
|
||||
$this->container->get( Installer::class )->init();
|
||||
$this->container->get( GoogleAnalytics::class )->init();
|
||||
$this->container->get( CheckoutFields::class )->init();
|
||||
}
|
||||
|
||||
// Load assets unless this is a request specifically for the store API.
|
||||
if ( ! $is_store_api_request ) {
|
||||
// Template related functionality. These won't be loaded for store API requests, but may be loaded for
|
||||
// regular rest requests to maintain compatibility with the store editor.
|
||||
$this->container->get( BlockPatterns::class );
|
||||
$this->container->get( BlockTypesController::class );
|
||||
$this->container->get( BlockTemplatesController::class );
|
||||
$this->container->get( ProductSearchResultsTemplate::class );
|
||||
$this->container->get( ProductAttributeTemplate::class );
|
||||
$this->container->get( CartTemplate::class );
|
||||
$this->container->get( CheckoutTemplate::class );
|
||||
$this->container->get( CheckoutHeaderTemplate::class );
|
||||
$this->container->get( OrderConfirmationTemplate::class );
|
||||
$this->container->get( ClassicTemplatesCompatibility::class );
|
||||
$this->container->get( ArchiveProductTemplatesCompatibility::class )->init();
|
||||
$this->container->get( SingleProductTemplateCompatibility::class )->init();
|
||||
$this->container->get( Notices::class )->init();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See if files have been built or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_built() {
|
||||
return file_exists(
|
||||
$this->package->get_path( 'assets/client/blocks/featured-product.js' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a notice stating that the build has not been done yet.
|
||||
*/
|
||||
protected function add_build_notice() {
|
||||
if ( $this->is_built() ) {
|
||||
return;
|
||||
}
|
||||
add_action(
|
||||
'admin_notices',
|
||||
function() {
|
||||
echo '<div class="error"><p>';
|
||||
printf(
|
||||
/* translators: %1$s is the install command, %2$s is the build command, %3$s is the watch command. */
|
||||
esc_html__( 'WooCommerce Blocks development mode requires files to be built. From the plugin directory, run %1$s to install dependencies, %2$s to build the files or %3$s to build the files and watch for changes.', 'woocommerce' ),
|
||||
'<code>npm install</code>',
|
||||
'<code>npm run build</code>',
|
||||
'<code>npm start</code>'
|
||||
);
|
||||
echo '</p></div>';
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and set up the Interactivity API if enabled.
|
||||
*/
|
||||
protected function load_interactivity_api() {
|
||||
require_once __DIR__ . '/../Interactivity/load.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Register core dependencies with the container.
|
||||
*/
|
||||
protected function register_dependencies() {
|
||||
$this->container->register(
|
||||
FeatureGating::class,
|
||||
function () {
|
||||
return new FeatureGating();
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
AssetApi::class,
|
||||
function ( Container $container ) {
|
||||
return new AssetApi( $container->get( Package::class ) );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
AssetDataRegistry::class,
|
||||
function( Container $container ) {
|
||||
return new AssetDataRegistry( $container->get( AssetApi::class ) );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
AssetsController::class,
|
||||
function( Container $container ) {
|
||||
return new AssetsController( $container->get( AssetApi::class ) );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
PaymentMethodRegistry::class,
|
||||
function() {
|
||||
return new PaymentMethodRegistry();
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
Installer::class,
|
||||
function () {
|
||||
return new Installer();
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
BlockTypesController::class,
|
||||
function ( Container $container ) {
|
||||
$asset_api = $container->get( AssetApi::class );
|
||||
$asset_data_registry = $container->get( AssetDataRegistry::class );
|
||||
return new BlockTypesController( $asset_api, $asset_data_registry );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
BlockTemplatesController::class,
|
||||
function ( Container $container ) {
|
||||
return new BlockTemplatesController( $container->get( Package::class ) );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
ProductSearchResultsTemplate::class,
|
||||
function () {
|
||||
return new ProductSearchResultsTemplate();
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
ProductAttributeTemplate::class,
|
||||
function () {
|
||||
return new ProductAttributeTemplate();
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
CartTemplate::class,
|
||||
function () {
|
||||
return new CartTemplate();
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
CheckoutTemplate::class,
|
||||
function () {
|
||||
return new CheckoutTemplate();
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
CheckoutHeaderTemplate::class,
|
||||
function () {
|
||||
return new CheckoutHeaderTemplate();
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
OrderConfirmationTemplate::class,
|
||||
function () {
|
||||
return new OrderConfirmationTemplate();
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
ClassicTemplatesCompatibility::class,
|
||||
function ( Container $container ) {
|
||||
$asset_data_registry = $container->get( AssetDataRegistry::class );
|
||||
return new ClassicTemplatesCompatibility( $asset_data_registry );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
ArchiveProductTemplatesCompatibility::class,
|
||||
function () {
|
||||
return new ArchiveProductTemplatesCompatibility();
|
||||
}
|
||||
);
|
||||
|
||||
$this->container->register(
|
||||
SingleProductTemplateCompatibility::class,
|
||||
function () {
|
||||
return new SingleProductTemplateCompatibility();
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
DraftOrders::class,
|
||||
function( Container $container ) {
|
||||
return new DraftOrders( $container->get( Package::class ) );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
CreateAccount::class,
|
||||
function( Container $container ) {
|
||||
return new CreateAccount( $container->get( Package::class ) );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
GoogleAnalytics::class,
|
||||
function( Container $container ) {
|
||||
$asset_api = $container->get( AssetApi::class );
|
||||
return new GoogleAnalytics( $asset_api );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
Notices::class,
|
||||
function( Container $container ) {
|
||||
return new Notices( $container->get( Package::class ) );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
Hydration::class,
|
||||
function( Container $container ) {
|
||||
return new Hydration( $container->get( AssetDataRegistry::class ) );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
CheckoutFields::class,
|
||||
function( Container $container ) {
|
||||
return new CheckoutFields( $container->get( AssetDataRegistry::class ) );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
PaymentsApi::class,
|
||||
function ( Container $container ) {
|
||||
$payment_method_registry = $container->get( PaymentMethodRegistry::class );
|
||||
$asset_data_registry = $container->get( AssetDataRegistry::class );
|
||||
return new PaymentsApi( $payment_method_registry, $asset_data_registry );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
StoreApi::class,
|
||||
function () {
|
||||
return new StoreApi();
|
||||
}
|
||||
);
|
||||
// Maintains backwards compatibility with previous Store API namespace.
|
||||
$this->container->register(
|
||||
'Automattic\WooCommerce\Blocks\StoreApi\Formatters',
|
||||
function( Container $container ) {
|
||||
$this->deprecated_dependency( 'Automattic\WooCommerce\Blocks\StoreApi\Formatters', '7.2.0', 'Automattic\WooCommerce\StoreApi\Formatters', '7.4.0' );
|
||||
return $container->get( StoreApi::class )->container()->get( \Automattic\WooCommerce\StoreApi\Formatters::class );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
'Automattic\WooCommerce\Blocks\Domain\Services\ExtendRestApi',
|
||||
function( Container $container ) {
|
||||
$this->deprecated_dependency( 'Automattic\WooCommerce\Blocks\Domain\Services\ExtendRestApi', '7.2.0', 'Automattic\WooCommerce\StoreApi\Schemas\ExtendSchema', '7.4.0' );
|
||||
return $container->get( StoreApi::class )->container()->get( \Automattic\WooCommerce\StoreApi\Schemas\ExtendSchema::class );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
'Automattic\WooCommerce\Blocks\StoreApi\SchemaController',
|
||||
function( Container $container ) {
|
||||
$this->deprecated_dependency( 'Automattic\WooCommerce\Blocks\StoreApi\SchemaController', '7.2.0', 'Automattic\WooCommerce\StoreApi\SchemaController', '7.4.0' );
|
||||
return $container->get( StoreApi::class )->container()->get( SchemaController::class );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
'Automattic\WooCommerce\Blocks\StoreApi\RoutesController',
|
||||
function( Container $container ) {
|
||||
$this->deprecated_dependency( 'Automattic\WooCommerce\Blocks\StoreApi\RoutesController', '7.2.0', 'Automattic\WooCommerce\StoreApi\RoutesController', '7.4.0' );
|
||||
return $container->get( StoreApi::class )->container()->get( RoutesController::class );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
BlockPatterns::class,
|
||||
function () {
|
||||
return new BlockPatterns( $this->package );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
ShippingController::class,
|
||||
function ( $container ) {
|
||||
$asset_api = $container->get( AssetApi::class );
|
||||
$asset_data_registry = $container->get( AssetDataRegistry::class );
|
||||
return new ShippingController( $asset_api, $asset_data_registry );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
TasksController::class,
|
||||
function() {
|
||||
return new TasksController();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws a deprecation notice for a dependency without breaking requests.
|
||||
*
|
||||
* @param string $function Class or function being deprecated.
|
||||
* @param string $version Version in which it was deprecated.
|
||||
* @param string $replacement Replacement class or function, if applicable.
|
||||
* @param string $trigger_error_version Optional version to start surfacing this as a PHP error rather than a log. Defaults to $version.
|
||||
*/
|
||||
protected function deprecated_dependency( $function, $version, $replacement = '', $trigger_error_version = '' ) {
|
||||
if ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$trigger_error_version = $trigger_error_version ? $trigger_error_version : $version;
|
||||
$error_message = $replacement ? sprintf(
|
||||
'%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.',
|
||||
$function,
|
||||
$version,
|
||||
$replacement
|
||||
) : sprintf(
|
||||
'%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.',
|
||||
$function,
|
||||
$version
|
||||
);
|
||||
/**
|
||||
* Fires when a deprecated function is called.
|
||||
*
|
||||
* @since 7.3.0
|
||||
*/
|
||||
do_action( 'deprecated_function_run', $function, $replacement, $version );
|
||||
|
||||
$log_error = false;
|
||||
|
||||
// If headers have not been sent yet, log to avoid breaking the request.
|
||||
if ( ! headers_sent() ) {
|
||||
$log_error = true;
|
||||
}
|
||||
|
||||
// If the $trigger_error_version was not yet reached, only log the error.
|
||||
if ( version_compare( $this->package->get_version(), $trigger_error_version, '<' ) ) {
|
||||
$log_error = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters whether to trigger an error for deprecated functions. (Same as WP core)
|
||||
*
|
||||
* @since 7.3.0
|
||||
*
|
||||
* @param bool $trigger Whether to trigger the error for deprecated functions. Default true.
|
||||
*/
|
||||
if ( ! apply_filters( 'deprecated_function_trigger_error', true ) ) {
|
||||
$log_error = true;
|
||||
}
|
||||
|
||||
if ( $log_error ) {
|
||||
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
|
||||
error_log( $error_message );
|
||||
} else {
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
|
||||
trigger_error( $error_message, E_USER_DEPRECATED );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register payment method integrations with the container.
|
||||
*/
|
||||
protected function register_payment_methods() {
|
||||
$this->container->register(
|
||||
Cheque::class,
|
||||
function( Container $container ) {
|
||||
$asset_api = $container->get( AssetApi::class );
|
||||
return new Cheque( $asset_api );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
PayPal::class,
|
||||
function( Container $container ) {
|
||||
$asset_api = $container->get( AssetApi::class );
|
||||
return new PayPal( $asset_api );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
BankTransfer::class,
|
||||
function( Container $container ) {
|
||||
$asset_api = $container->get( AssetApi::class );
|
||||
return new BankTransfer( $asset_api );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
CashOnDelivery::class,
|
||||
function( Container $container ) {
|
||||
$asset_api = $container->get( AssetApi::class );
|
||||
return new CashOnDelivery( $asset_api );
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
140
wp/wp-content/plugins/woocommerce/src/Blocks/Domain/Package.php
Normal file
140
wp/wp-content/plugins/woocommerce/src/Blocks/Domain/Package.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Domain;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Options;
|
||||
use Automattic\WooCommerce\Blocks\Domain\Services\FeatureGating;
|
||||
|
||||
|
||||
/**
|
||||
* Main package class.
|
||||
*
|
||||
* Returns information about the package and handles init.
|
||||
*
|
||||
* @since 2.5.0
|
||||
*/
|
||||
class Package {
|
||||
|
||||
/**
|
||||
* Holds the current version of the blocks plugin.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $version;
|
||||
|
||||
/**
|
||||
* Holds the main path to the blocks plugin directory.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* Holds locally the plugin_dir_url to avoid recomputing it.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $plugin_dir_url;
|
||||
|
||||
/**
|
||||
* Holds the feature gating class instance.
|
||||
*
|
||||
* @var FeatureGating
|
||||
*/
|
||||
private $feature_gating;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $version Version of the plugin.
|
||||
* @param string $plugin_path Path to the main plugin file.
|
||||
* @param FeatureGating $feature_gating Feature gating class instance.
|
||||
*/
|
||||
public function __construct( $version, $plugin_path, FeatureGating $feature_gating ) {
|
||||
$this->version = $version;
|
||||
$this->path = $plugin_path;
|
||||
$this->feature_gating = $feature_gating;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the plugin.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_version() {
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the plugin stored in the database.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_version_stored_on_db() {
|
||||
return get_option( Options::WC_BLOCK_VERSION, '' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the version of the plugin stored in the database.
|
||||
* This is useful during the first installation or after the upgrade process.
|
||||
*/
|
||||
public function set_version_stored_on_db() {
|
||||
update_option( Options::WC_BLOCK_VERSION, $this->get_version() );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the plugin directory.
|
||||
*
|
||||
* @param string $relative_path If provided, the relative path will be
|
||||
* appended to the plugin path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_path( $relative_path = '' ) {
|
||||
return trailingslashit( $this->path ) . $relative_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the url to the blocks plugin directory.
|
||||
*
|
||||
* @param string $relative_url If provided, the relative url will be
|
||||
* appended to the plugin url.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_url( $relative_url = '' ) {
|
||||
if ( ! $this->plugin_dir_url ) {
|
||||
// Append index.php so WP does not return the parent directory.
|
||||
$this->plugin_dir_url = plugin_dir_url( $this->path . '/index.php' );
|
||||
}
|
||||
|
||||
return $this->plugin_dir_url . $relative_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the the FeatureGating class.
|
||||
*
|
||||
* @return FeatureGating
|
||||
*/
|
||||
public function feature() {
|
||||
return $this->feature_gating;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if we're executing the code in an experimental build mode.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_experimental_build() {
|
||||
return $this->feature()->is_experimental_build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if we're executing the code in an feature plugin or experimental build mode.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_feature_plugin_build() {
|
||||
return $this->feature()->is_feature_plugin_build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,794 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Blocks\Domain\Services;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Assets\AssetDataRegistry;
|
||||
use WC_Customer;
|
||||
|
||||
/**
|
||||
* Service class managing checkout fields and its related extensibility points.
|
||||
*/
|
||||
class CheckoutFields {
|
||||
|
||||
/**
|
||||
* Core checkout fields.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $core_fields;
|
||||
|
||||
/**
|
||||
* Additional checkout fields.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $additional_fields = array();
|
||||
|
||||
/**
|
||||
* Fields locations.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $fields_locations;
|
||||
|
||||
/**
|
||||
* Supported field types
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $supported_field_types = [ 'text', 'select', 'checkbox' ];
|
||||
|
||||
/**
|
||||
* Instance of the asset data registry.
|
||||
*
|
||||
* @var AssetDataRegistry
|
||||
*/
|
||||
private $asset_data_registry;
|
||||
|
||||
/**
|
||||
* Billing fields meta key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const BILLING_FIELDS_KEY = '_additional_billing_fields';
|
||||
|
||||
/**
|
||||
* Shipping fields meta key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SHIPPING_FIELDS_KEY = '_additional_shipping_fields';
|
||||
|
||||
/**
|
||||
* Additional fields meta key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const ADDITIONAL_FIELDS_KEY = '_additional_fields';
|
||||
|
||||
/**
|
||||
* Sets up core fields.
|
||||
*
|
||||
* @param AssetDataRegistry $asset_data_registry Instance of the asset data registry.
|
||||
*/
|
||||
public function __construct( AssetDataRegistry $asset_data_registry ) {
|
||||
$this->asset_data_registry = $asset_data_registry;
|
||||
$this->core_fields = array(
|
||||
'email' => array(
|
||||
'label' => __( 'Email address', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Email address (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'email',
|
||||
'autocapitalize' => 'none',
|
||||
'index' => 0,
|
||||
),
|
||||
'first_name' => array(
|
||||
'label' => __( 'First name', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'First name (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'given-name',
|
||||
'autocapitalize' => 'sentences',
|
||||
'index' => 10,
|
||||
),
|
||||
'last_name' => array(
|
||||
'label' => __( 'Last name', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Last name (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'family-name',
|
||||
'autocapitalize' => 'sentences',
|
||||
'index' => 20,
|
||||
),
|
||||
'company' => array(
|
||||
'label' => __( 'Company', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Company (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => false,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'organization',
|
||||
'autocapitalize' => 'sentences',
|
||||
'index' => 30,
|
||||
),
|
||||
'address_1' => array(
|
||||
'label' => __( 'Address', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Address (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'address-line1',
|
||||
'autocapitalize' => 'sentences',
|
||||
'index' => 40,
|
||||
),
|
||||
'address_2' => array(
|
||||
'label' => __( 'Apartment, suite, etc.', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Apartment, suite, etc. (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => false,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'address-line2',
|
||||
'autocapitalize' => 'sentences',
|
||||
'index' => 50,
|
||||
),
|
||||
'country' => array(
|
||||
'label' => __( 'Country/Region', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Country/Region (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'country',
|
||||
'index' => 50,
|
||||
),
|
||||
'city' => array(
|
||||
'label' => __( 'City', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'City (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'address-level2',
|
||||
'autocapitalize' => 'sentences',
|
||||
'index' => 70,
|
||||
),
|
||||
'state' => array(
|
||||
'label' => __( 'State/County', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'State/County (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'address-level1',
|
||||
'autocapitalize' => 'sentences',
|
||||
'index' => 80,
|
||||
),
|
||||
'postcode' => array(
|
||||
'label' => __( 'Postal code', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Postal code (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'postal-code',
|
||||
'autocapitalize' => 'characters',
|
||||
'index' => 90,
|
||||
),
|
||||
'phone' => array(
|
||||
'label' => __( 'Phone', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Phone (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => false,
|
||||
'hidden' => false,
|
||||
'type' => 'tel',
|
||||
'autocomplete' => 'tel',
|
||||
'autocapitalize' => 'characters',
|
||||
'index' => 100,
|
||||
),
|
||||
);
|
||||
|
||||
$this->fields_locations = array(
|
||||
// omit email from shipping and billing fields.
|
||||
'address' => array_merge( \array_diff_key( array_keys( $this->core_fields ), array( 'email' ) ) ),
|
||||
'contact' => array( 'email' ),
|
||||
'additional' => array(),
|
||||
);
|
||||
|
||||
add_filter( 'woocommerce_get_country_locale_default', array( $this, 'update_default_locale_with_fields' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize hooks. This is not run Store API requests.
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'woocommerce_blocks_checkout_enqueue_data', array( $this, 'add_fields_data' ) );
|
||||
add_action( 'woocommerce_blocks_cart_enqueue_data', array( $this, 'add_fields_data' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add fields data to the asset data registry.
|
||||
*/
|
||||
public function add_fields_data() {
|
||||
$this->asset_data_registry->add( 'defaultFields', array_merge( $this->get_core_fields(), $this->get_additional_fields() ), true );
|
||||
$this->asset_data_registry->add( 'addressFieldsLocations', $this->fields_locations, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an additional field for Checkout.
|
||||
*
|
||||
* @param array $options The field options.
|
||||
*
|
||||
* @return \WP_Error|void True if the field was registered, a WP_Error otherwise.
|
||||
*/
|
||||
public function register_checkout_field( $options ) {
|
||||
if ( empty( $options['id'] ) ) {
|
||||
wc_get_logger()->warning( 'A checkout field cannot be registered without an id.' );
|
||||
return;
|
||||
}
|
||||
|
||||
// Having fewer than 2 after exploding around a / means there is no namespace.
|
||||
if ( count( explode( '/', $options['id'] ) ) < 2 ) {
|
||||
wc_get_logger()->warning(
|
||||
sprintf( 'Unable to register field with id: "%s". %s', esc_html( $options['id'] ), 'A checkout field id must consist of namespace/name.' )
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( empty( $options['label'] ) ) {
|
||||
wc_get_logger()->warning(
|
||||
sprintf( 'Unable to register field with id: "%s". %s', esc_html( $options['id'] ), 'The field label is required.' )
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( empty( $options['location'] ) ) {
|
||||
wc_get_logger()->warning(
|
||||
sprintf( 'Unable to register field with id: "%s". %s', esc_html( $options['id'] ), 'The field location is required.' )
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! in_array( $options['location'], array_keys( $this->fields_locations ), true ) ) {
|
||||
wc_get_logger()->warning(
|
||||
sprintf( 'Unable to register field with id: "%s". %s', esc_html( $options['id'] ), 'The field location is invalid.' )
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$type = 'text';
|
||||
if ( ! empty( $options['type'] ) ) {
|
||||
if ( ! in_array( $options['type'], $this->supported_field_types, true ) ) {
|
||||
wc_get_logger()->warning(
|
||||
sprintf(
|
||||
'Unable to register field with id: "%s". Registering a field with type "%s" is not supported. The supported types are: %s.',
|
||||
esc_html( $options['id'] ),
|
||||
esc_html( $options['type'] ),
|
||||
implode( ', ', $this->supported_field_types )
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
$type = $options['type'];
|
||||
}
|
||||
|
||||
// At this point, the essentials fields and its location should be set.
|
||||
$location = $options['location'];
|
||||
$id = $options['id'];
|
||||
// Check to see if field is already in the array.
|
||||
if ( ! empty( $this->additional_fields[ $id ] ) || in_array( $id, $this->fields_locations[ $location ], true ) ) {
|
||||
wc_get_logger()->warning(
|
||||
sprintf( 'Unable to register field with id: "%s". %s', esc_html( $id ), 'The field is already registered.' )
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Hidden fields are not supported right now. They will be registered with hidden => false.
|
||||
if ( ! empty( $options['hidden'] ) && true === $options['hidden'] ) {
|
||||
wc_get_logger()->warning(
|
||||
sprintf( 'Registering a field with hidden set to true is not supported. The field "%s" will be registered as visible.', esc_html( $id ) )
|
||||
);
|
||||
}
|
||||
|
||||
$field_data = array(
|
||||
'label' => $options['label'],
|
||||
'hidden' => false,
|
||||
'type' => $type,
|
||||
'optionalLabel' => empty( $options['optionalLabel'] ) ? '' : $options['optionalLabel'],
|
||||
'required' => empty( $options['required'] ) ? false : $options['required'],
|
||||
'autocomplete' => empty( $options['autocomplete'] ) ? '' : $options['autocomplete'],
|
||||
'autocapitalize' => empty( $options['autocapitalize'] ) ? '' : $options['autocapitalize'],
|
||||
);
|
||||
|
||||
/**
|
||||
* Handle Checkbox fields.
|
||||
*/
|
||||
if ( 'checkbox' === $type ) {
|
||||
// Checkbox fields are always optional. Log a warning if it's set explicitly as true.
|
||||
$field_data['required'] = false;
|
||||
if ( isset( $options['required'] ) && true === $options['required'] ) {
|
||||
wc_get_logger()->warning(
|
||||
sprintf( 'Registering checkbox fields as required is not supported. "%s" will be registered as optional.', esc_html( $id ) )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Select fields.
|
||||
*/
|
||||
if ( 'select' === $type ) {
|
||||
if ( empty( $options['options'] ) || ! is_array( $options['options'] ) ) {
|
||||
wc_get_logger()->warning(
|
||||
sprintf( 'Unable to register field with id: "%s". %s', esc_html( $id ), 'Fields of type "select" must have an array of "options".' )
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Select fields are always required. Log a warning if it's set explicitly as false.
|
||||
$field_data['required'] = true;
|
||||
if ( isset( $options['required'] ) && false === $options['required'] ) {
|
||||
wc_get_logger()->warning(
|
||||
sprintf( 'Registering select fields as optional is not supported. "%s" will be registered as required.', esc_html( $id ) )
|
||||
);
|
||||
}
|
||||
|
||||
$cleaned_options = array();
|
||||
$added_values = array();
|
||||
|
||||
// Check all entries in $options['options'] has a key and value member.
|
||||
foreach ( $options['options'] as $option ) {
|
||||
if ( ! isset( $option['value'] ) || ! isset( $option['label'] ) ) {
|
||||
wc_get_logger()->warning(
|
||||
sprintf( 'Unable to register field with id: "%s". %s', esc_html( $id ), 'Fields of type "select" must have an array of "options" and each option must contain a "value" and "label" member.' )
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$sanitized_value = sanitize_text_field( $option['value'] );
|
||||
$sanitized_label = sanitize_text_field( $option['label'] );
|
||||
|
||||
if ( in_array( $sanitized_value, $added_values, true ) ) {
|
||||
wc_get_logger()->warning(
|
||||
sprintf( 'Duplicate key found when registering field with id: "%s". The value in each option of "select" fields must be unique. Duplicate value "%s" found. The duplicate key will be removed.', esc_html( $id ), esc_html( $sanitized_value ) )
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
$added_values[] = $sanitized_value;
|
||||
|
||||
$cleaned_options[] = array(
|
||||
'value' => $sanitized_value,
|
||||
'label' => $sanitized_label,
|
||||
);
|
||||
}
|
||||
|
||||
$field_data['options'] = $cleaned_options;
|
||||
}
|
||||
|
||||
// Insert new field into the correct location array.
|
||||
$this->additional_fields[ $id ] = $field_data;
|
||||
|
||||
$this->fields_locations[ $location ][] = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all core fields.
|
||||
*
|
||||
* @return array An array of fields.
|
||||
*/
|
||||
public function get_core_fields() {
|
||||
return $this->core_fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all additional fields.
|
||||
*
|
||||
* @return array An array of fields.
|
||||
*/
|
||||
public function get_additional_fields() {
|
||||
return $this->additional_fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the default locale with additional fields without country limitations.
|
||||
*
|
||||
* @param array $locale The locale to update.
|
||||
* @return mixed
|
||||
*/
|
||||
public function update_default_locale_with_fields( $locale ) {
|
||||
foreach ( $this->fields_locations['address'] as $field_id => $additional_field ) {
|
||||
if ( empty( $locale[ $field_id ] ) ) {
|
||||
$locale[ $field_id ] = $additional_field;
|
||||
}
|
||||
}
|
||||
return $locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of fields keys for the address group.
|
||||
*
|
||||
* @return array An array of fields keys.
|
||||
*/
|
||||
public function get_address_fields_keys() {
|
||||
return $this->fields_locations['address'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of fields keys for the contact group.
|
||||
*
|
||||
* @return array An array of fields keys.
|
||||
*/
|
||||
public function get_contact_fields_keys() {
|
||||
return $this->fields_locations['contact'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of fields keys for the additional area group.
|
||||
*
|
||||
* @return array An array of fields keys.
|
||||
*/
|
||||
public function get_additional_fields_keys() {
|
||||
return $this->fields_locations['additional'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of fields for a given group.
|
||||
*
|
||||
* @param string $location The location to get fields for (address|contact|additional).
|
||||
*
|
||||
* @return array An array of fields.
|
||||
*/
|
||||
public function get_fields_for_location( $location ) {
|
||||
if ( in_array( $location, array_keys( $this->fields_locations ), true ) ) {
|
||||
return $this->fields_locations[ $location ];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a field value for a given group.
|
||||
*
|
||||
* @param string $key The field key.
|
||||
* @param mixed $value The field value.
|
||||
* @param string $location The gslocation to validate the field for (address|contact|additional).
|
||||
*
|
||||
* @return true|\WP_Error True if the field is valid, a WP_Error otherwise.
|
||||
*/
|
||||
public function validate_field_for_location( $key, $value, $location ) {
|
||||
if ( ! $this->is_field( $key ) ) {
|
||||
return new \WP_Error(
|
||||
'woocommerce_blocks_checkout_field_invalid',
|
||||
\sprintf(
|
||||
// translators: % is field key.
|
||||
__( 'The field %s is invalid.', 'woocommerce' ),
|
||||
$key
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! in_array( $key, $this->fields_locations[ $location ], true ) ) {
|
||||
return new \WP_Error(
|
||||
'woocommerce_blocks_checkout_field_invalid_location',
|
||||
\sprintf(
|
||||
// translators: %1$s is field key, %2$s location.
|
||||
__( 'The field %1$s is invalid for the location %2$s.', 'woocommerce' ),
|
||||
$key,
|
||||
$location
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$field = $this->additional_fields[ $key ];
|
||||
if ( ! empty( $field['required'] ) && empty( $value ) ) {
|
||||
return new \WP_Error(
|
||||
'woocommerce_blocks_checkout_field_required',
|
||||
\sprintf(
|
||||
// translators: %s is field key.
|
||||
__( 'The field %s is required.', 'woocommerce' ),
|
||||
$key
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given key is a valid field.
|
||||
*
|
||||
* @param string $key The field key.
|
||||
*
|
||||
* @return bool True if the field is valid, false otherwise.
|
||||
*/
|
||||
public function is_field( $key ) {
|
||||
return array_key_exists( $key, $this->additional_fields );
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists a field value for a given order. This would also optionally set the field value on the customer.
|
||||
*
|
||||
* @param string $key The field key.
|
||||
* @param mixed $value The field value.
|
||||
* @param \WC_Order $order The order to persist the field for.
|
||||
* @param bool $set_customer Whether to set the field value on the customer or not.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function persist_field_for_order( $key, $value, $order, $set_customer = true ) {
|
||||
$this->set_array_meta( $key, $value, $order );
|
||||
if ( $set_customer ) {
|
||||
if ( isset( wc()->customer ) ) {
|
||||
$this->set_array_meta( $key, $value, wc()->customer );
|
||||
} elseif ( $order->get_customer_id() ) {
|
||||
$customer = new \WC_Customer( $order->get_customer_id() );
|
||||
$this->set_array_meta( $key, $value, $customer );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists a field value for a given customer.
|
||||
*
|
||||
* @param string $key The field key.
|
||||
* @param mixed $value The field value.
|
||||
* @param \WC_Customer $customer The customer to persist the field for.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function persist_field_for_customer( $key, $value, $customer ) {
|
||||
$this->set_array_meta( $key, $value, $customer );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a field value in an array meta, supporting routing things to billing, shipping, or additional fields, based on a prefix for the key.
|
||||
*
|
||||
* @param string $key The field key.
|
||||
* @param mixed $value The field value.
|
||||
* @param \WC_Customer|\WC_Order $object The object to set the field value for.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function set_array_meta( $key, $value, $object ) {
|
||||
$meta_key = '';
|
||||
|
||||
if ( 0 === strpos( $key, '/billing/' ) ) {
|
||||
$meta_key = self::BILLING_FIELDS_KEY;
|
||||
$key = str_replace( '/billing/', '', $key );
|
||||
} elseif ( 0 === strpos( $key, '/shipping/' ) ) {
|
||||
$meta_key = self::SHIPPING_FIELDS_KEY;
|
||||
$key = str_replace( '/shipping/', '', $key );
|
||||
} else {
|
||||
$meta_key = self::ADDITIONAL_FIELDS_KEY;
|
||||
}
|
||||
|
||||
if ( $object instanceof \WC_Customer ) {
|
||||
if ( ! $object->get_id() ) {
|
||||
$meta_data = wc()->session->get( $meta_key, array() );
|
||||
} else {
|
||||
$meta_data = get_user_meta( $object->get_id(), $meta_key, true );
|
||||
}
|
||||
} elseif ( $object instanceof \WC_Order ) {
|
||||
$meta_data = $object->get_meta( $meta_key, true );
|
||||
}
|
||||
|
||||
if ( ! is_array( $meta_data ) ) {
|
||||
$meta_data = array();
|
||||
}
|
||||
|
||||
$meta_data[ $key ] = $value;
|
||||
if ( $object instanceof \WC_Customer ) {
|
||||
if ( ! $object->get_id() ) {
|
||||
wc()->session->set( $meta_key, $meta_data );
|
||||
} else {
|
||||
update_user_meta( $object->get_id(), $meta_key, $meta_data );
|
||||
}
|
||||
} elseif ( $object instanceof \WC_Order ) {
|
||||
$object->update_meta_data( $meta_key, $meta_data );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a field value for a given object.
|
||||
*
|
||||
* @param string $key The field key.
|
||||
* @param \WC_Customer $customer The customer to get the field value for.
|
||||
* @param string $group The group to get the field value for (shipping|billing|'') in which '' refers to the additional group.
|
||||
*
|
||||
* @return mixed The field value.
|
||||
*/
|
||||
public function get_field_from_customer( $key, $customer, $group = '' ) {
|
||||
return $this->get_field_from_object( $key, $customer, $group );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a field value for a given order.
|
||||
*
|
||||
* @param string $field The field key.
|
||||
* @param \WC_Order $order The order to get the field value for.
|
||||
* @param string $group The group to get the field value for (shipping|billing|'') in which '' refers to the additional group.
|
||||
*
|
||||
* @return mixed The field value.
|
||||
*/
|
||||
public function get_field_from_order( $field, $order, $group = '' ) {
|
||||
return $this->get_field_from_object( $field, $order, $group );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a field value for a given object.
|
||||
*
|
||||
* @param string $key The field key.
|
||||
* @param \WC_Customer|\WC_Order $object The customer to get the field value for.
|
||||
* @param string $group The group to get the field value for (shipping|billing|'') in which '' refers to the additional group.
|
||||
*
|
||||
* @return mixed The field value.
|
||||
*/
|
||||
private function get_field_from_object( $key, $object, $group = '' ) {
|
||||
$meta_key = '';
|
||||
if ( 0 === strpos( $key, '/billing/' ) || 'billing' === $group ) {
|
||||
$meta_key = self::BILLING_FIELDS_KEY;
|
||||
$key = str_replace( '/billing/', '', $key );
|
||||
} elseif ( 0 === strpos( $key, '/shipping/' ) || 'shipping' === $group ) {
|
||||
$meta_key = self::SHIPPING_FIELDS_KEY;
|
||||
$key = str_replace( '/shipping/', '', $key );
|
||||
} else {
|
||||
$meta_key = self::ADDITIONAL_FIELDS_KEY;
|
||||
}
|
||||
|
||||
if ( $object instanceof \WC_Customer ) {
|
||||
if ( ! $object->get_id() ) {
|
||||
$meta_data = wc()->session->get( $meta_key, array() );
|
||||
} else {
|
||||
$meta_data = get_user_meta( $object->get_id(), $meta_key, true );
|
||||
}
|
||||
} elseif ( $object instanceof \WC_Order ) {
|
||||
$meta_data = $object->get_meta( $meta_key, true );
|
||||
}
|
||||
|
||||
if ( ! is_array( $meta_data ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( ! isset( $meta_data[ $key ] ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $meta_data[ $key ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all fields values for a given customer.
|
||||
*
|
||||
* @param \WC_Customer $customer The customer to get the fields for.
|
||||
* @param bool $all Whether to return all fields or only the ones that are still registered. Default false.
|
||||
*
|
||||
* @return array An array of fields.
|
||||
*/
|
||||
public function get_all_fields_from_customer( $customer, $all = false ) {
|
||||
$customer_id = $customer->get_id();
|
||||
$meta_data = array(
|
||||
'billing' => array(),
|
||||
'shipping' => array(),
|
||||
'additional' => array(),
|
||||
);
|
||||
if ( ! $customer_id ) {
|
||||
if ( isset( wc()->session ) ) {
|
||||
$meta_data['billing'] = wc()->session->get( self::BILLING_FIELDS_KEY, array() );
|
||||
$meta_data['shipping'] = wc()->session->get( self::SHIPPING_FIELDS_KEY, array() );
|
||||
$meta_data['additional'] = wc()->session->get( self::ADDITIONAL_FIELDS_KEY, array() );
|
||||
}
|
||||
} else {
|
||||
$meta_data['billing'] = get_user_meta( $customer_id, self::BILLING_FIELDS_KEY, true );
|
||||
$meta_data['shipping'] = get_user_meta( $customer_id, self::SHIPPING_FIELDS_KEY, true );
|
||||
$meta_data['additional'] = get_user_meta( $customer_id, self::ADDITIONAL_FIELDS_KEY, true );
|
||||
}
|
||||
|
||||
return $this->format_meta_data( $meta_data, $all );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all fields values for a given order.
|
||||
*
|
||||
* @param \WC_Order $order The order to get the fields for.
|
||||
* @param bool $all Whether to return all fields or only the ones that are still registered. Default false.
|
||||
*
|
||||
* @return array An array of fields.
|
||||
*/
|
||||
public function get_all_fields_from_order( $order, $all = false ) {
|
||||
$meta_data = array(
|
||||
'billing' => array(),
|
||||
'shipping' => array(),
|
||||
'additional' => array(),
|
||||
);
|
||||
if ( $order instanceof \WC_Order ) {
|
||||
$meta_data['billing'] = $order->get_meta( self::BILLING_FIELDS_KEY, true );
|
||||
$meta_data['shipping'] = $order->get_meta( self::SHIPPING_FIELDS_KEY, true );
|
||||
$meta_data['additional'] = $order->get_meta( self::ADDITIONAL_FIELDS_KEY, true );
|
||||
}
|
||||
return $this->format_meta_data( $meta_data, $all );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all fields values for a given meta object. It would add the billing or shipping prefix to the keys.
|
||||
*
|
||||
* @param array $meta The meta data to format.
|
||||
* @param bool $all Whether to return all fields or only the ones that are still registered. Default false.
|
||||
*
|
||||
* @return array An array of fields.
|
||||
*/
|
||||
private function format_meta_data( $meta, $all = false ) {
|
||||
$billing_fields = $meta['billing'] ?? array();
|
||||
$shipping_fields = $meta['shipping'] ?? array();
|
||||
$additional_fields = $meta['additional'] ?? array();
|
||||
|
||||
$fields = array();
|
||||
|
||||
if ( is_array( $billing_fields ) ) {
|
||||
foreach ( $billing_fields as $key => $value ) {
|
||||
if ( ! $all && ! $this->is_field( $key ) ) {
|
||||
continue;
|
||||
}
|
||||
$fields[ '/billing/' . $key ] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_array( $shipping_fields ) ) {
|
||||
foreach ( $shipping_fields as $key => $value ) {
|
||||
if ( ! $all && ! $this->is_field( $key ) ) {
|
||||
continue;
|
||||
}
|
||||
$fields[ '/shipping/' . $key ] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_array( $additional_fields ) ) {
|
||||
foreach ( $additional_fields as $key => $value ) {
|
||||
if ( ! $all && ! $this->is_field( $key ) ) {
|
||||
continue;
|
||||
}
|
||||
$fields[ $key ] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* From a set of fields, returns only the ones that should be saved to the customer.
|
||||
* For now, this only supports fields in address location.
|
||||
*
|
||||
* @param array $fields The fields to filter.
|
||||
*
|
||||
* @return array The filtered fields.
|
||||
*/
|
||||
public function filter_fields_for_customer( $fields ) {
|
||||
$customer_fields_keys = $this->get_address_fields_keys();
|
||||
return array_filter(
|
||||
$fields,
|
||||
function( $key ) use ( $customer_fields_keys ) {
|
||||
return in_array( $key, $customer_fields_keys, true );
|
||||
},
|
||||
ARRAY_FILTER_USE_KEY
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Domain\Services;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Domain\Package;
|
||||
use Automattic\WooCommerce\Blocks\Domain\Services\Email\CustomerNewAccount;
|
||||
|
||||
/**
|
||||
* Service class implementing new create account emails used for order processing via the Block Based Checkout.
|
||||
*/
|
||||
class CreateAccount {
|
||||
/**
|
||||
* Reference to the Package instance
|
||||
*
|
||||
* @var Package
|
||||
*/
|
||||
private $package;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Package $package An instance of (Woo Blocks) Package.
|
||||
*/
|
||||
public function __construct( Package $package ) {
|
||||
$this->package = $package;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init - register handlers for WooCommerce core email hooks.
|
||||
*/
|
||||
public function init() {
|
||||
// Override core email handlers to add our new improved "new account" email.
|
||||
add_action(
|
||||
'woocommerce_email',
|
||||
function ( $wc_emails_instance ) {
|
||||
// Remove core "new account" handler; we are going to replace it.
|
||||
remove_action( 'woocommerce_created_customer_notification', array( $wc_emails_instance, 'customer_new_account' ), 10, 3 );
|
||||
|
||||
// Add custom "new account" handler.
|
||||
add_action(
|
||||
'woocommerce_created_customer_notification',
|
||||
function( $customer_id, $new_customer_data = array(), $password_generated = false ) use ( $wc_emails_instance ) {
|
||||
// If this is a block-based signup, send a new email with password reset link (no password in email).
|
||||
if ( isset( $new_customer_data['source'] ) && 'store-api' === $new_customer_data['source'] ) {
|
||||
$this->customer_new_account( $customer_id, $new_customer_data );
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, trigger the existing legacy email (with new password inline).
|
||||
$wc_emails_instance->customer_new_account( $customer_id, $new_customer_data, $password_generated );
|
||||
},
|
||||
10,
|
||||
3
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger new account email.
|
||||
* This is intended as a replacement to WC_Emails::customer_new_account(),
|
||||
* with a set password link instead of emailing the new password in email
|
||||
* content.
|
||||
*
|
||||
* @param int $customer_id The ID of the new customer account.
|
||||
* @param array $new_customer_data Assoc array of data for the new account.
|
||||
*/
|
||||
public function customer_new_account( $customer_id = 0, array $new_customer_data = array() ) {
|
||||
$new_account_email = new CustomerNewAccount( $this->package );
|
||||
$new_account_email->trigger( $customer_id, $new_customer_data );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Domain\Services;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Domain\Package;
|
||||
use Exception;
|
||||
use WC_Order;
|
||||
|
||||
/**
|
||||
* Service class for adding DraftOrder functionality to WooCommerce core.
|
||||
*
|
||||
* Sets up all logic related to the Checkout Draft Orders service
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class DraftOrders {
|
||||
|
||||
const DB_STATUS = 'wc-checkout-draft';
|
||||
const STATUS = 'checkout-draft';
|
||||
|
||||
/**
|
||||
* Holds the Package instance
|
||||
*
|
||||
* @var Package
|
||||
*/
|
||||
private $package;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Package $package An instance of the package class.
|
||||
*/
|
||||
public function __construct( Package $package ) {
|
||||
$this->package = $package;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all hooks related to adding Checkout Draft order functionality to Woo Core.
|
||||
*/
|
||||
public function init() {
|
||||
add_filter( 'wc_order_statuses', [ $this, 'register_draft_order_status' ] );
|
||||
add_filter( 'woocommerce_register_shop_order_post_statuses', [ $this, 'register_draft_order_post_status' ] );
|
||||
add_filter( 'woocommerce_analytics_excluded_order_statuses', [ $this, 'append_draft_order_post_status' ] );
|
||||
add_filter( 'woocommerce_valid_order_statuses_for_payment', [ $this, 'append_draft_order_post_status' ] );
|
||||
add_filter( 'woocommerce_valid_order_statuses_for_payment_complete', [ $this, 'append_draft_order_post_status' ] );
|
||||
// Hook into the query to retrieve My Account orders so draft status is excluded.
|
||||
add_action( 'woocommerce_my_account_my_orders_query', [ $this, 'delete_draft_order_post_status_from_args' ] );
|
||||
add_action( 'woocommerce_cleanup_draft_orders', [ $this, 'delete_expired_draft_orders' ] );
|
||||
add_action( 'admin_init', [ $this, 'install' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Installation related logic for Draft order functionality.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function install() {
|
||||
$this->maybe_create_cronjobs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe create cron events.
|
||||
*/
|
||||
protected function maybe_create_cronjobs() {
|
||||
if ( function_exists( 'as_next_scheduled_action' ) && false === as_next_scheduled_action( 'woocommerce_cleanup_draft_orders' ) ) {
|
||||
as_schedule_recurring_action( strtotime( 'midnight tonight' ), DAY_IN_SECONDS, 'woocommerce_cleanup_draft_orders' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register custom order status for orders created via the API during checkout.
|
||||
*
|
||||
* Draft order status is used before payment is attempted, during checkout, when a cart is converted to an order.
|
||||
*
|
||||
* @param array $statuses Array of statuses.
|
||||
* @internal
|
||||
* @return array
|
||||
*/
|
||||
public function register_draft_order_status( array $statuses ) {
|
||||
$statuses[ self::DB_STATUS ] = _x( 'Draft', 'Order status', 'woocommerce' );
|
||||
return $statuses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register custom order post status for orders created via the API during checkout.
|
||||
*
|
||||
* @param array $statuses Array of statuses.
|
||||
* @internal
|
||||
|
||||
* @return array
|
||||
*/
|
||||
public function register_draft_order_post_status( array $statuses ) {
|
||||
$statuses[ self::DB_STATUS ] = $this->get_post_status_properties();
|
||||
return $statuses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the properties of this post status for registration.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_post_status_properties() {
|
||||
return [
|
||||
'label' => _x( 'Draft', 'Order status', 'woocommerce' ),
|
||||
'public' => false,
|
||||
'exclude_from_search' => false,
|
||||
'show_in_admin_all_list' => false,
|
||||
'show_in_admin_status_list' => true,
|
||||
/* translators: %s: number of orders */
|
||||
'label_count' => _n_noop( 'Drafts <span class="count">(%s)</span>', 'Drafts <span class="count">(%s)</span>', 'woocommerce' ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove draft status from the 'status' argument of an $args array.
|
||||
*
|
||||
* @param array $args Array of arguments containing statuses in the status key.
|
||||
* @internal
|
||||
* @return array
|
||||
*/
|
||||
public function delete_draft_order_post_status_from_args( $args ) {
|
||||
if ( ! array_key_exists( 'status', $args ) ) {
|
||||
$statuses = [];
|
||||
foreach ( wc_get_order_statuses() as $key => $label ) {
|
||||
if ( self::DB_STATUS !== $key ) {
|
||||
$statuses[] = str_replace( 'wc-', '', $key );
|
||||
}
|
||||
}
|
||||
$args['status'] = $statuses;
|
||||
} elseif ( self::DB_STATUS === $args['status'] ) {
|
||||
$args['status'] = '';
|
||||
} elseif ( is_array( $args['status'] ) ) {
|
||||
$args['status'] = array_diff_key( $args['status'], array( self::STATUS => null ) );
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append draft status to a list of statuses.
|
||||
*
|
||||
* @param array $statuses Array of statuses.
|
||||
* @internal
|
||||
|
||||
* @return array
|
||||
*/
|
||||
public function append_draft_order_post_status( $statuses ) {
|
||||
$statuses[] = self::STATUS;
|
||||
return $statuses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete draft orders older than a day in batches of 20.
|
||||
*
|
||||
* Ran on a daily cron schedule.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function delete_expired_draft_orders() {
|
||||
$count = 0;
|
||||
$batch_size = 20;
|
||||
$this->ensure_draft_status_registered();
|
||||
$orders = wc_get_orders(
|
||||
[
|
||||
'date_modified' => '<=' . strtotime( '-1 DAY' ),
|
||||
'limit' => $batch_size,
|
||||
'status' => self::DB_STATUS,
|
||||
'type' => 'shop_order',
|
||||
]
|
||||
);
|
||||
|
||||
// do we bail because the query results are unexpected?
|
||||
try {
|
||||
$this->assert_order_results( $orders, $batch_size );
|
||||
if ( $orders ) {
|
||||
foreach ( $orders as $order ) {
|
||||
$order->delete( true );
|
||||
$count ++;
|
||||
}
|
||||
}
|
||||
if ( $batch_size === $count && function_exists( 'as_enqueue_async_action' ) ) {
|
||||
as_enqueue_async_action( 'woocommerce_cleanup_draft_orders' );
|
||||
}
|
||||
} catch ( Exception $error ) {
|
||||
wc_caught_exception( $error, __METHOD__ );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Since it's possible for third party code to clobber the `$wp_post_statuses` global,
|
||||
* we need to do a final check here to make sure the draft post status is
|
||||
* registered with the global so that it is not removed by WP_Query status
|
||||
* validation checks.
|
||||
*/
|
||||
private function ensure_draft_status_registered() {
|
||||
$is_registered = get_post_stati( [ 'name' => self::DB_STATUS ] );
|
||||
if ( empty( $is_registered ) ) {
|
||||
register_post_status(
|
||||
self::DB_STATUS,
|
||||
$this->get_post_status_properties()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts whether incoming order results are expected given the query
|
||||
* this service class executes.
|
||||
*
|
||||
* @param WC_Order[] $order_results The order results being asserted.
|
||||
* @param int $expected_batch_size The expected batch size for the results.
|
||||
* @throws Exception If any assertions fail, an exception is thrown.
|
||||
*/
|
||||
private function assert_order_results( $order_results, $expected_batch_size ) {
|
||||
// if not an array, then just return because it won't get handled
|
||||
// anyways.
|
||||
if ( ! is_array( $order_results ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$suffix = ' This is an indicator that something is filtering WooCommerce or WordPress queries and modifying the query parameters.';
|
||||
|
||||
// if count is greater than our expected batch size, then that's a problem.
|
||||
if ( count( $order_results ) > 20 ) {
|
||||
throw new Exception( 'There are an unexpected number of results returned from the query.' . $suffix );
|
||||
}
|
||||
|
||||
// if any of the returned orders are not draft (or not a WC_Order), then that's a problem.
|
||||
foreach ( $order_results as $order ) {
|
||||
if ( ! ( $order instanceof WC_Order ) ) {
|
||||
throw new Exception( 'The returned results contain a value that is not a WC_Order.' . $suffix );
|
||||
}
|
||||
if ( ! $order->has_status( self::STATUS ) ) {
|
||||
throw new Exception( 'The results contain an order that is not a `wc-checkout-draft` status in the results.' . $suffix );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Domain\Services\Email;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Domain\Package;
|
||||
|
||||
/**
|
||||
* Customer New Account.
|
||||
*
|
||||
* An email sent to the customer when they create an account.
|
||||
* This is intended as a replacement to \WC_Email_Customer_New_Account(),
|
||||
* with a set password link instead of emailing the new password in email
|
||||
* content.
|
||||
*
|
||||
* @extends \WC_Email
|
||||
*/
|
||||
class CustomerNewAccount extends \WC_Email {
|
||||
|
||||
/**
|
||||
* User login name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $user_login;
|
||||
|
||||
/**
|
||||
* User email.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $user_email;
|
||||
|
||||
/**
|
||||
* Magic link to set initial password.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $set_password_url;
|
||||
|
||||
/**
|
||||
* Override (force) default template path
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $default_template_path;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Package $package An instance of (Woo Blocks) Package.
|
||||
*/
|
||||
public function __construct( Package $package ) {
|
||||
// Note - we're using the same ID as the real email.
|
||||
// This ensures that any merchant tweaks (Settings > Emails)
|
||||
// apply to this email (consistent with the core email).
|
||||
$this->id = 'customer_new_account';
|
||||
$this->customer_email = true;
|
||||
$this->title = __( 'New account', 'woocommerce' );
|
||||
$this->description = __( '“New Account” emails are sent when a customer signs up via the checkout flow.', 'woocommerce' );
|
||||
$this->template_html = 'emails/customer-new-account-blocks.php';
|
||||
$this->template_plain = 'emails/plain/customer-new-account-blocks.php';
|
||||
$this->default_template_path = $package->get_path( '/templates/' );
|
||||
|
||||
// Call parent constructor.
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get email subject.
|
||||
*
|
||||
* @since 3.1.0
|
||||
* @return string
|
||||
*/
|
||||
public function get_default_subject() {
|
||||
return __( 'Your {site_title} account has been created!', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get email heading.
|
||||
*
|
||||
* @since 3.1.0
|
||||
* @return string
|
||||
*/
|
||||
public function get_default_heading() {
|
||||
return __( 'Welcome to {site_title}', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger.
|
||||
*
|
||||
* @param int $user_id User ID.
|
||||
* @param string $user_pass User password.
|
||||
* @param bool $password_generated Whether the password was generated automatically or not.
|
||||
*/
|
||||
public function trigger( $user_id, $user_pass = '', $password_generated = false ) {
|
||||
$this->setup_locale();
|
||||
|
||||
if ( $user_id ) {
|
||||
$this->object = new \WP_User( $user_id );
|
||||
|
||||
// Generate a magic link so user can set initial password.
|
||||
$key = get_password_reset_key( $this->object );
|
||||
if ( ! is_wp_error( $key ) ) {
|
||||
$action = 'newaccount';
|
||||
$this->set_password_url = wc_get_account_endpoint_url( 'lost-password' ) . "?action=$action&key=$key&login=" . rawurlencode( $this->object->user_login );
|
||||
}
|
||||
|
||||
$this->user_login = stripslashes( $this->object->user_login );
|
||||
$this->user_email = stripslashes( $this->object->user_email );
|
||||
$this->recipient = $this->user_email;
|
||||
}
|
||||
|
||||
if ( $this->is_enabled() && $this->get_recipient() ) {
|
||||
$this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments(), $this->set_password_url );
|
||||
}
|
||||
|
||||
$this->restore_locale();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content html.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content_html() {
|
||||
return wc_get_template_html(
|
||||
$this->template_html,
|
||||
array(
|
||||
'email_heading' => $this->get_heading(),
|
||||
'additional_content' => $this->get_additional_content(),
|
||||
'user_login' => $this->user_login,
|
||||
'blogname' => $this->get_blogname(),
|
||||
'set_password_url' => $this->set_password_url,
|
||||
'sent_to_admin' => false,
|
||||
'plain_text' => false,
|
||||
'email' => $this,
|
||||
),
|
||||
'',
|
||||
$this->default_template_path
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content plain.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content_plain() {
|
||||
return wc_get_template_html(
|
||||
$this->template_plain,
|
||||
array(
|
||||
'email_heading' => $this->get_heading(),
|
||||
'additional_content' => $this->get_additional_content(),
|
||||
'user_login' => $this->user_login,
|
||||
'blogname' => $this->get_blogname(),
|
||||
'set_password_url' => $this->set_password_url,
|
||||
'sent_to_admin' => false,
|
||||
'plain_text' => true,
|
||||
'email' => $this,
|
||||
),
|
||||
'',
|
||||
$this->default_template_path
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default content to show below main email content.
|
||||
*
|
||||
* @since 3.7.0
|
||||
* @return string
|
||||
*/
|
||||
public function get_default_additional_content() {
|
||||
return __( 'We look forward to seeing you soon.', 'woocommerce' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Domain\Services;
|
||||
|
||||
/**
|
||||
* Service class that handles the feature flags.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class FeatureGating {
|
||||
|
||||
/**
|
||||
* Current flag value.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $flag;
|
||||
|
||||
const EXPERIMENTAL_FLAG = 3;
|
||||
const FEATURE_PLUGIN_FLAG = 2;
|
||||
const CORE_FLAG = 1;
|
||||
|
||||
/**
|
||||
* Current environment
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $environment;
|
||||
|
||||
const PRODUCTION_ENVIRONMENT = 'production';
|
||||
const DEVELOPMENT_ENVIRONMENT = 'development';
|
||||
const TEST_ENVIRONMENT = 'test';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param int $flag Hardcoded flag value. Useful for tests.
|
||||
* @param string $environment Hardcoded environment value. Useful for tests.
|
||||
*/
|
||||
public function __construct( $flag = 0, $environment = 'unset' ) {
|
||||
$this->flag = $flag;
|
||||
$this->environment = $environment;
|
||||
$this->load_flag();
|
||||
$this->load_environment();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set correct flag.
|
||||
*/
|
||||
public function load_flag() {
|
||||
if ( 0 === $this->flag ) {
|
||||
$default_flag = defined( 'WC_BLOCKS_IS_FEATURE_PLUGIN' ) ? self::FEATURE_PLUGIN_FLAG : self::CORE_FLAG;
|
||||
if ( file_exists( __DIR__ . '/../../../../blocks.ini' ) ) {
|
||||
$allowed_flags = [ self::EXPERIMENTAL_FLAG, self::FEATURE_PLUGIN_FLAG, self::CORE_FLAG ];
|
||||
$woo_options = parse_ini_file( __DIR__ . '/../../../../blocks.ini' );
|
||||
$this->flag = is_array( $woo_options ) && in_array( intval( $woo_options['woocommerce_blocks_phase'] ), $allowed_flags, true ) ? $woo_options['woocommerce_blocks_phase'] : $default_flag;
|
||||
} else {
|
||||
$this->flag = $default_flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set correct environment.
|
||||
*/
|
||||
public function load_environment() {
|
||||
if ( 'unset' === $this->environment ) {
|
||||
if ( file_exists( __DIR__ . '/../../../../blocks.ini' ) ) {
|
||||
$allowed_environments = [ self::PRODUCTION_ENVIRONMENT, self::DEVELOPMENT_ENVIRONMENT, self::TEST_ENVIRONMENT ];
|
||||
$woo_options = parse_ini_file( __DIR__ . '/../../../../blocks.ini' );
|
||||
$this->environment = is_array( $woo_options ) && in_array( $woo_options['woocommerce_blocks_env'], $allowed_environments, true ) ? $woo_options['woocommerce_blocks_env'] : self::PRODUCTION_ENVIRONMENT;
|
||||
} else {
|
||||
$this->environment = self::PRODUCTION_ENVIRONMENT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current flag value.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_flag() {
|
||||
return $this->flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if we're executing the code in an experimental build mode.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_experimental_build() {
|
||||
return $this->flag >= self::EXPERIMENTAL_FLAG;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if we're executing the code in an feature plugin or experimental build mode.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_feature_plugin_build() {
|
||||
return $this->flag >= self::FEATURE_PLUGIN_FLAG;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current environment value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_environment() {
|
||||
return $this->environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if we're executing the code in an development environment.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_development_environment() {
|
||||
return self::DEVELOPMENT_ENVIRONMENT === $this->environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if we're executing the code in a production environment.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_production_environment() {
|
||||
return self::PRODUCTION_ENVIRONMENT === $this->environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if we're executing the code in a test environment.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_test_environment() {
|
||||
return self::TEST_ENVIRONMENT === $this->environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns core flag value.
|
||||
*
|
||||
* @return number
|
||||
*/
|
||||
public static function get_core_flag() {
|
||||
return self::CORE_FLAG;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns feature plugin flag value.
|
||||
*
|
||||
* @return number
|
||||
*/
|
||||
public static function get_feature_plugin_flag() {
|
||||
return self::FEATURE_PLUGIN_FLAG;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns experimental flag value.
|
||||
*
|
||||
* @return number
|
||||
*/
|
||||
public static function get_experimental_flag() {
|
||||
return self::EXPERIMENTAL_FLAG;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the block templates controller refactor should be used to display blocks.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_block_templates_controller_refactor_enabled() {
|
||||
if ( file_exists( __DIR__ . '/../../../../blocks.ini' ) ) {
|
||||
$conf = parse_ini_file( __DIR__ . '/../../../../blocks.ini' );
|
||||
return $this->is_development_environment() && isset( $conf['use_block_templates_controller_refactor'] ) && true === (bool) $conf['use_block_templates_controller_refactor'];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Domain\Services;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Package;
|
||||
use Automattic\WooCommerce\Blocks\Assets\Api as AssetApi;
|
||||
|
||||
/**
|
||||
* Service class to integrate Blocks with the Google Analytics extension,
|
||||
*/
|
||||
class GoogleAnalytics {
|
||||
/**
|
||||
* Instance of the asset API.
|
||||
*
|
||||
* @var AssetApi
|
||||
*/
|
||||
protected $asset_api;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param AssetApi $asset_api Instance of the asset API.
|
||||
*/
|
||||
public function __construct( AssetApi $asset_api ) {
|
||||
$this->asset_api = $asset_api;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook into WP.
|
||||
*/
|
||||
public function init() {
|
||||
// Require Google Analytics Integration to be activated.
|
||||
if ( ! class_exists( 'WC_Google_Analytics_Integration', false ) ) {
|
||||
return;
|
||||
}
|
||||
add_action( 'init', array( $this, 'register_assets' ) );
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
||||
add_filter( 'script_loader_tag', array( $this, 'async_script_loader_tags' ), 10, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register scripts.
|
||||
*/
|
||||
public function register_assets() {
|
||||
$this->asset_api->register_script( 'wc-blocks-google-analytics', 'assets/client/blocks/wc-blocks-google-analytics.js', [ 'google-tag-manager' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue the Google Tag Manager script if prerequisites are met.
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
$settings = $this->get_google_analytics_settings();
|
||||
$prefix = strstr( strtoupper( $settings['ga_id'] ), '-', true );
|
||||
|
||||
// Require tracking to be enabled with a valid GA ID.
|
||||
if ( ! in_array( $prefix, [ 'G', 'GT' ], true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter to disable Google Analytics tracking.
|
||||
*
|
||||
* @internal Matches filter name in GA extension.
|
||||
* @since 4.9.0
|
||||
*
|
||||
* @param boolean $disable_tracking If true, tracking will be disabled.
|
||||
*/
|
||||
if ( apply_filters( 'woocommerce_ga_disable_tracking', ! wc_string_to_bool( $settings['ga_event_tracking_enabled'] ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! wp_script_is( 'google-tag-manager', 'registered' ) ) {
|
||||
// phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
|
||||
wp_register_script( 'google-tag-manager', 'https://www.googletagmanager.com/gtag/js?id=' . $settings['ga_id'], [], null, false );
|
||||
wp_add_inline_script(
|
||||
'google-tag-manager',
|
||||
"
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', '" . esc_js( $settings['ga_id'] ) . "', { 'send_page_view': false });"
|
||||
);
|
||||
}
|
||||
wp_enqueue_script( 'wc-blocks-google-analytics' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get settings from the GA integration extension.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_google_analytics_settings() {
|
||||
return wp_parse_args(
|
||||
get_option( 'woocommerce_google_analytics_settings' ),
|
||||
[
|
||||
'ga_id' => '',
|
||||
'ga_event_tracking_enabled' => 'no',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add async to script tags with defined handles.
|
||||
*
|
||||
* @param string $tag HTML for the script tag.
|
||||
* @param string $handle Handle of script.
|
||||
* @param string $src Src of script.
|
||||
* @return string
|
||||
*/
|
||||
public function async_script_loader_tags( $tag, $handle, $src ) {
|
||||
if ( ! in_array( $handle, array( 'google-tag-manager' ), true ) ) {
|
||||
return $tag;
|
||||
}
|
||||
// If script was output manually in wp_head, abort.
|
||||
if ( did_action( 'woocommerce_gtag_snippet' ) ) {
|
||||
return '';
|
||||
}
|
||||
return str_replace( '<script src', '<script async src', $tag );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Domain\Services;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Assets\AssetDataRegistry;
|
||||
|
||||
/**
|
||||
* Service class that handles hydration of API data for blocks.
|
||||
*/
|
||||
class Hydration {
|
||||
/**
|
||||
* Instance of the asset data registry.
|
||||
*
|
||||
* @var AssetDataRegistry
|
||||
*/
|
||||
protected $asset_data_registry;
|
||||
|
||||
/**
|
||||
* Cached notices to restore after hydrating the API.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $cached_store_notices = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param AssetDataRegistry $asset_data_registry Instance of the asset data registry.
|
||||
*/
|
||||
public function __construct( AssetDataRegistry $asset_data_registry ) {
|
||||
$this->asset_data_registry = $asset_data_registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrates the asset data registry with data from the API. Disables notices and nonces so requests contain valid
|
||||
* data that is not polluted by the current session.
|
||||
*
|
||||
* @param array $path API paths to hydrate e.g. '/wc/store/v1/cart'.
|
||||
* @return array Response data.
|
||||
*/
|
||||
public function get_rest_api_response_data( $path = '' ) {
|
||||
$this->cache_store_notices();
|
||||
$this->disable_nonce_check();
|
||||
|
||||
// Preload the request and add it to the array. It will be $preloaded_requests['path'] and contain 'body' and 'headers'.
|
||||
$preloaded_requests = rest_preload_api_request( [], $path );
|
||||
|
||||
$this->restore_cached_store_notices();
|
||||
$this->restore_nonce_check();
|
||||
|
||||
// Returns just the single preloaded request, or an empty array if it doesn't exist.
|
||||
return $preloaded_requests[ $path ] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the nonce check temporarily.
|
||||
*/
|
||||
protected function disable_nonce_check() {
|
||||
add_filter( 'woocommerce_store_api_disable_nonce_check', [ $this, 'disable_nonce_check_callback' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to disable the nonce check. While we could use `__return_true`, we use a custom named callback so that
|
||||
* we can remove it later without affecting other filters.
|
||||
*/
|
||||
public function disable_nonce_check_callback() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the nonce check.
|
||||
*/
|
||||
protected function restore_nonce_check() {
|
||||
remove_filter( 'woocommerce_store_api_disable_nonce_check', [ $this, 'disable_nonce_check_callback' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache notices before hydrating the API if the customer has a session.
|
||||
*/
|
||||
protected function cache_store_notices() {
|
||||
if ( ! did_action( 'woocommerce_init' ) || null === WC()->session ) {
|
||||
return;
|
||||
}
|
||||
$this->cached_store_notices = WC()->session->get( 'wc_notices', array() );
|
||||
WC()->session->set( 'wc_notices', null );
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore notices into current session from cache.
|
||||
*/
|
||||
protected function restore_cached_store_notices() {
|
||||
if ( ! did_action( 'woocommerce_init' ) || null === WC()->session ) {
|
||||
return;
|
||||
}
|
||||
WC()->session->set( 'wc_notices', $this->cached_store_notices );
|
||||
$this->cached_store_notices = [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Domain\Services;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Domain\Package;
|
||||
use Automattic\WooCommerce\Blocks\Utils\CartCheckoutUtils;
|
||||
|
||||
/**
|
||||
* Service class for adding new-style Notices to WooCommerce core.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Notices {
|
||||
/**
|
||||
* Holds the Package instance
|
||||
*
|
||||
* @var Package
|
||||
*/
|
||||
private $package;
|
||||
|
||||
/**
|
||||
* Templates used for notices.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $notice_templates = array(
|
||||
'notices/error.php',
|
||||
'notices/notice.php',
|
||||
'notices/success.php',
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Package $package An instance of the package class.
|
||||
*/
|
||||
public function __construct( Package $package ) {
|
||||
$this->package = $package;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize notice hooks.
|
||||
*/
|
||||
public function init() {
|
||||
add_filter( 'woocommerce_kses_notice_allowed_tags', [ $this, 'add_kses_notice_allowed_tags' ] );
|
||||
add_action( 'wp_head', [ $this, 'enqueue_notice_styles' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow SVG icon in notices.
|
||||
*
|
||||
* @param array $allowed_tags Allowed tags.
|
||||
* @return array
|
||||
*/
|
||||
public function add_kses_notice_allowed_tags( $allowed_tags ) {
|
||||
$svg_args = array(
|
||||
'svg' => array(
|
||||
'aria-hidden' => true,
|
||||
'xmlns' => true,
|
||||
'width' => true,
|
||||
'height' => true,
|
||||
'viewbox' => true,
|
||||
'focusable' => true,
|
||||
),
|
||||
'path' => array(
|
||||
'd' => true,
|
||||
),
|
||||
);
|
||||
return array_merge( $allowed_tags, $svg_args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all notices with the new block based notices.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_notice_styles() {
|
||||
wp_enqueue_style( 'wc-blocks-style' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Blocks\Domain\Services\OnboardingTasks;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
|
||||
|
||||
/**
|
||||
* Review the cart/checkout Task
|
||||
*/
|
||||
class ReviewCheckoutTask extends Task {
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return 'review-checkout-experience';
|
||||
}
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return __( 'Review your checkout experience', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_time() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional Info.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_additional_info() {
|
||||
return __( 'Make sure cart and checkout flows are configured correctly for your shoppers.', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Task completion.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
return $this->is_visited();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the store uses blocks on the cart or checkout page.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function has_cart_block() {
|
||||
$cart_page_id = wc_get_page_id( 'cart' );
|
||||
$has_block_cart = $cart_page_id && ( has_block( 'woocommerce/cart', $cart_page_id ) || has_block( 'woocommerce/classic-shortcode', $cart_page_id ) );
|
||||
|
||||
return $has_block_cart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the store uses blocks on the cart or checkout page.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function has_checkout_block() {
|
||||
$cart_page_id = wc_get_page_id( 'cart' );
|
||||
$has_block_cart = $cart_page_id && ( has_block( 'woocommerce/cart', $cart_page_id ) || has_block( 'woocommerce/classic-shortcode', $cart_page_id ) );
|
||||
|
||||
return $has_block_cart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the store uses blocks on the cart or checkout page.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function has_cart_or_checkout_block() {
|
||||
return $this->has_cart_block() || $this->has_checkout_block();
|
||||
}
|
||||
|
||||
/**
|
||||
* Task visibility.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_view() {
|
||||
return $this->has_cart_or_checkout_block();
|
||||
}
|
||||
|
||||
/**
|
||||
* Action URL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_action_url() {
|
||||
$base_url = wc_current_theme_is_fse_theme() ? 'site-editor.php?postType=page&postId=' : 'post.php?action=edit&post=';
|
||||
$page_id = $this->has_cart_block() ? wc_get_page_id( 'cart' ) : wc_get_page_id( 'checkout' );
|
||||
$focus = $this->has_cart_block() ? 'cart' : 'checkout';
|
||||
|
||||
return admin_url( $base_url . absint( $page_id ) . '&focus=' . $focus . '&canvas=edit' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Blocks\Domain\Services\OnboardingTasks;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Domain\Services\OnboardingTasks\ReviewCheckoutTask;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\TaskLists;
|
||||
|
||||
/**
|
||||
* Onboarding Tasks Controller
|
||||
*/
|
||||
class TasksController {
|
||||
|
||||
/**
|
||||
* Init tasks.
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'init', [ $this, 'register_tasks' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register tasks.
|
||||
*/
|
||||
public function register_tasks() {
|
||||
TaskLists::add_task(
|
||||
'extended',
|
||||
new ReviewCheckoutTask()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Package;
|
||||
|
||||
if ( ! function_exists( 'woocommerce_blocks_register_checkout_field' ) && Package::feature()->is_experimental_build() ) {
|
||||
|
||||
/**
|
||||
* Register a checkout field.
|
||||
*
|
||||
* @param array $options Field arguments.
|
||||
* @throws Exception If field registration fails.
|
||||
*/
|
||||
function woocommerce_blocks_register_checkout_field( $options ) {
|
||||
|
||||
// Check if `woocommerce_blocks_loaded` ran. If not then the CheckoutFields class will not be available yet.
|
||||
// In that case, re-hook `woocommerce_blocks_loaded` and try running this again.
|
||||
$woocommerce_blocks_loaded_ran = did_action( 'woocommerce_blocks_loaded' );
|
||||
if ( ! $woocommerce_blocks_loaded_ran ) {
|
||||
add_action(
|
||||
'woocommerce_blocks_loaded',
|
||||
function() use ( $options ) {
|
||||
woocommerce_blocks_register_checkout_field( $options );
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
$checkout_fields = Package::container()->get( \Automattic\WooCommerce\Blocks\Domain\Services\CheckoutFields::class );
|
||||
$result = $checkout_fields->register_checkout_field( $options );
|
||||
if ( is_wp_error( $result ) ) {
|
||||
throw new Exception( $result->get_error_message() );
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user