rebase on oct-10-2023
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\API;
|
||||
|
||||
use AllowDynamicProperties;
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
@@ -16,6 +17,7 @@ use Automattic\WooCommerce\Internal\Admin\Loader;
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
#[AllowDynamicProperties]
|
||||
class Init {
|
||||
/**
|
||||
* The single instance of the class.
|
||||
@@ -37,7 +39,7 @@ class Init {
|
||||
}
|
||||
|
||||
/**
|
||||
* Boostrap REST API.
|
||||
* Bootstrap REST API.
|
||||
*/
|
||||
public function __construct() {
|
||||
// Hook in data stores.
|
||||
@@ -87,6 +89,7 @@ class Init {
|
||||
'Automattic\WooCommerce\Admin\API\OnboardingProfile',
|
||||
'Automattic\WooCommerce\Admin\API\OnboardingTasks',
|
||||
'Automattic\WooCommerce\Admin\API\OnboardingThemes',
|
||||
'Automattic\WooCommerce\Admin\API\OnboardingPlugins',
|
||||
'Automattic\WooCommerce\Admin\API\NavigationFavorites',
|
||||
'Automattic\WooCommerce\Admin\API\Taxes',
|
||||
'Automattic\WooCommerce\Admin\API\MobileAppMagicLink',
|
||||
|
||||
@@ -34,15 +34,6 @@ class Notes extends \WC_REST_CRUD_Controller {
|
||||
*/
|
||||
protected $rest_base = 'admin/notes';
|
||||
|
||||
/**
|
||||
* Allowed promo notes for experimental-activate-promo.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $allowed_promo_notes = array(
|
||||
'wcpay-promo-2022-us-incentive-20-off',
|
||||
);
|
||||
|
||||
/**
|
||||
* Register the routes for admin notes.
|
||||
*/
|
||||
@@ -458,9 +449,17 @@ class Notes extends \WC_REST_CRUD_Controller {
|
||||
* @return WP_REST_Request|WP_Error
|
||||
*/
|
||||
public function activate_promo_note( $request ) {
|
||||
/**
|
||||
* Filter allowed promo notes for experimental-activate-promo.
|
||||
*
|
||||
* @param array $promo_notes Array of allowed promo notes.
|
||||
* @since 7.8.0
|
||||
*/
|
||||
$allowed_promo_notes = apply_filters( 'woocommerce_admin_allowed_promo_notes', [] );
|
||||
|
||||
$promo_note_name = $request->get_param( 'promo_note_name' );
|
||||
|
||||
if ( ! in_array( $promo_note_name, $this->allowed_promo_notes, true ) ) {
|
||||
if ( ! in_array( $promo_note_name, $allowed_promo_notes, true ) ) {
|
||||
return new \WP_Error(
|
||||
'woocommerce_note_invalid_promo_note_name',
|
||||
__( 'Please provide a valid promo note name.', 'woocommerce' ),
|
||||
|
||||
@@ -10,6 +10,11 @@ namespace Automattic\WooCommerce\Admin\API;
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Internal\Admin\RemoteFreeExtensions\Init as RemoteFreeExtensions;
|
||||
use WC_REST_Data_Controller;
|
||||
use WP_Error;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
use WP_REST_Server;
|
||||
|
||||
/**
|
||||
* Onboarding Payments Controller.
|
||||
@@ -17,7 +22,7 @@ use Automattic\WooCommerce\Internal\Admin\RemoteFreeExtensions\Init as RemoteFre
|
||||
* @internal
|
||||
* @extends WC_REST_Data_Controller
|
||||
*/
|
||||
class OnboardingFreeExtensions extends \WC_REST_Data_Controller {
|
||||
class OnboardingFreeExtensions extends WC_REST_Data_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
@@ -42,7 +47,7 @@ class OnboardingFreeExtensions extends \WC_REST_Data_Controller {
|
||||
'/' . $this->rest_base,
|
||||
array(
|
||||
array(
|
||||
'methods' => \WP_REST_Server::READABLE,
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_available_extensions' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
),
|
||||
@@ -59,7 +64,7 @@ class OnboardingFreeExtensions extends \WC_REST_Data_Controller {
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( ! wc_rest_check_manager_permissions( 'settings', 'read' ) ) {
|
||||
return new \WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -68,12 +73,61 @@ class OnboardingFreeExtensions extends \WC_REST_Data_Controller {
|
||||
/**
|
||||
* Return available payment methods.
|
||||
*
|
||||
* @param \WP_REST_Request $request Request data.
|
||||
* @param WP_REST_Request $request Request data.
|
||||
*
|
||||
* @return \WP_Error|\WP_REST_Response
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function get_available_extensions( $request ) {
|
||||
return RemoteFreeExtensions::get_extensions();
|
||||
$extensions = RemoteFreeExtensions::get_extensions();
|
||||
/**
|
||||
* Allows removing Jetpack suggestions from WooCommerce Admin when false.
|
||||
*
|
||||
* In this instance it is removed from the list of extensions suggested in the Onboarding Profiler. This list is first retrieved from the WooCommerce.com API, then if a plugin with the 'jetpack' slug is found, it is removed.
|
||||
*
|
||||
* @since 7.8
|
||||
*/
|
||||
if ( false === apply_filters( 'woocommerce_suggest_jetpack', true ) ) {
|
||||
foreach ( $extensions as &$extension ) {
|
||||
$extension['plugins'] = array_filter(
|
||||
$extension['plugins'],
|
||||
function( $plugin ) {
|
||||
return 'jetpack' !== $plugin->key;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$extensions = $this->replace_jetpack_with_jetpack_boost_for_treatment( $extensions );
|
||||
|
||||
return new WP_REST_Response( $extensions );
|
||||
}
|
||||
|
||||
private function replace_jetpack_with_jetpack_boost_for_treatment( array $extensions ) {
|
||||
$is_treatment = \WooCommerce\Admin\Experimental_Abtest::in_treatment( 'woocommerce_jetpack_copy' );
|
||||
|
||||
if ( ! $is_treatment ) {
|
||||
return $extensions;
|
||||
}
|
||||
|
||||
$has_core_profiler = array_search( 'obw/core-profiler', array_column( $extensions, 'key' ) );
|
||||
|
||||
if ( $has_core_profiler === false ) {
|
||||
return $extensions;
|
||||
}
|
||||
|
||||
$has_jetpack = array_search( 'jetpack', array_column( $extensions[ $has_core_profiler ]['plugins'], 'key' ) );
|
||||
|
||||
if ( $has_jetpack === false ) {
|
||||
return $extensions;
|
||||
}
|
||||
|
||||
$jetpack = &$extensions[ $has_core_profiler ]['plugins'][ $has_jetpack ];
|
||||
$jetpack->key = 'jetpack-boost';
|
||||
$jetpack->name = 'Jetpack Boost';
|
||||
$jetpack->label = __( 'Optimize store performance with Jetpack Boost', 'woocommerce' );
|
||||
$jetpack->description = __( 'Speed up your store and improve your SEO with performance-boosting tools from Jetpack. Learn more', 'woocommerce' );
|
||||
$jetpack->learn_more_link = 'https://jetpack.com/boost/';
|
||||
|
||||
return $extensions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,414 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API Onboarding Profile Controller
|
||||
*
|
||||
* Handles requests to /onboarding/profile
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\API;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use ActionScheduler;
|
||||
use Automattic\Jetpack\Connection\Manager;
|
||||
use Automattic\Jetpack\Constants;
|
||||
use Automattic\WooCommerce\Admin\PluginsHelper;
|
||||
use Automattic\WooCommerce\Admin\PluginsInstallLoggers\AsynPluginsInstallLogger;
|
||||
use WC_REST_Data_Controller;
|
||||
use WP_Error;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* Onboarding Plugins controller.
|
||||
*
|
||||
* @internal
|
||||
* @extends WC_REST_Data_Controller
|
||||
*/
|
||||
class OnboardingPlugins extends WC_REST_Data_Controller {
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc-admin';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'onboarding/plugins';
|
||||
|
||||
/**
|
||||
* Register routes.
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/install-and-activate-async',
|
||||
array(
|
||||
array(
|
||||
'methods' => 'POST',
|
||||
'callback' => array( $this, 'install_and_activate_async' ),
|
||||
'permission_callback' => array( $this, 'can_install_and_activate_plugins' ),
|
||||
'args' => array(
|
||||
'plugins' => array(
|
||||
'description' => 'A list of plugins to install',
|
||||
'type' => 'array',
|
||||
'items' => 'string',
|
||||
'sanitize_callback' => function ( $value ) {
|
||||
return array_map(
|
||||
function ( $value ) {
|
||||
return sanitize_text_field( $value );
|
||||
},
|
||||
$value
|
||||
);
|
||||
},
|
||||
'required' => true,
|
||||
),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_install_async_schema' ),
|
||||
)
|
||||
);
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/install-and-activate',
|
||||
array(
|
||||
array(
|
||||
'methods' => 'POST',
|
||||
'callback' => array( $this, 'install_and_activate' ),
|
||||
'permission_callback' => array( $this, 'can_install_and_activate_plugins' ),
|
||||
|
||||
),
|
||||
'schema' => array( $this, 'get_install_activate_schema' ),
|
||||
)
|
||||
);
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/scheduled-installs/(?P<job_id>\w+)',
|
||||
array(
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this, 'get_scheduled_installs' ),
|
||||
'permission_callback' => array( $this, 'can_install_plugins' ),
|
||||
),
|
||||
'schema' => array( $this, 'get_install_async_schema' ),
|
||||
)
|
||||
);
|
||||
|
||||
// This is an experimental endpoint and is subject to change in the future.
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/jetpack-authorization-url',
|
||||
array(
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this, 'get_jetpack_authorization_url' ),
|
||||
'permission_callback' => array( $this, 'can_install_plugins' ),
|
||||
'args' => array(
|
||||
'redirect_url' => array(
|
||||
'description' => 'The URL to redirect to after authorization',
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
'required' => true,
|
||||
),
|
||||
'from' => array(
|
||||
'description' => 'from value for the jetpack authorization page',
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
'required' => false,
|
||||
'default' => 'woocommerce-onboarding',
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
/*
|
||||
* This is a temporary solution to override /jetpack/v4/connection/data endpoint
|
||||
* registered by Jetpack Connection when Jetpack is not installed.
|
||||
*
|
||||
* For more details, see https://github.com/woocommerce/woocommerce/issues/38979
|
||||
*/
|
||||
if ( Constants::get_constant( 'JETPACK__VERSION' ) === null && wp_is_mobile() ) {
|
||||
register_rest_route(
|
||||
'jetpack/v4',
|
||||
'/connection/data',
|
||||
array(
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
'permission_callback' => '__return_true',
|
||||
'callback' => function() {
|
||||
return new WP_REST_Response( null, 404 );
|
||||
},
|
||||
),
|
||||
),
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install and activate a plugin.
|
||||
*
|
||||
* @param WP_REST_Request $request WP Request object.
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function install_and_activate( WP_REST_Request $request ) {
|
||||
$response = array();
|
||||
$response['install'] = PluginsHelper::install_plugins( $request->get_param( 'plugins' ) );
|
||||
$response['activate'] = PluginsHelper::activate_plugins( $response['install']['installed'] );
|
||||
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue plugin install request.
|
||||
*
|
||||
* @param WP_REST_Request $request WP_REST_Request object.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function install_and_activate_async( WP_REST_Request $request ) {
|
||||
$plugins = $request->get_param( 'plugins' );
|
||||
$job_id = uniqid();
|
||||
|
||||
WC()->queue()->add( 'woocommerce_plugins_install_and_activate_async_callback', array( $plugins, $job_id ) );
|
||||
|
||||
$plugin_status = array();
|
||||
foreach ( $plugins as $plugin ) {
|
||||
$plugin_status[ $plugin ] = array(
|
||||
'status' => 'pending',
|
||||
'errors' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'job_id' => $job_id,
|
||||
'status' => 'pending',
|
||||
'plugins' => $plugin_status,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current status of given job.
|
||||
*
|
||||
* @param WP_REST_Request $request WP_REST_Request object.
|
||||
*
|
||||
* @return array|WP_REST_Response
|
||||
*/
|
||||
public function get_scheduled_installs( WP_REST_Request $request ) {
|
||||
$job_id = $request->get_param( 'job_id' );
|
||||
|
||||
$actions = WC()->queue()->search(
|
||||
array(
|
||||
'hook' => 'woocommerce_plugins_install_and_activate_async_callback',
|
||||
'search' => $job_id,
|
||||
'orderby' => 'date',
|
||||
'order' => 'DESC',
|
||||
)
|
||||
);
|
||||
|
||||
$actions = array_filter(
|
||||
PluginsHelper::get_action_data( $actions ),
|
||||
function( $action ) use ( $job_id ) {
|
||||
return $action['job_id'] === $job_id;
|
||||
}
|
||||
);
|
||||
|
||||
if ( empty( $actions ) ) {
|
||||
return new WP_REST_Response( null, 404 );
|
||||
}
|
||||
|
||||
$response = array(
|
||||
'job_id' => $actions[0]['job_id'],
|
||||
'status' => $actions[0]['status'],
|
||||
);
|
||||
|
||||
$option = get_option( 'woocommerce_onboarding_plugins_install_and_activate_async_' . $job_id );
|
||||
if ( isset( $option['plugins'] ) ) {
|
||||
$response['plugins'] = $option['plugins'];
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return Jetpack authorization URL.
|
||||
*
|
||||
* @param WP_REST_Request $request WP_REST_Request object.
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception If there is an error registering the site.
|
||||
*/
|
||||
public function get_jetpack_authorization_url( WP_REST_Request $request ) {
|
||||
$manager = new Manager( 'woocommerce' );
|
||||
$errors = new WP_Error();
|
||||
|
||||
// Register the site to wp.com.
|
||||
if ( ! $manager->is_connected() ) {
|
||||
$result = $manager->try_registration();
|
||||
if ( is_wp_error( $result ) ) {
|
||||
$errors->add( $result->get_error_code(), $result->get_error_message() );
|
||||
}
|
||||
}
|
||||
|
||||
$redirect_url = $request->get_param( 'redirect_url' );
|
||||
$calypso_env = defined( 'WOOCOMMERCE_CALYPSO_ENVIRONMENT' ) && in_array( WOOCOMMERCE_CALYPSO_ENVIRONMENT, [ 'development', 'wpcalypso', 'horizon', 'stage' ], true ) ? WOOCOMMERCE_CALYPSO_ENVIRONMENT : 'production';
|
||||
|
||||
return [
|
||||
'success' => ! $errors->has_errors(),
|
||||
'errors' => $errors->get_error_messages(),
|
||||
'url' => add_query_arg(
|
||||
[
|
||||
'from' => $request->get_param( 'from' ),
|
||||
'calypso_env' => $calypso_env,
|
||||
],
|
||||
$manager->get_authorization_url( null, $redirect_url )
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the current user has permission to install plugins
|
||||
*
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function can_install_plugins() {
|
||||
if ( ! current_user_can( 'install_plugins' ) ) {
|
||||
return new WP_Error(
|
||||
'woocommerce_rest_cannot_update',
|
||||
__( 'Sorry, you cannot manage plugins.', 'woocommerce' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the current user has permission to install and activate plugins
|
||||
*
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function can_install_and_activate_plugins() {
|
||||
if ( ! current_user_can( 'install_plugins' ) || ! current_user_can( 'activate_plugins' ) ) {
|
||||
return new WP_Error(
|
||||
'woocommerce_rest_cannot_update',
|
||||
__( 'Sorry, you cannot manage plugins.', 'woocommerce' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON Schema for both install-async and scheduled-installs endpoints.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_install_async_schema() {
|
||||
return array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'Install Async Schema',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'job_id' => 'integer',
|
||||
'status' => array(
|
||||
'type' => 'string',
|
||||
'enum' => array( 'pending', 'complete', 'failed' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON Schema for install-and-activate endpoint.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_install_activate_schema() {
|
||||
$error_schema = array(
|
||||
'type' => 'object',
|
||||
'patternProperties' => array(
|
||||
'^.*$' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
'items' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
);
|
||||
|
||||
$install_schema = array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'installed' => array(
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
'results' => array(
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
'errors' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'errors' => $error_schema,
|
||||
'error_data' => $error_schema,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$activate_schema = array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'activated' => array(
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
'active' => array(
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
'errors' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'errors' => $error_schema,
|
||||
'error_data' => $error_schema,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'Install and Activate Schema',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'install' => $install_schema,
|
||||
'activate' => $activate_schema,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -261,21 +261,21 @@ class OnboardingProfile extends \WC_REST_Data_Controller {
|
||||
*/
|
||||
public static function get_profile_properties() {
|
||||
$properties = array(
|
||||
'completed' => array(
|
||||
'completed' => array(
|
||||
'type' => 'boolean',
|
||||
'description' => __( 'Whether or not the profile was completed.', 'woocommerce' ),
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
),
|
||||
'skipped' => array(
|
||||
'skipped' => array(
|
||||
'type' => 'boolean',
|
||||
'description' => __( 'Whether or not the profile was skipped.', 'woocommerce' ),
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
),
|
||||
'industry' => array(
|
||||
'industry' => array(
|
||||
'type' => 'array',
|
||||
'description' => __( 'Industry.', 'woocommerce' ),
|
||||
'context' => array( 'view' ),
|
||||
@@ -285,7 +285,7 @@ class OnboardingProfile extends \WC_REST_Data_Controller {
|
||||
'type' => 'object',
|
||||
),
|
||||
),
|
||||
'product_types' => array(
|
||||
'product_types' => array(
|
||||
'type' => 'array',
|
||||
'description' => __( 'Types of products sold.', 'woocommerce' ),
|
||||
'context' => array( 'view' ),
|
||||
@@ -297,7 +297,7 @@ class OnboardingProfile extends \WC_REST_Data_Controller {
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
'product_count' => array(
|
||||
'product_count' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'Number of products to be added.', 'woocommerce' ),
|
||||
'context' => array( 'view' ),
|
||||
@@ -311,7 +311,7 @@ class OnboardingProfile extends \WC_REST_Data_Controller {
|
||||
'1000+',
|
||||
),
|
||||
),
|
||||
'selling_venues' => array(
|
||||
'selling_venues' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'Other places the store is selling products.', 'woocommerce' ),
|
||||
'context' => array( 'view' ),
|
||||
@@ -325,7 +325,7 @@ class OnboardingProfile extends \WC_REST_Data_Controller {
|
||||
'other-woocommerce',
|
||||
),
|
||||
),
|
||||
'number_employees' => array(
|
||||
'number_employees' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'Number of employees of the store.', 'woocommerce' ),
|
||||
'context' => array( 'view' ),
|
||||
@@ -340,7 +340,7 @@ class OnboardingProfile extends \WC_REST_Data_Controller {
|
||||
'not specified',
|
||||
),
|
||||
),
|
||||
'revenue' => array(
|
||||
'revenue' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'Current annual revenue of the store.', 'woocommerce' ),
|
||||
'context' => array( 'view' ),
|
||||
@@ -356,7 +356,7 @@ class OnboardingProfile extends \WC_REST_Data_Controller {
|
||||
'rather-not-say',
|
||||
),
|
||||
),
|
||||
'other_platform' => array(
|
||||
'other_platform' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'Name of other platform used to sell.', 'woocommerce' ),
|
||||
'context' => array( 'view' ),
|
||||
@@ -374,14 +374,14 @@ class OnboardingProfile extends \WC_REST_Data_Controller {
|
||||
'other',
|
||||
),
|
||||
),
|
||||
'other_platform_name' => array(
|
||||
'other_platform_name' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'Name of other platform used to sell (not listed).', 'woocommerce' ),
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
),
|
||||
'business_extensions' => array(
|
||||
'business_extensions' => array(
|
||||
'type' => 'array',
|
||||
'description' => __( 'Extra business extensions to install.', 'woocommerce' ),
|
||||
'context' => array( 'view' ),
|
||||
@@ -391,6 +391,7 @@ class OnboardingProfile extends \WC_REST_Data_Controller {
|
||||
'items' => array(
|
||||
'enum' => array(
|
||||
'jetpack',
|
||||
'jetpack-boost',
|
||||
'woocommerce-services',
|
||||
'woocommerce-payments',
|
||||
'mailchimp-for-woocommerce',
|
||||
@@ -401,12 +402,12 @@ class OnboardingProfile extends \WC_REST_Data_Controller {
|
||||
'mailpoet',
|
||||
'codistoconnect',
|
||||
'tiktok-for-business',
|
||||
'tiktok-for-business:alt'
|
||||
'tiktok-for-business:alt',
|
||||
),
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
'theme' => array(
|
||||
'theme' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'Selected store theme.', 'woocommerce' ),
|
||||
'context' => array( 'view' ),
|
||||
@@ -414,41 +415,48 @@ class OnboardingProfile extends \WC_REST_Data_Controller {
|
||||
'sanitize_callback' => 'sanitize_title_with_dashes',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
),
|
||||
'setup_client' => array(
|
||||
'setup_client' => array(
|
||||
'type' => 'boolean',
|
||||
'description' => __( 'Whether or not this store was setup for a client.', 'woocommerce' ),
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
),
|
||||
'wccom_connected' => array(
|
||||
'wccom_connected' => array(
|
||||
'type' => 'boolean',
|
||||
'description' => __( 'Whether or not the store was connected to WooCommerce.com during the extension flow.', 'woocommerce' ),
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
),
|
||||
'is_agree_marketing' => array(
|
||||
'is_agree_marketing' => array(
|
||||
'type' => 'boolean',
|
||||
'description' => __( 'Whether or not this store agreed to receiving marketing contents from WooCommerce.com.', 'woocommerce' ),
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
),
|
||||
'store_email' => array(
|
||||
'store_email' => array(
|
||||
'type' => 'string',
|
||||
'description' => __( 'Store email address.', 'woocommerce' ),
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
'validate_callback' => array( __CLASS__, 'rest_validate_marketing_email' ),
|
||||
),
|
||||
'is_store_country_set' => array(
|
||||
'is_store_country_set' => array(
|
||||
'type' => 'boolean',
|
||||
'description' => __( 'Whether or not this store country is set via onboarding profiler.', 'woocommerce' ),
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
),
|
||||
'is_plugins_page_skipped' => array(
|
||||
'type' => 'boolean',
|
||||
'description' => __( 'Whether or not plugins step in core profiler was skipped.', 'woocommerce' ),
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
),
|
||||
);
|
||||
|
||||
return apply_filters( 'woocommerce_rest_onboarding_profile_properties', $properties );
|
||||
|
||||
@@ -39,7 +39,7 @@ class OnboardingTasks extends \WC_REST_Data_Controller {
|
||||
/**
|
||||
* Duration to milisecond mapping.
|
||||
*
|
||||
* @var string
|
||||
* @var array
|
||||
*/
|
||||
protected $duration_to_ms = array(
|
||||
'day' => DAY_IN_SECONDS * 1000,
|
||||
@@ -295,10 +295,14 @@ class OnboardingTasks extends \WC_REST_Data_Controller {
|
||||
/**
|
||||
* Check if a given request has access to manage woocommerce.
|
||||
*
|
||||
* @deprecated 7.8.0 snooze task is deprecated.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function snooze_task_permissions_check( $request ) {
|
||||
wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '7.8.0' );
|
||||
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
return new \WP_Error( 'woocommerce_rest_cannot_create', __( 'Sorry, you are not allowed to snooze onboarding tasks.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
@@ -355,7 +359,7 @@ class OnboardingTasks extends \WC_REST_Data_Controller {
|
||||
* @return WP_REST_Response|WP_Error
|
||||
*/
|
||||
public static function create_product_from_template( $request ) {
|
||||
$template_name = $request->get_param( 'template_name' );
|
||||
$template_name = basename( $request->get_param( 'template_name' ) );
|
||||
$template_path = __DIR__ . '/Templates/' . $template_name . '_product.csv';
|
||||
$template_path = apply_filters( 'woocommerce_product_template_csv_file_path', $template_path, $template_name );
|
||||
|
||||
@@ -758,6 +762,7 @@ class OnboardingTasks extends \WC_REST_Data_Controller {
|
||||
|
||||
if ( ! $task && $id ) {
|
||||
$task = new DeprecatedExtendedTask(
|
||||
null,
|
||||
array(
|
||||
'id' => $id,
|
||||
'is_dismissable' => true,
|
||||
@@ -791,6 +796,7 @@ class OnboardingTasks extends \WC_REST_Data_Controller {
|
||||
|
||||
if ( ! $task && $id ) {
|
||||
$task = new DeprecatedExtendedTask(
|
||||
null,
|
||||
array(
|
||||
'id' => $id,
|
||||
'is_dismissable' => true,
|
||||
@@ -816,11 +822,15 @@ class OnboardingTasks extends \WC_REST_Data_Controller {
|
||||
/**
|
||||
* Snooze an onboarding task.
|
||||
*
|
||||
* @deprecated 7.8.0 snooze task is deprecated.
|
||||
*
|
||||
* @param WP_REST_Request $request Request data.
|
||||
*
|
||||
* @return WP_REST_Response|WP_Error
|
||||
*/
|
||||
public function snooze_task( $request ) {
|
||||
wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '7.8.0' );
|
||||
|
||||
$task_id = $request->get_param( 'id' );
|
||||
$task_list_id = $request->get_param( 'task_list_id' );
|
||||
$duration = $request->get_param( 'duration' );
|
||||
@@ -829,6 +839,7 @@ class OnboardingTasks extends \WC_REST_Data_Controller {
|
||||
|
||||
if ( ! $task && $task_id ) {
|
||||
$task = new DeprecatedExtendedTask(
|
||||
null,
|
||||
array(
|
||||
'id' => $task_id,
|
||||
'is_snoozeable' => true,
|
||||
@@ -853,15 +864,20 @@ class OnboardingTasks extends \WC_REST_Data_Controller {
|
||||
/**
|
||||
* Undo snooze of a single task.
|
||||
*
|
||||
* @deprecated 7.8.0 undo snooze task is deprecated.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Request|WP_Error
|
||||
*/
|
||||
public function undo_snooze_task( $request ) {
|
||||
wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '7.8.0' );
|
||||
|
||||
$id = $request->get_param( 'id' );
|
||||
$task = TaskLists::get_task( $id );
|
||||
|
||||
if ( ! $task && $id ) {
|
||||
$task = new DeprecatedExtendedTask(
|
||||
null,
|
||||
array(
|
||||
'id' => $id,
|
||||
'is_snoozeable' => true,
|
||||
@@ -949,6 +965,7 @@ class OnboardingTasks extends \WC_REST_Data_Controller {
|
||||
|
||||
if ( ! $task && $id ) {
|
||||
$task = new DeprecatedExtendedTask(
|
||||
null,
|
||||
array(
|
||||
'id' => $id,
|
||||
)
|
||||
|
||||
@@ -69,14 +69,22 @@ class Options extends \WC_REST_Data_Controller {
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
$params = explode( ',', $request['options'] );
|
||||
$params = ( isset( $request['options'] ) && is_string( $request['options'] ) ) ? explode( ',', $request['options'] ) : array();
|
||||
|
||||
if ( ! isset( $request['options'] ) || ! is_array( $params ) ) {
|
||||
if ( ! $params ) {
|
||||
return new \WP_Error( 'woocommerce_rest_cannot_view', __( 'You must supply an array of options.', 'woocommerce' ), 500 );
|
||||
}
|
||||
|
||||
foreach ( $params as $option ) {
|
||||
if ( ! $this->user_has_permission( $option, $request ) ) {
|
||||
if ( 'production' !== wp_get_environment_type() ) {
|
||||
return new \WP_Error(
|
||||
'woocommerce_rest_cannot_view',
|
||||
__( 'Sorry, you cannot view these options, please remember to update the option permissions in Options API to allow viewing these options in non-production environments.', 'woocommerce' ),
|
||||
array( 'status' => rest_authorization_required_code() )
|
||||
);
|
||||
}
|
||||
|
||||
return new \WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view these options.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
}
|
||||
@@ -99,7 +107,12 @@ class Options extends \WC_REST_Data_Controller {
|
||||
return $permissions[ $option ];
|
||||
}
|
||||
|
||||
wc_deprecated_function( 'Automattic\WooCommerce\Admin\API\Options::' . ( $is_update ? 'update_options' : 'get_options' ), '3.1' );
|
||||
// Don't allow to update options in non-production environments if the option is not whitelisted. This is to force developers to update the option permissions when adding new options.
|
||||
if ( 'production' !== wp_get_environment_type() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wc_deprecated_function( 'Automattic\WooCommerce\Admin\API\Options::' . ( $is_update ? 'update_options' : 'get_options' ), '6.3' );
|
||||
return current_user_can( 'manage_options' );
|
||||
}
|
||||
|
||||
@@ -133,7 +146,7 @@ class Options extends \WC_REST_Data_Controller {
|
||||
*/
|
||||
public function get_option_permissions( $request ) {
|
||||
$permissions = self::get_default_option_permissions();
|
||||
return apply_filters_deprecated( 'woocommerce_rest_api_option_permissions', array( $permissions, $request ), '3.1.0' );
|
||||
return apply_filters_deprecated( 'woocommerce_rest_api_option_permissions', array( $permissions, $request ), '6.3.0' );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,6 +196,26 @@ class Options extends \WC_REST_Data_Controller {
|
||||
'woocommerce_admin_created_default_shipping_zones',
|
||||
'woocommerce_admin_reviewed_default_shipping_zones',
|
||||
'woocommerce_admin_reviewed_store_location_settings',
|
||||
'woocommerce_ces_product_feedback_shown',
|
||||
'woocommerce_marketing_overview_multichannel_banner_dismissed',
|
||||
'woocommerce_dimension_unit',
|
||||
'woocommerce_weight_unit',
|
||||
'woocommerce_product_editor_show_feedback_bar',
|
||||
'woocommerce_product_tour_modal_hidden',
|
||||
'woocommerce_block_product_tour_shown',
|
||||
'woocommerce_revenue_report_date_tour_shown',
|
||||
'woocommerce_date_type',
|
||||
'date_format',
|
||||
'time_format',
|
||||
'woocommerce_onboarding_profile',
|
||||
'woocommerce_default_country',
|
||||
'blogname',
|
||||
'wcpay_welcome_page_incentives_dismissed',
|
||||
'wcpay_welcome_page_viewed_timestamp',
|
||||
'wcpay_welcome_page_exit_survey_more_info_needed_timestamp',
|
||||
// WC Test helper options.
|
||||
'wc-admin-test-helper-rest-api-filters',
|
||||
'wc_admin_helper_feature_values',
|
||||
);
|
||||
|
||||
$theme_permissions = array(
|
||||
@@ -203,13 +236,13 @@ class Options extends \WC_REST_Data_Controller {
|
||||
* @return array Options object with option values.
|
||||
*/
|
||||
public function get_options( $request ) {
|
||||
$params = explode( ',', $request['options'] );
|
||||
$options = array();
|
||||
|
||||
if ( ! is_array( $params ) ) {
|
||||
return array();
|
||||
if ( empty( $request['options'] ) || ! is_string( $request['options'] ) ) {
|
||||
return $options;
|
||||
}
|
||||
|
||||
$params = explode( ',', $request['options'] );
|
||||
foreach ( $params as $option ) {
|
||||
$options[ $option ] = get_option( $option );
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ class PaymentGatewaySuggestions extends \WC_REST_Data_Controller {
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'payment-gateway-suggestions',
|
||||
'type' => 'array',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'content' => array(
|
||||
'description' => __( 'Suggestion description.', 'woocommerce' ),
|
||||
|
||||
@@ -597,7 +597,7 @@ class Plugins extends \WC_REST_Data_Controller {
|
||||
*/
|
||||
public function connect_wcpay() {
|
||||
if ( ! class_exists( 'WC_Payments_Account' ) ) {
|
||||
return new \WP_Error( 'woocommerce_rest_helper_connect', __( 'There was an error communicating with the WooCommerce Payments plugin.', 'woocommerce' ), 500 );
|
||||
return new \WP_Error( 'woocommerce_rest_helper_connect', __( 'There was an error communicating with the WooPayments plugin.', 'woocommerce' ), 500 );
|
||||
}
|
||||
|
||||
$connect_url = add_query_arg(
|
||||
|
||||
@@ -129,7 +129,7 @@ class ProductVariations extends \WC_REST_Product_Variations_Controller {
|
||||
unset( $args['s'] );
|
||||
}
|
||||
|
||||
// Retreive variations without specifying a parent product.
|
||||
// Retrieve variations without specifying a parent product.
|
||||
if ( "/{$this->namespace}/variations" === $request->get_route() ) {
|
||||
unset( $args['post_parent'] );
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ class Products extends \WC_REST_Products_Controller {
|
||||
/**
|
||||
* Check whether the request is for products low in stock.
|
||||
*
|
||||
* It matches requests with paramaters:
|
||||
* It matches requests with parameters:
|
||||
*
|
||||
* low_in_stock = true
|
||||
* page = 1
|
||||
|
||||
@@ -77,28 +77,13 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
$out_data[] = $this->prepare_response_for_collection( $item );
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $out_data );
|
||||
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||
|
||||
$page = $report_data->page_no;
|
||||
$max_pages = $report_data->pages;
|
||||
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
if ( $page > 1 ) {
|
||||
$prev_page = $page - 1;
|
||||
if ( $prev_page > $max_pages ) {
|
||||
$prev_page = $max_pages;
|
||||
}
|
||||
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
if ( $max_pages > $page ) {
|
||||
$next_page = $page + 1;
|
||||
$next_link = add_query_arg( 'page', $next_page, $base );
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
return $this->add_pagination_headers(
|
||||
$request,
|
||||
$out_data,
|
||||
(int) $report_data->total,
|
||||
(int) $report_data->page_no,
|
||||
(int) $report_data->pages
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,9 +193,9 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = array();
|
||||
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
$params['page'] = array(
|
||||
$params = array();
|
||||
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
$params['page'] = array(
|
||||
'description' => __( 'Current page of the collection.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 1,
|
||||
@@ -218,7 +203,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'minimum' => 1,
|
||||
);
|
||||
$params['per_page'] = array(
|
||||
$params['per_page'] = array(
|
||||
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 10,
|
||||
@@ -227,26 +212,26 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['after'] = array(
|
||||
$params['after'] = array(
|
||||
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['before'] = array(
|
||||
$params['before'] = array(
|
||||
'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['order'] = array(
|
||||
$params['order'] = array(
|
||||
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'desc',
|
||||
'enum' => array( 'asc', 'desc' ),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['orderby'] = array(
|
||||
$params['orderby'] = array(
|
||||
'description' => __( 'Sort collection by object attribute.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'category_id',
|
||||
@@ -260,7 +245,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['interval'] = array(
|
||||
$params['interval'] = array(
|
||||
'description' => __( 'Time interval to use for buckets in the returned data.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'week',
|
||||
@@ -274,7 +259,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['status_is'] = array(
|
||||
$params['status_is'] = array(
|
||||
'description' => __( 'Limit result set to items that have the specified order status.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_slug_list',
|
||||
@@ -284,7 +269,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'type' => 'string',
|
||||
),
|
||||
);
|
||||
$params['status_is_not'] = array(
|
||||
$params['status_is_not'] = array(
|
||||
'description' => __( 'Limit result set to items that don\'t have the specified order status.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_slug_list',
|
||||
@@ -294,7 +279,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'type' => 'string',
|
||||
),
|
||||
);
|
||||
$params['categories'] = array(
|
||||
$params['categories'] = array(
|
||||
'description' => __( 'Limit result set to all items that have the specified term assigned in the categories taxonomy.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
@@ -303,7 +288,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'type' => 'integer',
|
||||
),
|
||||
);
|
||||
$params['extended_info'] = array(
|
||||
$params['extended_info'] = array(
|
||||
'description' => __( 'Add additional piece of info about each category to the report.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'default' => false,
|
||||
|
||||
@@ -9,27 +9,15 @@ namespace Automattic\WooCommerce\Admin\API\Reports;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Admin\API\Reports\GenericController;
|
||||
|
||||
/**
|
||||
* REST API Reports controller class.
|
||||
*
|
||||
* @internal
|
||||
* @extends WC_REST_Reports_Controller
|
||||
* @extends GenericController
|
||||
*/
|
||||
class Controller extends \WC_REST_Reports_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc-analytics';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'reports';
|
||||
class Controller extends GenericController {
|
||||
|
||||
/**
|
||||
* Get all reports.
|
||||
|
||||
@@ -9,22 +9,18 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Coupons;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Admin\API\Reports\GenericController;
|
||||
use Automattic\WooCommerce\Admin\API\Reports\ExportableInterface;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* REST API Reports coupons controller class.
|
||||
*
|
||||
* @internal
|
||||
* @extends WC_REST_Reports_Controller
|
||||
* @extends GenericController
|
||||
*/
|
||||
class Controller extends \WC_REST_Reports_Controller implements ExportableInterface {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc-analytics';
|
||||
class Controller extends GenericController implements ExportableInterface {
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
@@ -71,46 +67,24 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
$data[] = $this->prepare_response_for_collection( $item );
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $data );
|
||||
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||
|
||||
$page = $report_data->page_no;
|
||||
$max_pages = $report_data->pages;
|
||||
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
if ( $page > 1 ) {
|
||||
$prev_page = $page - 1;
|
||||
if ( $prev_page > $max_pages ) {
|
||||
$prev_page = $max_pages;
|
||||
}
|
||||
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
if ( $max_pages > $page ) {
|
||||
$next_page = $page + 1;
|
||||
$next_link = add_query_arg( 'page', $next_page, $base );
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
return $this->add_pagination_headers(
|
||||
$request,
|
||||
$data,
|
||||
(int) $report_data->total,
|
||||
(int) $report_data->page_no,
|
||||
(int) $report_data->pages
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a report object for serialization.
|
||||
*
|
||||
* @param stdClass $report Report data.
|
||||
* @param array $report Report data.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function prepare_item_for_response( $report, $request ) {
|
||||
$data = $report;
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
$response = parent::prepare_item_for_response( $report, $request );
|
||||
$response->add_links( $this->prepare_links( $report ) );
|
||||
|
||||
/**
|
||||
@@ -221,57 +195,15 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = array();
|
||||
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
$params['page'] = array(
|
||||
'description' => __( 'Current page of the collection.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 1,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'minimum' => 1,
|
||||
$params = parent::get_collection_params();
|
||||
$params['orderby']['default'] = 'coupon_id';
|
||||
$params['orderby']['enum'] = array(
|
||||
'coupon_id',
|
||||
'code',
|
||||
'amount',
|
||||
'orders_count',
|
||||
);
|
||||
$params['per_page'] = array(
|
||||
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 10,
|
||||
'minimum' => 1,
|
||||
'maximum' => 100,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['after'] = array(
|
||||
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['before'] = array(
|
||||
'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['order'] = array(
|
||||
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'desc',
|
||||
'enum' => array( 'asc', 'desc' ),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['orderby'] = array(
|
||||
'description' => __( 'Sort collection by object attribute.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'coupon_id',
|
||||
'enum' => array(
|
||||
'coupon_id',
|
||||
'code',
|
||||
'amount',
|
||||
'orders_count',
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['coupons'] = array(
|
||||
$params['coupons'] = array(
|
||||
'description' => __( 'Limit result set to coupons assigned specific coupon IDs.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
@@ -280,19 +212,13 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
'type' => 'integer',
|
||||
),
|
||||
);
|
||||
$params['extended_info'] = array(
|
||||
$params['extended_info'] = array(
|
||||
'description' => __( 'Add additional piece of info about each coupon to the report.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'default' => false,
|
||||
'sanitize_callback' => 'wc_string_to_bool',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['force_cache_refresh'] = array(
|
||||
'description' => __( 'Force retrieval of fresh data instead of from the cache.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'sanitize_callback' => 'wp_validate_boolean',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
@@ -9,22 +9,18 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Coupons\Stats;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Admin\API\Reports\GenericStatsController;
|
||||
use Automattic\WooCommerce\Admin\API\Reports\ParameterException;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* REST API Reports coupons stats controller class.
|
||||
*
|
||||
* @internal
|
||||
* @extends WC_REST_Reports_Controller
|
||||
* @extends GenericStatsController
|
||||
*/
|
||||
class Controller extends \WC_REST_Reports_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc-analytics';
|
||||
class Controller extends GenericStatsController {
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
@@ -82,28 +78,13 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
$out_data['intervals'][] = $this->prepare_response_for_collection( $item );
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $out_data );
|
||||
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||
|
||||
$page = $report_data->page_no;
|
||||
$max_pages = $report_data->pages;
|
||||
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
if ( $page > 1 ) {
|
||||
$prev_page = $page - 1;
|
||||
if ( $prev_page > $max_pages ) {
|
||||
$prev_page = $max_pages;
|
||||
}
|
||||
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
if ( $max_pages > $page ) {
|
||||
$next_page = $page + 1;
|
||||
$next_link = add_query_arg( 'page', $next_page, $base );
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
return $this->add_pagination_headers(
|
||||
$request,
|
||||
$out_data,
|
||||
(int) $report_data->total,
|
||||
(int) $report_data->page_no,
|
||||
(int) $report_data->pages
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,12 +97,7 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
public function prepare_item_for_response( $report, $request ) {
|
||||
$data = get_object_vars( $report );
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
$response = parent::prepare_item_for_response( $data, $request );
|
||||
|
||||
/**
|
||||
* Filter a report returned from the API.
|
||||
@@ -136,12 +112,13 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Report's schema, conforming to JSON Schema.
|
||||
* Get the Report's item properties schema.
|
||||
* Will be used by `get_item_schema` as `totals` and `subtotals`.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$data_values = array(
|
||||
protected function get_item_properties_schema() {
|
||||
return array(
|
||||
'amount' => array(
|
||||
'description' => __( 'Net discount amount.', 'woocommerce' ),
|
||||
'type' => 'number',
|
||||
@@ -165,99 +142,16 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
'indicator' => true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
$segments = array(
|
||||
'segments' => array(
|
||||
'description' => __( 'Reports data grouped by segment condition.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'items' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'segment_id' => array(
|
||||
'description' => __( 'Segment identificator.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'subtotals' => array(
|
||||
'description' => __( 'Interval subtotals.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => $data_values,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$totals = array_merge( $data_values, $segments );
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'report_coupons_stats',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'totals' => array(
|
||||
'description' => __( 'Totals data.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => $totals,
|
||||
),
|
||||
'intervals' => array(
|
||||
'description' => __( 'Reports data grouped by intervals.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'items' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'interval' => array(
|
||||
'description' => __( 'Type of interval.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'enum' => array( 'day', 'week', 'month', 'year' ),
|
||||
),
|
||||
'date_start' => array(
|
||||
'description' => __( "The date the report start, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_start_gmt' => array(
|
||||
'description' => __( 'The date the report start, as GMT.', 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_end' => array(
|
||||
'description' => __( "The date the report end, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_end_gmt' => array(
|
||||
'description' => __( 'The date the report end, as GMT.', 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'subtotals' => array(
|
||||
'description' => __( 'Interval subtotals.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => $totals,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
/**
|
||||
* Get the Report's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$schema = parent::get_item_schema();
|
||||
$schema['title'] = 'report_coupons_stats';
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
@@ -268,71 +162,14 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = array();
|
||||
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
$params['page'] = array(
|
||||
'description' => __( 'Current page of the collection.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 1,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'minimum' => 1,
|
||||
$params = parent::get_collection_params();
|
||||
$params['orderby']['enum'] = array(
|
||||
'date',
|
||||
'amount',
|
||||
'coupons_count',
|
||||
'orders_count',
|
||||
);
|
||||
$params['per_page'] = array(
|
||||
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 10,
|
||||
'minimum' => 1,
|
||||
'maximum' => 100,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['after'] = array(
|
||||
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['before'] = array(
|
||||
'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['order'] = array(
|
||||
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'desc',
|
||||
'enum' => array( 'asc', 'desc' ),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['orderby'] = array(
|
||||
'description' => __( 'Sort collection by object attribute.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'date',
|
||||
'enum' => array(
|
||||
'date',
|
||||
'amount',
|
||||
'coupons_count',
|
||||
'orders_count',
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['interval'] = array(
|
||||
'description' => __( 'Time interval to use for buckets in the returned data.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'week',
|
||||
'enum' => array(
|
||||
'hour',
|
||||
'day',
|
||||
'week',
|
||||
'month',
|
||||
'quarter',
|
||||
'year',
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['coupons'] = array(
|
||||
$params['coupons'] = array(
|
||||
'description' => __( 'Limit result set to coupons assigned specific coupon IDs.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
@@ -341,7 +178,7 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
'type' => 'integer',
|
||||
),
|
||||
);
|
||||
$params['segmentby'] = array(
|
||||
$params['segmentby'] = array(
|
||||
'description' => __( 'Segment the response by additional constraint.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'enum' => array(
|
||||
@@ -352,7 +189,7 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['fields'] = array(
|
||||
$params['fields'] = array(
|
||||
'description' => __( 'Limit stats fields to the specified items.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_slug_list',
|
||||
@@ -361,12 +198,6 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
'type' => 'string',
|
||||
),
|
||||
);
|
||||
$params['force_cache_refresh'] = array(
|
||||
'description' => __( 'Force retrieval of fresh data instead of from the cache.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'sanitize_callback' => 'wp_validate_boolean',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Customers;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Admin\API\Reports\GenericController;
|
||||
use Automattic\WooCommerce\Admin\API\Reports\ExportableTraits;
|
||||
use Automattic\WooCommerce\Admin\API\Reports\ExportableInterface;
|
||||
use Automattic\WooCommerce\Admin\API\Reports\TimeInterval;
|
||||
@@ -17,21 +18,14 @@ use Automattic\WooCommerce\Admin\API\Reports\TimeInterval;
|
||||
* REST API Reports customers controller class.
|
||||
*
|
||||
* @internal
|
||||
* @extends WC_REST_Reports_Controller
|
||||
* @extends GenericController
|
||||
*/
|
||||
class Controller extends \WC_REST_Reports_Controller implements ExportableInterface {
|
||||
class Controller extends GenericController implements ExportableInterface {
|
||||
/**
|
||||
* Exportable traits.
|
||||
*/
|
||||
use ExportableTraits;
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc-analytics';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
@@ -79,6 +73,7 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
$args['customers'] = $request['customers'];
|
||||
$args['users'] = $request['users'];
|
||||
$args['force_cache_refresh'] = $request['force_cache_refresh'];
|
||||
$args['filter_empty'] = $request['filter_empty'];
|
||||
|
||||
$between_params_numeric = array( 'orders_count', 'total_spend', 'avg_order_value' );
|
||||
$normalized_params_numeric = TimeInterval::normalize_between_params( $request, $between_params_numeric, false );
|
||||
@@ -107,28 +102,13 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
$data[] = $this->prepare_response_for_collection( $item );
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $data );
|
||||
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||
|
||||
$page = $report_data->page_no;
|
||||
$max_pages = $report_data->pages;
|
||||
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
if ( $page > 1 ) {
|
||||
$prev_page = $page - 1;
|
||||
if ( $prev_page > $max_pages ) {
|
||||
$prev_page = $max_pages;
|
||||
}
|
||||
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
if ( $max_pages > $page ) {
|
||||
$next_page = $page + 1;
|
||||
$next_link = add_query_arg( 'page', $next_page, $base );
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
return $this->add_pagination_headers(
|
||||
$request,
|
||||
$data,
|
||||
(int) $report_data->total,
|
||||
(int) $report_data->page_no,
|
||||
(int) $report_data->pages
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -325,8 +305,7 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = array();
|
||||
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
$params = parent::get_collection_params();
|
||||
$params['registered_before'] = array(
|
||||
'description' => __( 'Limit response to objects registered before (or at) a given ISO8601 compliant datetime.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
@@ -339,60 +318,19 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['after'] = array(
|
||||
'description' => __( 'Limit response to resources with orders published after a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['before'] = array(
|
||||
'description' => __( 'Limit response to resources with orders published before a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['page'] = array(
|
||||
'description' => __( 'Current page of the collection.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 1,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'minimum' => 1,
|
||||
);
|
||||
$params['per_page'] = array(
|
||||
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 10,
|
||||
'minimum' => 1,
|
||||
'maximum' => 100,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['order'] = array(
|
||||
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'desc',
|
||||
'enum' => array( 'asc', 'desc' ),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['orderby'] = array(
|
||||
'description' => __( 'Sort collection by object attribute.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'date_registered',
|
||||
'enum' => array(
|
||||
'username',
|
||||
'name',
|
||||
'country',
|
||||
'city',
|
||||
'state',
|
||||
'postcode',
|
||||
'date_registered',
|
||||
'date_last_active',
|
||||
'orders_count',
|
||||
'total_spend',
|
||||
'avg_order_value',
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
$params['orderby']['default'] = 'date_registered';
|
||||
$params['orderby']['enum'] = array(
|
||||
'username',
|
||||
'name',
|
||||
'country',
|
||||
'city',
|
||||
'state',
|
||||
'postcode',
|
||||
'date_registered',
|
||||
'date_last_active',
|
||||
'orders_count',
|
||||
'total_spend',
|
||||
'avg_order_value',
|
||||
);
|
||||
$params['match'] = array(
|
||||
'description' => __( 'Indicates whether all the conditions should be true for the resulting set, or if any one of them is sufficient. Match affects the following parameters: status_is, status_is_not, product_includes, product_excludes, coupon_includes, coupon_excludes, customer, categories', 'woocommerce' ),
|
||||
@@ -417,6 +355,7 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
'name',
|
||||
'username',
|
||||
'email',
|
||||
'all',
|
||||
),
|
||||
);
|
||||
$params['name_includes'] = array(
|
||||
@@ -585,11 +524,21 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
'type' => 'integer',
|
||||
),
|
||||
);
|
||||
$params['force_cache_refresh'] = array(
|
||||
'description' => __( 'Force retrieval of fresh data instead of from the cache.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'sanitize_callback' => 'wp_validate_boolean',
|
||||
$params['filter_empty'] = array(
|
||||
'description' => __( 'Filter out results where any of the passed fields are empty', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'items' => array(
|
||||
'type' => 'string',
|
||||
'enum' => array(
|
||||
'email',
|
||||
'name',
|
||||
'country',
|
||||
'city',
|
||||
'state',
|
||||
'postcode',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $params;
|
||||
|
||||
@@ -297,6 +297,7 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
|
||||
'name',
|
||||
'username',
|
||||
'email',
|
||||
'all',
|
||||
);
|
||||
|
||||
if ( ! empty( $query_args['search'] ) ) {
|
||||
@@ -304,6 +305,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
|
||||
|
||||
if ( empty( $query_args['searchby'] ) || 'name' === $query_args['searchby'] || ! in_array( $query_args['searchby'], $search_params, true ) ) {
|
||||
$searchby = "CONCAT_WS( ' ', first_name, last_name )";
|
||||
} elseif ( 'all' === $query_args['searchby'] ) {
|
||||
$searchby = "CONCAT_WS( ' ', first_name, last_name, username, email )";
|
||||
} else {
|
||||
$searchby = $query_args['searchby'];
|
||||
}
|
||||
@@ -311,6 +314,30 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
|
||||
$where_clauses[] = $wpdb->prepare( "{$searchby} LIKE %s", $name_like ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
}
|
||||
|
||||
$filter_empty_params = array(
|
||||
'email',
|
||||
'name',
|
||||
'country',
|
||||
'city',
|
||||
'state',
|
||||
'postcode',
|
||||
);
|
||||
|
||||
if ( ! empty( $query_args['filter_empty'] ) ) {
|
||||
$fields_to_filter_by = array_intersect( $query_args['filter_empty'], $filter_empty_params );
|
||||
if ( in_array( 'name', $fields_to_filter_by, true ) ) {
|
||||
$fields_to_filter_by = array_diff( $fields_to_filter_by, array( 'name' ) );
|
||||
$fields_to_filter_by[] = "CONCAT_WS( ' ', first_name, last_name )";
|
||||
}
|
||||
$fields_with_not_condition = array_map(
|
||||
function ( $field ) {
|
||||
return $field . ' <> \'\'';
|
||||
},
|
||||
$fields_to_filter_by
|
||||
);
|
||||
$where_clauses[] = '(' . implode( ' AND ', $fields_with_not_condition ) . ')';
|
||||
}
|
||||
|
||||
// Allow a list of customer IDs to be specified.
|
||||
if ( ! empty( $query_args['customers'] ) ) {
|
||||
$included_customers = $this->get_filtered_ids( $query_args, 'customers' );
|
||||
|
||||
@@ -218,6 +218,7 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
'name',
|
||||
'username',
|
||||
'email',
|
||||
'all',
|
||||
),
|
||||
);
|
||||
$params['name_includes'] = array(
|
||||
@@ -386,7 +387,7 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
'type' => 'string',
|
||||
),
|
||||
);
|
||||
$params['force_cache_refresh'] = array(
|
||||
$params['force_cache_refresh'] = array(
|
||||
'description' => __( 'Force retrieval of fresh data instead of from the cache.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'sanitize_callback' => 'wp_validate_boolean',
|
||||
|
||||
@@ -30,6 +30,13 @@ class DataStore extends SqlQuery {
|
||||
*/
|
||||
protected $cache_timeout = 3600;
|
||||
|
||||
/**
|
||||
* Cache identifier.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $cache_key = '';
|
||||
|
||||
/**
|
||||
* Table used as a data store for this report.
|
||||
*
|
||||
@@ -51,6 +58,13 @@ class DataStore extends SqlQuery {
|
||||
*/
|
||||
protected $column_types = array();
|
||||
|
||||
/**
|
||||
* SQL columns to select in the db query.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $report_columns = array();
|
||||
|
||||
// @todo This does not really belong here, maybe factor out the comparison as separate class?
|
||||
/**
|
||||
* Order by property, used in the cmp function.
|
||||
@@ -129,7 +143,7 @@ class DataStore extends SqlQuery {
|
||||
self::set_db_table_name();
|
||||
$this->assign_report_columns();
|
||||
|
||||
if ( property_exists( $this, 'report_columns' ) ) {
|
||||
if ( $this->report_columns ) {
|
||||
$this->report_columns = apply_filters(
|
||||
'woocommerce_admin_report_columns',
|
||||
$this->report_columns,
|
||||
|
||||
@@ -52,29 +52,13 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
$data[] = $this->prepare_response_for_collection( $item );
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
$response->header( 'X-WP-Total', (int) $downloads_data->total );
|
||||
$response->header( 'X-WP-TotalPages', (int) $downloads_data->pages );
|
||||
|
||||
$page = $downloads_data->page_no;
|
||||
$max_pages = $downloads_data->pages;
|
||||
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
if ( $page > 1 ) {
|
||||
$prev_page = $page - 1;
|
||||
if ( $prev_page > $max_pages ) {
|
||||
$prev_page = $max_pages;
|
||||
}
|
||||
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
if ( $max_pages > $page ) {
|
||||
$next_page = $page + 1;
|
||||
$next_link = add_query_arg( 'page', $next_page, $base );
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
return $this->add_pagination_headers(
|
||||
$request,
|
||||
$data,
|
||||
(int) $downloads_data->total,
|
||||
(int) $downloads_data->page_no,
|
||||
(int) $downloads_data->pages
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,20 +9,17 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Downloads\Stats;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Admin\API\Reports\GenericStatsController;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* REST API Reports downloads stats controller class.
|
||||
*
|
||||
* @internal
|
||||
* @extends WC_REST_Reports_Controller
|
||||
* @extends GenericStatsController
|
||||
*/
|
||||
class Controller extends \WC_REST_Reports_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc-analytics';
|
||||
class Controller extends GenericStatsController {
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
@@ -82,46 +79,24 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
$out_data['intervals'][] = $this->prepare_response_for_collection( $item );
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $out_data );
|
||||
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||
|
||||
$page = $report_data->page_no;
|
||||
$max_pages = $report_data->pages;
|
||||
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
if ( $page > 1 ) {
|
||||
$prev_page = $page - 1;
|
||||
if ( $prev_page > $max_pages ) {
|
||||
$prev_page = $max_pages;
|
||||
}
|
||||
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
if ( $max_pages > $page ) {
|
||||
$next_page = $page + 1;
|
||||
$next_link = add_query_arg( 'page', $next_page, $base );
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
return $this->add_pagination_headers(
|
||||
$request,
|
||||
$out_data,
|
||||
(int) $report_data->total,
|
||||
(int) $report_data->page_no,
|
||||
(int) $report_data->pages
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a report object for serialization.
|
||||
*
|
||||
* @param Array $report Report data.
|
||||
* @param array $report Report data.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function prepare_item_for_response( $report, $request ) {
|
||||
$data = $report;
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
$response = parent::prepare_item_for_response( $report, $request );
|
||||
|
||||
/**
|
||||
* Filter a report returned from the API.
|
||||
@@ -135,13 +110,15 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
return apply_filters( 'woocommerce_rest_prepare_report_downloads_stats', $response, $report, $request );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the Report's schema, conforming to JSON Schema.
|
||||
* Get the Report's item properties schema.
|
||||
* Will be used by `get_item_schema` as `totals` and `subtotals`.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$totals = array(
|
||||
protected function get_item_properties_schema() {
|
||||
return array(
|
||||
'download_count' => array(
|
||||
'title' => __( 'Downloads', 'woocommerce' ),
|
||||
'description' => __( 'Number of downloads.', 'woocommerce' ),
|
||||
@@ -151,6 +128,15 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
'indicator' => true,
|
||||
),
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Get the Report's schema, conforming to JSON Schema.
|
||||
* It does not have the segments as in GenericStatsController.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$totals = $this->get_item_properties_schema();
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
@@ -225,67 +211,10 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = array();
|
||||
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
$params['page'] = array(
|
||||
'description' => __( 'Current page of the collection.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 1,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'minimum' => 1,
|
||||
);
|
||||
$params['per_page'] = array(
|
||||
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 10,
|
||||
'minimum' => 1,
|
||||
'maximum' => 100,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['after'] = array(
|
||||
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['before'] = array(
|
||||
'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['order'] = array(
|
||||
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'desc',
|
||||
'enum' => array( 'asc', 'desc' ),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['orderby'] = array(
|
||||
'description' => __( 'Sort collection by object attribute.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'date',
|
||||
'enum' => array(
|
||||
'date',
|
||||
'download_count',
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['interval'] = array(
|
||||
'description' => __( 'Time interval to use for buckets in the returned data.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'week',
|
||||
'enum' => array(
|
||||
'hour',
|
||||
'day',
|
||||
'week',
|
||||
'month',
|
||||
'quarter',
|
||||
'year',
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
$params = parent::get_collection_params();
|
||||
$params['orderby']['enum'] = array(
|
||||
'date',
|
||||
'download_count',
|
||||
);
|
||||
$params['match'] = array(
|
||||
'description' => __( 'Indicates whether all the conditions should be true for the resulting set, or if any one of them is sufficient. Match affects the following parameters: status_is, status_is_not, product_includes, product_excludes, coupon_includes, coupon_excludes, customer, categories', 'woocommerce' ),
|
||||
@@ -378,12 +307,6 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
'type' => 'string',
|
||||
),
|
||||
);
|
||||
$params['force_cache_refresh'] = array(
|
||||
'description' => __( 'Force retrieval of fresh data instead of from the cache.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'sanitize_callback' => 'wp_validate_boolean',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Admin\API\Reports;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* WC REST API Reports controller extended
|
||||
* to be shared as a generic base for all Analytics controllers.
|
||||
*
|
||||
* @internal
|
||||
* @extends WC_REST_Reports_Controller
|
||||
*/
|
||||
abstract class GenericController extends \WC_REST_Reports_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc-analytics';
|
||||
|
||||
|
||||
/**
|
||||
* Add pagination headers and links.
|
||||
*
|
||||
* @param WP_REST_Request $request Request data.
|
||||
* @param WP_REST_Response|array $response Response data.
|
||||
* @param int $total Total results.
|
||||
* @param int $page Current page.
|
||||
* @param int $max_pages Total amount of pages.
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function add_pagination_headers( $request, $response, int $total, int $page, int $max_pages ) {
|
||||
$response = rest_ensure_response( $response );
|
||||
$response->header( 'X-WP-Total', $total );
|
||||
$response->header( 'X-WP-TotalPages', $max_pages );
|
||||
|
||||
$base = add_query_arg(
|
||||
$request->get_query_params(),
|
||||
rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) )
|
||||
);
|
||||
|
||||
if ( $page > 1 ) {
|
||||
$prev_page = $page - 1;
|
||||
if ( $prev_page > $max_pages ) {
|
||||
$prev_page = $max_pages;
|
||||
}
|
||||
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
|
||||
if ( $max_pages > $page ) {
|
||||
$next_page = $page + 1;
|
||||
$next_link = add_query_arg( 'page', $next_page, $base );
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query params for collections.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = array();
|
||||
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
$params['page'] = array(
|
||||
'description' => __( 'Current page of the collection.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 1,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'minimum' => 1,
|
||||
);
|
||||
$params['per_page'] = array(
|
||||
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 10,
|
||||
'minimum' => 1,
|
||||
'maximum' => 100,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['after'] = array(
|
||||
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['before'] = array(
|
||||
'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['order'] = array(
|
||||
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'desc',
|
||||
'enum' => array( 'asc', 'desc' ),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['orderby'] = array(
|
||||
'description' => __( 'Sort collection by object attribute.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'date',
|
||||
'enum' => array(
|
||||
'date',
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['force_cache_refresh'] = array(
|
||||
'description' => __( 'Force retrieval of fresh data instead of from the cache.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'sanitize_callback' => 'wp_validate_boolean',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a report object for serialization.
|
||||
*
|
||||
* @param array $report Report data.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function prepare_item_for_response( $report, $request ) {
|
||||
$data = $report;
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Admin\API\Reports;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Admin\API\Reports\GenericController;
|
||||
|
||||
/**
|
||||
* Generic base for all Stats controllers.
|
||||
*
|
||||
* @internal
|
||||
* @extends GenericController
|
||||
*/
|
||||
abstract class GenericStatsController extends GenericController {
|
||||
|
||||
/**
|
||||
* Get the query params for collections.
|
||||
* Adds intervals to the generic list.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = parent::get_collection_params();
|
||||
$params['interval'] = array(
|
||||
'description' => __( 'Time interval to use for buckets in the returned data.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'week',
|
||||
'enum' => array(
|
||||
'hour',
|
||||
'day',
|
||||
'week',
|
||||
'month',
|
||||
'quarter',
|
||||
'year',
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Report's item properties schema.
|
||||
* Will be used by `get_item_schema` as `totals` and `subtotals`.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function get_item_properties_schema();
|
||||
|
||||
/**
|
||||
* Get the Report's schema, conforming to JSON Schema.
|
||||
*
|
||||
* Please note, it does not call add_additional_fields_schema,
|
||||
* as you may want to update the `title` first.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$data_values = $this->get_item_properties_schema();
|
||||
|
||||
$segments = array(
|
||||
'segments' => array(
|
||||
'description' => __( 'Reports data grouped by segment condition.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'items' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'segment_id' => array(
|
||||
'description' => __( 'Segment identificator.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'subtotals' => array(
|
||||
'description' => __( 'Interval subtotals.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => $data_values,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$totals = array_merge( $data_values, $segments );
|
||||
|
||||
return array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'report_stats',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'totals' => array(
|
||||
'description' => __( 'Totals data.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => $totals,
|
||||
),
|
||||
'intervals' => array(
|
||||
'description' => __( 'Reports data grouped by intervals.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'items' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'interval' => array(
|
||||
'description' => __( 'Type of interval.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'enum' => array( 'day', 'week', 'month', 'year' ),
|
||||
),
|
||||
'date_start' => array(
|
||||
'description' => __( "The date the report start, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_start_gmt' => array(
|
||||
'description' => __( 'The date the report start, as GMT.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_end' => array(
|
||||
'description' => __( "The date the report end, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_end_gmt' => array(
|
||||
'description' => __( 'The date the report end, as GMT.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'subtotals' => array(
|
||||
'description' => __( 'Interval subtotals.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => $totals,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -84,28 +84,13 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
$data[] = $this->prepare_response_for_collection( $item );
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $data );
|
||||
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||
|
||||
$page = $report_data->page_no;
|
||||
$max_pages = $report_data->pages;
|
||||
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
if ( $page > 1 ) {
|
||||
$prev_page = $page - 1;
|
||||
if ( $prev_page > $max_pages ) {
|
||||
$prev_page = $max_pages;
|
||||
}
|
||||
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
if ( $max_pages > $page ) {
|
||||
$next_page = $page + 1;
|
||||
$next_link = add_query_arg( 'page', $next_page, $base );
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
return $this->add_pagination_headers(
|
||||
$request,
|
||||
$data,
|
||||
(int) $report_data->total,
|
||||
(int) $report_data->page_no,
|
||||
(int) $report_data->pages
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,9 +242,9 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = array();
|
||||
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
$params['page'] = array(
|
||||
$params = array();
|
||||
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
$params['page'] = array(
|
||||
'description' => __( 'Current page of the collection.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 1,
|
||||
@@ -267,7 +252,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'minimum' => 1,
|
||||
);
|
||||
$params['per_page'] = array(
|
||||
$params['per_page'] = array(
|
||||
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 10,
|
||||
@@ -276,26 +261,26 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['after'] = array(
|
||||
$params['after'] = array(
|
||||
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['before'] = array(
|
||||
$params['before'] = array(
|
||||
'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['order'] = array(
|
||||
$params['order'] = array(
|
||||
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'desc',
|
||||
'enum' => array( 'asc', 'desc' ),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['orderby'] = array(
|
||||
$params['orderby'] = array(
|
||||
'description' => __( 'Sort collection by object attribute.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'date',
|
||||
@@ -306,7 +291,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['product_includes'] = array(
|
||||
$params['product_includes'] = array(
|
||||
'description' => __( 'Limit result set to items that have the specified product(s) assigned.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -316,7 +301,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['product_excludes'] = array(
|
||||
$params['product_excludes'] = array(
|
||||
'description' => __( 'Limit result set to items that don\'t have the specified product(s) assigned.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -326,7 +311,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
);
|
||||
$params['variation_includes'] = array(
|
||||
$params['variation_includes'] = array(
|
||||
'description' => __( 'Limit result set to items that have the specified variation(s) assigned.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -336,7 +321,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['variation_excludes'] = array(
|
||||
$params['variation_excludes'] = array(
|
||||
'description' => __( 'Limit result set to items that don\'t have the specified variation(s) assigned.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -346,7 +331,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
);
|
||||
$params['coupon_includes'] = array(
|
||||
$params['coupon_includes'] = array(
|
||||
'description' => __( 'Limit result set to items that have the specified coupon(s) assigned.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -356,7 +341,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['coupon_excludes'] = array(
|
||||
$params['coupon_excludes'] = array(
|
||||
'description' => __( 'Limit result set to items that don\'t have the specified coupon(s) assigned.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -366,7 +351,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
);
|
||||
$params['tax_rate_includes'] = array(
|
||||
$params['tax_rate_includes'] = array(
|
||||
'description' => __( 'Limit result set to items that have the specified tax rate(s) assigned.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -376,7 +361,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['tax_rate_excludes'] = array(
|
||||
$params['tax_rate_excludes'] = array(
|
||||
'description' => __( 'Limit result set to items that don\'t have the specified tax rate(s) assigned.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -386,7 +371,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
);
|
||||
$params['status_is'] = array(
|
||||
$params['status_is'] = array(
|
||||
'description' => __( 'Limit result set to items that have the specified order status.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_slug_list',
|
||||
@@ -396,7 +381,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'type' => 'string',
|
||||
),
|
||||
);
|
||||
$params['status_is_not'] = array(
|
||||
$params['status_is_not'] = array(
|
||||
'description' => __( 'Limit result set to items that don\'t have the specified order status.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_slug_list',
|
||||
@@ -406,7 +391,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'type' => 'string',
|
||||
),
|
||||
);
|
||||
$params['customer_type'] = array(
|
||||
$params['customer_type'] = array(
|
||||
'description' => __( 'Limit result set to returning or new customers.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => '',
|
||||
@@ -417,7 +402,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['refunds'] = array(
|
||||
$params['refunds'] = array(
|
||||
'description' => __( 'Limit result set to specific types of refunds.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => '',
|
||||
@@ -430,14 +415,14 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['extended_info'] = array(
|
||||
$params['extended_info'] = array(
|
||||
'description' => __( 'Add additional piece of info about each coupon to the report.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'default' => false,
|
||||
'sanitize_callback' => 'wc_string_to_bool',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['order_includes'] = array(
|
||||
$params['order_includes'] = array(
|
||||
'description' => __( 'Limit result set to items that have the specified order ids.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
@@ -446,7 +431,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'type' => 'integer',
|
||||
),
|
||||
);
|
||||
$params['order_excludes'] = array(
|
||||
$params['order_excludes'] = array(
|
||||
'description' => __( 'Limit result set to items that don\'t have the specified order ids.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
@@ -455,7 +440,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'type' => 'integer',
|
||||
),
|
||||
);
|
||||
$params['attribute_is'] = array(
|
||||
$params['attribute_is'] = array(
|
||||
'description' => __( 'Limit result set to orders that include products with the specified attributes.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -464,7 +449,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'default' => array(),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['attribute_is_not'] = array(
|
||||
$params['attribute_is_not'] = array(
|
||||
'description' => __( 'Limit result set to orders that don\'t include products with the specified attributes.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
|
||||
@@ -94,28 +94,13 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller {
|
||||
$out_data['intervals'][] = $this->prepare_response_for_collection( $item );
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $out_data );
|
||||
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||
|
||||
$page = $report_data->page_no;
|
||||
$max_pages = $report_data->pages;
|
||||
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
if ( $page > 1 ) {
|
||||
$prev_page = $page - 1;
|
||||
if ( $prev_page > $max_pages ) {
|
||||
$prev_page = $max_pages;
|
||||
}
|
||||
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
if ( $max_pages > $page ) {
|
||||
$next_page = $page + 1;
|
||||
$next_link = add_query_arg( 'page', $next_page, $base );
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
return $this->add_pagination_headers(
|
||||
$request,
|
||||
$out_data,
|
||||
(int) $report_data->total,
|
||||
(int) $report_data->page_no,
|
||||
(int) $report_data->pages
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -427,7 +412,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller {
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
|
||||
);
|
||||
$params['product_excludes'] = array(
|
||||
$params['product_excludes'] = array(
|
||||
'description' => __( 'Limit result set to items that don\'t have the specified product(s) assigned.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -436,7 +421,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller {
|
||||
'default' => array(),
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
);
|
||||
$params['variation_includes'] = array(
|
||||
$params['variation_includes'] = array(
|
||||
'description' => __( 'Limit result set to items that have the specified variation(s) assigned.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -446,7 +431,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller {
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['variation_excludes'] = array(
|
||||
$params['variation_excludes'] = array(
|
||||
'description' => __( 'Limit result set to items that don\'t have the specified variation(s) assigned.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -456,7 +441,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller {
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
);
|
||||
$params['coupon_includes'] = array(
|
||||
$params['coupon_includes'] = array(
|
||||
'description' => __( 'Limit result set to items that have the specified coupon(s) assigned.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -465,7 +450,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller {
|
||||
'default' => array(),
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
);
|
||||
$params['coupon_excludes'] = array(
|
||||
$params['coupon_excludes'] = array(
|
||||
'description' => __( 'Limit result set to items that don\'t have the specified coupon(s) assigned.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -474,7 +459,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller {
|
||||
'default' => array(),
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
);
|
||||
$params['tax_rate_includes'] = array(
|
||||
$params['tax_rate_includes'] = array(
|
||||
'description' => __( 'Limit result set to items that have the specified tax rate(s) assigned.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -484,7 +469,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller {
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['tax_rate_excludes'] = array(
|
||||
$params['tax_rate_excludes'] = array(
|
||||
'description' => __( 'Limit result set to items that don\'t have the specified tax rate(s) assigned.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -494,7 +479,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller {
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
);
|
||||
$params['customer'] = array(
|
||||
$params['customer'] = array(
|
||||
'description' => __( 'Alias for customer_type (deprecated).', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'enum' => array(
|
||||
@@ -503,7 +488,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller {
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['customer_type'] = array(
|
||||
$params['customer_type'] = array(
|
||||
'description' => __( 'Limit result set to orders that have the specified customer_type', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'enum' => array(
|
||||
@@ -512,7 +497,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller {
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['refunds'] = array(
|
||||
$params['refunds'] = array(
|
||||
'description' => __( 'Limit result set to specific types of refunds.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => '',
|
||||
@@ -525,7 +510,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller {
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['attribute_is'] = array(
|
||||
$params['attribute_is'] = array(
|
||||
'description' => __( 'Limit result set to orders that include products with the specified attributes.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -534,7 +519,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller {
|
||||
'default' => array(),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['attribute_is_not'] = array(
|
||||
$params['attribute_is_not'] = array(
|
||||
'description' => __( 'Limit result set to orders that don\'t include products with the specified attributes.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -543,7 +528,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller {
|
||||
'default' => array(),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['segmentby'] = array(
|
||||
$params['segmentby'] = array(
|
||||
'description' => __( 'Segment the response by additional constraint.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'enum' => array(
|
||||
@@ -555,7 +540,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller {
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['fields'] = array(
|
||||
$params['fields'] = array(
|
||||
'description' => __( 'Limit stats fields to the specified items.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_slug_list',
|
||||
|
||||
@@ -7,7 +7,10 @@
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\API\Reports\PerformanceIndicators;
|
||||
|
||||
use Automattic\WooCommerce\Admin\API\Reports\GenericController;
|
||||
use Automattic\WooCommerce\Admin\API\Reports\TimeInterval;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
@@ -15,16 +18,9 @@ defined( 'ABSPATH' ) || exit;
|
||||
* REST API Reports Performance indicators controller class.
|
||||
*
|
||||
* @internal
|
||||
* @extends WC_REST_Reports_Controller
|
||||
* @extends GenericController
|
||||
*/
|
||||
class Controller extends \WC_REST_Reports_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc-analytics';
|
||||
class Controller extends GenericController {
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
@@ -294,13 +290,13 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
$objects[] = $this->prepare_response_for_collection( $prepared );
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $objects );
|
||||
$response->header( 'X-WP-Total', count( $data ) );
|
||||
$response->header( 'X-WP-TotalPages', 1 );
|
||||
|
||||
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
|
||||
return $response;
|
||||
return $this->add_pagination_headers(
|
||||
$request,
|
||||
$objects,
|
||||
(int) count( $data ),
|
||||
1,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -458,19 +454,14 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
/**
|
||||
* Prepare a report object for serialization.
|
||||
*
|
||||
* @param stdClass $stat_data Report data.
|
||||
* @param array $stat_data Report data.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function prepare_item_for_response( $stat_data, $request ) {
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $stat_data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
$response = parent::prepare_item_for_response( $stat_data, $request );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
$response->add_links( $this->prepare_links( $data ) );
|
||||
$response->add_links( $this->prepare_links( $stat_data ) );
|
||||
|
||||
/**
|
||||
* Filter a report returned from the API.
|
||||
|
||||
@@ -9,22 +9,18 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Products;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Admin\API\Reports\GenericController;
|
||||
use Automattic\WooCommerce\Admin\API\Reports\ExportableInterface;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* REST API Reports products controller class.
|
||||
*
|
||||
* @internal
|
||||
* @extends WC_REST_Reports_Controller
|
||||
* @extends GenericController
|
||||
*/
|
||||
class Controller extends \WC_REST_Reports_Controller implements ExportableInterface {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc-analytics';
|
||||
class Controller extends GenericController implements ExportableInterface {
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
@@ -77,28 +73,13 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
$data[] = $this->prepare_response_for_collection( $item );
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $data );
|
||||
$response->header( 'X-WP-Total', (int) $products_data->total );
|
||||
$response->header( 'X-WP-TotalPages', (int) $products_data->pages );
|
||||
|
||||
$page = $products_data->page_no;
|
||||
$max_pages = $products_data->pages;
|
||||
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
if ( $page > 1 ) {
|
||||
$prev_page = $page - 1;
|
||||
if ( $prev_page > $max_pages ) {
|
||||
$prev_page = $max_pages;
|
||||
}
|
||||
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
if ( $max_pages > $page ) {
|
||||
$next_page = $page + 1;
|
||||
$next_link = add_query_arg( 'page', $next_page, $base );
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
return $this->add_pagination_headers(
|
||||
$request,
|
||||
$data,
|
||||
(int) $products_data->total,
|
||||
(int) $products_data->page_no,
|
||||
(int) $products_data->pages
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,14 +90,7 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function prepare_item_for_response( $report, $request ) {
|
||||
$data = $report;
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
$response = parent::prepare_item_for_response( $report, $request );
|
||||
$response->add_links( $this->prepare_links( $report ) );
|
||||
|
||||
/**
|
||||
@@ -256,60 +230,17 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = array();
|
||||
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
$params['page'] = array(
|
||||
'description' => __( 'Current page of the collection.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 1,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'minimum' => 1,
|
||||
$params = parent::get_collection_params();
|
||||
$params['orderby']['enum'] = array(
|
||||
'date',
|
||||
'net_revenue',
|
||||
'orders_count',
|
||||
'items_sold',
|
||||
'product_name',
|
||||
'variations',
|
||||
'sku',
|
||||
);
|
||||
$params['per_page'] = array(
|
||||
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 10,
|
||||
'minimum' => 1,
|
||||
'maximum' => 100,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['after'] = array(
|
||||
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['before'] = array(
|
||||
'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['order'] = array(
|
||||
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'desc',
|
||||
'enum' => array( 'asc', 'desc' ),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['orderby'] = array(
|
||||
'description' => __( 'Sort collection by object attribute.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'date',
|
||||
'enum' => array(
|
||||
'date',
|
||||
'net_revenue',
|
||||
'orders_count',
|
||||
'items_sold',
|
||||
'product_name',
|
||||
'variations',
|
||||
'sku',
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['categories'] = array(
|
||||
$params['categories'] = array(
|
||||
'description' => __( 'Limit result to items from the specified categories.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
@@ -318,7 +249,7 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
'type' => 'integer',
|
||||
),
|
||||
);
|
||||
$params['match'] = array(
|
||||
$params['match'] = array(
|
||||
'description' => __( 'Indicates whether all the conditions should be true for the resulting set, or if any one of them is sufficient. Match affects the following parameters: status_is, status_is_not, product_includes, product_excludes, coupon_includes, coupon_excludes, customer, categories', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'all',
|
||||
@@ -328,7 +259,7 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['products'] = array(
|
||||
$params['products'] = array(
|
||||
'description' => __( 'Limit result to items with specified product ids.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
@@ -345,12 +276,6 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
'sanitize_callback' => 'wc_string_to_bool',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['force_cache_refresh'] = array(
|
||||
'description' => __( 'Force retrieval of fresh data instead of from the cache.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'sanitize_callback' => 'wp_validate_boolean',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
@@ -9,22 +9,18 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Products\Stats;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Admin\API\Reports\GenericStatsController;
|
||||
use Automattic\WooCommerce\Admin\API\Reports\ParameterException;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* REST API Reports products stats controller class.
|
||||
*
|
||||
* @internal
|
||||
* @extends WC_REST_Reports_Controller
|
||||
* @extends GenericStatsController
|
||||
*/
|
||||
class Controller extends \WC_REST_Reports_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc-analytics';
|
||||
class Controller extends GenericStatsController {
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
@@ -96,46 +92,24 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
$out_data['intervals'][] = $this->prepare_response_for_collection( $item );
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $out_data );
|
||||
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||
|
||||
$page = $report_data->page_no;
|
||||
$max_pages = $report_data->pages;
|
||||
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
if ( $page > 1 ) {
|
||||
$prev_page = $page - 1;
|
||||
if ( $prev_page > $max_pages ) {
|
||||
$prev_page = $max_pages;
|
||||
}
|
||||
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
if ( $max_pages > $page ) {
|
||||
$next_page = $page + 1;
|
||||
$next_link = add_query_arg( 'page', $next_page, $base );
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
return $this->add_pagination_headers(
|
||||
$request,
|
||||
$out_data,
|
||||
(int) $report_data->total,
|
||||
(int) $report_data->page_no,
|
||||
(int) $report_data->pages
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a report object for serialization.
|
||||
*
|
||||
* @param Array $report Report data.
|
||||
* @param array $report Report data.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function prepare_item_for_response( $report, $request ) {
|
||||
$data = $report;
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
$response = parent::prepare_item_for_response( $report, $request );
|
||||
|
||||
/**
|
||||
* Filter a report returned from the API.
|
||||
@@ -150,12 +124,13 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Report's schema, conforming to JSON Schema.
|
||||
* Get the Report's item properties schema.
|
||||
* Will be used by `get_item_schema` as `totals` and `subtotals`.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$data_values = array(
|
||||
protected function get_item_properties_schema() {
|
||||
return array(
|
||||
'items_sold' => array(
|
||||
'title' => __( 'Products sold', 'woocommerce' ),
|
||||
'description' => __( 'Number of product items sold.', 'woocommerce' ),
|
||||
@@ -178,106 +153,27 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
'readonly' => true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
$segments = array(
|
||||
'segments' => array(
|
||||
'description' => __( 'Reports data grouped by segment condition.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'items' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'segment_id' => array(
|
||||
'description' => __( 'Segment identificator.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'segment_label' => array(
|
||||
'description' => __( 'Human readable segment label, either product or variation name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'enum' => array( 'day', 'week', 'month', 'year' ),
|
||||
),
|
||||
'subtotals' => array(
|
||||
'description' => __( 'Interval subtotals.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => $data_values,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
/**
|
||||
* Get the Report's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$schema = parent::get_item_schema();
|
||||
$schema['title'] = 'report_products_stats';
|
||||
|
||||
$segment_label = array(
|
||||
'description' => __( 'Human readable segment label, either product or variation name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'enum' => array( 'day', 'week', 'month', 'year' ),
|
||||
);
|
||||
|
||||
$totals = array_merge( $data_values, $segments );
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'report_products_stats',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'totals' => array(
|
||||
'description' => __( 'Totals data.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => $totals,
|
||||
),
|
||||
'intervals' => array(
|
||||
'description' => __( 'Reports data grouped by intervals.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'items' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'interval' => array(
|
||||
'description' => __( 'Type of interval.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'enum' => array( 'day', 'week', 'month', 'year' ),
|
||||
),
|
||||
'date_start' => array(
|
||||
'description' => __( "The date the report start, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_start_gmt' => array(
|
||||
'description' => __( 'The date the report start, as GMT.', 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_end' => array(
|
||||
'description' => __( "The date the report end, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_end_gmt' => array(
|
||||
'description' => __( 'The date the report end, as GMT.', 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'subtotals' => array(
|
||||
'description' => __( 'Interval subtotals.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => $totals,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
$schema['properties']['totals']['properties']['segments']['items']['properties']['segment_label'] = $segment_label;
|
||||
$schema['properties']['intervals']['items']['properties']['subtotals']['properties']['segments']['items']['properties']['segment_label'] = $segment_label;
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
@@ -310,76 +206,19 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = array();
|
||||
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
$params['page'] = array(
|
||||
'description' => __( 'Current page of the collection.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 1,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'minimum' => 1,
|
||||
$params = parent::get_collection_params();
|
||||
$params['orderby']['enum'] = array(
|
||||
'date',
|
||||
'net_revenue',
|
||||
'coupons',
|
||||
'refunds',
|
||||
'shipping',
|
||||
'taxes',
|
||||
'net_revenue',
|
||||
'orders_count',
|
||||
'items_sold',
|
||||
);
|
||||
$params['per_page'] = array(
|
||||
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 10,
|
||||
'minimum' => 1,
|
||||
'maximum' => 100,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['after'] = array(
|
||||
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['before'] = array(
|
||||
'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['order'] = array(
|
||||
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'desc',
|
||||
'enum' => array( 'asc', 'desc' ),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['orderby'] = array(
|
||||
'description' => __( 'Sort collection by object attribute.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'date',
|
||||
'enum' => array(
|
||||
'date',
|
||||
'net_revenue',
|
||||
'coupons',
|
||||
'refunds',
|
||||
'shipping',
|
||||
'taxes',
|
||||
'net_revenue',
|
||||
'orders_count',
|
||||
'items_sold',
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['interval'] = array(
|
||||
'description' => __( 'Time interval to use for buckets in the returned data.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'week',
|
||||
'enum' => array(
|
||||
'hour',
|
||||
'day',
|
||||
'week',
|
||||
'month',
|
||||
'quarter',
|
||||
'year',
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['categories'] = array(
|
||||
$params['categories'] = array(
|
||||
'description' => __( 'Limit result to items from the specified categories.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
@@ -388,7 +227,7 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
'type' => 'integer',
|
||||
),
|
||||
);
|
||||
$params['products'] = array(
|
||||
$params['products'] = array(
|
||||
'description' => __( 'Limit result to items with specified product ids.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
@@ -397,7 +236,7 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
'type' => 'integer',
|
||||
),
|
||||
);
|
||||
$params['variations'] = array(
|
||||
$params['variations'] = array(
|
||||
'description' => __( 'Limit result to items with specified variation ids.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
@@ -406,7 +245,7 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
'type' => 'integer',
|
||||
),
|
||||
);
|
||||
$params['segmentby'] = array(
|
||||
$params['segmentby'] = array(
|
||||
'description' => __( 'Segment the response by additional constraint.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'enum' => array(
|
||||
@@ -416,7 +255,7 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['fields'] = array(
|
||||
$params['fields'] = array(
|
||||
'description' => __( 'Limit stats fields to the specified items.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_slug_list',
|
||||
@@ -425,12 +264,6 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
'type' => 'string',
|
||||
),
|
||||
);
|
||||
$params['force_cache_refresh'] = array(
|
||||
'description' => __( 'Force retrieval of fresh data instead of from the cache.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'sanitize_callback' => 'wp_validate_boolean',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
@@ -9,30 +9,26 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Revenue\Stats;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Admin\API\Reports\GenericStatsController;
|
||||
use Automattic\WooCommerce\Admin\API\Reports\Revenue\Query as RevenueQuery;
|
||||
use Automattic\WooCommerce\Admin\API\Reports\ExportableInterface;
|
||||
use Automattic\WooCommerce\Admin\API\Reports\ExportableTraits;
|
||||
use Automattic\WooCommerce\Admin\API\Reports\ParameterException;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* REST API Reports revenue stats controller class.
|
||||
*
|
||||
* @internal
|
||||
* @extends WC_REST_Reports_Controller
|
||||
* @extends GenericStatsController
|
||||
*/
|
||||
class Controller extends \WC_REST_Reports_Controller implements ExportableInterface {
|
||||
class Controller extends GenericStatsController implements ExportableInterface {
|
||||
/**
|
||||
* Exportable traits.
|
||||
*/
|
||||
use ExportableTraits;
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc-analytics';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
@@ -87,28 +83,13 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
$out_data['intervals'][] = $this->prepare_response_for_collection( $item );
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $out_data );
|
||||
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||
|
||||
$page = $report_data->page_no;
|
||||
$max_pages = $report_data->pages;
|
||||
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
if ( $page > 1 ) {
|
||||
$prev_page = $page - 1;
|
||||
if ( $prev_page > $max_pages ) {
|
||||
$prev_page = $max_pages;
|
||||
}
|
||||
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
if ( $max_pages > $page ) {
|
||||
$next_page = $page + 1;
|
||||
$next_link = add_query_arg( 'page', $next_page, $base );
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
return $this->add_pagination_headers(
|
||||
$request,
|
||||
$out_data,
|
||||
(int) $report_data->total,
|
||||
(int) $report_data->page_no,
|
||||
(int) $report_data->pages
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,19 +113,12 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
/**
|
||||
* Prepare a report object for serialization.
|
||||
*
|
||||
* @param Array $report Report data.
|
||||
* @param array $report Report data.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function prepare_item_for_response( $report, $request ) {
|
||||
$data = $report;
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
$response = parent::prepare_item_for_response( $report, $request );
|
||||
|
||||
/**
|
||||
* Filter a report returned from the API.
|
||||
@@ -159,12 +133,13 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Report's schema, conforming to JSON Schema.
|
||||
* Get the Report's item properties schema.
|
||||
* Will be used by `get_item_schema` as `totals` and `subtotals`.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$data_values = array(
|
||||
protected function get_item_properties_schema() {
|
||||
return array(
|
||||
'total_sales' => array(
|
||||
'description' => __( 'Total sales.', 'woocommerce' ),
|
||||
'type' => 'number',
|
||||
@@ -231,12 +206,6 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'products' => array(
|
||||
'description' => __( 'Products sold.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'gross_sales' => array(
|
||||
'description' => __( 'Gross sales.', 'woocommerce' ),
|
||||
'type' => 'number',
|
||||
@@ -246,103 +215,23 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
'format' => 'currency',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
$segments = array(
|
||||
'segments' => array(
|
||||
'description' => __( 'Reports data grouped by segment condition.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'items' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'segment_id' => array(
|
||||
'description' => __( 'Segment identificator.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'subtotals' => array(
|
||||
'description' => __( 'Interval subtotals.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => $data_values,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
/**
|
||||
* Get the Report's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$schema = parent::get_item_schema();
|
||||
$schema['title'] = 'report_revenue_stats';
|
||||
|
||||
$totals = array_merge( $data_values, $segments );
|
||||
|
||||
// Products is not shown in intervals.
|
||||
unset( $data_values['products'] );
|
||||
|
||||
$intervals = array_merge( $data_values, $segments );
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'report_revenue_stats',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'totals' => array(
|
||||
'description' => __( 'Totals data.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => $totals,
|
||||
),
|
||||
'intervals' => array(
|
||||
'description' => __( 'Reports data grouped by intervals.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'items' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'interval' => array(
|
||||
'description' => __( 'Type of interval.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'enum' => array( 'day', 'week', 'month', 'year' ),
|
||||
),
|
||||
'date_start' => array(
|
||||
'description' => __( "The date the report start, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_start_gmt' => array(
|
||||
'description' => __( 'The date the report start, as GMT.', 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_end' => array(
|
||||
'description' => __( "The date the report end, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_end_gmt' => array(
|
||||
'description' => __( 'The date the report end, as GMT.', 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'subtotals' => array(
|
||||
'description' => __( 'Interval subtotals.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => $intervals,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// Products is not shown in intervals, only in totals.
|
||||
$schema['properties']['totals']['properties']['products'] = array(
|
||||
'description' => __( 'Products sold.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
@@ -354,77 +243,20 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = array();
|
||||
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
$params['page'] = array(
|
||||
'description' => __( 'Current page of the collection.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 1,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'minimum' => 1,
|
||||
$params = parent::get_collection_params();
|
||||
$params['orderby']['enum'] = array(
|
||||
'date',
|
||||
'total_sales',
|
||||
'coupons',
|
||||
'refunds',
|
||||
'shipping',
|
||||
'taxes',
|
||||
'net_revenue',
|
||||
'orders_count',
|
||||
'items_sold',
|
||||
'gross_sales',
|
||||
);
|
||||
$params['per_page'] = array(
|
||||
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 10,
|
||||
'minimum' => 1,
|
||||
'maximum' => 100,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['after'] = array(
|
||||
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['before'] = array(
|
||||
'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['order'] = array(
|
||||
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'desc',
|
||||
'enum' => array( 'asc', 'desc' ),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['orderby'] = array(
|
||||
'description' => __( 'Sort collection by object attribute.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'date',
|
||||
'enum' => array(
|
||||
'date',
|
||||
'total_sales',
|
||||
'coupons',
|
||||
'refunds',
|
||||
'shipping',
|
||||
'taxes',
|
||||
'net_revenue',
|
||||
'orders_count',
|
||||
'items_sold',
|
||||
'gross_sales',
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['interval'] = array(
|
||||
'description' => __( 'Time interval to use for buckets in the returned data.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'week',
|
||||
'enum' => array(
|
||||
'hour',
|
||||
'day',
|
||||
'week',
|
||||
'month',
|
||||
'quarter',
|
||||
'year',
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['segmentby'] = array(
|
||||
$params['segmentby'] = array(
|
||||
'description' => __( 'Segment the response by additional constraint.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'enum' => array(
|
||||
@@ -436,12 +268,6 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['force_cache_refresh'] = array(
|
||||
'description' => __( 'Force retrieval of fresh data instead of from the cache.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'sanitize_callback' => 'wp_validate_boolean',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
@@ -542,6 +542,87 @@ class Segmenter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate segments for totals where the segmenting property is bound to product (e.g. category, product_id, variation_id).
|
||||
*
|
||||
* @param array $segmenting_selections SELECT part of segmenting SQL query--one for 'product_level' and one for 'order_level'.
|
||||
* @param string $segmenting_from FROM part of segmenting SQL query.
|
||||
* @param string $segmenting_where WHERE part of segmenting SQL query.
|
||||
* @param string $segmenting_groupby GROUP BY part of segmenting SQL query.
|
||||
* @param string $segmenting_dimension_name Name of the segmenting dimension.
|
||||
* @param string $table_name Name of SQL table which is the stats table for orders.
|
||||
* @param array $totals_query Array of SQL clauses for totals query.
|
||||
* @param string $unique_orders_table Name of temporary SQL table that holds unique orders.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_product_related_totals_segments( $segmenting_selections, $segmenting_from, $segmenting_where, $segmenting_groupby, $segmenting_dimension_name, $table_name, $totals_query, $unique_orders_table ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate segments for intervals where the segmenting property is bound to product (e.g. category, product_id, variation_id).
|
||||
*
|
||||
* @param array $segmenting_selections SELECT part of segmenting SQL query--one for 'product_level' and one for 'order_level'.
|
||||
* @param string $segmenting_from FROM part of segmenting SQL query.
|
||||
* @param string $segmenting_where WHERE part of segmenting SQL query.
|
||||
* @param string $segmenting_groupby GROUP BY part of segmenting SQL query.
|
||||
* @param string $segmenting_dimension_name Name of the segmenting dimension.
|
||||
* @param string $table_name Name of SQL table which is the stats table for orders.
|
||||
* @param array $intervals_query Array of SQL clauses for intervals query.
|
||||
* @param string $unique_orders_table Name of temporary SQL table that holds unique orders.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_product_related_intervals_segments( $segmenting_selections, $segmenting_from, $segmenting_where, $segmenting_groupby, $segmenting_dimension_name, $table_name, $intervals_query, $unique_orders_table ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate segments for totals query where the segmenting property is bound to order (e.g. coupon or customer type).
|
||||
*
|
||||
* @param string $segmenting_select SELECT part of segmenting SQL query.
|
||||
* @param string $segmenting_from FROM part of segmenting SQL query.
|
||||
* @param string $segmenting_where WHERE part of segmenting SQL query.
|
||||
* @param string $segmenting_groupby GROUP BY part of segmenting SQL query.
|
||||
* @param string $table_name Name of SQL table which is the stats table for orders.
|
||||
* @param array $totals_query Array of SQL clauses for intervals query.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_order_related_totals_segments( $segmenting_select, $segmenting_from, $segmenting_where, $segmenting_groupby, $table_name, $totals_query ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate segments for intervals query where the segmenting property is bound to order (e.g. coupon or customer type).
|
||||
*
|
||||
* @param string $segmenting_select SELECT part of segmenting SQL query.
|
||||
* @param string $segmenting_from FROM part of segmenting SQL query.
|
||||
* @param string $segmenting_where WHERE part of segmenting SQL query.
|
||||
* @param string $segmenting_groupby GROUP BY part of segmenting SQL query.
|
||||
* @param string $table_name Name of SQL table which is the stats table for orders.
|
||||
* @param array $intervals_query Array of SQL clauses for intervals query.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_order_related_intervals_segments( $segmenting_select, $segmenting_from, $segmenting_where, $segmenting_groupby, $table_name, $intervals_query ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array of segments formatted for REST response.
|
||||
*
|
||||
* @param string $type Type of segments to return--'totals' or 'intervals'.
|
||||
* @param array $query_params SQL query parameter array.
|
||||
* @param string $table_name Name of main SQL table for the data store (used as basis for JOINS).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_segments( $type, $query_params, $table_name ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate segments for segmenting property bound to product (e.g. category, product_id, variation_id).
|
||||
*
|
||||
|
||||
@@ -9,22 +9,18 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Stock;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Admin\API\Reports\GenericController;
|
||||
use Automattic\WooCommerce\Admin\API\Reports\ExportableInterface;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* REST API Reports stock controller class.
|
||||
*
|
||||
* @internal
|
||||
* @extends WC_REST_Reports_Controller
|
||||
* @extends GenericController
|
||||
*/
|
||||
class Controller extends \WC_REST_Reports_Controller implements ExportableInterface {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc-analytics';
|
||||
class Controller extends GenericController implements ExportableInterface {
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
@@ -136,30 +132,13 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
$objects[] = $this->prepare_response_for_collection( $data );
|
||||
}
|
||||
|
||||
$page = (int) $query_args['paged'];
|
||||
$max_pages = $query_results['pages'];
|
||||
|
||||
$response = rest_ensure_response( $objects );
|
||||
$response->header( 'X-WP-Total', $query_results['total'] );
|
||||
$response->header( 'X-WP-TotalPages', (int) $max_pages );
|
||||
|
||||
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
|
||||
if ( $page > 1 ) {
|
||||
$prev_page = $page - 1;
|
||||
if ( $prev_page > $max_pages ) {
|
||||
$prev_page = $max_pages;
|
||||
}
|
||||
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
if ( $max_pages > $page ) {
|
||||
$next_page = $page + 1;
|
||||
$next_link = add_query_arg( 'page', $next_page, $base );
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
return $this->add_pagination_headers(
|
||||
$request,
|
||||
$objects,
|
||||
(int) $query_results['total'],
|
||||
(int) $query_args['paged'],
|
||||
(int) $query_results['pages']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -319,12 +298,7 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
$data['low_stock_amount'] = absint( max( get_option( 'woocommerce_notify_low_stock_amount' ), 1 ) );
|
||||
}
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
$response = parent::prepare_item_for_response( $data, $request );
|
||||
$response->add_links( $this->prepare_links( $product ) );
|
||||
|
||||
/**
|
||||
@@ -441,26 +415,9 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = array();
|
||||
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
$params['page'] = array(
|
||||
'description' => __( 'Current page of the collection.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 1,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'minimum' => 1,
|
||||
);
|
||||
$params['per_page'] = array(
|
||||
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 10,
|
||||
'minimum' => 1,
|
||||
'maximum' => 100,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['exclude'] = array(
|
||||
$params = parent::get_collection_params();
|
||||
unset( $params['after'], $params['before'], $params['force_cache_refresh'] );
|
||||
$params['exclude'] = array(
|
||||
'description' => __( 'Ensure result set excludes specific IDs.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -469,7 +426,7 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
'default' => array(),
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
);
|
||||
$params['include'] = array(
|
||||
$params['include'] = array(
|
||||
'description' => __( 'Limit result set to specific ids.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -478,35 +435,24 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
'default' => array(),
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
);
|
||||
$params['offset'] = array(
|
||||
$params['offset'] = array(
|
||||
'description' => __( 'Offset the result set by a specific number of items.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['order'] = array(
|
||||
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'asc',
|
||||
'enum' => array( 'asc', 'desc' ),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
$params['order']['default'] = 'asc';
|
||||
$params['orderby']['default'] = 'stock_status';
|
||||
$params['orderby']['enum'] = array(
|
||||
'stock_status',
|
||||
'stock_quantity',
|
||||
'date',
|
||||
'id',
|
||||
'include',
|
||||
'title',
|
||||
'sku',
|
||||
);
|
||||
$params['orderby'] = array(
|
||||
'description' => __( 'Sort collection by object attribute.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'stock_status',
|
||||
'enum' => array(
|
||||
'stock_status',
|
||||
'stock_quantity',
|
||||
'date',
|
||||
'id',
|
||||
'include',
|
||||
'title',
|
||||
'sku',
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['parent'] = array(
|
||||
$params['parent'] = array(
|
||||
'description' => __( 'Limit result set to those of particular parent IDs.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -515,7 +461,7 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
'default' => array(),
|
||||
);
|
||||
$params['parent_exclude'] = array(
|
||||
$params['parent_exclude'] = array(
|
||||
'description' => __( 'Limit result set to all items except those of a particular parent ID.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -524,7 +470,7 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
'default' => array(),
|
||||
);
|
||||
$params['type'] = array(
|
||||
$params['type'] = array(
|
||||
'description' => __( 'Limit result set to items assigned a stock report type.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'all',
|
||||
|
||||
@@ -9,28 +9,24 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Taxes;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Admin\API\Reports\GenericController;
|
||||
use Automattic\WooCommerce\Admin\API\Reports\ExportableInterface;
|
||||
use Automattic\WooCommerce\Admin\API\Reports\ExportableTraits;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* REST API Reports taxes controller class.
|
||||
*
|
||||
* @internal
|
||||
* @extends WC_REST_Reports_Controller
|
||||
* @extends GenericController
|
||||
*/
|
||||
class Controller extends \WC_REST_Reports_Controller implements ExportableInterface {
|
||||
class Controller extends GenericController implements ExportableInterface {
|
||||
/**
|
||||
* Exportable traits.
|
||||
*/
|
||||
use ExportableTraits;
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc-analytics';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
@@ -76,28 +72,13 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
$data[] = $this->prepare_response_for_collection( $item );
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $data );
|
||||
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||
|
||||
$page = $report_data->page_no;
|
||||
$max_pages = $report_data->pages;
|
||||
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
if ( $page > 1 ) {
|
||||
$prev_page = $page - 1;
|
||||
if ( $prev_page > $max_pages ) {
|
||||
$prev_page = $max_pages;
|
||||
}
|
||||
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
if ( $max_pages > $page ) {
|
||||
$next_page = $page + 1;
|
||||
$next_link = add_query_arg( 'page', $next_page, $base );
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
return $this->add_pagination_headers(
|
||||
$request,
|
||||
$data,
|
||||
(int) $report_data->total,
|
||||
(int) $report_data->page_no,
|
||||
(int) $report_data->pages
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,12 +89,7 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function prepare_item_for_response( $report, $request ) {
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$report = $this->add_additional_fields_to_object( $report, $request );
|
||||
$report = $this->filter_response_by_context( $report, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $report );
|
||||
$response = parent::prepare_item_for_response( $report, $request );
|
||||
$response->add_links( $this->prepare_links( $report ) );
|
||||
|
||||
/**
|
||||
@@ -227,61 +203,19 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = array();
|
||||
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
$params['page'] = array(
|
||||
'description' => __( 'Current page of the collection.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 1,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'minimum' => 1,
|
||||
$params = parent::get_collection_params();
|
||||
$params['orderby']['default'] = 'tax_rate_id';
|
||||
$params['orderby']['enum'] = array(
|
||||
'name',
|
||||
'tax_rate_id',
|
||||
'tax_code',
|
||||
'rate',
|
||||
'order_tax',
|
||||
'total_tax',
|
||||
'shipping_tax',
|
||||
'orders_count',
|
||||
);
|
||||
$params['per_page'] = array(
|
||||
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 10,
|
||||
'minimum' => 1,
|
||||
'maximum' => 100,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['after'] = array(
|
||||
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['before'] = array(
|
||||
'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['order'] = array(
|
||||
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'desc',
|
||||
'enum' => array( 'asc', 'desc' ),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['orderby'] = array(
|
||||
'description' => __( 'Sort collection by object attribute.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'tax_rate_id',
|
||||
'enum' => array(
|
||||
'name',
|
||||
'tax_rate_id',
|
||||
'tax_code',
|
||||
'rate',
|
||||
'order_tax',
|
||||
'total_tax',
|
||||
'shipping_tax',
|
||||
'orders_count',
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['taxes'] = array(
|
||||
$params['taxes'] = array(
|
||||
'description' => __( 'Limit result set to items assigned one or more tax rates.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
@@ -290,12 +224,6 @@ class Controller extends \WC_REST_Reports_Controller implements ExportableInterf
|
||||
'type' => 'string',
|
||||
),
|
||||
);
|
||||
$params['force_cache_refresh'] = array(
|
||||
'description' => __( 'Force retrieval of fresh data instead of from the cache.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'sanitize_callback' => 'wp_validate_boolean',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
@@ -9,20 +9,17 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Taxes\Stats;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Admin\API\Reports\GenericStatsController;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* REST API Reports taxes stats controller class.
|
||||
*
|
||||
* @internal
|
||||
* @extends WC_REST_Reports_Controller
|
||||
* @extends GenericStatsController
|
||||
*/
|
||||
class Controller extends \WC_REST_Reports_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc-analytics';
|
||||
class Controller extends GenericStatsController {
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
@@ -106,28 +103,13 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
$out_data['intervals'][] = $this->prepare_response_for_collection( $item );
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $out_data );
|
||||
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||
|
||||
$page = $report_data->page_no;
|
||||
$max_pages = $report_data->pages;
|
||||
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
if ( $page > 1 ) {
|
||||
$prev_page = $page - 1;
|
||||
if ( $prev_page > $max_pages ) {
|
||||
$prev_page = $max_pages;
|
||||
}
|
||||
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
if ( $max_pages > $page ) {
|
||||
$next_page = $page + 1;
|
||||
$next_link = add_query_arg( 'page', $next_page, $base );
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
return $this->add_pagination_headers(
|
||||
$request,
|
||||
$out_data,
|
||||
(int) $report_data->total,
|
||||
(int) $report_data->page_no,
|
||||
(int) $report_data->pages
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,12 +122,7 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
public function prepare_item_for_response( $report, $request ) {
|
||||
$data = get_object_vars( $report );
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
$response = parent::prepare_item_for_response( $data, $request );
|
||||
|
||||
/**
|
||||
* Filter a report returned from the API.
|
||||
@@ -160,12 +137,13 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Report's schema, conforming to JSON Schema.
|
||||
* Get the Report's item properties schema.
|
||||
* Will be used by `get_item_schema` as `totals` and `subtotals`.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$data_values = array(
|
||||
protected function get_item_properties_schema() {
|
||||
return array(
|
||||
'total_tax' => array(
|
||||
'description' => __( 'Total tax.', 'woocommerce' ),
|
||||
'type' => 'number',
|
||||
@@ -203,99 +181,16 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
'readonly' => true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
$segments = array(
|
||||
'segments' => array(
|
||||
'description' => __( 'Reports data grouped by segment condition.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'items' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'segment_id' => array(
|
||||
'description' => __( 'Segment identificator.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'subtotals' => array(
|
||||
'description' => __( 'Interval subtotals.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => $data_values,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$totals = array_merge( $data_values, $segments );
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'report_taxes_stats',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'totals' => array(
|
||||
'description' => __( 'Totals data.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => $totals,
|
||||
),
|
||||
'intervals' => array(
|
||||
'description' => __( 'Reports data grouped by intervals.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'items' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'interval' => array(
|
||||
'description' => __( 'Type of interval.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'enum' => array( 'day', 'week', 'month', 'year' ),
|
||||
),
|
||||
'date_start' => array(
|
||||
'description' => __( "The date the report start, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_start_gmt' => array(
|
||||
'description' => __( 'The date the report start, as GMT.', 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_end' => array(
|
||||
'description' => __( "The date the report end, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_end_gmt' => array(
|
||||
'description' => __( 'The date the report end, as GMT.', 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'subtotals' => array(
|
||||
'description' => __( 'Interval subtotals.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => $totals,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
/**
|
||||
* Get the Report's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$schema = parent::get_item_schema();
|
||||
$schema['title'] = 'report_taxes_stats';
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
@@ -306,72 +201,15 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = array();
|
||||
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
$params['page'] = array(
|
||||
'description' => __( 'Current page of the collection.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 1,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'minimum' => 1,
|
||||
$params = parent::get_collection_params();
|
||||
$params['orderby']['enum'] = array(
|
||||
'date',
|
||||
'items_sold',
|
||||
'total_sales',
|
||||
'orders_count',
|
||||
'products_count',
|
||||
);
|
||||
$params['per_page'] = array(
|
||||
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 10,
|
||||
'minimum' => 1,
|
||||
'maximum' => 100,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['after'] = array(
|
||||
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['before'] = array(
|
||||
'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['order'] = array(
|
||||
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'desc',
|
||||
'enum' => array( 'asc', 'desc' ),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['orderby'] = array(
|
||||
'description' => __( 'Sort collection by object attribute.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'date',
|
||||
'enum' => array(
|
||||
'date',
|
||||
'items_sold',
|
||||
'total_sales',
|
||||
'orders_count',
|
||||
'products_count',
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['interval'] = array(
|
||||
'description' => __( 'Time interval to use for buckets in the returned data.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'week',
|
||||
'enum' => array(
|
||||
'hour',
|
||||
'day',
|
||||
'week',
|
||||
'month',
|
||||
'quarter',
|
||||
'year',
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['taxes'] = array(
|
||||
$params['taxes'] = array(
|
||||
'description' => __( 'Limit result set to all items that have the specified term assigned in the taxes taxonomy.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
@@ -380,7 +218,7 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
'type' => 'integer',
|
||||
),
|
||||
);
|
||||
$params['segmentby'] = array(
|
||||
$params['segmentby'] = array(
|
||||
'description' => __( 'Segment the response by additional constraint.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'enum' => array(
|
||||
@@ -388,7 +226,7 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['fields'] = array(
|
||||
$params['fields'] = array(
|
||||
'description' => __( 'Limit stats fields to the specified items.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_slug_list',
|
||||
@@ -397,12 +235,6 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
'type' => 'string',
|
||||
),
|
||||
);
|
||||
$params['force_cache_refresh'] = array(
|
||||
'description' => __( 'Force retrieval of fresh data instead of from the cache.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'sanitize_callback' => 'wp_validate_boolean',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
@@ -82,28 +82,13 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
$data[] = $this->prepare_response_for_collection( $item );
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $data );
|
||||
$response->header( 'X-WP-Total', (int) $products_data->total );
|
||||
$response->header( 'X-WP-TotalPages', (int) $products_data->pages );
|
||||
|
||||
$page = $products_data->page_no;
|
||||
$max_pages = $products_data->pages;
|
||||
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
if ( $page > 1 ) {
|
||||
$prev_page = $page - 1;
|
||||
if ( $prev_page > $max_pages ) {
|
||||
$prev_page = $max_pages;
|
||||
}
|
||||
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
if ( $max_pages > $page ) {
|
||||
$next_page = $page + 1;
|
||||
$next_link = add_query_arg( 'page', $next_page, $base );
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
return $this->add_pagination_headers(
|
||||
$request,
|
||||
$data,
|
||||
(int) $products_data->total,
|
||||
(int) $products_data->page_no,
|
||||
(int) $products_data->pages
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,9 +243,9 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = array();
|
||||
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
$params['page'] = array(
|
||||
$params = array();
|
||||
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
$params['page'] = array(
|
||||
'description' => __( 'Current page of the collection.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 1,
|
||||
@@ -268,7 +253,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'minimum' => 1,
|
||||
);
|
||||
$params['per_page'] = array(
|
||||
$params['per_page'] = array(
|
||||
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 10,
|
||||
@@ -277,19 +262,19 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['after'] = array(
|
||||
$params['after'] = array(
|
||||
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['before'] = array(
|
||||
$params['before'] = array(
|
||||
'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['match'] = array(
|
||||
$params['match'] = array(
|
||||
'description' => __( 'Indicates whether all the conditions should be true for the resulting set, or if any one of them is sufficient. Match affects the following parameters: status_is, status_is_not, product_includes, product_excludes, coupon_includes, coupon_excludes, customer, categories', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'all',
|
||||
@@ -299,14 +284,14 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['order'] = array(
|
||||
$params['order'] = array(
|
||||
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'desc',
|
||||
'enum' => array( 'asc', 'desc' ),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['orderby'] = array(
|
||||
$params['orderby'] = array(
|
||||
'description' => __( 'Sort collection by object attribute.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'date',
|
||||
@@ -319,7 +304,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['product_includes'] = array(
|
||||
$params['product_includes'] = array(
|
||||
'description' => __( 'Limit result set to items that have the specified parent product(s).', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -329,7 +314,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['product_excludes'] = array(
|
||||
$params['product_excludes'] = array(
|
||||
'description' => __( 'Limit result set to items that don\'t have the specified parent product(s).', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -339,7 +324,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
);
|
||||
$params['variations'] = array(
|
||||
$params['variations'] = array(
|
||||
'description' => __( 'Limit result to items with specified variation ids.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
@@ -348,14 +333,14 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'type' => 'integer',
|
||||
),
|
||||
);
|
||||
$params['extended_info'] = array(
|
||||
$params['extended_info'] = array(
|
||||
'description' => __( 'Add additional piece of info about each variation to the report.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'default' => false,
|
||||
'sanitize_callback' => 'wc_string_to_bool',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['attribute_is'] = array(
|
||||
$params['attribute_is'] = array(
|
||||
'description' => __( 'Limit result set to variations that include the specified attributes.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -364,7 +349,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'default' => array(),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['attribute_is_not'] = array(
|
||||
$params['attribute_is_not'] = array(
|
||||
'description' => __( 'Limit result set to variations that don\'t include the specified attributes.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
@@ -373,7 +358,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'default' => array(),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['category_includes'] = array(
|
||||
$params['category_includes'] = array(
|
||||
'description' => __( 'Limit result set to variations in the specified categories.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
@@ -382,7 +367,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'type' => 'integer',
|
||||
),
|
||||
);
|
||||
$params['category_excludes'] = array(
|
||||
$params['category_excludes'] = array(
|
||||
'description' => __( 'Limit result set to variations not in the specified categories.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
|
||||
@@ -9,22 +9,18 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Variations\Stats;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Admin\API\Reports\GenericStatsController;
|
||||
use Automattic\WooCommerce\Admin\API\Reports\ParameterException;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* REST API Reports variations stats controller class.
|
||||
*
|
||||
* @internal
|
||||
* @extends WC_REST_Reports_Controller
|
||||
* @extends GenericStatsController
|
||||
*/
|
||||
class Controller extends \WC_REST_Reports_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc-analytics';
|
||||
class Controller extends GenericStatsController {
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
@@ -100,46 +96,24 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
$out_data['intervals'][] = $this->prepare_response_for_collection( $item );
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $out_data );
|
||||
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||
|
||||
$page = $report_data->page_no;
|
||||
$max_pages = $report_data->pages;
|
||||
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
if ( $page > 1 ) {
|
||||
$prev_page = $page - 1;
|
||||
if ( $prev_page > $max_pages ) {
|
||||
$prev_page = $max_pages;
|
||||
}
|
||||
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
||||
$response->link_header( 'prev', $prev_link );
|
||||
}
|
||||
if ( $max_pages > $page ) {
|
||||
$next_page = $page + 1;
|
||||
$next_link = add_query_arg( 'page', $next_page, $base );
|
||||
$response->link_header( 'next', $next_link );
|
||||
}
|
||||
|
||||
return $response;
|
||||
return $this->add_pagination_headers(
|
||||
$request,
|
||||
$out_data,
|
||||
(int) $report_data->total,
|
||||
(int) $report_data->page_no,
|
||||
(int) $report_data->pages
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a report object for serialization.
|
||||
*
|
||||
* @param Array $report Report data.
|
||||
* @param array $report Report data.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function prepare_item_for_response( $report, $request ) {
|
||||
$data = $report;
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
$response = parent::prepare_item_for_response( $report, $request );
|
||||
|
||||
/**
|
||||
* Filter a report returned from the API.
|
||||
@@ -154,12 +128,13 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Report's schema, conforming to JSON Schema.
|
||||
* Get the Report's item properties schema.
|
||||
* Will be used by `get_item_schema` as `totals` and `subtotals`.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$data_values = array(
|
||||
protected function get_item_properties_schema() {
|
||||
return array(
|
||||
'items_sold' => array(
|
||||
'title' => __( 'Variations Sold', 'woocommerce' ),
|
||||
'description' => __( 'Number of variation items sold.', 'woocommerce' ),
|
||||
@@ -182,106 +157,27 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
'readonly' => true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
$segments = array(
|
||||
'segments' => array(
|
||||
'description' => __( 'Reports data grouped by segment condition.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'items' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'segment_id' => array(
|
||||
'description' => __( 'Segment identificator.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'segment_label' => array(
|
||||
'description' => __( 'Human readable segment label, either product or variation name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'enum' => array( 'day', 'week', 'month', 'year' ),
|
||||
),
|
||||
'subtotals' => array(
|
||||
'description' => __( 'Interval subtotals.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => $data_values,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
/**
|
||||
* Get the Report's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$schema = parent::get_item_schema();
|
||||
$schema['title'] = 'report_variations_stats';
|
||||
|
||||
$segment_label = array(
|
||||
'description' => __( 'Human readable segment label, either product or variation name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'enum' => array( 'day', 'week', 'month', 'year' ),
|
||||
);
|
||||
|
||||
$totals = array_merge( $data_values, $segments );
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'report_variations_stats',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'totals' => array(
|
||||
'description' => __( 'Totals data.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => $totals,
|
||||
),
|
||||
'intervals' => array(
|
||||
'description' => __( 'Reports data grouped by intervals.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'items' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'interval' => array(
|
||||
'description' => __( 'Type of interval.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'enum' => array( 'day', 'week', 'month', 'year' ),
|
||||
),
|
||||
'date_start' => array(
|
||||
'description' => __( "The date the report start, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_start_gmt' => array(
|
||||
'description' => __( 'The date the report start, as GMT.', 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_end' => array(
|
||||
'description' => __( "The date the report end, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_end_gmt' => array(
|
||||
'description' => __( 'The date the report end, as GMT.', 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'subtotals' => array(
|
||||
'description' => __( 'Interval subtotals.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => $totals,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
$schema['properties']['totals']['properties']['segments']['items']['properties']['segment_label'] = $segment_label;
|
||||
$schema['properties']['intervals']['items']['properties']['subtotals']['properties']['segments']['items']['properties']['segment_label'] = $segment_label;
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
@@ -313,37 +209,7 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = array();
|
||||
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
$params['page'] = array(
|
||||
'description' => __( 'Current page of the collection.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 1,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'minimum' => 1,
|
||||
);
|
||||
$params['per_page'] = array(
|
||||
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 10,
|
||||
'minimum' => 1,
|
||||
'maximum' => 100,
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['after'] = array(
|
||||
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['before'] = array(
|
||||
'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params = parent::get_collection_params();
|
||||
$params['match'] = array(
|
||||
'description' => __( 'Indicates whether all the conditions should be true for the resulting set, or if any one of them is sufficient. Match affects the following parameters: status_is, status_is_not, product_includes, product_excludes, coupon_includes, coupon_excludes, customer, categories', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
@@ -354,43 +220,16 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['order'] = array(
|
||||
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'desc',
|
||||
'enum' => array( 'asc', 'desc' ),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['orderby'] = array(
|
||||
'description' => __( 'Sort collection by object attribute.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'date',
|
||||
'enum' => array(
|
||||
'date',
|
||||
'net_revenue',
|
||||
'coupons',
|
||||
'refunds',
|
||||
'shipping',
|
||||
'taxes',
|
||||
'net_revenue',
|
||||
'orders_count',
|
||||
'items_sold',
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['interval'] = array(
|
||||
'description' => __( 'Time interval to use for buckets in the returned data.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'week',
|
||||
'enum' => array(
|
||||
'hour',
|
||||
'day',
|
||||
'week',
|
||||
'month',
|
||||
'quarter',
|
||||
'year',
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
$params['orderby']['enum'] = array(
|
||||
'date',
|
||||
'net_revenue',
|
||||
'coupons',
|
||||
'refunds',
|
||||
'shipping',
|
||||
'taxes',
|
||||
'net_revenue',
|
||||
'orders_count',
|
||||
'items_sold',
|
||||
);
|
||||
$params['category_includes'] = array(
|
||||
'description' => __( 'Limit result to items from the specified categories.', 'woocommerce' ),
|
||||
@@ -476,12 +315,6 @@ class Controller extends \WC_REST_Reports_Controller {
|
||||
'default' => array(),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['force_cache_refresh'] = array(
|
||||
'description' => __( 'Force retrieval of fresh data instead of from the cache.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'sanitize_callback' => 'wp_validate_boolean',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
Type,Name,Published,"Is featured?","Visibility in catalog","Short description",Description,"Date sale price starts","Date sale price ends","Tax status","Tax class","In stock?",Stock,"Backorders allowed?","Sold individually?","Weight (lbs)","Length (in)","Width (in)","Height (in)","Allow customer reviews?","Purchase note","Sale price","Regular price",Categories,Tags,"Shipping class",Images,"Download limit","Download expiry days",Parent,"Grouped products",Upsells,Cross-sells,"External URL","Button text",Position,"Attribute 1 name","Attribute 1 value(s)","Attribute 1 visible","Attribute 1 global","Attribute 2 name","Attribute 2 value(s)","Attribute 2 visible","Attribute 2 global","Meta: _wpcom_is_markdown","Download 1 name","Download 1 URL","Download 2 name","Download 2 URL"
|
||||
"simple, downloadable, virtual",Album,0,0,visible,"This is a simple, virtual product.","Album reviewed and chosen by Woo.",,,taxable,,1,,0,0,,,,,1,,,15,Music,,,https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2022/05/album-1.jpg,1,1,,,,,,,0,,,,,,,,,1,"Single 1",https://demo.woothemes.com/woocommerce/wp-content/uploads/sites/56/2017/08/single.jpg,"Single 2",https://demo.woothemes.com/woocommerce/wp-content/uploads/sites/56/2017/08/album.jpg
|
||||
Type,Name,Published
|
||||
"simple, downloadable, virtual",,-1
|
||||
|
@@ -1,2 +1,2 @@
|
||||
Type,Name,Published,"Is featured?","Visibility in catalog","Short description",Description,"Date sale price starts","Date sale price ends","Tax status","Tax class","In stock?",Stock,"Backorders allowed?","Sold individually?","Weight (lbs)","Length (in)","Width (in)","Height (in)","Allow customer reviews?","Purchase note","Sale price","Regular price",Categories,Tags,"Shipping class",Images,"Download limit","Download expiry days",Parent,"Grouped products",Upsells,Cross-sells,"External URL","Button text",Position,"Attribute 1 name","Attribute 1 value(s)","Attribute 1 visible","Attribute 1 global","Attribute 2 name","Attribute 2 value(s)","Attribute 2 visible","Attribute 2 global","Meta: _wpcom_is_markdown","Download 1 name","Download 1 URL","Download 2 name","Download 2 URL"
|
||||
external,WordPress Pennant,0,0,visible,"This is an external product.","High Quality 36 x 12 inches Wordpress Pennant.",,,"taxable",,"1",,"0","0",,,,,"1",,,"111.05",Decor,,,https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/pennant-1.jpg,,,,,,,"https://mercantile.wordpress.org/product/wordpress-pennant/","Buy on the WordPress swag store!",0,,,,,,,,,"1",,,,
|
||||
Type,Name,Published
|
||||
external,,-1
|
||||
|
@@ -1,2 +1,2 @@
|
||||
Type,Name,Published,"Is featured?","Visibility in catalog","Short description",Description,"Date sale price starts","Date sale price ends","Tax status","Tax class","In stock?",Stock,"Backorders allowed?","Sold individually?","Weight (lbs)","Length (in)","Width (in)","Height (in)","Allow customer reviews?","Purchase note","Sale price","Regular price",Categories,Tags,"Shipping class",Images,"Download limit","Download expiry days",Parent,"Grouped products",Upsells,Cross-sells,"External URL","Button text",Position,"Attribute 1 name","Attribute 1 value(s)","Attribute 1 visible","Attribute 1 global","Attribute 2 name","Attribute 2 value(s)","Attribute 2 visible","Attribute 2 global","Meta: _wpcom_is_markdown","Download 1 name","Download 1 URL","Download 2 name","Download 2 URL"
|
||||
grouped,Logo Collection,0,0,visible,"This is a grouped product.","This collection consists of beanies, t-shirts and hoodies.",,,"taxable",,"1",,"0","0",,,,,"1",,,,Clothing,,,"https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/logo-1.jpg, https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/beanie-with-logo-1.jpg, https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/t-shirt-with-logo-1.jpg, https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/hoodie-with-logo-2.jpg",,,,"woo-hoodie-with-logo, woo-tshirt, woo-beanie",,,,,0,,,,,,,,,"1",,,,
|
||||
Type,Name,Published
|
||||
grouped,,-1
|
||||
|
||||
|
@@ -1,2 +1,2 @@
|
||||
Type,Name,Published,"Is featured?","Visibility in catalog","Short description",Description,"Date sale price starts","Date sale price ends","Tax status","Tax class","In stock?",Stock,"Backorders allowed?","Sold individually?","Weight (lbs)","Length (in)","Width (in)","Height (in)","Allow customer reviews?","Purchase note","Sale price","Regular price",Categories,Tags,"Shipping class",Images,"Download limit","Download expiry days",Parent,"Grouped products",Upsells,Cross-sells,"External URL","Button text",Position,"Attribute 1 name","Attribute 1 value(s)","Attribute 1 visible","Attribute 1 global","Attribute 2 name","Attribute 2 value(s)","Attribute 2 visible","Attribute 2 global","Meta: _wpcom_is_markdown","Download 1 name","Download 1 URL","Download 2 name","Download 2 URL"
|
||||
simple,"Hoodie with Logo",0,0,visible,"This is a simple product.","Hoodie in sweatshirt fabric made from a cotton blend.",,,taxable,,1,,0,0,2,10,6,3,1,,,45,"Clothing > Hoodies",,,https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/hoodie-with-logo-2.jpg,,,,,,,,,0,Color,Blue,1,1,,,,,1,,,,
|
||||
Type,Name,Published
|
||||
simple,,-1
|
||||
|
@@ -1,2 +1,2 @@
|
||||
Type,Name,Published,"Is featured?","Visibility in catalog","Short description",Description,"Date sale price starts","Date sale price ends","Tax status","Tax class","In stock?",Stock,"Backorders allowed?","Sold individually?","Weight (lbs)","Length (in)","Width (in)","Height (in)","Allow customer reviews?","Purchase note","Sale price","Regular price",Categories,Tags,"Shipping class",Images,"Download limit","Download expiry days",Parent,"Grouped products",Upsells,Cross-sells,"External URL","Button text",Position,"Attribute 1 name","Attribute 1 value(s)","Attribute 1 visible","Attribute 1 global","Attribute 2 name","Attribute 2 value(s)","Attribute 2 visible","Attribute 2 global","Meta: _wpcom_is_markdown","Download 1 name","Download 1 URL","Download 2 name","Download 2 URL"
|
||||
variable,"V-Neck T-Shirt",0,1,visible,"This is a variable product.","Relaxed-fit T-shirt in soft cotton (Available in Blue Green and Red).",,,taxable,,1,,0,0,.5,24,1,2,1,,,,"Clothing > Tshirts",,,"https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/vneck-tee-2.jpg, https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/vnech-tee-green-1.jpg, https://woocommercecore.mystagingwebsite.com/wp-content/uploads/2017/12/vnech-tee-blue-1.jpg",,,,,,,,,0,Color,"Blue, Green, Red",1,1,Size,"Large, Medium, Small",1,1,1,,,,
|
||||
Type,Name,Published
|
||||
variable,,-1
|
||||
|
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\BlockTemplates;
|
||||
|
||||
/**
|
||||
* Interface for block containers.
|
||||
*/
|
||||
interface BlockContainerInterface extends BlockInterface, ContainerInterface {}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\BlockTemplates;
|
||||
|
||||
/**
|
||||
* Interface for block configuration used to specify blocks in BlockTemplate.
|
||||
*/
|
||||
interface BlockInterface {
|
||||
/**
|
||||
* Key for the block name in the block configuration.
|
||||
*/
|
||||
public const NAME_KEY = 'blockName';
|
||||
|
||||
/**
|
||||
* Key for the block ID in the block configuration.
|
||||
*/
|
||||
public const ID_KEY = 'id';
|
||||
|
||||
/**
|
||||
* Key for the internal order in the block configuration.
|
||||
*/
|
||||
public const ORDER_KEY = 'order';
|
||||
|
||||
/**
|
||||
* Key for the block attributes in the block configuration.
|
||||
*/
|
||||
public const ATTRIBUTES_KEY = 'attributes';
|
||||
|
||||
/**
|
||||
* Get the block name.
|
||||
*/
|
||||
public function get_name(): string;
|
||||
|
||||
/**
|
||||
* Get the block ID.
|
||||
*/
|
||||
public function get_id(): string;
|
||||
|
||||
/**
|
||||
* Get the block order.
|
||||
*/
|
||||
public function get_order(): int;
|
||||
|
||||
/**
|
||||
* Set the block order.
|
||||
*
|
||||
* @param int $order The block order.
|
||||
*/
|
||||
public function set_order( int $order );
|
||||
|
||||
/**
|
||||
* Get the block attributes.
|
||||
*/
|
||||
public function get_attributes(): array;
|
||||
|
||||
/**
|
||||
* Set the block attributes.
|
||||
*
|
||||
* @param array $attributes The block attributes.
|
||||
*/
|
||||
public function set_attributes( array $attributes );
|
||||
|
||||
/**
|
||||
* Get the parent container that the block belongs to.
|
||||
*/
|
||||
public function &get_parent(): ?ContainerInterface;
|
||||
|
||||
/**
|
||||
* Get the root template that the block belongs to.
|
||||
*/
|
||||
public function &get_root_template(): BlockTemplateInterface;
|
||||
|
||||
/**
|
||||
* Get the block configuration as a formatted template.
|
||||
*
|
||||
* @return array The block configuration as a formatted template.
|
||||
*/
|
||||
public function get_formatted_template(): array;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\BlockTemplates;
|
||||
|
||||
/**
|
||||
* Interface for block-based template.
|
||||
*/
|
||||
interface BlockTemplateInterface extends ContainerInterface {
|
||||
/**
|
||||
* Get the template ID.
|
||||
*/
|
||||
public function get_id(): string;
|
||||
|
||||
/**
|
||||
* Get the template title.
|
||||
*/
|
||||
public function get_title(): string;
|
||||
|
||||
/**
|
||||
* Get the template description.
|
||||
*/
|
||||
public function get_description(): string;
|
||||
|
||||
/**
|
||||
* Get the template area.
|
||||
*/
|
||||
public function get_area(): string;
|
||||
|
||||
/**
|
||||
* Get a block by ID.
|
||||
*
|
||||
* @param string $block_id The block ID.
|
||||
*/
|
||||
public function get_block( string $block_id ): ?BlockInterface;
|
||||
|
||||
/**
|
||||
* Generate a block ID based on a base.
|
||||
*
|
||||
* @param string $id_base The base to use when generating an ID.
|
||||
* @return string
|
||||
*/
|
||||
public function generate_block_id( string $id_base ): string;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\BlockTemplates;
|
||||
|
||||
/**
|
||||
* Interface for block containers.
|
||||
*/
|
||||
interface ContainerInterface {
|
||||
/**
|
||||
* Get the root template that the block belongs to.
|
||||
*/
|
||||
public function &get_root_template(): BlockTemplateInterface;
|
||||
|
||||
/**
|
||||
* Get the block configuration as a formatted template.
|
||||
*/
|
||||
public function get_formatted_template(): array;
|
||||
}
|
||||
@@ -48,7 +48,7 @@ class Package {
|
||||
*/
|
||||
public static function init() {
|
||||
// Avoid double initialization when the feature plugin is in use.
|
||||
if ( defined( 'WC_ADMIN_VERSION_NUMBER' ) ) {
|
||||
if (defined( 'WC_ADMIN_VERSION_NUMBER' ) ) {
|
||||
self::$active_version = WC_ADMIN_VERSION_NUMBER;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ defined( 'ABSPATH' ) || exit;
|
||||
* A facade to allow deprecating an entire class.
|
||||
*/
|
||||
class DeprecatedClassFacade {
|
||||
|
||||
/**
|
||||
* The instance that this facade covers over.
|
||||
*
|
||||
@@ -32,6 +33,21 @@ class DeprecatedClassFacade {
|
||||
*/
|
||||
protected $instance;
|
||||
|
||||
/**
|
||||
* The name of the non-deprecated class that this facade covers.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $facade_over_classname;
|
||||
|
||||
/**
|
||||
* The version that this class was deprecated in.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $deprecated_in_version = '';
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Async Product Editor Category Field.
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\AsyncProductEditorCategoryField;
|
||||
|
||||
use Automattic\Jetpack\Constants;
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;
|
||||
use Automattic\WooCommerce\Admin\PageController;
|
||||
|
||||
/**
|
||||
* Loads assets related to the async category field for the product editor.
|
||||
*/
|
||||
class Init {
|
||||
|
||||
const FEATURE_ID = 'async-product-editor-category-field';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
if ( Features::is_enabled( self::FEATURE_ID ) ) {
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
||||
add_filter( 'woocommerce_taxonomy_args_product_cat', array( $this, 'add_metabox_args' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds meta_box_cb callback arguments for custom metabox.
|
||||
*
|
||||
* @param array $args Category taxonomy args.
|
||||
* @return array $args category taxonomy args.
|
||||
*/
|
||||
public function add_metabox_args( $args ) {
|
||||
if ( ! isset( $args['meta_box_cb'] ) ) {
|
||||
$args['meta_box_cb'] = 'WC_Meta_Box_Product_Categories::output';
|
||||
$args['meta_box_sanitize_cb'] = 'taxonomy_meta_box_sanitize_cb_checkboxes';
|
||||
}
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts needed for the product form block editor.
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
if ( ! PageController::is_embed_page() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
WCAdminAssets::register_script( 'wp-admin-scripts', 'product-category-metabox', true );
|
||||
wp_localize_script(
|
||||
'wc-admin-product-category-metabox',
|
||||
'wc_product_category_metabox_params',
|
||||
array(
|
||||
'search_categories_nonce' => wp_create_nonce( 'search-categories' ),
|
||||
'search_taxonomy_terms_nonce' => wp_create_nonce( 'search-taxonomy-terms' ),
|
||||
)
|
||||
);
|
||||
wp_enqueue_script( 'product-category-metabox' );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue styles needed for the rich text editor.
|
||||
*/
|
||||
public function enqueue_styles() {
|
||||
if ( ! PageController::is_embed_page() ) {
|
||||
return;
|
||||
}
|
||||
$version = Constants::get_constant( 'WC_VERSION' );
|
||||
|
||||
wp_register_style(
|
||||
'woocommerce_admin_product_category_metabox_styles',
|
||||
WCAdminAssets::get_url( 'product-category-metabox/style', 'css' ),
|
||||
array(),
|
||||
$version
|
||||
);
|
||||
wp_style_add_data( 'woocommerce_admin_product_category_metabox_styles', 'rtl', 'replace' );
|
||||
|
||||
wp_enqueue_style( 'woocommerce_admin_product_category_metabox_styles' );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -40,7 +40,6 @@ class Features {
|
||||
protected static $beta_features = array(
|
||||
'navigation',
|
||||
'new-product-management-experience',
|
||||
'product-block-editor',
|
||||
'settings',
|
||||
);
|
||||
|
||||
|
||||
@@ -21,6 +21,13 @@ class Favorites {
|
||||
*/
|
||||
const META_NAME = 'navigation_favorites';
|
||||
|
||||
/**
|
||||
* Favorites instance.
|
||||
*
|
||||
* @var Favorites|null
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* Get class instance.
|
||||
*/
|
||||
|
||||
@@ -16,6 +16,27 @@ class DeprecatedExtendedTask extends Task {
|
||||
*/
|
||||
public $id = '';
|
||||
|
||||
/**
|
||||
* Additional info.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $additional_info = '';
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $content = '';
|
||||
|
||||
/**
|
||||
* Whether the task is complete or not.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $is_complete = false;
|
||||
|
||||
/**
|
||||
* Snoozeable.
|
||||
*
|
||||
@@ -30,6 +51,35 @@ class DeprecatedExtendedTask extends Task {
|
||||
*/
|
||||
public $is_dismissable = false;
|
||||
|
||||
/**
|
||||
* Whether the store is capable of viewing the task.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $can_view = true;
|
||||
|
||||
/**
|
||||
* Level.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $level = 3;
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $time;
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $title = '';
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -76,7 +126,7 @@ class DeprecatedExtendedTask extends Task {
|
||||
}
|
||||
|
||||
/**
|
||||
* Additonal info.
|
||||
* Additional info.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -36,7 +36,10 @@ class TaskLists {
|
||||
protected static $default_tasks_loaded = false;
|
||||
|
||||
/**
|
||||
* Array of default tasks.
|
||||
* The contents of this array is used in init_tasks() to run their init() methods.
|
||||
* If the classes do not have an init() method then nothing is executed.
|
||||
* Beyond that, adding tasks to this list has no effect, see init_default_lists() for the list of tasks.
|
||||
* that are added for each task list.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
@@ -108,21 +111,39 @@ class TaskLists {
|
||||
* Initialize default lists.
|
||||
*/
|
||||
public static function init_default_lists() {
|
||||
$tasks = array(
|
||||
'CustomizeStore',
|
||||
'StoreDetails',
|
||||
'Purchase',
|
||||
'Products',
|
||||
'WooCommercePayments',
|
||||
'Payments',
|
||||
'Tax',
|
||||
'Shipping',
|
||||
'Marketing',
|
||||
'Appearance',
|
||||
);
|
||||
|
||||
if ( Features::is_enabled( 'core-profiler' ) ) {
|
||||
$key = array_search( 'StoreDetails', $tasks, true );
|
||||
if ( false !== $key ) {
|
||||
unset( $tasks[ $key ] );
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the old Personalize your store task if the new CustomizeStore is enabled.
|
||||
$task_to_remove = Features::is_enabled( 'customize-store' ) ? 'Appearance' : 'CustomizeStore';
|
||||
$store_customisation_task_index = array_search( $task_to_remove, $tasks, true );
|
||||
|
||||
if ( false !== $store_customisation_task_index ) {
|
||||
unset( $tasks[ $store_customisation_task_index ] );
|
||||
}
|
||||
|
||||
self::add_list(
|
||||
array(
|
||||
'id' => 'setup',
|
||||
'title' => __( 'Get ready to start selling', 'woocommerce' ),
|
||||
'tasks' => array(
|
||||
'StoreDetails',
|
||||
'Purchase',
|
||||
'Products',
|
||||
'WooCommercePayments',
|
||||
'Payments',
|
||||
'Tax',
|
||||
'Shipping',
|
||||
'Marketing',
|
||||
'Appearance',
|
||||
),
|
||||
'tasks' => $tasks,
|
||||
'display_progress_header' => true,
|
||||
'event_prefix' => 'tasklist_',
|
||||
'options' => array(
|
||||
@@ -152,45 +173,6 @@ class TaskLists {
|
||||
),
|
||||
)
|
||||
);
|
||||
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(
|
||||
@@ -200,13 +182,6 @@ class TaskLists {
|
||||
)
|
||||
);
|
||||
|
||||
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(
|
||||
@@ -225,7 +200,6 @@ class TaskLists {
|
||||
if ( ! wp_is_mobile() ) { // Permit In-App Marketplace Tour on desktops only.
|
||||
$tour_task = new TourInAppMarketplace();
|
||||
self::add_task( 'extended', $tour_task );
|
||||
self::add_task( 'extended_two_column', $tour_task );
|
||||
}
|
||||
|
||||
if ( has_filter( 'woocommerce_admin_experimental_onboarding_tasklists' ) ) {
|
||||
@@ -253,7 +227,7 @@ class TaskLists {
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporarily store the active task to persist across page loads when neccessary.
|
||||
* Temporarily store the active task to persist across page loads when necessary.
|
||||
* Most tasks do not need this.
|
||||
*/
|
||||
public static function set_active_task() {
|
||||
@@ -472,7 +446,7 @@ class TaskLists {
|
||||
|
||||
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
|
||||
$submenu['woocommerce'][ $key ][0] .= ' <span class="awaiting-mod update-plugins remaining-tasks-badge woocommerce-task-list-remaining-tasks-badge"><span class="count-' . esc_attr( $tasks_count ) . '">' . absint( $tasks_count ) . '</span></span>'; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks;
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\Payments;
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\WooCommercePayments;
|
||||
use Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions\Init;
|
||||
|
||||
/**
|
||||
* Payments Task
|
||||
@@ -14,10 +15,18 @@ class AdditionalPayments extends Payments {
|
||||
|
||||
/**
|
||||
* Used to cache is_complete() method result.
|
||||
*
|
||||
* @var null
|
||||
*/
|
||||
private $is_complete_result = null;
|
||||
|
||||
/**
|
||||
* Used to cache can_view() method result.
|
||||
*
|
||||
* @var null
|
||||
*/
|
||||
private $can_view_result = null;
|
||||
|
||||
|
||||
/**
|
||||
* ID.
|
||||
@@ -67,8 +76,8 @@ class AdditionalPayments extends Payments {
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
if ( $this->is_complete_result === null ) {
|
||||
$this->is_complete_result = self::has_gateways();
|
||||
if ( null === $this->is_complete_result ) {
|
||||
$this->is_complete_result = self::has_enabled_additional_gateways();
|
||||
}
|
||||
|
||||
return $this->is_complete_result;
|
||||
@@ -85,33 +94,102 @@ class AdditionalPayments extends Payments {
|
||||
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 ( null !== $this->can_view_result ) {
|
||||
return $this->can_view_result;
|
||||
}
|
||||
if ( $this->get_parent_id() === 'extended_two_column' && WooCommercePayments::is_connected() ) {
|
||||
|
||||
// Show task if woocommerce-payments is connected or if there are any suggested gateways in other category enabled.
|
||||
$this->can_view_result = (
|
||||
WooCommercePayments::is_connected() ||
|
||||
self::has_enabled_other_category_gateways()
|
||||
);
|
||||
|
||||
// Early return if task is not visible.
|
||||
if ( ! $this->can_view_result ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
// Show task if there are any suggested gateways in additional category.
|
||||
$this->can_view_result = ! empty( self::get_suggestion_gateways( 'category_additional' ) );
|
||||
|
||||
return $this->can_view_result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the store has any enabled gateways.
|
||||
* Check if the store has any enabled gateways in other category.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_gateways() {
|
||||
private static function has_enabled_other_category_gateways() {
|
||||
$other_gateways = self::get_suggestion_gateways( 'category_other' );
|
||||
$other_gateways_ids = wp_list_pluck( $other_gateways, 'id' );
|
||||
|
||||
return self::has_enabled_gateways(
|
||||
function( $gateway ) use ( $other_gateways_ids ) {
|
||||
return in_array( $gateway->id, $other_gateways_ids, true );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the store has any enabled gateways in additional category.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function has_enabled_additional_gateways() {
|
||||
$additional_gateways = self::get_suggestion_gateways( 'category_additional' );
|
||||
$additional_gateways_ids = wp_list_pluck( $additional_gateways, 'id' );
|
||||
|
||||
return self::has_enabled_gateways(
|
||||
function( $gateway ) use ( $additional_gateways_ids ) {
|
||||
return 'yes' === $gateway->enabled
|
||||
&& in_array( $gateway->id, $additional_gateways_ids, true );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the store has any enabled gateways based on the given criteria.
|
||||
*
|
||||
* @param callable|null $filter A callback function to filter the gateways.
|
||||
* @return bool
|
||||
*/
|
||||
private static function has_enabled_gateways( $filter = null ) {
|
||||
$gateways = WC()->payment_gateways->get_available_payment_gateways();
|
||||
$enabled_gateways = array_filter(
|
||||
$gateways,
|
||||
function( $gateway ) {
|
||||
return 'yes' === $gateway->enabled && 'woocommerce_payments' !== $gateway->id;
|
||||
function( $gateway ) use ( $filter ) {
|
||||
if ( is_callable( $filter ) ) {
|
||||
return 'yes' === $gateway->enabled && call_user_func( $filter, $gateway );
|
||||
} else {
|
||||
return 'yes' === $gateway->enabled;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return ! empty( $enabled_gateways );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of gateways to suggest.
|
||||
*
|
||||
* @param string $filter_by Filter by category. "category_additional" or "category_other".
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function get_suggestion_gateways( $filter_by = 'category_additional' ) {
|
||||
$country = wc_get_base_location()['country'];
|
||||
$plugin_suggestions = Init::get_suggestions();
|
||||
$plugin_suggestions = array_filter(
|
||||
$plugin_suggestions,
|
||||
function( $plugin ) use ( $country, $filter_by ) {
|
||||
if ( ! isset( $plugin->{$filter_by} ) || ! isset( $plugin->plugins[0] ) ) {
|
||||
return false;
|
||||
}
|
||||
return in_array( $country, $plugin->{$filter_by}, true );
|
||||
}
|
||||
);
|
||||
return $plugin_suggestions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,10 +76,11 @@ class Appearance extends Task {
|
||||
*/
|
||||
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(),
|
||||
'has_homepage' => self::has_homepage(),
|
||||
'has_products' => Products::has_products(),
|
||||
'stylesheet' => get_option( 'stylesheet' ),
|
||||
'theme_mods' => get_theme_mods(),
|
||||
'support_custom_logo' => false !== get_theme_support( 'custom-logo' ),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
|
||||
|
||||
/**
|
||||
* Customize Your Store Task
|
||||
*/
|
||||
class CustomizeStore extends Task {
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return 'customize-store';
|
||||
}
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return __( 'Customize your store ', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_time() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Task completion.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
return get_option( 'woocommerce_admin_customize_store_completed' ) === 'yes';
|
||||
}
|
||||
|
||||
/**
|
||||
* Task visibility.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_view() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Action URL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_action_url() {
|
||||
return admin_url( 'wp-admin/admin.php?page=wc-admin&path=%2Fcustomize-store' );
|
||||
}
|
||||
}
|
||||
@@ -69,6 +69,16 @@ class StoreDetails extends Task {
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
$country = WC()->countries->get_base_country();
|
||||
$country_locale = WC()->countries->get_country_locale();
|
||||
$locale = $country_locale[ $country ] ?? array();
|
||||
|
||||
$hide_postcode = $locale['postcode']['hidden'] ?? false;
|
||||
// If postcode is hidden, just check that the store address and city are set.
|
||||
if ( $hide_postcode ) {
|
||||
return get_option( 'woocommerce_store_address', '' ) !== '' && get_option( 'woocommerce_store_city', '' ) !== '';
|
||||
}
|
||||
|
||||
// Mark as completed if the store address, city and postcode are set. We don't need to check the country because it's set by default.
|
||||
return get_option( 'woocommerce_store_address', '' ) !== '' && get_option( 'woocommerce_store_city', '' ) !== '' &&
|
||||
get_option( 'woocommerce_store_postcode', '' ) !== '';
|
||||
|
||||
@@ -6,12 +6,19 @@ 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;
|
||||
use Automattic\WooCommerce\Internal\Admin\WCPayPromotion\Init as WCPayPromotionInit;
|
||||
|
||||
/**
|
||||
* WooCommercePayments Task
|
||||
*/
|
||||
class WooCommercePayments extends Task {
|
||||
/**
|
||||
* Used to cache is_complete() method result.
|
||||
*
|
||||
* @var null
|
||||
*/
|
||||
private $is_complete_result = null;
|
||||
|
||||
/**
|
||||
* ID.
|
||||
*
|
||||
@@ -27,7 +34,7 @@ class WooCommercePayments extends Task {
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return __( 'Set up WooCommerce Payments', 'woocommerce' );
|
||||
return __( 'Set up WooPayments', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,7 +44,7 @@ class WooCommercePayments extends Task {
|
||||
*/
|
||||
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.",
|
||||
"You're only one step away from getting paid. Verify your business details to start managing transactions with WooPayments.",
|
||||
'woocommerce'
|
||||
);
|
||||
}
|
||||
@@ -66,8 +73,15 @@ class WooCommercePayments extends Task {
|
||||
* @return string
|
||||
*/
|
||||
public function get_additional_info() {
|
||||
if ( WCPayPromotionInit::is_woopay_eligible() ) {
|
||||
return __(
|
||||
'By using WooPayments you agree to be bound by our <a href="https://wordpress.com/tos/" target="_blank">Terms of Service</a> (including WooPay <a href="https://wordpress.com/tos/#more-woopay-specifically" target="_blank">merchant terms</a>) and acknowledge that you have read our <a href="https://automattic.com/privacy/" target="_blank">Privacy Policy</a>',
|
||||
'woocommerce'
|
||||
);
|
||||
}
|
||||
|
||||
return __(
|
||||
'By using 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>',
|
||||
'By using WooPayments you agree to be bound by our <a href="https://wordpress.com/tos/" target="_blank">Terms of Service</a> and acknowledge that you have read our <a href="https://automattic.com/privacy/" target="_blank">Privacy Policy</a>',
|
||||
'woocommerce'
|
||||
);
|
||||
}
|
||||
@@ -78,7 +92,11 @@ class WooCommercePayments extends Task {
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
return self::is_connected();
|
||||
if ( null === $this->is_complete_result ) {
|
||||
$this->is_complete_result = self::is_connected();
|
||||
}
|
||||
|
||||
return $this->is_complete_result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,8 +109,7 @@ class WooCommercePayments extends Task {
|
||||
|
||||
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() );
|
||||
self::is_supported();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -317,8 +317,8 @@ class DefaultPaymentGateways {
|
||||
),
|
||||
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' ),
|
||||
'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' ),
|
||||
@@ -390,10 +390,58 @@ class DefaultPaymentGateways {
|
||||
'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_countries(
|
||||
array(
|
||||
'US',
|
||||
'CA',
|
||||
'MX',
|
||||
'BR',
|
||||
'AR',
|
||||
'CL',
|
||||
'CO',
|
||||
'EC',
|
||||
'PE',
|
||||
'UY',
|
||||
'VE',
|
||||
'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',
|
||||
'AU',
|
||||
'NZ',
|
||||
'HK',
|
||||
'JP',
|
||||
'SG',
|
||||
'CN',
|
||||
'ID',
|
||||
'IN',
|
||||
)
|
||||
),
|
||||
self::get_rules_for_cbd( false ),
|
||||
),
|
||||
@@ -446,7 +494,6 @@ class DefaultPaymentGateways {
|
||||
'SG',
|
||||
'CN',
|
||||
'ID',
|
||||
'IN',
|
||||
),
|
||||
'category_additional' => array(
|
||||
'US',
|
||||
@@ -500,6 +547,7 @@ class DefaultPaymentGateways {
|
||||
'SG',
|
||||
'CN',
|
||||
'ID',
|
||||
'IN',
|
||||
),
|
||||
),
|
||||
array(
|
||||
@@ -548,7 +596,13 @@ class DefaultPaymentGateways {
|
||||
'JP',
|
||||
)
|
||||
),
|
||||
self::get_rules_for_selling_venues( array( 'brick-mortar', 'brick-mortar-other' ) ),
|
||||
(object) array(
|
||||
'type' => 'or',
|
||||
'operands' => (object) array(
|
||||
self::get_rules_for_selling_venues( array( 'brick-mortar', 'brick-mortar-other' ) ),
|
||||
self::get_rules_selling_offline(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -677,6 +731,7 @@ class DefaultPaymentGateways {
|
||||
'AR',
|
||||
'CL',
|
||||
'CO',
|
||||
'EC',
|
||||
'PE',
|
||||
'UY',
|
||||
'MX',
|
||||
@@ -689,6 +744,7 @@ class DefaultPaymentGateways {
|
||||
'AR',
|
||||
'CL',
|
||||
'CO',
|
||||
'EC',
|
||||
'PE',
|
||||
'UY',
|
||||
'MX',
|
||||
@@ -699,15 +755,15 @@ class DefaultPaymentGateways {
|
||||
// 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' ),
|
||||
'title' => __( 'WooPayments', 'woocommerce' ),
|
||||
'content' => __(
|
||||
'Manage transactions without leaving your WordPress Dashboard. Only with WooCommerce Payments.',
|
||||
'Manage transactions without leaving your WordPress Dashboard. Only with WooPayments.',
|
||||
'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' ),
|
||||
'description' => __( 'With WooPayments, 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() ),
|
||||
@@ -741,15 +797,15 @@ class DefaultPaymentGateways {
|
||||
),
|
||||
array(
|
||||
'id' => 'woocommerce_payments:without-in-person-payments',
|
||||
'title' => __( 'WooCommerce Payments', 'woocommerce' ),
|
||||
'title' => __( 'WooPayments', 'woocommerce' ),
|
||||
'content' => __(
|
||||
'Manage transactions without leaving your WordPress Dashboard. Only with WooCommerce Payments.',
|
||||
'Manage transactions without leaving your WordPress Dashboard. Only with WooPayments.',
|
||||
'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' ),
|
||||
'description' => __( 'With WooPayments, 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' ) ) ),
|
||||
@@ -776,15 +832,15 @@ class DefaultPaymentGateways {
|
||||
// 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' ),
|
||||
'title' => __( 'WooPayments', 'woocommerce' ),
|
||||
'content' => __(
|
||||
'Manage transactions without leaving your WordPress Dashboard. Only with WooCommerce Payments.',
|
||||
'Manage transactions without leaving your WordPress Dashboard. Only with WooPayments.',
|
||||
'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' ),
|
||||
'description' => __( 'With WooPayments, 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' ) ),
|
||||
@@ -848,7 +904,7 @@ class DefaultPaymentGateways {
|
||||
* @return array Array of countries.
|
||||
*/
|
||||
public static function get_wcpay_countries() {
|
||||
return array( 'US', 'PR', 'AU', 'CA', 'CY', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'IE', 'IT', 'LU', 'LT', 'LV', 'NO', 'NZ', 'MT', 'AT', 'BE', 'NL', 'PL', 'PT', 'CH', 'HK', 'SI', 'SK', 'SG' );
|
||||
return array( 'US', 'PR', 'AU', 'CA', 'CY', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'IE', 'IT', 'LU', 'LT', 'LV', 'NO', 'NZ', 'MT', 'AT', 'BE', 'NL', 'PL', 'PT', 'CH', 'HK', 'SI', 'SK', 'SG', 'BG', 'CZ', 'HR', 'HU', 'RO', 'SE', 'JP', 'AE' );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -907,6 +963,29 @@ class DefaultPaymentGateways {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rules for when selling offline for core profiler.
|
||||
*
|
||||
* @return object Rules to match.
|
||||
*/
|
||||
public static function get_rules_selling_offline() {
|
||||
return (object) array(
|
||||
'type' => 'option',
|
||||
'transformers' => array(
|
||||
(object) array(
|
||||
'use' => 'dot_notation',
|
||||
'arguments' => (object) array(
|
||||
'path' => 'selling_online_answer',
|
||||
),
|
||||
),
|
||||
),
|
||||
'option_name' => 'woocommerce_onboarding_profile',
|
||||
'operation' => 'contains',
|
||||
'value' => 'no_im_selling_offline',
|
||||
'default' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default rules for CBD based on given argument.
|
||||
*
|
||||
@@ -1107,7 +1186,7 @@ class DefaultPaymentGateways {
|
||||
'BO' => [],
|
||||
'CL' => [ 'woo-mercado-pago-custom', 'ppcp-gateway' ],
|
||||
'CO' => [ 'woo-mercado-pago-custom', 'ppcp-gateway' ],
|
||||
'EC' => [ 'ppcp-gateway' ],
|
||||
'EC' => [ 'woo-mercado-pago-custom', 'ppcp-gateway' ],
|
||||
'FK' => [],
|
||||
'GF' => [],
|
||||
'GY' => [],
|
||||
@@ -1153,9 +1232,9 @@ class DefaultPaymentGateways {
|
||||
'GU' => [],
|
||||
'ID' => [ 'stripe', 'ppcp-gateway' ],
|
||||
'IN' => [ 'stripe', 'razorpay', 'payubiz', 'ppcp-gateway' ],
|
||||
'ZA' => [ 'payfast', 'paystack', 'ppcp-gateway' ],
|
||||
'NG' => [ 'paystack', 'ppcp-gateway' ],
|
||||
'GH' => [ 'paystack', 'ppcp-gateway' ],
|
||||
'ZA' => [ 'payfast', 'paystack' ],
|
||||
'NG' => [ 'paystack' ],
|
||||
'GH' => [ 'paystack' ],
|
||||
);
|
||||
|
||||
// If the country code is not in the list, return default priority.
|
||||
|
||||
@@ -20,10 +20,32 @@ class BlockRegistry {
|
||||
* Array of all available product blocks.
|
||||
*/
|
||||
const PRODUCT_BLOCKS = [
|
||||
'woocommerce/product-name',
|
||||
'woocommerce/product-pricing',
|
||||
'woocommerce/conditional',
|
||||
'woocommerce/product-catalog-visibility-field',
|
||||
'woocommerce/product-category-field',
|
||||
'woocommerce/product-checkbox-field',
|
||||
'woocommerce/product-collapsible',
|
||||
'woocommerce/product-description-field',
|
||||
'woocommerce/product-images-field',
|
||||
'woocommerce/product-inventory-email-field',
|
||||
'woocommerce/product-sku-field',
|
||||
'woocommerce/product-name-field',
|
||||
'woocommerce/product-pricing-field',
|
||||
'woocommerce/product-radio-field',
|
||||
'woocommerce/product-regular-price-field',
|
||||
'woocommerce/product-sale-price-field',
|
||||
'woocommerce/product-schedule-sale-fields',
|
||||
'woocommerce/product-section',
|
||||
'woocommerce/product-shipping-class-field',
|
||||
'woocommerce/product-shipping-dimensions-fields',
|
||||
'woocommerce/product-summary-field',
|
||||
'woocommerce/product-tab',
|
||||
'woocommerce/product-inventory-quantity-field',
|
||||
'woocommerce/product-toggle-field',
|
||||
'woocommerce/product-variation-items-field',
|
||||
'woocommerce/product-variations-fields',
|
||||
'woocommerce/product-password-field',
|
||||
'woocommerce/product-has-variations-notice',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,32 +15,52 @@ use WP_Block_Editor_Context;
|
||||
* Loads assets related to the product block editor.
|
||||
*/
|
||||
class Init {
|
||||
|
||||
const FEATURE_ID = 'product-block-editor';
|
||||
|
||||
/**
|
||||
* Option name used to toggle this feature.
|
||||
*/
|
||||
const TOGGLE_OPTION_NAME = 'woocommerce_' . self::FEATURE_ID . '_enabled';
|
||||
|
||||
/**
|
||||
* The context name used to identify the editor.
|
||||
*/
|
||||
const EDITOR_CONTEXT_NAME = 'woocommerce/edit-product';
|
||||
|
||||
/**
|
||||
* Supported post types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $supported_post_types = array( 'simple' );
|
||||
|
||||
/**
|
||||
* Redirection controller.
|
||||
*
|
||||
* @var RedirectionController
|
||||
*/
|
||||
private $redirection_controller;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
if ( ! Features::is_enabled( 'new-product-management-experience' ) && Features::is_enabled( self::FEATURE_ID ) ) {
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) );
|
||||
add_action( 'get_edit_post_link', array( $this, 'update_edit_product_link' ), 10, 2 );
|
||||
if ( Features::is_enabled( 'product-variation-management' ) ) {
|
||||
array_push($this->supported_post_types, 'variable');
|
||||
}
|
||||
if ( Features::is_enabled( self::FEATURE_ID ) ) {
|
||||
|
||||
$this->redirection_controller = new RedirectionController( $this->supported_post_types );
|
||||
|
||||
if ( \Automattic\WooCommerce\Utilities\FeaturesUtil::feature_is_enabled( 'product_block_editor' ) ) {
|
||||
if ( ! Features::is_enabled( 'new-product-management-experience' ) ) {
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'dequeue_conflicting_styles' ), 100 );
|
||||
add_action( 'get_edit_post_link', array( $this, 'update_edit_product_link' ), 10, 2 );
|
||||
}
|
||||
add_filter( 'woocommerce_admin_get_user_data_fields', array( $this, 'add_user_data_fields' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
||||
add_filter( 'woocommerce_register_post_type_product', array( $this, 'add_product_template' ) );
|
||||
|
||||
add_action( 'current_screen', array( $this, 'set_current_screen_to_block_editor_if_wc_admin' ) );
|
||||
|
||||
$block_registry = new BlockRegistry();
|
||||
$block_registry->init();
|
||||
|
||||
$tracks = new Tracks();
|
||||
$tracks->init();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,8 +76,9 @@ class Init {
|
||||
|
||||
$editor_settings = array();
|
||||
if ( ! empty( $post_type_object->template ) ) {
|
||||
$editor_settings['template'] = $post_type_object->template;
|
||||
$editor_settings['templateLock'] = ! empty( $post_type_object->template_lock ) ? $post_type_object->template_lock : false;
|
||||
$editor_settings['template'] = $post_type_object->template;
|
||||
$editor_settings['templateLock'] = ! empty( $post_type_object->template_lock ) ? $post_type_object->template_lock : false;
|
||||
$editor_settings['__unstableResolvedAssets'] = $this->get_resolved_assets();
|
||||
}
|
||||
|
||||
$editor_settings = get_block_editor_settings( $editor_settings, $block_editor_context );
|
||||
@@ -75,6 +96,7 @@ class Init {
|
||||
sprintf( 'wp.blocks.setCategories( %s );', wp_json_encode( $editor_settings['blockCategories'] ) ),
|
||||
'before'
|
||||
);
|
||||
wp_tinymce_inline_scripts();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,6 +117,17 @@ class Init {
|
||||
do_action( 'enqueue_block_editor_assets' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeue conflicting styles.
|
||||
*/
|
||||
public function dequeue_conflicting_styles() {
|
||||
if ( ! PageController::is_admin_or_embed_page() ) {
|
||||
return;
|
||||
}
|
||||
// Dequeing this to avoid conflicts, until we remove the 'woocommerce-page' class.
|
||||
wp_dequeue_style( 'woocommerce-blocktheme' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the edit product links when the new experience is enabled.
|
||||
*
|
||||
@@ -116,6 +149,105 @@ class Init {
|
||||
return $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resolved assets needed for the iframe editor.
|
||||
*
|
||||
* @return array Styles and scripts.
|
||||
*/
|
||||
private function get_resolved_assets() {
|
||||
if ( function_exists( 'gutenberg_resolve_assets_override' ) ) {
|
||||
return gutenberg_resolve_assets_override();
|
||||
}
|
||||
|
||||
global $pagenow;
|
||||
|
||||
$script_handles = array(
|
||||
'wp-polyfill',
|
||||
);
|
||||
// Note for core merge: only 'wp-edit-blocks' should be in this array.
|
||||
$style_handles = array(
|
||||
'wp-edit-blocks',
|
||||
);
|
||||
|
||||
if ( current_theme_supports( 'wp-block-styles' ) ) {
|
||||
$style_handles[] = 'wp-block-library-theme';
|
||||
}
|
||||
|
||||
if ( 'widgets.php' === $pagenow || 'customize.php' === $pagenow ) {
|
||||
$style_handles[] = 'wp-widgets';
|
||||
$style_handles[] = 'wp-edit-widgets';
|
||||
}
|
||||
|
||||
$block_registry = \WP_Block_Type_Registry::get_instance();
|
||||
|
||||
foreach ( $block_registry->get_all_registered() as $block_type ) {
|
||||
// In older WordPress versions, like 6.0, these properties are not defined.
|
||||
if ( isset( $block_type->style_handles ) && is_array( $block_type->style_handles ) ) {
|
||||
$style_handles = array_merge( $style_handles, $block_type->style_handles );
|
||||
}
|
||||
|
||||
if ( isset( $block_type->editor_style_handles ) && is_array( $block_type->editor_style_handles ) ) {
|
||||
$style_handles = array_merge( $style_handles, $block_type->editor_style_handles );
|
||||
}
|
||||
|
||||
if ( isset( $block_type->script_handles ) && is_array( $block_type->script_handles ) ) {
|
||||
$script_handles = array_merge( $script_handles, $block_type->script_handles );
|
||||
}
|
||||
}
|
||||
|
||||
$style_handles = array_unique( $style_handles );
|
||||
$done = wp_styles()->done;
|
||||
|
||||
ob_start();
|
||||
|
||||
// We do not need reset styles for the iframed editor.
|
||||
wp_styles()->done = array( 'wp-reset-editor-styles' );
|
||||
wp_styles()->do_items( $style_handles );
|
||||
wp_styles()->done = $done;
|
||||
|
||||
$styles = ob_get_clean();
|
||||
|
||||
$script_handles = array_unique( $script_handles );
|
||||
$done = wp_scripts()->done;
|
||||
|
||||
ob_start();
|
||||
|
||||
wp_scripts()->done = array();
|
||||
wp_scripts()->do_items( $script_handles );
|
||||
wp_scripts()->done = $done;
|
||||
|
||||
$scripts = ob_get_clean();
|
||||
|
||||
/*
|
||||
* Generate font @font-face styles for the site editor iframe.
|
||||
* Use the registered font families for printing.
|
||||
*/
|
||||
if ( class_exists( '\WP_Fonts' ) ) {
|
||||
$wp_fonts = wp_fonts();
|
||||
$registered = $wp_fonts->get_registered_font_families();
|
||||
if ( ! empty( $registered ) ) {
|
||||
$queue = $wp_fonts->queue;
|
||||
$done = $wp_fonts->done;
|
||||
|
||||
$wp_fonts->done = array();
|
||||
$wp_fonts->queue = $registered;
|
||||
|
||||
ob_start();
|
||||
$wp_fonts->do_items();
|
||||
$styles .= ob_get_clean();
|
||||
|
||||
// Reset the Web Fonts API.
|
||||
$wp_fonts->done = $done;
|
||||
$wp_fonts->queue = $queue;
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'styles' => $styles,
|
||||
'scripts' => $scripts,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue styles needed for the rich text editor.
|
||||
*
|
||||
@@ -131,6 +263,7 @@ class Init {
|
||||
array(
|
||||
'id' => 'general',
|
||||
'title' => __( 'General', 'woocommerce' ),
|
||||
'order' => 10,
|
||||
),
|
||||
array(
|
||||
array(
|
||||
@@ -138,19 +271,17 @@ class Init {
|
||||
array(
|
||||
'title' => __( 'Basic details', 'woocommerce' ),
|
||||
'description' => __( 'This info will be displayed on the product page, category pages, social media, and search results.', 'woocommerce' ),
|
||||
'icon' => array(
|
||||
'src' => '<svg xmlns="http://www.w3.org/2000/svg" view-box="0 0 24 24"><path fill-rule="evenodd" d="M5 5.5h14a.5.5 0 01.5.5v1.5a.5.5 0 01-.5.5H5a.5.5 0 01-.5-.5V6a.5.5 0 01.5-.5zM4 9.232A2 2 0 013 7.5V6a2 2 0 012-2h14a2 2 0 012 2v1.5a2 2 0 01-1 1.732V18a2 2 0 01-2 2H6a2 2 0 01-2-2V9.232zm1.5.268V18a.5.5 0 00.5.5h12a.5.5 0 00.5-.5V9.5h-13z" clip-rule="evenodd" /></svg>',
|
||||
),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-name',
|
||||
'woocommerce/product-name-field',
|
||||
array(
|
||||
'name' => 'Product name',
|
||||
'name' => 'Product name',
|
||||
'autoFocus' => true,
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-summary',
|
||||
'woocommerce/product-summary-field',
|
||||
),
|
||||
array(
|
||||
'core/columns',
|
||||
@@ -163,11 +294,11 @@ class Init {
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-pricing',
|
||||
'woocommerce/product-regular-price-field',
|
||||
array(
|
||||
'name' => 'regular_price',
|
||||
'name' => 'regular_price',
|
||||
'label' => __( 'List price', 'woocommerce' ),
|
||||
'showPricingSection' => true,
|
||||
'help' => __( 'Manage more settings in <PricingTab>Pricing.</PricingTab>', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -179,9 +310,8 @@ class Init {
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-pricing',
|
||||
'woocommerce/product-sale-price-field',
|
||||
array(
|
||||
'name' => 'sale_price',
|
||||
'label' => __( 'Sale price', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
@@ -191,20 +321,32 @@ class Init {
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-section',
|
||||
array(
|
||||
'title' => __( 'Description', 'woocommerce' ),
|
||||
'description' => __( 'What makes this product unique? What are its most important features? Enrich the product page by adding rich content using blocks.', 'woocommerce' ),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-description-field',
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-section',
|
||||
array(
|
||||
'title' => __( 'Images', 'woocommerce' ),
|
||||
'description' => sprintf(
|
||||
/* translators: %1$s: Images guide link opening tag. %2$s: Images guide link closing tag.*/
|
||||
__( 'Drag images, upload new ones or select files from your library. For best results, use JPEG files that are 1000 by 1000 pixels or larger. %1$sHow to prepare images?%2$s.', 'woocommerce' ),
|
||||
__( 'Drag images, upload new ones or select files from your library. For best results, use JPEG files that are 1000 by 1000 pixels or larger. %1$sHow to prepare images?%2$s', 'woocommerce' ),
|
||||
'<a href="http://woocommerce.com/#" target="_blank" rel="noreferrer">',
|
||||
'</a>'
|
||||
),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-images',
|
||||
'woocommerce/product-images-field',
|
||||
array(
|
||||
'images' => array(),
|
||||
),
|
||||
@@ -216,10 +358,82 @@ class Init {
|
||||
array(
|
||||
'woocommerce/product-tab',
|
||||
array(
|
||||
'id' => 'pricing',
|
||||
'title' => __( 'Pricing', 'woocommerce' ),
|
||||
'id' => 'organization',
|
||||
'title' => __( 'Organization', 'woocommerce' ),
|
||||
'order' => 15,
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-section',
|
||||
array(
|
||||
'title' => __( 'Product catalog', 'woocommerce' ),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-category-field',
|
||||
array(
|
||||
'name' => 'categories',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-catalog-visibility-field',
|
||||
array(
|
||||
'label' => __( 'Hide in product catalog', 'woocommerce' ),
|
||||
'visibilty' => 'search',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-catalog-visibility-field',
|
||||
array(
|
||||
'label' => __( 'Hide from search results', 'woocommerce' ),
|
||||
'visibilty' => 'catalog',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-checkbox-field',
|
||||
array(
|
||||
'label' => __( 'Enable product reviews', 'woocommerce' ),
|
||||
'property' => 'reviews_allowed',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-password-field',
|
||||
array(
|
||||
'label' => __( 'Require a password', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-section',
|
||||
array(
|
||||
'title' => __( 'Attributes', 'woocommerce' ),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-attributes-field',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-tab',
|
||||
array(
|
||||
'id' => 'pricing',
|
||||
'title' => __( 'Pricing', 'woocommerce' ),
|
||||
'order' => 20,
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-has-variations-notice',
|
||||
array(
|
||||
'id' => 'wc-product-notice-has-options',
|
||||
'content' => __( 'This product has options, such as size or color. You can now manage each variation\'s price and other details individually.', 'woocommerce' ),
|
||||
'buttonText' => __( 'Go to Variations', 'woocommerce' ),
|
||||
'type' => 'info',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-section',
|
||||
array(
|
||||
@@ -230,53 +444,55 @@ class Init {
|
||||
'<a href="https://woocommerce.com/posts/how-to-price-products-strategies-expert-tips/" target="_blank" rel="noreferrer">',
|
||||
'</a>'
|
||||
),
|
||||
'icon' => array(
|
||||
'src' => '<svg xmlns="http://www.w3.org/2000/svg" view-box="0 0 24 24"><path fill-rule="evenodd" d="M16.83 6.342l.602.3.625-.25.443-.176v12.569l-.443-.178-.625-.25-.603.301-1.444.723-2.41-.804-.475-.158-.474.158-2.41.803-1.445-.722-.603-.3-.625.25-.443.177V6.215l.443.178.625.25.603-.301 1.444-.722 2.41.803.475.158.474-.158 2.41-.803 1.445.722zM20 4l-1.5.6-1 .4-2-1-3 1-3-1-2 1-1-.4L5 4v17l1.5-.6 1-.4 2 1 3-1 3 1 2-1 1 .4 1.5.6V4zm-3.5 6.25v-1.5h-8v1.5h8zm0 3v-1.5h-8v1.5h8zm-8 3v-1.5h8v1.5h-8z" clip-rule="evenodd" /></svg>',
|
||||
),
|
||||
'blockGap' => 'unit-40',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'core/columns',
|
||||
'woocommerce/product-section',
|
||||
array(),
|
||||
array(
|
||||
array(
|
||||
'core/column',
|
||||
array(
|
||||
'templateLock' => 'all',
|
||||
),
|
||||
'core/columns',
|
||||
array(),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-pricing',
|
||||
'core/column',
|
||||
array(
|
||||
'name' => 'regular_price',
|
||||
'label' => __( 'List price', 'woocommerce' ),
|
||||
'showPricingSection' => true,
|
||||
'templateLock' => 'all',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-regular-price-field',
|
||||
array(
|
||||
'name' => 'regular_price',
|
||||
'label' => __( 'List price', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'core/column',
|
||||
array(
|
||||
'templateLock' => 'all',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-sale-price-field',
|
||||
array(
|
||||
'label' => __( 'Sale price', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'core/column',
|
||||
array(
|
||||
'templateLock' => 'all',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-pricing',
|
||||
array(
|
||||
'name' => 'sale_price',
|
||||
'label' => __( 'Sale price', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
'woocommerce/product-schedule-sale-fields',
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-schedule-sale-fields',
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-radio',
|
||||
'woocommerce/product-radio-field',
|
||||
array(
|
||||
'title' => __( 'Charge sales tax on', 'woocommerce' ),
|
||||
'property' => 'tax_status',
|
||||
@@ -297,7 +513,7 @@ class Init {
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/collapsible',
|
||||
'woocommerce/product-collapsible',
|
||||
array(
|
||||
'toggleText' => __( 'Advanced', 'woocommerce' ),
|
||||
'initialCollapsed' => true,
|
||||
@@ -305,7 +521,7 @@ class Init {
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-radio',
|
||||
'woocommerce/product-radio-field',
|
||||
array(
|
||||
'title' => __( 'Tax class', 'woocommerce' ),
|
||||
'description' => sprintf(
|
||||
@@ -342,8 +558,18 @@ class Init {
|
||||
array(
|
||||
'id' => 'inventory',
|
||||
'title' => __( 'Inventory', 'woocommerce' ),
|
||||
'order' => 30,
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-has-variations-notice',
|
||||
array(
|
||||
'id' => 'wc-product-notice-has-options',
|
||||
'content' => __( 'This product has options, such as size or color. You can now manage each variation\'s price and other details individually.', 'woocommerce' ),
|
||||
'buttonText' => __( 'Go to Variations', 'woocommerce' ),
|
||||
'type' => 'info',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-section',
|
||||
array(
|
||||
@@ -354,25 +580,24 @@ class Init {
|
||||
'<a href="' . admin_url( 'admin.php?page=wc-settings&tab=products§ion=inventory' ) . '" target="_blank" rel="noreferrer">',
|
||||
'</a>'
|
||||
),
|
||||
'icon' => array(
|
||||
'src' => '<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M2 2H14C14.2761 2 14.5 2.22386 14.5 2.5V9.5H11.5H10C10 10.6046 9.10457 11.5 8 11.5C6.89543 11.5 6 10.6046 6 9.5H4.5H1.5V2.5C1.5 2.22386 1.72386 2 2 2ZM1.5 11V14.5C1.5 14.7761 1.72386 15 2 15H14C14.2761 15 14.5 14.7761 14.5 14.5V11H11.1632C10.6015 12.1825 9.3962 13 8 13C6.6038 13 5.39855 12.1825 4.83682 11H1.5ZM0 9.5V2.5C0 1.39543 0.895431 0.5 2 0.5H14C15.1046 0.5 16 1.39543 16 2.5V9.5V11V14.5C16 15.6046 15.1046 16.5 14 16.5H2C0.895431 16.5 0 15.6046 0 14.5V11V9.5Z" fill="#1E1E1E"/></svg>',
|
||||
),
|
||||
'blockGap' => 'unit-40',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-sku',
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-track-inventory-fields',
|
||||
),
|
||||
array(
|
||||
'woocommerce/collapsible',
|
||||
array(
|
||||
'toggleText' => __( 'Advanced', 'woocommerce' ),
|
||||
'initialCollapsed' => true,
|
||||
'persistRender' => true,
|
||||
),
|
||||
'woocommerce/product-section',
|
||||
array(),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-sku-field',
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-toggle-field',
|
||||
array(
|
||||
'label' => __( 'Track stock quantity for this product', 'woocommerce' ),
|
||||
'property' => 'manage_stock',
|
||||
'disabled' => 'yes' !== get_option( 'woocommerce_manage_stock' ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/conditional',
|
||||
array(
|
||||
@@ -382,53 +607,115 @@ class Init {
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-radio',
|
||||
'woocommerce/product-inventory-quantity-field',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/conditional',
|
||||
array(
|
||||
'mustMatch' => array(
|
||||
'manage_stock' => array( false ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-radio-field',
|
||||
array(
|
||||
'title' => __( 'Stock status', 'woocommerce' ),
|
||||
'property' => 'stock_status',
|
||||
'options' => array(
|
||||
array(
|
||||
'title' => __( 'When out of stock', 'woocommerce' ),
|
||||
'property' => 'backorders',
|
||||
'options' => array(
|
||||
'label' => __( 'In stock', 'woocommerce' ),
|
||||
'value' => 'instock',
|
||||
),
|
||||
array(
|
||||
'label' => __( 'Out of stock', 'woocommerce' ),
|
||||
'value' => 'outofstock',
|
||||
),
|
||||
array(
|
||||
'label' => __( 'On backorder', 'woocommerce' ),
|
||||
'value' => 'onbackorder',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-collapsible',
|
||||
array(
|
||||
'toggleText' => __( 'Advanced', 'woocommerce' ),
|
||||
'initialCollapsed' => true,
|
||||
'persistRender' => true,
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-section',
|
||||
array(
|
||||
'blockGap' => 'unit-40',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/conditional',
|
||||
array(
|
||||
'mustMatch' => array(
|
||||
'manage_stock' => array( true ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-radio-field',
|
||||
array(
|
||||
'label' => __( 'Allow purchases', 'woocommerce' ),
|
||||
'value' => 'yes',
|
||||
),
|
||||
array(
|
||||
'label' => __(
|
||||
'Allow purchases, but notify customers',
|
||||
'woocommerce'
|
||||
'title' => __( 'When out of stock', 'woocommerce' ),
|
||||
'property' => 'backorders',
|
||||
'options' => array(
|
||||
array(
|
||||
'label' => __( 'Allow purchases', 'woocommerce' ),
|
||||
'value' => 'yes',
|
||||
),
|
||||
array(
|
||||
'label' => __(
|
||||
'Allow purchases, but notify customers',
|
||||
'woocommerce'
|
||||
),
|
||||
'value' => 'notify',
|
||||
),
|
||||
array(
|
||||
'label' => __( "Don't allow purchases", 'woocommerce' ),
|
||||
'value' => 'no',
|
||||
),
|
||||
),
|
||||
'value' => 'notify',
|
||||
),
|
||||
array(
|
||||
'label' => __( "Don't allow purchases", 'woocommerce' ),
|
||||
'value' => 'no',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-inventory-email-field',
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-inventory-email',
|
||||
'woocommerce/product-checkbox-field',
|
||||
array(
|
||||
'title' => __(
|
||||
'Restrictions',
|
||||
'woocommerce'
|
||||
),
|
||||
'label' => __(
|
||||
'Limit purchases to 1 item per order',
|
||||
'woocommerce'
|
||||
),
|
||||
'property' => 'sold_individually',
|
||||
'tooltip' => __(
|
||||
'When checked, customers will be able to purchase only 1 item in a single order. This is particularly useful for items that have limited quantity, like art or handmade goods.',
|
||||
'woocommerce'
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-checkbox',
|
||||
array(
|
||||
'title' => __(
|
||||
'Restrictions',
|
||||
'woocommerce'
|
||||
),
|
||||
'label' => __(
|
||||
'Limit purchases to 1 item per order',
|
||||
'woocommerce'
|
||||
),
|
||||
'property' => 'sold_individually',
|
||||
'tooltip' => __(
|
||||
'When checked, customers will be able to purchase only 1 item in a single order. This is particularly useful for items that have limited quantity, like art or handmade goods.',
|
||||
'woocommerce'
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -442,8 +729,18 @@ class Init {
|
||||
array(
|
||||
'id' => 'shipping',
|
||||
'title' => __( 'Shipping', 'woocommerce' ),
|
||||
'order' => 40,
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-has-variations-notice',
|
||||
array(
|
||||
'id' => 'wc-product-notice-has-options',
|
||||
'content' => __( 'This product has options, such as size or color. You can now manage each variation\'s price and other details individually.', 'woocommerce' ),
|
||||
'buttonText' => __( 'Go to Variations', 'woocommerce' ),
|
||||
'type' => 'info',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-section',
|
||||
array(
|
||||
@@ -454,16 +751,10 @@ class Init {
|
||||
'<a href="https://woocommerce.com/posts/how-to-calculate-shipping-costs-for-your-woocommerce-store/" target="_blank" rel="noreferrer">',
|
||||
'</a>'
|
||||
),
|
||||
'icon' => array(
|
||||
'src' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M3.5 7.75C3.5 6.7835 4.2835 6 5.25 6H14.75H15.5V6.75V9H17.25H17.5607L17.7803 9.21967L20.7803 12.2197L21 12.4393V12.75V14.75C21 15.7165 20.2165 16.5 19.25 16.5H19.2377C19.2458 16.5822 19.25 16.6656 19.25 16.75C19.25 18.1307 18.1307 19.25 16.75 19.25C15.3693 19.25 14.25 18.1307 14.25 16.75C14.25 16.6656 14.2542 16.5822 14.2623 16.5H14H10.2377C10.2458 16.5822 10.25 16.6656 10.25 16.75C10.25 18.1307 9.13071 19.25 7.75 19.25C6.36929 19.25 5.25 18.1307 5.25 16.75C5.25 16.6656 5.25418 16.5822 5.26234 16.5H4.25H3.5V15.75V7.75ZM14 15V9.75V9V7.5H5.25C5.11193 7.5 5 7.61193 5 7.75V15H5.96464C6.41837 14.5372 7.05065 14.25 7.75 14.25C8.44935 14.25 9.08163 14.5372 9.53536 15H14ZM18.5354 15H19.25C19.3881 15 19.5 14.8881 19.5 14.75V13.0607L16.9393 10.5H15.5V14.5845C15.8677 14.3717 16.2946 14.25 16.75 14.25C17.4493 14.25 18.0816 14.5372 18.5354 15ZM6.7815 16.5C6.76094 16.5799 6.75 16.6637 6.75 16.75C6.75 17.3023 7.19772 17.75 7.75 17.75C8.30228 17.75 8.75 17.3023 8.75 16.75C8.75 16.6637 8.73906 16.5799 8.7185 16.5C8.60749 16.0687 8.21596 15.75 7.75 15.75C7.28404 15.75 6.89251 16.0687 6.7815 16.5ZM15.7815 16.5C15.7609 16.5799 15.75 16.6637 15.75 16.75C15.75 17.3023 16.1977 17.75 16.75 17.75C17.3023 17.75 17.75 17.3023 17.75 16.75C17.75 16.6637 17.7391 16.5799 17.7185 16.5C17.7144 16.4841 17.7099 16.4683 17.705 16.4526C17.5784 16.0456 17.1987 15.75 16.75 15.75C16.284 15.75 15.8925 16.0687 15.7815 16.5Z" fill="#1E1E1E"/></svg>',
|
||||
),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-shipping-fee-fields',
|
||||
array(
|
||||
'title' => __( 'Shipping fee', 'woocommerce' ),
|
||||
),
|
||||
'woocommerce/product-shipping-class-field',
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-shipping-dimensions-fields',
|
||||
@@ -473,7 +764,86 @@ class Init {
|
||||
),
|
||||
),
|
||||
);
|
||||
if ( Features::is_enabled( 'product-variation-management' ) ) {
|
||||
array_push(
|
||||
$args['template'],
|
||||
array(
|
||||
'woocommerce/product-tab',
|
||||
array(
|
||||
'id' => 'variations',
|
||||
'title' => __( 'Variations', 'woocommerce' ),
|
||||
'order' => 40,
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-variations-fields',
|
||||
array(
|
||||
'description' => sprintf(
|
||||
/* translators: %1$s: Sell your product in multiple variations like size or color. strong opening tag. %2$s: Sell your product in multiple variations like size or color. strong closing tag.*/
|
||||
__( '%1$sSell your product in multiple variations like size or color.%2$s Get started by adding options for the buyers to choose on the product page.', 'woocommerce' ),
|
||||
'<strong>',
|
||||
'</strong>'
|
||||
),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'woocommerce/product-section',
|
||||
array(
|
||||
'title' => __( 'Variation options', 'woocommerce' ),
|
||||
),
|
||||
array( array( 'woocommerce/product-variations-options-field' ) ),
|
||||
),
|
||||
array(
|
||||
'woocommerce/product-section',
|
||||
array(
|
||||
'title' => __( 'Variations', 'woocommerce' ),
|
||||
),
|
||||
array( array( 'woocommerce/product-variation-items-field' ) ),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds fields so that we can store user preferences for the variations block.
|
||||
*
|
||||
* @param array $user_data_fields User data fields.
|
||||
* @return array
|
||||
*/
|
||||
public function add_user_data_fields( $user_data_fields ) {
|
||||
return array_merge(
|
||||
$user_data_fields,
|
||||
array(
|
||||
'variable_product_block_tour_shown',
|
||||
'product_block_variable_options_notice_dismissed',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current screen to the block editor if a wc-admin page.
|
||||
*/
|
||||
public function set_current_screen_to_block_editor_if_wc_admin() {
|
||||
$screen = get_current_screen();
|
||||
|
||||
// phpcs:ignore Squiz.PHP.CommentedOutCode.Found
|
||||
// (no idea why I need that phpcs:ignore above, but I'm tired trying to re-write this comment to get it to pass)
|
||||
// we can't check the 'path' query param because client-side routing is used within wc-admin,
|
||||
// so this action handler is only called on the initial page load from the server, which might
|
||||
// not be the product edit page (it mostly likely isn't).
|
||||
if ( PageController::is_admin_page() ) {
|
||||
$screen->is_block_editor( true );
|
||||
|
||||
wp_add_inline_script(
|
||||
'wp-blocks',
|
||||
'wp.blocks && wp.blocks.unstable__bootstrapServerSideBlockDefinitions && wp.blocks.unstable__bootstrapServerSideBlockDefinitions(' . wp_json_encode( get_block_editor_server_block_settings() ) . ');'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Product Editor Redirection Controller
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\ProductBlockEditor;
|
||||
|
||||
use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;
|
||||
|
||||
/**
|
||||
* Handle redirecting to the old or new editor based on features and support.
|
||||
*/
|
||||
class RedirectionController {
|
||||
|
||||
/**
|
||||
* Supported post types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $supported_post_types;
|
||||
|
||||
/**
|
||||
* Set up the hooks used for redirection.
|
||||
*
|
||||
* @param array $supported_post_types Array of supported post types.
|
||||
*/
|
||||
public function __construct( $supported_post_types ) {
|
||||
$this->supported_post_types = $supported_post_types;
|
||||
|
||||
if ( \Automattic\WooCommerce\Utilities\FeaturesUtil::feature_is_enabled( 'product_block_editor' ) ) {
|
||||
add_action( 'current_screen', array( $this, 'maybe_redirect_to_new_editor' ), 30, 0 );
|
||||
add_action( 'current_screen', array( $this, 'redirect_non_supported_product_types' ), 30, 0 );
|
||||
} else {
|
||||
add_action( 'current_screen', array( $this, 'maybe_redirect_to_old_editor' ), 30, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current screen is the legacy add product screen.
|
||||
*/
|
||||
protected function is_legacy_add_new_screen(): bool {
|
||||
$screen = get_current_screen();
|
||||
return 'post' === $screen->base && 'product' === $screen->post_type && 'add' === $screen->action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current screen is the legacy edit product screen.
|
||||
*/
|
||||
protected function is_legacy_edit_screen(): bool {
|
||||
$screen = get_current_screen();
|
||||
return 'post' === $screen->base
|
||||
&& 'product' === $screen->post_type
|
||||
&& isset( $_GET['post'] )
|
||||
&& isset( $_GET['action'] )
|
||||
&& 'edit' === $_GET['action'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a product is supported by the new experience.
|
||||
*
|
||||
* @param integer $product_id Product ID.
|
||||
*/
|
||||
protected function is_product_supported( $product_id ): bool {
|
||||
$product = $product_id ? wc_get_product( $product_id ) : null;
|
||||
$digital_product = $product->is_downloadable() || $product->is_virtual();
|
||||
return $product && in_array( $product->get_type(), $this->supported_post_types, true ) && ! $digital_product;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects from old product form to the new product form if the
|
||||
* feature `product_block_editor` is enabled.
|
||||
*/
|
||||
public function maybe_redirect_to_new_editor(): void {
|
||||
if ( $this->is_legacy_add_new_screen() ) {
|
||||
wp_safe_redirect( admin_url( 'admin.php?page=wc-admin&path=/add-product' ) );
|
||||
exit();
|
||||
}
|
||||
|
||||
if ( $this->is_legacy_edit_screen() ) {
|
||||
$product_id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : null;
|
||||
if ( ! $this->is_product_supported( $product_id ) ) {
|
||||
return;
|
||||
}
|
||||
wp_safe_redirect( admin_url( 'admin.php?page=wc-admin&path=/product/' . $product_id ) );
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects from new product form to the old product form if the
|
||||
* feature `product_block_editor` is enabled.
|
||||
*/
|
||||
public function maybe_redirect_to_old_editor(): void {
|
||||
$route = $this->get_parsed_route();
|
||||
|
||||
if ( 'add-product' === $route['page'] ) {
|
||||
wp_safe_redirect( admin_url( 'post-new.php?post_type=product' ) );
|
||||
exit();
|
||||
}
|
||||
|
||||
if ( 'product' === $route['page'] ) {
|
||||
wp_safe_redirect( admin_url( 'post.php?post=' . $route['product_id'] . '&action=edit' ) );
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parsed WooCommerce Admin path.
|
||||
*/
|
||||
protected function get_parsed_route(): array {
|
||||
if ( ! \Automattic\WooCommerce\Admin\PageController::is_admin_page() || ! isset( $_GET['path'] ) ) {
|
||||
return array(
|
||||
'page' => null,
|
||||
'product_id' => null,
|
||||
);
|
||||
}
|
||||
|
||||
$path = esc_url_raw( wp_unslash( $_GET['path'] ) );
|
||||
$path_pieces = explode( '/', wp_parse_url( $path, PHP_URL_PATH ) );
|
||||
|
||||
return array(
|
||||
'page' => $path_pieces[1],
|
||||
'product_id' => 'product' === $path_pieces[1] ? absint( $path_pieces[2] ) : null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect non supported product types to legacy editor.
|
||||
*/
|
||||
public function redirect_non_supported_product_types(): void {
|
||||
$route = $this->get_parsed_route();
|
||||
$product_id = $route['product_id'];
|
||||
|
||||
if ( 'product' === $route['page'] && ! $this->is_product_supported( $product_id ) ) {
|
||||
wp_safe_redirect( admin_url( 'post.php?post=' . $route['product_id'] . '&action=edit' ) );
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Product Block Editor
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\ProductBlockEditor;
|
||||
|
||||
/**
|
||||
* Add tracks for the product block editor.
|
||||
*/
|
||||
class Tracks {
|
||||
|
||||
/**
|
||||
* Initialize the tracks.
|
||||
*/
|
||||
public function init() {
|
||||
add_filter( 'woocommerce_product_source', array( $this, 'add_product_source' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a URL is a product editor page.
|
||||
*
|
||||
* @param string $url Url to check.
|
||||
* @return boolean
|
||||
*/
|
||||
protected function is_product_editor_page( $url ) {
|
||||
$query_string = wp_parse_url( wp_get_referer(), PHP_URL_QUERY );
|
||||
parse_str( $query_string, $query );
|
||||
|
||||
if ( ! isset( $query['page'] ) || 'wc-admin' !== $query['page'] || ! isset( $query['path'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$path_pieces = explode( '/', $query['path'] );
|
||||
$route = $path_pieces[1];
|
||||
|
||||
return 'add-product' === $route || 'product' === $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the product source if we're on the product editor page.
|
||||
*
|
||||
* @param string $source Source of product.
|
||||
* @return string
|
||||
*/
|
||||
public function add_product_source( $source ) {
|
||||
if ( $this->is_product_editor_page( wp_get_referer() ) ) {
|
||||
return 'product-block-editor-v1';
|
||||
}
|
||||
|
||||
return $source;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -209,12 +209,29 @@ class DefaultShippingPartners {
|
||||
'available_layouts' => array( 'row', 'column' ),
|
||||
),
|
||||
array(
|
||||
'title' => 'WooCommerce Shipping',
|
||||
'name' => 'WooCommerce Shipping',
|
||||
'slug' => 'woocommerce-services',
|
||||
'description' => __( 'Save time and money by printing your shipping labels right from your computer with WooCommerce Shipping. Try WooCommerce Shipping for free.', 'woocommerce' ),
|
||||
'dependencies' => array( 'jetpack' ),
|
||||
'layout_column' => array(
|
||||
'image' => $asset_base_url . 'wcs-column.svg',
|
||||
'features' => $column_layout_features,
|
||||
'features' => array(
|
||||
array(
|
||||
'icon' => $asset_base_url . 'printer.svg',
|
||||
'title' => __( 'Buy postage when you need it', 'woocommerce' ),
|
||||
'description' => __( 'No need to wonder where that stampbook went.', 'woocommerce' ),
|
||||
),
|
||||
array(
|
||||
'icon' => $asset_base_url . 'paper.svg',
|
||||
'title' => __( 'Print at home', 'woocommerce' ),
|
||||
'description' => __( 'Pick up an order, then just pay, print, package and post.', 'woocommerce' ),
|
||||
),
|
||||
array(
|
||||
'icon' => $asset_base_url . 'discount.svg',
|
||||
'title' => __( 'Discounted rates', 'woocommerce' ),
|
||||
'description' => __( 'Access discounted shipping rates with DHL and USPS.', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
'learn_more_link' => 'https://woocommerce.com/products/shipping/',
|
||||
'is_visible' => array(
|
||||
|
||||
@@ -29,6 +29,7 @@ class InstalledExtensions {
|
||||
$google = self::get_google_extension_data();
|
||||
$amazon_ebay = self::get_amazon_ebay_extension_data();
|
||||
$mailpoet = self::get_mailpoet_extension_data();
|
||||
$klaviyo = self::get_klaviyo_extension_data();
|
||||
$creative_mail = self::get_creative_mail_extension_data();
|
||||
$tiktok = self::get_tiktok_extension_data();
|
||||
$jetpack_crm = self::get_jetpack_crm_extension_data();
|
||||
@@ -73,6 +74,10 @@ class InstalledExtensions {
|
||||
$data[] = $mailpoet;
|
||||
}
|
||||
|
||||
if ( $klaviyo ) {
|
||||
$data[] = $klaviyo;
|
||||
}
|
||||
|
||||
if ( $creative_mail ) {
|
||||
$data[] = $creative_mail;
|
||||
}
|
||||
@@ -380,6 +385,33 @@ class InstalledExtensions {
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Klaviyo extension data.
|
||||
*
|
||||
* @return array|bool
|
||||
*/
|
||||
protected static function get_klaviyo_extension_data() {
|
||||
$slug = 'klaviyo';
|
||||
|
||||
if ( ! PluginsHelper::is_plugin_installed( $slug ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = self::get_extension_base_data( $slug );
|
||||
$data['icon'] = plugins_url( 'assets/images/marketing/klaviyo.png', WC_PLUGIN_FILE );
|
||||
|
||||
if ( 'activated' === $data['status'] ) {
|
||||
$klaviyo_options = get_option( 'klaviyo_settings' );
|
||||
if ( isset( $klaviyo_options['klaviyo_public_api_key'] ) ) {
|
||||
$data['status'] = 'configured';
|
||||
}
|
||||
|
||||
$data['settingsUrl'] = admin_url( 'admin.php?page=klaviyo_settings' );
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Creative Mail for WooCommerce extension data.
|
||||
*
|
||||
|
||||
@@ -391,7 +391,7 @@ class DataStore extends \WC_Data_Store_WP implements \WC_Object_Data_Store_Inter
|
||||
* @param string $type Comma separated list of note types.
|
||||
* @param string $status Comma separated list of statuses.
|
||||
* @param string $context Optional argument that the woocommerce_note_where_clauses filter can use to determine whether to apply extra conditions. Extensions should define their own contexts and use them to avoid adding to notes where clauses when not needed.
|
||||
* @return array An array of objects containing a note id.
|
||||
* @return string Count of objects with given type, status and context.
|
||||
*/
|
||||
public function get_notes_count( $type = array(), $status = array(), $context = self::WC_ADMIN_NOTE_OPER_GLOBAL ) {
|
||||
global $wpdb;
|
||||
|
||||
@@ -7,6 +7,16 @@
|
||||
|
||||
namespace Automattic\WooCommerce\Admin;
|
||||
|
||||
use ActionScheduler;
|
||||
use ActionScheduler_DBStore;
|
||||
use ActionScheduler_QueueRunner;
|
||||
use Automatic_Upgrader_Skin;
|
||||
use Automattic\WooCommerce\Admin\PluginsInstallLoggers\AsyncPluginsInstallLogger;
|
||||
use Automattic\WooCommerce\Admin\PluginsInstallLoggers\PluginsInstallLogger;
|
||||
use Plugin_Upgrader;
|
||||
use WP_Error;
|
||||
use WP_Upgrader;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
if ( ! function_exists( 'get_plugins' ) ) {
|
||||
@@ -23,6 +33,7 @@ class PluginsHelper {
|
||||
*/
|
||||
public static function init() {
|
||||
add_action( 'woocommerce_plugins_install_callback', array( __CLASS__, 'install_plugins' ), 10, 2 );
|
||||
add_action( 'woocommerce_plugins_install_and_activate_async_callback', array( __CLASS__, 'install_and_activate_plugins_async_callback' ), 10, 2 );
|
||||
add_action( 'woocommerce_plugins_activate_callback', array( __CLASS__, 'activate_plugins' ), 10, 2 );
|
||||
}
|
||||
|
||||
@@ -60,8 +71,9 @@ class PluginsHelper {
|
||||
*/
|
||||
public static function get_installed_plugin_slugs() {
|
||||
return array_map(
|
||||
function( $plugin_path ) {
|
||||
function ( $plugin_path ) {
|
||||
$path_parts = explode( '/', $plugin_path );
|
||||
|
||||
return $path_parts[0];
|
||||
},
|
||||
array_keys( get_plugins() )
|
||||
@@ -93,8 +105,9 @@ class PluginsHelper {
|
||||
*/
|
||||
public static function get_active_plugin_slugs() {
|
||||
return array_map(
|
||||
function( $plugin_path ) {
|
||||
function ( $plugin_path ) {
|
||||
$path_parts = explode( '/', $plugin_path );
|
||||
|
||||
return $path_parts[0];
|
||||
},
|
||||
get_option( 'active_plugins', array() )
|
||||
@@ -110,6 +123,7 @@ class PluginsHelper {
|
||||
*/
|
||||
public static function is_plugin_installed( $plugin ) {
|
||||
$plugin_path = self::get_plugin_path_from_slug( $plugin );
|
||||
|
||||
return $plugin_path ? array_key_exists( $plugin_path, get_plugins() ) : false;
|
||||
}
|
||||
|
||||
@@ -122,6 +136,7 @@ class PluginsHelper {
|
||||
*/
|
||||
public static function is_plugin_active( $plugin ) {
|
||||
$plugin_path = self::get_plugin_path_from_slug( $plugin );
|
||||
|
||||
return $plugin_path ? in_array( $plugin_path, get_option( 'active_plugins', array() ), true ) : false;
|
||||
}
|
||||
|
||||
@@ -142,20 +157,26 @@ class PluginsHelper {
|
||||
/**
|
||||
* Install an array of plugins.
|
||||
*
|
||||
* @param array $plugins Plugins to install.
|
||||
* @param array $plugins Plugins to install.
|
||||
* @param PluginsInstallLogger|null $logger an optional logger.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function install_plugins( $plugins ) {
|
||||
public static function install_plugins( $plugins, PluginsInstallLogger $logger = null ) {
|
||||
/**
|
||||
* Filter the list of plugins to install.
|
||||
*
|
||||
* @param array $plugins A list of the plugins to install.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*/
|
||||
$plugins = apply_filters( 'woocommerce_admin_plugins_pre_install', $plugins );
|
||||
|
||||
if ( empty( $plugins ) || ! is_array( $plugins ) ) {
|
||||
return new \WP_Error( 'woocommerce_plugins_invalid_plugins', __( 'Plugins must be a non-empty array.', 'woocommerce' ) );
|
||||
return new WP_Error(
|
||||
'woocommerce_plugins_invalid_plugins',
|
||||
__( 'Plugins must be a non-empty array.', 'woocommerce' )
|
||||
);
|
||||
}
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
@@ -169,13 +190,15 @@ class PluginsHelper {
|
||||
$installed_plugins = array();
|
||||
$results = array();
|
||||
$time = array();
|
||||
$errors = new \WP_Error();
|
||||
$errors = new WP_Error();
|
||||
|
||||
foreach ( $plugins as $plugin ) {
|
||||
$slug = sanitize_key( $plugin );
|
||||
$logger && $logger->install_requested( $plugin );
|
||||
|
||||
if ( isset( $existing_plugins[ $slug ] ) ) {
|
||||
$installed_plugins[] = $plugin;
|
||||
$logger && $logger->installed( $plugin, 0 );
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -193,8 +216,14 @@ class PluginsHelper {
|
||||
|
||||
if ( is_wp_error( $api ) ) {
|
||||
$properties = array(
|
||||
/* translators: %s: plugin slug (example: woocommerce-services) */
|
||||
'error_message' => sprintf( __( 'The requested plugin `%s` could not be installed. Plugin API call failed.', 'woocommerce' ), $slug ),
|
||||
'error_message' => sprintf(
|
||||
// translators: %s: plugin slug (example: woocommerce-services).
|
||||
__(
|
||||
'The requested plugin `%s` could not be installed. Plugin API call failed.',
|
||||
'woocommerce'
|
||||
),
|
||||
$slug
|
||||
),
|
||||
'api_error_message' => $api->get_error_message(),
|
||||
'slug' => $slug,
|
||||
);
|
||||
@@ -204,32 +233,40 @@ class PluginsHelper {
|
||||
* Action triggered when a plugin API call failed.
|
||||
*
|
||||
* @param string $slug The plugin slug.
|
||||
* @param \WP_Error $api The API response.
|
||||
* @param WP_Error $api The API response.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*/
|
||||
do_action( 'woocommerce_plugins_install_api_error', $slug, $api );
|
||||
|
||||
$errors->add(
|
||||
$plugin,
|
||||
sprintf(
|
||||
/* translators: %s: plugin slug (example: woocommerce-services) */
|
||||
__( 'The requested plugin `%s` could not be installed. Plugin API call failed.', 'woocommerce' ),
|
||||
$slug
|
||||
)
|
||||
$error_message = sprintf(
|
||||
/* translators: %s: plugin slug (example: woocommerce-services) */
|
||||
__( 'The requested plugin `%s` could not be installed. Plugin API call failed.', 'woocommerce' ),
|
||||
$slug
|
||||
);
|
||||
|
||||
$errors->add( $plugin, $error_message );
|
||||
$logger && $logger->add_error( $plugin, $error_message );
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$upgrader = new \Plugin_Upgrader( new \Automatic_Upgrader_Skin() );
|
||||
$result = $upgrader->install( $api->download_link );
|
||||
$upgrader = new Plugin_Upgrader( new Automatic_Upgrader_Skin() );
|
||||
$result = $upgrader->install( $api->download_link );
|
||||
// result can be false or WP_Error.
|
||||
$results[ $plugin ] = $result;
|
||||
$time[ $plugin ] = round( ( microtime( true ) - $start_time ) * 1000 );
|
||||
|
||||
if ( is_wp_error( $result ) || is_null( $result ) ) {
|
||||
$properties = array(
|
||||
/* translators: %s: plugin slug (example: woocommerce-services) */
|
||||
'error_message' => sprintf( __( 'The requested plugin `%s` could not be installed.', 'woocommerce' ), $slug ),
|
||||
'error_message' => sprintf(
|
||||
/* translators: %s: plugin slug (example: woocommerce-services) */
|
||||
__(
|
||||
'The requested plugin `%s` could not be installed.',
|
||||
'woocommerce'
|
||||
),
|
||||
$slug
|
||||
),
|
||||
'slug' => $slug,
|
||||
'api_version' => $api->version,
|
||||
'api_download_link' => $api->download_link,
|
||||
@@ -243,26 +280,33 @@ class PluginsHelper {
|
||||
*
|
||||
* @param string $slug The plugin slug.
|
||||
* @param object $api The plugin API object.
|
||||
* @param \WP_Error|null $result The result of the plugin installation.
|
||||
* @param \Plugin_Upgrader $upgrader The plugin upgrader.
|
||||
* @param WP_Error|null $result The result of the plugin installation.
|
||||
* @param Plugin_Upgrader $upgrader The plugin upgrader.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*/
|
||||
*/
|
||||
do_action( 'woocommerce_plugins_install_error', $slug, $api, $result, $upgrader );
|
||||
|
||||
$install_error_message = sprintf(
|
||||
/* translators: %s: plugin slug (example: woocommerce-services) */
|
||||
__( 'The requested plugin `%s` could not be installed. Upgrader install failed.', 'woocommerce' ),
|
||||
$slug
|
||||
);
|
||||
$errors->add(
|
||||
$plugin,
|
||||
sprintf(
|
||||
/* translators: %s: plugin slug (example: woocommerce-services) */
|
||||
__( 'The requested plugin `%s` could not be installed. Upgrader install failed.', 'woocommerce' ),
|
||||
$slug
|
||||
)
|
||||
$install_error_message
|
||||
);
|
||||
$logger && $logger->add_error( $plugin, $install_error_message );
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$installed_plugins[] = $plugin;
|
||||
$logger && $logger->installed( $plugin, $time[ $plugin ] );
|
||||
}
|
||||
|
||||
$logger && $logger->complete();
|
||||
|
||||
$data = array(
|
||||
'installed' => $installed_plugins,
|
||||
'results' => $results,
|
||||
@@ -273,19 +317,41 @@ class PluginsHelper {
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback regsitered by OnboardingPlugins::install_and_activate_async.
|
||||
*
|
||||
* It is used to call install_plugins and activate_plugins with a custom logger.
|
||||
*
|
||||
* @param array $plugins A list of plugins to install.
|
||||
* @param string $job_id An unique job I.D.
|
||||
* @return bool
|
||||
*/
|
||||
public static function install_and_activate_plugins_async_callback( array $plugins, string $job_id ) {
|
||||
$option_name = 'woocommerce_onboarding_plugins_install_and_activate_async_' . $job_id;
|
||||
$logger = new AsyncPluginsInstallLogger( $option_name );
|
||||
self::install_plugins( $plugins, $logger );
|
||||
self::activate_plugins( $plugins, $logger );
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule plugin installation.
|
||||
*
|
||||
* @param array $plugins Plugins to install.
|
||||
*
|
||||
* @return string Job ID.
|
||||
*/
|
||||
public static function schedule_install_plugins( $plugins ) {
|
||||
if ( empty( $plugins ) || ! is_array( $plugins ) ) {
|
||||
return new \WP_Error( 'woocommerce_plugins_invalid_plugins', __( 'Plugins must be a non-empty array.', 'woocommerce' ), 404 );
|
||||
return new WP_Error(
|
||||
'woocommerce_plugins_invalid_plugins',
|
||||
__( 'Plugins must be a non-empty array.', 'woocommerce' ),
|
||||
404
|
||||
);
|
||||
}
|
||||
|
||||
$job_id = uniqid();
|
||||
WC()->queue()->schedule_single( time() + 5, 'woocommerce_plugins_install_callback', array( $plugins, $job_id ) );
|
||||
WC()->queue()->schedule_single( time() + 5, 'woocommerce_plugins_install_callback', array( $plugins ) );
|
||||
|
||||
return $job_id;
|
||||
}
|
||||
@@ -293,12 +359,18 @@ class PluginsHelper {
|
||||
/**
|
||||
* Activate the requested plugins.
|
||||
*
|
||||
* @param array $plugins Plugins.
|
||||
* @param array $plugins Plugins.
|
||||
* @param PluginsInstallLogger|null $logger Logger.
|
||||
*
|
||||
* @return WP_Error|array Plugin Status
|
||||
*/
|
||||
public static function activate_plugins( $plugins ) {
|
||||
public static function activate_plugins( $plugins, PluginsInstallLogger $logger = null ) {
|
||||
if ( empty( $plugins ) || ! is_array( $plugins ) ) {
|
||||
return new \WP_Error( 'woocommerce_plugins_invalid_plugins', __( 'Plugins must be a non-empty array.', 'woocommerce' ), 404 );
|
||||
return new WP_Error(
|
||||
'woocommerce_plugins_invalid_plugins',
|
||||
__( 'Plugins must be a non-empty array.', 'woocommerce' ),
|
||||
404
|
||||
);
|
||||
}
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
@@ -310,12 +382,13 @@ class PluginsHelper {
|
||||
* Filter the list of plugins to activate.
|
||||
*
|
||||
* @param array $plugins A list of the plugins to activate.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*/
|
||||
$plugins = apply_filters( 'woocommerce_admin_plugins_pre_activate', $plugins );
|
||||
|
||||
$plugin_paths = self::get_installed_plugins_paths();
|
||||
$errors = new \WP_Error();
|
||||
$errors = new WP_Error();
|
||||
$activated_plugins = array();
|
||||
|
||||
foreach ( $plugins as $plugin ) {
|
||||
@@ -323,11 +396,13 @@ class PluginsHelper {
|
||||
$path = isset( $plugin_paths[ $slug ] ) ? $plugin_paths[ $slug ] : false;
|
||||
|
||||
if ( ! $path ) {
|
||||
/* translators: %s: plugin slug (example: woocommerce-services) */
|
||||
$message = sprintf( __( 'The requested plugin `%s`. is not yet installed.', 'woocommerce' ), $slug );
|
||||
$errors->add(
|
||||
$plugin,
|
||||
/* translators: %s: plugin slug (example: woocommerce-services) */
|
||||
sprintf( __( 'The requested plugin `%s`. is not yet installed.', 'woocommerce' ), $slug )
|
||||
$message
|
||||
);
|
||||
$logger && $logger->add_error( $plugin, $message );
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -337,20 +412,25 @@ class PluginsHelper {
|
||||
* Action triggered when a plugin activation fails.
|
||||
*
|
||||
* @param string $slug The plugin slug.
|
||||
* @param null|\WP_Error $result The result of the plugin activation.
|
||||
* @param null|WP_Error $result The result of the plugin activation.
|
||||
*
|
||||
* @since 6.4.0
|
||||
*/
|
||||
do_action( 'woocommerce_plugins_activate_error', $slug, $result );
|
||||
|
||||
/* translators: %s: plugin slug (example: woocommerce-services) */
|
||||
$message = sprintf( __( 'The requested plugin `%s` could not be activated.', 'woocommerce' ), $slug );
|
||||
$errors->add(
|
||||
$plugin,
|
||||
/* translators: %s: plugin slug (example: woocommerce-services) */
|
||||
sprintf( __( 'The requested plugin `%s` could not be activated.', 'woocommerce' ), $slug )
|
||||
$message
|
||||
);
|
||||
$logger && $logger->add_error( $plugin, $message );
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$activated_plugins[] = $plugin;
|
||||
$logger && $logger->activated( $plugin );
|
||||
}
|
||||
|
||||
$data = array(
|
||||
@@ -366,15 +446,24 @@ class PluginsHelper {
|
||||
* Schedule plugin activation.
|
||||
*
|
||||
* @param array $plugins Plugins to activate.
|
||||
*
|
||||
* @return string Job ID.
|
||||
*/
|
||||
public static function schedule_activate_plugins( $plugins ) {
|
||||
if ( empty( $plugins ) || ! is_array( $plugins ) ) {
|
||||
return new \WP_Error( 'woocommerce_plugins_invalid_plugins', __( 'Plugins must be a non-empty array.', 'woocommerce' ), 404 );
|
||||
return new WP_Error(
|
||||
'woocommerce_plugins_invalid_plugins',
|
||||
__( 'Plugins must be a non-empty array.', 'woocommerce' ),
|
||||
404
|
||||
);
|
||||
}
|
||||
|
||||
$job_id = uniqid();
|
||||
WC()->queue()->schedule_single( time() + 5, 'woocommerce_plugins_activate_callback', array( $plugins, $job_id ) );
|
||||
WC()->queue()->schedule_single(
|
||||
time() + 5,
|
||||
'woocommerce_plugins_activate_callback',
|
||||
array( $plugins, $job_id )
|
||||
);
|
||||
|
||||
return $job_id;
|
||||
}
|
||||
@@ -383,6 +472,7 @@ class PluginsHelper {
|
||||
* Installation status.
|
||||
*
|
||||
* @param int $job_id Job ID.
|
||||
*
|
||||
* @return array Job data.
|
||||
*/
|
||||
public static function get_installation_status( $job_id = null ) {
|
||||
@@ -402,14 +492,14 @@ class PluginsHelper {
|
||||
* Gets the plugin data for the first action.
|
||||
*
|
||||
* @param array $actions Array of AS actions.
|
||||
*
|
||||
* @return array Array of action data.
|
||||
*/
|
||||
public static function get_action_data( $actions ) {
|
||||
$data = [];
|
||||
$data = array();
|
||||
|
||||
foreach ( $actions as $action_id => $action ) {
|
||||
$store = new \ActionScheduler_DBStore();
|
||||
$status = $store->get_status( $action_id );
|
||||
$store = new ActionScheduler_DBStore();
|
||||
$args = $action->get_args();
|
||||
$data[] = array(
|
||||
'job_id' => $args[1],
|
||||
@@ -425,6 +515,7 @@ class PluginsHelper {
|
||||
* Activation status.
|
||||
*
|
||||
* @param int $job_id Job ID.
|
||||
*
|
||||
* @return array Array of action data.
|
||||
*/
|
||||
public static function get_activation_status( $job_id = null ) {
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\PluginsInstallLoggers;
|
||||
|
||||
/**
|
||||
* A logger to log plugin installation progress in real time to an option.
|
||||
*/
|
||||
class AsyncPluginsInstallLogger implements PluginsInstallLogger {
|
||||
|
||||
/**
|
||||
* Variable to store logs.
|
||||
*
|
||||
* @var string $option_name option name to store logs.
|
||||
*/
|
||||
private $option_name;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $option_name option name.
|
||||
*/
|
||||
public function __construct( string $option_name ) {
|
||||
$this->option_name = $option_name;
|
||||
add_option(
|
||||
$this->option_name,
|
||||
array(
|
||||
'created_time' => time(),
|
||||
'status' => 'pending',
|
||||
'plugins' => array(),
|
||||
),
|
||||
'',
|
||||
'no'
|
||||
);
|
||||
|
||||
// Set status as failed in case we run out of exectuion time.
|
||||
register_shutdown_function(
|
||||
function () {
|
||||
$error = error_get_last();
|
||||
if ( isset( $error['type'] ) && E_ERROR === $error['type'] ) {
|
||||
$option = $this->get();
|
||||
$option['status'] = 'failed';
|
||||
$this->update( $option );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the option.
|
||||
*
|
||||
* @param array $data New data.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function update( array $data ) {
|
||||
return update_option( $this->option_name, $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retreive the option.
|
||||
*
|
||||
* @return false|mixed|void
|
||||
*/
|
||||
private function get() {
|
||||
return get_option( $this->option_name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add requested plugin.
|
||||
*
|
||||
* @param string $plugin_name plugin name.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function install_requested( string $plugin_name ) {
|
||||
$option = $this->get();
|
||||
if ( ! isset( $option['plugins'][ $plugin_name ] ) ) {
|
||||
$option['plugins'][ $plugin_name ] = array(
|
||||
'status' => 'installing',
|
||||
'errors' => array(),
|
||||
'install_duration' => 0,
|
||||
);
|
||||
}
|
||||
$this->update( $option );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add installed plugin.
|
||||
*
|
||||
* @param string $plugin_name plugin name.
|
||||
* @param int $duration time took to install plugin.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function installed( string $plugin_name, int $duration ) {
|
||||
$option = $this->get();
|
||||
|
||||
$option['plugins'][ $plugin_name ]['status'] = 'installed';
|
||||
$option['plugins'][ $plugin_name ]['install_duration'] = $duration;
|
||||
$this->update( $option );
|
||||
}
|
||||
|
||||
/**
|
||||
* Change status to activated.
|
||||
*
|
||||
* @param string $plugin_name plugin name.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function activated( string $plugin_name ) {
|
||||
$option = $this->get();
|
||||
|
||||
$option['plugins'][ $plugin_name ]['status'] = 'activated';
|
||||
$this->update( $option );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an error.
|
||||
*
|
||||
* @param string $plugin_name plugin name.
|
||||
* @param string|null $error_message error message.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_error( string $plugin_name, string $error_message = null ) {
|
||||
$option = $this->get();
|
||||
|
||||
$option['plugins'][ $plugin_name ]['errors'][] = $error_message;
|
||||
$option['plugins'][ $plugin_name ]['status'] = 'failed';
|
||||
$option['status'] = 'failed';
|
||||
|
||||
$this->update( $option );
|
||||
}
|
||||
|
||||
/**
|
||||
* Record completed_time.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function complete() {
|
||||
$option = $this->get();
|
||||
|
||||
$option['complete_time'] = time();
|
||||
$option['status'] = 'complete';
|
||||
|
||||
$this->update( $option );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\PluginsInstallLoggers;
|
||||
|
||||
/**
|
||||
* A logger used in PluginsHelper::install_plugins to log the installation progress.
|
||||
*/
|
||||
interface PluginsInstallLogger {
|
||||
|
||||
/**
|
||||
* Called when a plugin install requested.
|
||||
*
|
||||
* @param string $plugin_name plugin name.
|
||||
* @return mixed
|
||||
*/
|
||||
public function install_requested( string $plugin_name );
|
||||
|
||||
/**
|
||||
* Called when a plugin installed successfully.
|
||||
*
|
||||
* @param string $plugin_name plugin name.
|
||||
* @param int $duration # of seconds it took to install $plugin_name.
|
||||
* @return mixed
|
||||
*/
|
||||
public function installed( string $plugin_name, int $duration);
|
||||
|
||||
/**
|
||||
* Called when a plugin activated successfully.
|
||||
*
|
||||
* @param string $plugin_name plugin name.
|
||||
* @return mixed
|
||||
*/
|
||||
public function activated( string $plugin_name );
|
||||
|
||||
/**
|
||||
* Called when an error occurred while installing a plugin.
|
||||
*
|
||||
* @param string $plugin_name plugin name.
|
||||
* @param string|null $error_message error message.
|
||||
* @return mixed
|
||||
*/
|
||||
public function add_error( string $plugin_name, string $error_message = null);
|
||||
|
||||
/**
|
||||
* Called when all plugins are processed.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function complete();
|
||||
}
|
||||
|
||||
@@ -29,8 +29,12 @@ class BaseLocationCountryRuleProcessor implements RuleProcessorInterface {
|
||||
return false;
|
||||
}
|
||||
|
||||
$onboarding_profile = get_option( 'woocommerce_onboarding_profile', array() );
|
||||
$is_address_default = 'US' === $base_location['country'] && 'CA' === $base_location['state'] && empty( get_option( 'woocommerce_store_address', '' ) );
|
||||
$is_store_country_set = isset( $onboarding_profile['is_store_country_set'] ) && $onboarding_profile['is_store_country_set'];
|
||||
|
||||
// Return false if the location is the default country and if onboarding hasn't been finished or the store address not been updated.
|
||||
if ( 'US' === $base_location['country'] && 'CA' === $base_location['state'] && empty( get_option( 'woocommerce_store_address', '' ) ) && OnboardingProfile::needs_completion() ) {
|
||||
if ( $is_address_default && OnboardingProfile::needs_completion() && ! $is_store_country_set ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,9 +33,15 @@ class ComparisonOperation {
|
||||
case '!=':
|
||||
return $left_operand !== $right_operand;
|
||||
case 'contains':
|
||||
return in_array( $right_operand, $left_operand, true );
|
||||
if ( is_array( $left_operand ) && is_string( $right_operand ) ) {
|
||||
return in_array( $right_operand, $left_operand, true );
|
||||
}
|
||||
return strpos( $right_operand, $left_operand ) !== false;
|
||||
case '!contains':
|
||||
return ! in_array( $right_operand, $left_operand, true );
|
||||
if ( is_array( $left_operand ) && is_string( $right_operand ) ) {
|
||||
return ! in_array( $right_operand, $left_operand, true );
|
||||
}
|
||||
return strpos( $right_operand, $left_operand ) === false;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -11,6 +11,14 @@ defined( 'ABSPATH' ) || exit;
|
||||
* Rule processor that negates the rules in the rule's operand.
|
||||
*/
|
||||
class NotRuleProcessor implements RuleProcessorInterface {
|
||||
|
||||
/**
|
||||
* The rule evaluator to use.
|
||||
*
|
||||
* @var RuleEvaluator
|
||||
*/
|
||||
protected $rule_evaluator;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
|
||||
@@ -23,9 +23,33 @@ class OptionRuleProcessor implements RuleProcessorInterface {
|
||||
$is_contains = $rule->operation && strpos( $rule->operation, 'contains' ) !== false;
|
||||
$default_value = $is_contains ? array() : false;
|
||||
$default = isset( $rule->default ) ? $rule->default : $default_value;
|
||||
$option_value = get_option( $rule->option_name, $default );
|
||||
$option_value = $this->get_option_value( $rule, $default, $is_contains );
|
||||
|
||||
if ( $is_contains && ! is_array( $option_value ) ) {
|
||||
if ( isset( $rule->transformers ) && is_array( $rule->transformers ) ) {
|
||||
$option_value = TransformerService::apply( $option_value, $rule->transformers, $default );
|
||||
}
|
||||
|
||||
return ComparisonOperation::compare(
|
||||
$option_value,
|
||||
$rule->value,
|
||||
$rule->operation
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the option value and handles logging if necessary.
|
||||
*
|
||||
* @param object $rule The specific rule being processed.
|
||||
* @param mixed $default The default value.
|
||||
* @param bool $is_contains Indicates whether the operation is "contains".
|
||||
*
|
||||
* @return mixed The option value.
|
||||
*/
|
||||
private function get_option_value( $rule, $default, $is_contains ) {
|
||||
$option_value = get_option( $rule->option_name, $default );
|
||||
$is_contains_valid = $is_contains && ( is_array( $option_value ) || ( is_string( $option_value ) && is_string( $rule->value ) ) );
|
||||
|
||||
if ( $is_contains && ! $is_contains_valid ) {
|
||||
$logger = wc_get_logger();
|
||||
$logger->warning(
|
||||
sprintf(
|
||||
@@ -41,15 +65,7 @@ class OptionRuleProcessor implements RuleProcessorInterface {
|
||||
$option_value = array();
|
||||
}
|
||||
|
||||
if ( isset( $rule->transformers ) && is_array( $rule->transformers ) ) {
|
||||
$option_value = TransformerService::apply( $option_value, $rule->transformers, $default );
|
||||
}
|
||||
|
||||
return ComparisonOperation::compare(
|
||||
$option_value,
|
||||
$rule->value,
|
||||
$rule->operation
|
||||
);
|
||||
return $option_value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,6 +13,15 @@ defined( 'ABSPATH' ) || exit;
|
||||
* operands.
|
||||
*/
|
||||
class OrRuleProcessor implements RuleProcessorInterface {
|
||||
|
||||
/**
|
||||
* Rule evaluator to use.
|
||||
*
|
||||
* @var RuleEvaluator
|
||||
*/
|
||||
private $rule_evaluator;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
|
||||
@@ -11,6 +11,14 @@ defined( 'ABSPATH' ) || exit;
|
||||
* Rule processor for publishing based on the number of orders.
|
||||
*/
|
||||
class OrderCountRuleProcessor implements RuleProcessorInterface {
|
||||
|
||||
/**
|
||||
* The orders provider.
|
||||
*
|
||||
* @var OrdersProvider
|
||||
*/
|
||||
protected $orders_provider;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
|
||||
@@ -15,6 +15,15 @@ use Automattic\WooCommerce\Admin\PluginsProvider\PluginsProvider;
|
||||
* matches the specified version.
|
||||
*/
|
||||
class PluginVersionRuleProcessor implements RuleProcessorInterface {
|
||||
|
||||
/**
|
||||
* Plugins provider instance.
|
||||
*
|
||||
* @var PluginsProviderInterface
|
||||
*/
|
||||
private $plugins_provider;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
|
||||
@@ -13,6 +13,14 @@ use Automattic\WooCommerce\Admin\PluginsProvider\PluginsProvider;
|
||||
* Rule processor for sending when the provided plugins are activated.
|
||||
*/
|
||||
class PluginsActivatedRuleProcessor implements RuleProcessorInterface {
|
||||
|
||||
/**
|
||||
* The plugins provider.
|
||||
*
|
||||
* @var PluginsProviderInterface
|
||||
*/
|
||||
protected $plugins_provider;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
|
||||
@@ -13,6 +13,14 @@ defined( 'ABSPATH' ) || exit;
|
||||
* products.
|
||||
*/
|
||||
class ProductCountRuleProcessor implements RuleProcessorInterface {
|
||||
|
||||
/**
|
||||
* The product query.
|
||||
*
|
||||
* @var WC_Product_Query
|
||||
*/
|
||||
protected $product_query;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
|
||||
@@ -13,6 +13,14 @@ use Automattic\WooCommerce\Admin\DateTimeProvider\CurrentDateTimeProvider;
|
||||
* Rule processor for sending after a specified date/time.
|
||||
*/
|
||||
class PublishAfterTimeRuleProcessor implements RuleProcessorInterface {
|
||||
|
||||
/**
|
||||
* The DateTime provider.
|
||||
*
|
||||
* @var DateTimeProviderInterface
|
||||
*/
|
||||
protected $date_time_provider;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
|
||||
@@ -13,6 +13,14 @@ use Automattic\WooCommerce\Admin\DateTimeProvider\CurrentDateTimeProvider;
|
||||
* Rule processor for sending before a specified date/time.
|
||||
*/
|
||||
class PublishBeforeTimeRuleProcessor implements RuleProcessorInterface {
|
||||
|
||||
/**
|
||||
* The DateTime provider.
|
||||
*
|
||||
* @var DateTimeProviderInterface
|
||||
*/
|
||||
protected $date_time_provider;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
|
||||
@@ -40,19 +40,20 @@ class RemoteInboxNotificationsEngine {
|
||||
|
||||
// Hook into WCA updated. This is hooked up here rather than in
|
||||
// on_admin_init because that runs too late to hook into the action.
|
||||
add_action( 'woocommerce_run_on_woocommerce_admin_updated', array( __CLASS__, 'run_on_woocommerce_admin_updated' ) );
|
||||
add_action(
|
||||
'woocommerce_updated',
|
||||
function() {
|
||||
$next_hook = WC()->queue()->get_next(
|
||||
'woocommerce_run_on_woocommerce_admin_updated',
|
||||
array( __CLASS__, 'run_on_woocommerce_admin_updated' ),
|
||||
array(),
|
||||
'woocommerce-remote-inbox-engine'
|
||||
);
|
||||
if ( null === $next_hook ) {
|
||||
WC()->queue()->schedule_single(
|
||||
time(),
|
||||
'woocommerce_run_on_woocommerce_admin_updated',
|
||||
array( __CLASS__, 'run_on_woocommerce_admin_updated' ),
|
||||
array(),
|
||||
'woocommerce-remote-inbox-engine'
|
||||
);
|
||||
}
|
||||
@@ -205,9 +206,19 @@ class RemoteInboxNotificationsEngine {
|
||||
break;
|
||||
}
|
||||
|
||||
$localized_actions = SpecRunner::get_actions( $spec );
|
||||
|
||||
// Manually copy the action id from the db to the localized action, since they were not being provided.
|
||||
foreach ( $localized_actions as $localized_action ) {
|
||||
$action = $note_from_db->get_action( $localized_action->name );
|
||||
if ( $action ) {
|
||||
$localized_action->id = $action->id;
|
||||
}
|
||||
}
|
||||
|
||||
$note_from_db->set_title( $locale->title );
|
||||
$note_from_db->set_content( $locale->content );
|
||||
$note_from_db->set_actions( SpecRunner::get_actions( $spec ) );
|
||||
$note_from_db->set_actions( $localized_actions );
|
||||
}
|
||||
|
||||
return $note_from_db;
|
||||
|
||||
@@ -13,6 +13,14 @@ defined( 'ABSPATH' ) || exit;
|
||||
* rule evaluates to false.
|
||||
*/
|
||||
class RuleEvaluator {
|
||||
|
||||
/**
|
||||
* GetRuleProcessor to use.
|
||||
*
|
||||
* @var GetRuleProcessor
|
||||
*/
|
||||
private $get_rule_processor;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications\Transformers;
|
||||
|
||||
use Automattic\WooCommerce\Admin\RemoteInboxNotifications\TransformerInterface;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Prepare site URL for comparison.
|
||||
*
|
||||
* @package Automattic\WooCommerce\Admin\RemoteInboxNotifications\Transformers
|
||||
*/
|
||||
class PrepareUrl implements TransformerInterface {
|
||||
/**
|
||||
* Prepares the site URL by removing the protocol and trailing slash.
|
||||
*
|
||||
* @param mixed $value a value to transform.
|
||||
* @param stdClass|null $arguments arguments.
|
||||
* @param string|null $default default value.
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function transform( $value, stdClass $arguments = null, $default = null ) {
|
||||
$url_parts = wp_parse_url( rtrim( $value, '/' ) );
|
||||
return isset( $url_parts['path'] ) ? $url_parts['host'] . $url_parts['path'] : $url_parts['host'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate Transformer arguments.
|
||||
*
|
||||
* @param stdClass|null $arguments arguments to validate.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function validate( stdClass $arguments = null ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,14 @@ defined( 'ABSPATH' ) || exit;
|
||||
* given number of seconds.
|
||||
*/
|
||||
class WCAdminActiveForRuleProcessor implements RuleProcessorInterface {
|
||||
|
||||
/**
|
||||
* Provides the amount of time wcadmin has been active for.
|
||||
*
|
||||
* @var WCAdminActiveForProvider
|
||||
*/
|
||||
protected $wcadmin_active_for_provider;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
|
||||
@@ -20,6 +20,28 @@ if ( ! class_exists( 'WC_Email', false ) ) {
|
||||
* ReportCSVEmail Class.
|
||||
*/
|
||||
class ReportCSVEmail extends \WC_Email {
|
||||
|
||||
/**
|
||||
* Report labels.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $report_labels;
|
||||
|
||||
/**
|
||||
* Report type (e.g. 'customers').
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $report_type;
|
||||
|
||||
/**
|
||||
* Download URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $download_url;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
|
||||
@@ -34,7 +34,7 @@ class WPCacheEngine implements CacheEngine {
|
||||
*/
|
||||
public function cache_object( string $key, $object, int $expiration, string $group = '' ): bool {
|
||||
$prefixed_key = self::get_prefixed_key( $key, $group );
|
||||
return wp_cache_set( $prefixed_key, $object, $group, $expiration );
|
||||
return false !== wp_cache_set( $prefixed_key, $object, $group, $expiration );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,7 +47,7 @@ class WPCacheEngine implements CacheEngine {
|
||||
*/
|
||||
public function delete_cached_object( string $key, string $group = '' ): bool {
|
||||
$prefixed_key = self::get_prefixed_key( $key, $group );
|
||||
return wp_cache_delete( $prefixed_key, $group );
|
||||
return false !== wp_cache_delete( $prefixed_key, $group );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,6 +71,6 @@ class WPCacheEngine implements CacheEngine {
|
||||
* @return bool True if the group is deleted successfully, false otherwise.
|
||||
*/
|
||||
public function delete_cache_group( string $group = '' ): bool {
|
||||
return self::invalidate_cache_group( $group );
|
||||
return false !== self::invalidate_cache_group( $group );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,8 @@ final class ReserveStock {
|
||||
return;
|
||||
}
|
||||
|
||||
$held_stock_notes = array();
|
||||
|
||||
try {
|
||||
$items = array_filter(
|
||||
$order->get_items(),
|
||||
@@ -113,6 +115,11 @@ final class ReserveStock {
|
||||
$item_quantity = apply_filters( 'woocommerce_order_item_quantity', $item->get_quantity(), $order, $item );
|
||||
|
||||
$rows[ $managed_by_id ] = isset( $rows[ $managed_by_id ] ) ? $rows[ $managed_by_id ] + $item_quantity : $item_quantity;
|
||||
|
||||
if ( count( $held_stock_notes ) < 5 ) {
|
||||
// translators: %1$s is a product's formatted name, %2$d: is the quantity of said product to which the stock hold applied.
|
||||
$held_stock_notes[] = sprintf( _x( '- %1$s × %2$d', 'held stock note', 'woocommerce' ), $product->get_formatted_name(), $rows[ $managed_by_id ] );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $rows ) ) {
|
||||
@@ -124,6 +131,27 @@ final class ReserveStock {
|
||||
$this->release_stock_for_order( $order );
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// Add order note after successfully holding the stock.
|
||||
if ( ! empty( $held_stock_notes ) ) {
|
||||
$remaining_count = count( $rows ) - count( $held_stock_notes );
|
||||
if ( $remaining_count > 0 ) {
|
||||
$held_stock_notes[] = sprintf(
|
||||
// translators: %d is the remaining order items count.
|
||||
_nx( '- ...and %d more item.', '- ... and %d more items.', $remaining_count, 'held stock note', 'woocommerce' ),
|
||||
$remaining_count
|
||||
);
|
||||
}
|
||||
|
||||
$order->add_order_note(
|
||||
sprintf(
|
||||
// translators: %1$s is a time in minutes, %2$s is a list of products and quantities.
|
||||
_x( 'Stock hold of %1$s minutes applied to: %2$s', 'held stock note', 'woocommerce' ),
|
||||
$minutes,
|
||||
'<br>' . implode( '<br>', $held_stock_notes )
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,7 @@ use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\Proxie
|
||||
use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\RestockRefundedItemsAdjusterServiceProvider;
|
||||
use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\UtilsClassesServiceProvider;
|
||||
use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\BatchProcessingServiceProvider;
|
||||
use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\BlockTemplatesServiceProvider;
|
||||
|
||||
/**
|
||||
* PSR11 compliant dependency injection container for WooCommerce.
|
||||
@@ -67,6 +68,7 @@ final class Container {
|
||||
OrderAdminServiceProvider::class,
|
||||
FeaturesServiceProvider::class,
|
||||
MarketingServiceProvider::class,
|
||||
BlockTemplatesServiceProvider::class,
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -168,19 +168,21 @@ class CLIRunner {
|
||||
return WP_CLI::warning( __( 'There are no orders to sync, aborting.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
$assoc_args = wp_parse_args(
|
||||
$assoc_args = wp_parse_args(
|
||||
$assoc_args,
|
||||
array(
|
||||
'batch-size' => 500,
|
||||
)
|
||||
);
|
||||
$batch_size = ( (int) $assoc_args['batch-size'] ) === 0 ? 500 : (int) $assoc_args['batch-size'];
|
||||
$progress = WP_CLI\Utils\make_progress_bar( 'Order Data Sync', $order_count / $batch_size );
|
||||
$processed = 0;
|
||||
$batch_count = 1;
|
||||
$total_time = 0;
|
||||
$batch_size = ( (int) $assoc_args['batch-size'] ) === 0 ? 500 : (int) $assoc_args['batch-size'];
|
||||
$progress = WP_CLI\Utils\make_progress_bar( 'Order Data Sync', $order_count / $batch_size );
|
||||
$processed = 0;
|
||||
$batch_count = 1;
|
||||
$total_time = 0;
|
||||
$orders_remaining = true;
|
||||
|
||||
while ( $order_count > 0 ) {
|
||||
while ( $order_count > 0 || $orders_remaining ) {
|
||||
$remaining_count = $order_count;
|
||||
|
||||
WP_CLI::debug(
|
||||
sprintf(
|
||||
@@ -213,11 +215,8 @@ class CLIRunner {
|
||||
|
||||
$progress->tick();
|
||||
|
||||
$remaining_count = $this->count_unmigrated( array(), array( 'log' => false ) );
|
||||
if ( $remaining_count === $order_count ) {
|
||||
return WP_CLI::error( __( 'Infinite loop detected, aborting.', 'woocommerce' ) );
|
||||
}
|
||||
$order_count = $remaining_count;
|
||||
$orders_remaining = count( $this->synchronizer->get_next_batch_to_process( 1 ) ) > 0;
|
||||
$order_count = $remaining_count - $batch_size;
|
||||
}
|
||||
|
||||
$progress->finish();
|
||||
@@ -300,6 +299,16 @@ class CLIRunner {
|
||||
* default: false
|
||||
* ---
|
||||
*
|
||||
* [--order-types]
|
||||
* : Comma seperated list of order types that needs to be verified. For example, --order-types=shop_order,shop_order_refund
|
||||
* ---
|
||||
* default: Output of function `wc_get_order_types( 'cot-migration' )`
|
||||
*
|
||||
* [--re-migrate]
|
||||
* : Attempt to re-migrate orders that failed verification. You should only use this option when you have never run the site with HPOS as authoritative source of order data yet, or you have manually checked the reported errors, otherwise, you risk stale data overwriting the more recent data.
|
||||
* This option can only be enabled when --verbose flag is also set.
|
||||
* default: false
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
* # Verify migrated order data, 500 orders at a time.
|
||||
@@ -318,10 +327,12 @@ class CLIRunner {
|
||||
$assoc_args = wp_parse_args(
|
||||
$assoc_args,
|
||||
array(
|
||||
'batch-size' => 500,
|
||||
'start-from' => 0,
|
||||
'end-at' => -1,
|
||||
'verbose' => false,
|
||||
'batch-size' => 500,
|
||||
'start-from' => 0,
|
||||
'end-at' => - 1,
|
||||
'verbose' => false,
|
||||
'order-types' => '',
|
||||
're-migrate' => false,
|
||||
)
|
||||
);
|
||||
|
||||
@@ -332,9 +343,28 @@ class CLIRunner {
|
||||
$order_id_start = (int) $assoc_args['start-from'];
|
||||
$order_id_end = (int) $assoc_args['end-at'];
|
||||
$order_id_end = -1 === $order_id_end ? PHP_INT_MAX : $order_id_end;
|
||||
$order_count = $this->get_verify_order_count( $order_id_start, $order_id_end, false );
|
||||
$batch_size = ( (int) $assoc_args['batch-size'] ) === 0 ? 500 : (int) $assoc_args['batch-size'];
|
||||
$verbose = (bool) $assoc_args['verbose'];
|
||||
$order_types = wc_get_order_types( 'cot-migration' );
|
||||
$remigrate = (bool) $assoc_args['re-migrate'];
|
||||
if ( ! empty( $assoc_args['order-types'] ) ) {
|
||||
$passed_order_types = array_map( 'trim', explode( ',', $assoc_args['order-types'] ) );
|
||||
$order_types = array_intersect( $order_types, $passed_order_types );
|
||||
}
|
||||
|
||||
if ( 0 === count( $order_types ) ) {
|
||||
return WP_CLI::error(
|
||||
sprintf(
|
||||
/* Translators: %s is the comma seperated list of order types. */
|
||||
__( 'Passed order type does not match any registered order types. Following order types are registered: %s', 'woocommerce' ),
|
||||
implode( ',', wc_get_order_types( 'cot-migration' ) )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$order_types_pl = implode( ',', array_fill( 0, count( $order_types ), '%s' ) );
|
||||
|
||||
$order_count = $this->get_verify_order_count( $order_id_start, $order_id_end, $order_types, false );
|
||||
|
||||
$progress = WP_CLI\Utils\make_progress_bar( 'Order Data Verification', $order_count / $batch_size );
|
||||
|
||||
@@ -352,14 +382,21 @@ class CLIRunner {
|
||||
)
|
||||
);
|
||||
|
||||
$order_ids = $wpdb->get_col(
|
||||
// phpcs:disable WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Inputs are prepared.
|
||||
$order_ids = $wpdb->get_col(
|
||||
$wpdb->prepare(
|
||||
"SELECT ID FROM $wpdb->posts WHERE post_type = 'shop_order' AND ID >= %d AND ID <= %d ORDER BY ID ASC LIMIT %d",
|
||||
$order_id_start,
|
||||
$order_id_end,
|
||||
$batch_size
|
||||
"SELECT ID FROM $wpdb->posts WHERE post_type in ( $order_types_pl ) AND ID >= %d AND ID <= %d ORDER BY ID ASC LIMIT %d",
|
||||
array_merge(
|
||||
$order_types,
|
||||
array(
|
||||
$order_id_start,
|
||||
$order_id_end,
|
||||
$batch_size,
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
// phpcs:enable
|
||||
$batch_start_time = microtime( true );
|
||||
$failed_ids_in_current_batch = $this->post_to_cot_migrator->verify_migrated_orders( $order_ids );
|
||||
$failed_ids_in_current_batch = $this->verify_meta_data( $order_ids, $failed_ids_in_current_batch );
|
||||
@@ -370,8 +407,7 @@ class CLIRunner {
|
||||
$total_time += $batch_total_time;
|
||||
|
||||
if ( $verbose && count( $failed_ids_in_current_batch ) > 0 ) {
|
||||
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r -- This is a CLI command and debugging code is intended.
|
||||
$errors = print_r( $failed_ids_in_current_batch, true );
|
||||
$errors = wp_json_encode( $failed_ids_in_current_batch, JSON_PRETTY_PRINT );
|
||||
WP_CLI::warning(
|
||||
sprintf(
|
||||
/* Translators: %1$d is number of errors and %2$s is the formatted array of order IDs. */
|
||||
@@ -385,6 +421,35 @@ class CLIRunner {
|
||||
$errors
|
||||
)
|
||||
);
|
||||
if ( $remigrate ) {
|
||||
WP_CLI::warning(
|
||||
sprintf(
|
||||
__( 'Attempting to remigrate...', 'woocommerce' )
|
||||
)
|
||||
);
|
||||
$failed_ids = array_keys( $failed_ids_in_current_batch );
|
||||
$this->synchronizer->process_batch( $failed_ids );
|
||||
$errors_in_remigrate_batch = $this->post_to_cot_migrator->verify_migrated_orders( $failed_ids );
|
||||
$errors_in_remigrate_batch = $this->verify_meta_data( $failed_ids, $errors_in_remigrate_batch );
|
||||
if ( count( $errors_in_remigrate_batch ) > 0 ) {
|
||||
$formatted_errors = wp_json_encode( $errors_in_remigrate_batch, JSON_PRETTY_PRINT );
|
||||
WP_CLI::warning(
|
||||
sprintf(
|
||||
/* Translators: %1$d is number of errors and %2$s is the formatted array of order IDs. */
|
||||
_n(
|
||||
'%1$d error found: %2$s when re-migrating order. Please review the error above.',
|
||||
'%1$d errors found: %2$s when re-migrating orders. Please review the errors above.',
|
||||
count( $errors_in_remigrate_batch ),
|
||||
'woocommerce'
|
||||
),
|
||||
count( $errors_in_remigrate_batch ),
|
||||
$formatted_errors
|
||||
)
|
||||
);
|
||||
} else {
|
||||
WP_CLI::warning( 'Re-migration successful.', 'woocommerce' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$progress->tick();
|
||||
@@ -400,7 +465,7 @@ class CLIRunner {
|
||||
);
|
||||
|
||||
$order_id_start = max( $order_ids ) + 1;
|
||||
$remaining_count = $this->get_verify_order_count( $order_id_start, $order_id_end, false );
|
||||
$remaining_count = $this->get_verify_order_count( $order_id_start, $order_id_end, $order_types, false );
|
||||
if ( $remaining_count === $order_count ) {
|
||||
return WP_CLI::error( __( 'Infinite loop detected, aborting. No errors found.', 'woocommerce' ) );
|
||||
}
|
||||
@@ -429,7 +494,7 @@ class CLIRunner {
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$errors = print_r( $failed_ids, true );
|
||||
$errors = wp_json_encode( $failed_ids, JSON_PRETTY_PRINT );
|
||||
|
||||
return WP_CLI::error(
|
||||
sprintf(
|
||||
@@ -464,22 +529,32 @@ class CLIRunner {
|
||||
/**
|
||||
* Helper method to get count for orders needing verification.
|
||||
*
|
||||
* @param int $order_id_start Order ID to start from.
|
||||
* @param int $order_id_end Order ID to end at.
|
||||
* @param bool $log Whether to also log an error message.
|
||||
* @param int $order_id_start Order ID to start from.
|
||||
* @param int $order_id_end Order ID to end at.
|
||||
* @param array $order_types List of order types to verify.
|
||||
* @param bool $log Whether to also log an error message.
|
||||
*
|
||||
* @return int Order count.
|
||||
*/
|
||||
private function get_verify_order_count( int $order_id_start, int $order_id_end, $log = true ) : int {
|
||||
private function get_verify_order_count( int $order_id_start, int $order_id_end, array $order_types, bool $log = true ) : int {
|
||||
global $wpdb;
|
||||
|
||||
$order_types_placeholder = implode( ',', array_fill( 0, count( $order_types ), '%s' ) );
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Inputs are prepared.
|
||||
$order_count = (int) $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM $wpdb->posts WHERE post_type = 'shop_order' AND ID >= %d AND ID <= %d",
|
||||
$order_id_start,
|
||||
$order_id_end
|
||||
"SELECT COUNT(*) FROM $wpdb->posts WHERE post_type in ($order_types_placeholder) AND ID >= %d AND ID <= %d",
|
||||
array_merge(
|
||||
$order_types,
|
||||
array(
|
||||
$order_id_start,
|
||||
$order_id_end,
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
// phpcs:enable
|
||||
|
||||
if ( $log ) {
|
||||
WP_CLI::log(
|
||||
@@ -517,8 +592,8 @@ class CLIRunner {
|
||||
$order_ids_placeholder = implode( ', ', array_fill( 0, count( $order_ids ), '%d' ) );
|
||||
$meta_table = OrdersTableDataStore::get_meta_table_name();
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared -- table names are hardcoded, orders_ids and excluded_columns are prepared.
|
||||
$query = $wpdb->prepare(
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- table names are hardcoded, orders_ids and excluded_columns are prepared.
|
||||
$query = $wpdb->prepare(
|
||||
"
|
||||
SELECT {$wpdb->postmeta}.post_id as entity_id, {$wpdb->postmeta}.meta_key, {$wpdb->postmeta}.meta_value
|
||||
FROM $wpdb->postmeta
|
||||
@@ -537,7 +612,7 @@ ORDER BY {$wpdb->postmeta}.post_id ASC, {$wpdb->postmeta}.meta_key ASC;
|
||||
|
||||
$normalized_source_data = $this->normalize_raw_meta_data( $source_data );
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared -- table names are hardcoded, orders_ids and excluded_columns are prepared.
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- table names are hardcoded, orders_ids and excluded_columns are prepared.
|
||||
$migrated_query = $wpdb->prepare(
|
||||
"
|
||||
SELECT $meta_table.order_id as entity_id, $meta_table.meta_key, $meta_table.meta_value
|
||||
@@ -556,17 +631,17 @@ ORDER BY $meta_table.order_id ASC, $meta_table.meta_key ASC;
|
||||
foreach ( $normalized_source_data as $order_id => $meta ) {
|
||||
foreach ( $meta as $meta_key => $values ) {
|
||||
$migrated_meta_values = isset( $normalized_migrated_meta_data[ $order_id ][ $meta_key ] ) ? $normalized_migrated_meta_data[ $order_id ][ $meta_key ] : array();
|
||||
$diff = array_diff( $values, $migrated_meta_values );
|
||||
$diff = array_diff( $values, $migrated_meta_values );
|
||||
|
||||
if ( count( $diff ) ) {
|
||||
if ( ! isset( $failed_ids[ $order_id ] ) ) {
|
||||
$failed_ids[ $order_id ] = array();
|
||||
}
|
||||
$failed_ids[ $order_id ][] = array(
|
||||
'order_id' => $order_id,
|
||||
'meta_key' => $meta_key,
|
||||
'order_id' => $order_id,
|
||||
'meta_key' => $meta_key, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Not a meta query.
|
||||
'orig_meta_values' => $values,
|
||||
'new_meta_values' => $migrated_meta_values,
|
||||
'new_meta_values' => $migrated_meta_values,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -589,7 +664,7 @@ ORDER BY $meta_table.order_id ASC, $meta_table.meta_key ASC;
|
||||
$clubbed_data[ $row['entity_id'] ] = array();
|
||||
}
|
||||
if ( ! isset( $clubbed_data[ $row['entity_id'] ][ $row['meta_key'] ] ) ) {
|
||||
$clubbed_data[ $row['entity_id'] ][ $row['meta_key'] ] = array();
|
||||
$clubbed_data[ $row['entity_id'] ][ $row['meta_key'] ] = array(); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Not a meta query.
|
||||
}
|
||||
$clubbed_data[ $row['entity_id'] ][ $row['meta_key'] ][] = $row['meta_value'];
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
namespace Automattic\WooCommerce\Database\Migrations\CustomOrderTable;
|
||||
|
||||
use Automattic\WooCommerce\Database\Migrations\MetaToMetaTableMigrator;
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
|
||||
|
||||
/**
|
||||
* Helper class to migrate records from the WordPress post meta table
|
||||
@@ -39,13 +40,6 @@ class PostMetaToOrderMetaMigrator extends MetaToMetaTableMigrator {
|
||||
*/
|
||||
protected function get_meta_config(): array {
|
||||
global $wpdb;
|
||||
// TODO: Remove hardcoding.
|
||||
$this->table_names = array(
|
||||
'orders' => $wpdb->prefix . 'wc_orders',
|
||||
'addresses' => $wpdb->prefix . 'wc_order_addresses',
|
||||
'op_data' => $wpdb->prefix . 'wc_order_operational_data',
|
||||
'meta' => $wpdb->prefix . 'wc_orders_meta',
|
||||
);
|
||||
|
||||
return array(
|
||||
'source' => array(
|
||||
@@ -57,15 +51,15 @@ class PostMetaToOrderMetaMigrator extends MetaToMetaTableMigrator {
|
||||
'meta_value_column' => 'meta_value',
|
||||
),
|
||||
'entity' => array(
|
||||
'table_name' => $this->table_names['orders'],
|
||||
'source_id_column' => 'id',
|
||||
'id_column' => 'id',
|
||||
'table_name' => $wpdb->posts,
|
||||
'source_id_column' => 'ID',
|
||||
'id_column' => 'ID',
|
||||
),
|
||||
'excluded_keys' => $this->excluded_columns,
|
||||
),
|
||||
'destination' => array(
|
||||
'meta' => array(
|
||||
'table_name' => $this->table_names['meta'],
|
||||
'table_name' => OrdersTableDataStore::get_meta_table_name(),
|
||||
'entity_id_column' => 'order_id',
|
||||
'meta_key_column' => 'meta_key',
|
||||
'meta_value_column' => 'meta_value',
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
namespace Automattic\WooCommerce\Database\Migrations\CustomOrderTable;
|
||||
|
||||
use Automattic\WooCommerce\Database\Migrations\MetaToCustomTableMigrator;
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
|
||||
|
||||
/**
|
||||
* Helper class to migrate records from the WordPress post table
|
||||
@@ -38,21 +39,14 @@ class PostToOrderAddressTableMigrator extends MetaToCustomTableMigrator {
|
||||
*/
|
||||
protected function get_schema_config(): array {
|
||||
global $wpdb;
|
||||
// TODO: Remove hardcoding.
|
||||
$this->table_names = array(
|
||||
'orders' => $wpdb->prefix . 'wc_orders',
|
||||
'addresses' => $wpdb->prefix . 'wc_order_addresses',
|
||||
'op_data' => $wpdb->prefix . 'wc_order_operational_data',
|
||||
'meta' => $wpdb->prefix . 'wc_orders_meta',
|
||||
);
|
||||
|
||||
return array(
|
||||
'source' => array(
|
||||
'entity' => array(
|
||||
'table_name' => $this->table_names['orders'],
|
||||
'meta_rel_column' => 'id',
|
||||
'destination_rel_column' => 'id',
|
||||
'primary_key' => 'id',
|
||||
'table_name' => $wpdb->posts,
|
||||
'meta_rel_column' => 'ID',
|
||||
'destination_rel_column' => 'ID',
|
||||
'primary_key' => 'ID',
|
||||
),
|
||||
'meta' => array(
|
||||
'table_name' => $wpdb->postmeta,
|
||||
@@ -63,7 +57,7 @@ class PostToOrderAddressTableMigrator extends MetaToCustomTableMigrator {
|
||||
),
|
||||
),
|
||||
'destination' => array(
|
||||
'table_name' => $this->table_names['addresses'],
|
||||
'table_name' => OrdersTableDataStore::get_addresses_table_name(),
|
||||
'source_rel_column' => 'order_id',
|
||||
'primary_key' => 'id',
|
||||
'primary_key_type' => 'int',
|
||||
@@ -80,7 +74,7 @@ class PostToOrderAddressTableMigrator extends MetaToCustomTableMigrator {
|
||||
$type = $this->type;
|
||||
|
||||
return array(
|
||||
'id' => array(
|
||||
'ID' => array(
|
||||
'type' => 'int',
|
||||
'destination' => 'order_id',
|
||||
),
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
namespace Automattic\WooCommerce\Database\Migrations\CustomOrderTable;
|
||||
|
||||
use Automattic\WooCommerce\Database\Migrations\MetaToCustomTableMigrator;
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
|
||||
|
||||
/**
|
||||
* Helper class to migrate records from the WordPress post table
|
||||
@@ -22,21 +23,14 @@ class PostToOrderOpTableMigrator extends MetaToCustomTableMigrator {
|
||||
*/
|
||||
protected function get_schema_config(): array {
|
||||
global $wpdb;
|
||||
// TODO: Remove hardcoding.
|
||||
$this->table_names = array(
|
||||
'orders' => $wpdb->prefix . 'wc_orders',
|
||||
'addresses' => $wpdb->prefix . 'wc_order_addresses',
|
||||
'op_data' => $wpdb->prefix . 'wc_order_operational_data',
|
||||
'meta' => $wpdb->prefix . 'wc_orders_meta',
|
||||
);
|
||||
|
||||
return array(
|
||||
'source' => array(
|
||||
'entity' => array(
|
||||
'table_name' => $this->table_names['orders'],
|
||||
'meta_rel_column' => 'id',
|
||||
'destination_rel_column' => 'id',
|
||||
'primary_key' => 'id',
|
||||
'table_name' => $wpdb->posts,
|
||||
'meta_rel_column' => 'ID',
|
||||
'destination_rel_column' => 'ID',
|
||||
'primary_key' => 'ID',
|
||||
),
|
||||
'meta' => array(
|
||||
'table_name' => $wpdb->postmeta,
|
||||
@@ -47,7 +41,7 @@ class PostToOrderOpTableMigrator extends MetaToCustomTableMigrator {
|
||||
),
|
||||
),
|
||||
'destination' => array(
|
||||
'table_name' => $this->table_names['op_data'],
|
||||
'table_name' => OrdersTableDataStore::get_operational_data_table_name(),
|
||||
'source_rel_column' => 'order_id',
|
||||
'primary_key' => 'id',
|
||||
'primary_key_type' => 'int',
|
||||
@@ -63,7 +57,7 @@ class PostToOrderOpTableMigrator extends MetaToCustomTableMigrator {
|
||||
*/
|
||||
protected function get_core_column_mapping(): array {
|
||||
return array(
|
||||
'id' => array(
|
||||
'ID' => array(
|
||||
'type' => 'int',
|
||||
'destination' => 'order_id',
|
||||
),
|
||||
|
||||
@@ -22,7 +22,7 @@ class PostToOrderTableMigrator extends MetaToCustomTableMigrator {
|
||||
protected function get_schema_config(): array {
|
||||
global $wpdb;
|
||||
|
||||
$this->table_names = array(
|
||||
$table_names = array(
|
||||
'orders' => $wpdb->prefix . 'wc_orders',
|
||||
'addresses' => $wpdb->prefix . 'wc_order_addresses',
|
||||
'op_data' => $wpdb->prefix . 'wc_order_operational_data',
|
||||
@@ -46,7 +46,7 @@ class PostToOrderTableMigrator extends MetaToCustomTableMigrator {
|
||||
),
|
||||
),
|
||||
'destination' => array(
|
||||
'table_name' => $this->table_names['orders'],
|
||||
'table_name' => $table_names['orders'],
|
||||
'source_rel_column' => 'id',
|
||||
'primary_key' => 'id',
|
||||
'primary_key_type' => 'int',
|
||||
|
||||
@@ -26,7 +26,7 @@ class PostsToOrdersMigrationController {
|
||||
/**
|
||||
* Array of objects used to perform the migration.
|
||||
*
|
||||
* @var array
|
||||
* @var TableMigrator[]
|
||||
*/
|
||||
private $all_migrators;
|
||||
|
||||
@@ -40,13 +40,13 @@ class PostsToOrdersMigrationController {
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
$this->all_migrators = array();
|
||||
$this->all_migrators[] = new PostToOrderTableMigrator();
|
||||
$this->all_migrators[] = new PostToOrderAddressTableMigrator( 'billing' );
|
||||
$this->all_migrators[] = new PostToOrderAddressTableMigrator( 'shipping' );
|
||||
$this->all_migrators[] = new PostToOrderOpTableMigrator();
|
||||
$this->all_migrators[] = new PostMetaToOrderMetaMigrator( $this->get_migrated_meta_keys() );
|
||||
$this->error_logger = wc_get_logger();
|
||||
$this->all_migrators = array();
|
||||
$this->all_migrators['order'] = new PostToOrderTableMigrator();
|
||||
$this->all_migrators['order_address_billing'] = new PostToOrderAddressTableMigrator( 'billing' );
|
||||
$this->all_migrators['order_address_shipping'] = new PostToOrderAddressTableMigrator( 'shipping' );
|
||||
$this->all_migrators['order_operational_data'] = new PostToOrderOpTableMigrator();
|
||||
$this->all_migrators['order_meta'] = new PostMetaToOrderMetaMigrator( $this->get_migrated_meta_keys() );
|
||||
$this->error_logger = wc_get_logger();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,7 +56,7 @@ class PostsToOrdersMigrationController {
|
||||
*/
|
||||
public function get_migrated_meta_keys() {
|
||||
$migrated_meta_keys = array();
|
||||
foreach ( $this->all_migrators as $migrator ) {
|
||||
foreach ( $this->all_migrators as $name => $migrator ) {
|
||||
if ( method_exists( $migrator, 'get_meta_column_config' ) ) {
|
||||
$migrated_meta_keys = array_merge( $migrated_meta_keys, $migrator->get_meta_column_config() );
|
||||
}
|
||||
@@ -72,60 +72,111 @@ class PostsToOrdersMigrationController {
|
||||
public function migrate_orders( array $order_post_ids ): void {
|
||||
$this->error_logger = WC()->call_function( 'wc_get_logger' );
|
||||
|
||||
$using_transactions = $this->maybe_start_transaction();
|
||||
if ( null === $using_transactions ) {
|
||||
$data = array();
|
||||
try {
|
||||
foreach ( $this->all_migrators as $name => $migrator ) {
|
||||
$data[ $name ] = $migrator->fetch_sanitized_migration_data( $order_post_ids );
|
||||
if ( ! empty( $data[ $name ]['errors'] ) ) {
|
||||
$this->handle_migration_error( $order_post_ids, $data[ $name ]['errors'], null, null, $name );
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch ( \Exception $e ) {
|
||||
$this->handle_migration_error( $order_post_ids, $data, $e, null, 'Fetching data' );
|
||||
return;
|
||||
}
|
||||
|
||||
$errors_were_logged = false;
|
||||
$using_transactions = $this->maybe_start_transaction();
|
||||
|
||||
foreach ( $this->all_migrators as $migrator ) {
|
||||
$errors_were_logged = $this->do_orders_migration_step( $migrator, $order_post_ids );
|
||||
if ( $errors_were_logged && $using_transactions ) {
|
||||
$this->rollback_transaction();
|
||||
break;
|
||||
foreach ( $this->all_migrators as $name => $migrator ) {
|
||||
$results = $migrator->process_migration_data( $data[ $name ] );
|
||||
$errors = array_unique( $results['errors'] );
|
||||
$exception = $results['exception'];
|
||||
|
||||
if ( null === $exception && empty( $errors ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->handle_migration_error( $order_post_ids, $errors, $exception, $using_transactions, $name );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $errors_were_logged && $using_transactions ) {
|
||||
if ( $using_transactions ) {
|
||||
$this->commit_transaction();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Log migration errors if any.
|
||||
*
|
||||
* @param array $order_post_ids List of post IDs of the orders to migrate.
|
||||
* @param array $errors List of errors to log.
|
||||
* @param \Exception|null $exception Exception to log.
|
||||
* @param bool|null $using_transactions Whether transactions were used.
|
||||
* @param string $name Name of the migrator.
|
||||
*/
|
||||
private function handle_migration_error( array $order_post_ids, array $errors, ?\Exception $exception, ?bool $using_transactions, string $name ) {
|
||||
$batch = ArrayUtil::to_ranges_string( $order_post_ids );
|
||||
|
||||
if ( null !== $exception ) {
|
||||
$exception_class = get_class( $exception );
|
||||
$this->error_logger->error(
|
||||
"$name: when processing ids $batch: ($exception_class) {$exception->getMessage()}, {$exception->getTraceAsString()}",
|
||||
array(
|
||||
'source' => self::LOGS_SOURCE_NAME,
|
||||
'ids' => $order_post_ids,
|
||||
'exception' => $exception,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
foreach ( $errors as $error ) {
|
||||
$this->error_logger->error(
|
||||
"$name: when processing ids $batch: $error",
|
||||
array(
|
||||
'source' => self::LOGS_SOURCE_NAME,
|
||||
'ids' => $order_post_ids,
|
||||
'error' => $error,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( $using_transactions ) {
|
||||
$this->rollback_transaction();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a database transaction if the configuration mandates so.
|
||||
*
|
||||
* @return bool|null True if transaction started, false if transactions won't be used, null if transaction failed to start.
|
||||
*
|
||||
* @throws \Exception If the transaction isolation level is invalid.
|
||||
*/
|
||||
private function maybe_start_transaction(): ?bool {
|
||||
if ( 'yes' !== get_option( CustomOrdersTableController::USE_DB_TRANSACTIONS_OPTION ) ) {
|
||||
return false;
|
||||
|
||||
$use_transactions = get_option( CustomOrdersTableController::USE_DB_TRANSACTIONS_OPTION, 'yes' );
|
||||
if ( 'yes' !== $use_transactions ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$transaction_isolation_level = get_option( CustomOrdersTableController::DB_TRANSACTIONS_ISOLATION_LEVEL_OPTION, CustomOrdersTableController::DEFAULT_DB_TRANSACTIONS_ISOLATION_LEVEL );
|
||||
$valid_transaction_isolation_levels = array( 'READ UNCOMMITTED', 'READ COMMITTED', 'REPEATABLE READ', 'SERIALIZABLE' );
|
||||
if ( ! in_array( $transaction_isolation_level, $valid_transaction_isolation_levels, true ) ) {
|
||||
throw new \Exception( "Invalid database transaction isolation level name $transaction_isolation_level" );
|
||||
}
|
||||
|
||||
$transaction_isolation_level = get_option( CustomOrdersTableController::DB_TRANSACTIONS_ISOLATION_LEVEL_OPTION, CustomOrdersTableController::DEFAULT_DB_TRANSACTIONS_ISOLATION_LEVEL );
|
||||
$this->verify_transaction_isolation_level( $transaction_isolation_level );
|
||||
$set_transaction_isolation_level_command = "SET TRANSACTION ISOLATION LEVEL $transaction_isolation_level";
|
||||
|
||||
if ( ! $this->db_query( $set_transaction_isolation_level_command ) ) {
|
||||
// We suppress errors in transaction isolation level setting because it's not supported by all DB engines, additionally, this might be executing in context of another transaction with a different isolation level.
|
||||
if ( ! $this->db_query( $set_transaction_isolation_level_command, true ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->db_query( 'START TRANSACTION' ) ? true : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that a given database transaction isolation level name is valid, and throw an exception if not.
|
||||
*
|
||||
* @param string $transaction_isolation_level Transaction isolation level name to check.
|
||||
* @return void
|
||||
* @throws \Exception Invalid transaction isolation level name.
|
||||
*/
|
||||
private function verify_transaction_isolation_level( string $transaction_isolation_level ): void {
|
||||
if ( ! in_array( $transaction_isolation_level, CustomOrdersTableController::get_valid_transaction_isolation_levels(), true ) ) {
|
||||
throw new \Exception( 'Invalid database transaction isolation level name ' . $transaction_isolation_level );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit the current database transaction.
|
||||
*
|
||||
@@ -147,15 +198,23 @@ class PostsToOrdersMigrationController {
|
||||
/**
|
||||
* Execute a database query and log any errors.
|
||||
*
|
||||
* @param string $query The SQL query to execute.
|
||||
* @param string $query The SQL query to execute.
|
||||
* @param bool $supress_errors Whether to suppress errors.
|
||||
*
|
||||
* @return bool True if the query succeeded, false if there were errors.
|
||||
*/
|
||||
private function db_query( string $query ): bool {
|
||||
private function db_query( string $query, bool $supress_errors = false ): bool {
|
||||
$wpdb = WC()->get_global( 'wpdb' );
|
||||
|
||||
try {
|
||||
if ( $supress_errors ) {
|
||||
$suppress = $wpdb->suppress_errors( true );
|
||||
}
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
$wpdb->query( $query );
|
||||
if ( $supress_errors ) {
|
||||
$wpdb->suppress_errors( $suppress );
|
||||
}
|
||||
} catch ( \Exception $exception ) {
|
||||
$exception_class = get_class( $exception );
|
||||
$this->error_logger->error(
|
||||
@@ -183,52 +242,6 @@ class PostsToOrdersMigrationController {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs one step of the migration for a set of order posts using one given migration class.
|
||||
* All database errors and exceptions are logged.
|
||||
*
|
||||
* @param object $migration_class The migration class to use, must have a `process_migration_batch_for_ids(array of ids)` method.
|
||||
* @param array $order_post_ids List of post IDs of the orders to migrate.
|
||||
* @return bool True if errors were logged, false otherwise.
|
||||
*/
|
||||
private function do_orders_migration_step( object $migration_class, array $order_post_ids ): bool {
|
||||
$result = $migration_class->process_migration_batch_for_ids( $order_post_ids );
|
||||
|
||||
$errors = array_unique( $result['errors'] );
|
||||
$exception = $result['exception'];
|
||||
if ( null === $exception && empty( $errors ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$migration_class_name = ( new \ReflectionClass( $migration_class ) )->getShortName();
|
||||
$batch = ArrayUtil::to_ranges_string( $order_post_ids );
|
||||
|
||||
if ( null !== $exception ) {
|
||||
$exception_class = get_class( $exception );
|
||||
$this->error_logger->error(
|
||||
"$migration_class_name: when processing ids $batch: ($exception_class) {$exception->getMessage()}, {$exception->getTraceAsString()}",
|
||||
array(
|
||||
'source' => self::LOGS_SOURCE_NAME,
|
||||
'ids' => $order_post_ids,
|
||||
'exception' => $exception,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
foreach ( $errors as $error ) {
|
||||
$this->error_logger->error(
|
||||
"$migration_class_name: when processing ids $batch: $error",
|
||||
array(
|
||||
'source' => self::LOGS_SOURCE_NAME,
|
||||
'ids' => $order_post_ids,
|
||||
'error' => $error,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify whether the given order IDs were migrated properly or not.
|
||||
*
|
||||
|
||||
@@ -193,16 +193,20 @@ abstract class MetaToCustomTableMigrator extends TableMigrator {
|
||||
$columns[] = $schema['destination'];
|
||||
$placeholders[] = MigrationHelper::get_wpdb_placeholder_for_type( $schema['type'] );
|
||||
}
|
||||
$placeholders = "'" . implode( "', '", $placeholders ) . "'";
|
||||
|
||||
$values = array();
|
||||
foreach ( array_values( $batch ) as $row ) {
|
||||
$query_params = array();
|
||||
foreach ( $columns as $column ) {
|
||||
$query_params[] = $row[ $column ] ?? null;
|
||||
$row_values = array();
|
||||
foreach ( $columns as $index => $column ) {
|
||||
if ( ! isset( $row[ $column ] ) || is_null( $row[ $column ] ) ) {
|
||||
$row_values[] = 'NULL';
|
||||
} else {
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.NotPrepared -- $placeholders is a placeholder.
|
||||
$row_values[] = $wpdb->prepare( $placeholders[ $index ], $row[ $column ] );
|
||||
}
|
||||
}
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $placeholders can only contain combination of placeholders described in MigrationHelper::get_wpdb_placeholder_for_type
|
||||
$value_string = '(' . $wpdb->prepare( $placeholders, $query_params ) . ')';
|
||||
|
||||
$value_string = '(' . implode( ',', $row_values ) . ')';
|
||||
$values[] = $value_string;
|
||||
}
|
||||
|
||||
@@ -214,13 +218,14 @@ abstract class MetaToCustomTableMigrator extends TableMigrator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate a batch of entities from the posts table to the corresponding table.
|
||||
* Return data to be migrated for a batch of entities.
|
||||
*
|
||||
* @param array $entity_ids Ids of entities to migrate.
|
||||
*
|
||||
* @return void
|
||||
* @return array[] Data to be migrated. Would be of the form: array( 'data' => array( ... ), 'errors' => array( ... ) ).
|
||||
*/
|
||||
protected function process_migration_batch_for_ids_core( array $entity_ids ): void {
|
||||
public function fetch_sanitized_migration_data( $entity_ids ) {
|
||||
$this->clear_errors();
|
||||
$data = $this->fetch_data_for_migration_for_ids( $entity_ids );
|
||||
|
||||
foreach ( $data['errors'] as $entity_id => $errors ) {
|
||||
@@ -228,25 +233,59 @@ abstract class MetaToCustomTableMigrator extends TableMigrator {
|
||||
$this->add_error( "Error importing data for post with id $entity_id: column $column_name: $error_message" );
|
||||
}
|
||||
}
|
||||
return array(
|
||||
'data' => $data['data'],
|
||||
'errors' => $this->get_errors(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate a batch of entities from the posts table to the corresponding table.
|
||||
*
|
||||
* @param array $entity_ids Ids of entities to migrate.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function process_migration_batch_for_ids_core( array $entity_ids ): void {
|
||||
$data = $this->fetch_sanitized_migration_data( $entity_ids );
|
||||
$this->process_migration_data( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Process migration data for a batch of entities.
|
||||
*
|
||||
* @param array $data Data to be migrated. Should be of the form: array( 'data' => array( ... ) ) as returned by the `fetch_sanitized_migration_data` method.
|
||||
*
|
||||
* @return array Array of errors and exception if any.
|
||||
*/
|
||||
public function process_migration_data( array $data ) {
|
||||
$this->clear_errors();
|
||||
$exception = null;
|
||||
|
||||
if ( count( $data['data'] ) === 0 ) {
|
||||
return;
|
||||
return array(
|
||||
'errors' => $this->get_errors(),
|
||||
'exception' => null,
|
||||
);
|
||||
}
|
||||
|
||||
$entity_ids = array_keys( $data['data'] );
|
||||
$existing_records = $this->get_already_existing_records( $entity_ids );
|
||||
try {
|
||||
$entity_ids = array_keys( $data['data'] );
|
||||
$existing_records = $this->get_already_existing_records( $entity_ids );
|
||||
|
||||
$to_insert = array_diff_key( $data['data'], $existing_records );
|
||||
$this->process_insert_batch( $to_insert );
|
||||
$to_insert = array_diff_key( $data['data'], $existing_records );
|
||||
$this->process_insert_batch( $to_insert );
|
||||
|
||||
$existing_records = array_filter(
|
||||
$existing_records,
|
||||
function( $record_data ) {
|
||||
return '1' === $record_data->modified;
|
||||
}
|
||||
$to_update = array_intersect_key( $data['data'], $existing_records );
|
||||
$this->process_update_batch( $to_update, $existing_records );
|
||||
} catch ( \Exception $e ) {
|
||||
$exception = $e;
|
||||
}
|
||||
|
||||
return array(
|
||||
'errors' => $this->get_errors(),
|
||||
'exception' => $exception,
|
||||
);
|
||||
$to_update = array_intersect_key( $data['data'], $existing_records );
|
||||
$this->process_update_batch( $to_update, $existing_records );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -353,38 +392,13 @@ abstract class MetaToCustomTableMigrator extends TableMigrator {
|
||||
|
||||
$entity_id_placeholder = implode( ',', array_fill( 0, count( $entity_ids ), '%d' ) );
|
||||
|
||||
// Additional SQL to check if the row needs update according to the column mapping.
|
||||
// The IFNULL and CHAR(0) "hack" is needed because NULLs can't be directly compared in SQL.
|
||||
$modified_selector = array();
|
||||
$core_column_mapping = array_filter(
|
||||
$this->core_column_mapping,
|
||||
function( $mapping ) {
|
||||
return ! isset( $mapping['select_clause'] );
|
||||
}
|
||||
);
|
||||
foreach ( $core_column_mapping as $column_name => $mapping ) {
|
||||
if ( $column_name === $source_primary_key_column ) {
|
||||
continue;
|
||||
}
|
||||
$modified_selector[] =
|
||||
"IFNULL(source.$column_name,CHAR(0)) != IFNULL(destination.{$mapping['destination']},CHAR(0))"
|
||||
. ( 'string' === $mapping['type'] ? ' COLLATE ' . $wpdb->collate : '' );
|
||||
}
|
||||
|
||||
if ( empty( $modified_selector ) ) {
|
||||
$modified_selector = ', 1 AS modified';
|
||||
} else {
|
||||
$modified_selector = trim( implode( ' OR ', $modified_selector ) );
|
||||
$modified_selector = ", if( $modified_selector, 1, 0 ) AS modified";
|
||||
}
|
||||
|
||||
$additional_where = $this->get_additional_where_clause_for_get_data_to_insert_or_update( $entity_ids );
|
||||
|
||||
$already_migrated_entity_ids = $this->db_get_results(
|
||||
$wpdb->prepare(
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- All columns and table names are hardcoded.
|
||||
"
|
||||
SELECT source.`$source_primary_key_column` as source_id, destination.`$destination_primary_key_column` as destination_id $modified_selector
|
||||
SELECT source.`$source_primary_key_column` as source_id, destination.`$destination_primary_key_column` as destination_id
|
||||
FROM `$destination_table` destination
|
||||
JOIN `$source_table` source ON source.`$source_destination_join_column` = destination.`$destination_source_join_column`
|
||||
WHERE source.`$source_primary_key_column` IN ( $entity_id_placeholder ) $additional_where
|
||||
@@ -567,7 +581,7 @@ WHERE
|
||||
private function validate_data( $value, string $type ) {
|
||||
switch ( $type ) {
|
||||
case 'decimal':
|
||||
$value = wc_format_decimal( $value, false, true );
|
||||
$value = wc_format_decimal( floatval( $value ), false, true );
|
||||
break;
|
||||
case 'int':
|
||||
$value = (int) $value;
|
||||
@@ -832,12 +846,15 @@ WHERE $where_clause
|
||||
if ( ! isset( $row[ $alias ] ) ) {
|
||||
$row[ $alias ] = $this->get_type_defaults( $schema['type'] );
|
||||
}
|
||||
if ( in_array( $schema['type'], array( 'int', 'decimal' ), true ) ) {
|
||||
if ( is_null( $row[ $destination_alias ] ) ) {
|
||||
$row[ $destination_alias ] = $this->get_type_defaults( $schema['type'] );
|
||||
}
|
||||
if ( in_array( $schema['type'], array( 'int', 'decimal', 'float' ), true ) ) {
|
||||
if ( '' === $row[ $alias ] || null === $row[ $alias ] ) {
|
||||
$row[ $alias ] = 0; // $wpdb->prepare forces empty values to 0.
|
||||
}
|
||||
$row[ $alias ] = wc_format_decimal( $row[ $alias ], false, true );
|
||||
$row[ $destination_alias ] = wc_format_decimal( $row[ $destination_alias ], false, true );
|
||||
$row[ $alias ] = wc_format_decimal( floatval( $row[ $alias ] ), false, true );
|
||||
$row[ $destination_alias ] = wc_format_decimal( floatval( $row[ $destination_alias ] ), false, true );
|
||||
}
|
||||
if ( 'bool' === $schema['type'] ) {
|
||||
$row[ $alias ] = wc_string_to_bool( $row[ $alias ] );
|
||||
@@ -867,6 +884,7 @@ WHERE $where_clause
|
||||
switch ( $type ) {
|
||||
case 'float':
|
||||
case 'int':
|
||||
case 'decimal':
|
||||
return 0;
|
||||
case 'string':
|
||||
return '';
|
||||
|
||||
@@ -60,34 +60,78 @@ abstract class MetaToMetaTableMigrator extends TableMigrator {
|
||||
$this->schema_config = $this->get_meta_config();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return data to be migrated for a batch of entities.
|
||||
*
|
||||
* @param array $entity_ids Ids of entities to migrate.
|
||||
*
|
||||
* @return array[] Data to be migrated. Would be of the form: array( 'data' => array( ... ), 'errors' => array( ... ) ).
|
||||
*/
|
||||
public function fetch_sanitized_migration_data( $entity_ids ) {
|
||||
$this->clear_errors();
|
||||
$to_migrate = $this->fetch_data_for_migration_for_ids( $entity_ids );
|
||||
if ( empty( $to_migrate ) ) {
|
||||
return array(
|
||||
'data' => array(),
|
||||
'errors' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
$already_migrated = $this->get_already_migrated_records( array_keys( $to_migrate ) );
|
||||
|
||||
return array(
|
||||
'data' => $this->classify_update_insert_records( $to_migrate, $already_migrated ),
|
||||
'errors' => $this->get_errors(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate a batch of entities from the posts table to the corresponding table.
|
||||
*
|
||||
* @param array $entity_ids Ids of entities ro migrate.
|
||||
*/
|
||||
protected function process_migration_batch_for_ids_core( array $entity_ids ): void {
|
||||
$to_migrate = $this->fetch_data_for_migration_for_ids( $entity_ids );
|
||||
if ( empty( $to_migrate ) ) {
|
||||
return;
|
||||
$sanitized_data = $this->fetch_sanitized_migration_data( $entity_ids );
|
||||
$this->process_migration_data( $sanitized_data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Process migration data for a batch of entities.
|
||||
*
|
||||
* @param array $data Data to be migrated. Should be of the form: array( 'data' => array( ... ) ) as returned by the `fetch_sanitized_migration_data` method.
|
||||
*
|
||||
* @return array Array of errors and exception if any.
|
||||
*/
|
||||
public function process_migration_data( array $data ) {
|
||||
if ( isset( $data['data'] ) ) {
|
||||
$data = $data['data'];
|
||||
}
|
||||
$this->clear_errors();
|
||||
$exception = null;
|
||||
|
||||
$already_migrated = $this->get_already_migrated_records( array_keys( $to_migrate ) );
|
||||
|
||||
$data = $this->classify_update_insert_records( $to_migrate, $already_migrated );
|
||||
$to_insert = $data[0];
|
||||
$to_update = $data[1];
|
||||
|
||||
if ( ! empty( $to_insert ) ) {
|
||||
$insert_queries = $this->generate_insert_sql_for_batch( $to_insert );
|
||||
$processed_rows_count = $this->db_query( $insert_queries );
|
||||
$this->maybe_add_insert_or_update_error( 'insert', $processed_rows_count );
|
||||
try {
|
||||
if ( ! empty( $to_insert ) ) {
|
||||
$insert_queries = $this->generate_insert_sql_for_batch( $to_insert );
|
||||
$processed_rows_count = $this->db_query( $insert_queries );
|
||||
$this->maybe_add_insert_or_update_error( 'insert', $processed_rows_count );
|
||||
}
|
||||
|
||||
if ( ! empty( $to_update ) ) {
|
||||
$update_queries = $this->generate_update_sql_for_batch( $to_update );
|
||||
$processed_rows_count = $this->db_query( $update_queries );
|
||||
$this->maybe_add_insert_or_update_error( 'update', $processed_rows_count );
|
||||
}
|
||||
} catch ( \Exception $e ) {
|
||||
$exception = $e;
|
||||
}
|
||||
|
||||
if ( ! empty( $to_update ) ) {
|
||||
$update_queries = $this->generate_update_sql_for_batch( $to_update );
|
||||
$processed_rows_count = $this->db_query( $update_queries );
|
||||
$this->maybe_add_insert_or_update_error( 'update', $processed_rows_count );
|
||||
}
|
||||
return array(
|
||||
'errors' => $this->get_errors(),
|
||||
'exception' => $exception,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,7 +223,7 @@ abstract class MetaToMetaTableMigrator extends TableMigrator {
|
||||
* ...,
|
||||
* )
|
||||
*/
|
||||
private function fetch_data_for_migration_for_ids( array $entity_ids ): array {
|
||||
public function fetch_data_for_migration_for_ids( array $entity_ids ): array {
|
||||
if ( empty( $entity_ids ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
@@ -36,6 +36,10 @@ abstract class TableMigrator {
|
||||
* @return void
|
||||
*/
|
||||
protected function add_error( string $error ): void {
|
||||
if ( is_null( $this->errors ) ) {
|
||||
$this->errors = array();
|
||||
}
|
||||
|
||||
if ( ! in_array( $error, $this->errors, true ) ) {
|
||||
$this->errors[] = $error;
|
||||
}
|
||||
@@ -94,6 +98,8 @@ abstract class TableMigrator {
|
||||
*
|
||||
* @param array $entity_ids Order ids to migrate.
|
||||
* @return array An array containing the keys 'errors' (array of strings) and 'exception' (exception object or null).
|
||||
*
|
||||
* @deprecated 8.0.0 Use `fetch_sanitized_migration_data` and `process_migration_data` instead.
|
||||
*/
|
||||
public function process_migration_batch_for_ids( array $entity_ids ): array {
|
||||
$this->clear_errors();
|
||||
@@ -111,12 +117,38 @@ abstract class TableMigrator {
|
||||
);
|
||||
}
|
||||
|
||||
// phpcs:disable Squiz.Commenting.FunctionComment.InvalidNoReturn, Squiz.Commenting.FunctionCommentThrowTag.Missing -- Methods are not marked abstract for back compat.
|
||||
/**
|
||||
* Return data to be migrated for a batch of entities.
|
||||
*
|
||||
* @param array $entity_ids Ids of entities to migrate.
|
||||
*
|
||||
* @return array[] Data to be migrated. Would be of the form: array( 'data' => array( ... ), 'errors' => array( ... ) ).
|
||||
*/
|
||||
public function fetch_sanitized_migration_data( array $entity_ids ) {
|
||||
throw new \Exception( 'Not implemented' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Process migration data for a batch of entities.
|
||||
*
|
||||
* @param array $data Data to be migrated. Should be of the form: array( 'data' => array( ... ) ) as returned by the `fetch_sanitized_migration_data` method.
|
||||
*
|
||||
* @return array Array of errors and exception if any.
|
||||
*/
|
||||
public function process_migration_data( array $data ) {
|
||||
throw new \Exception( 'Not implemented' );
|
||||
}
|
||||
// phpcs:enable
|
||||
|
||||
/**
|
||||
* The core method that actually performs the migration for the supplied batch of order ids.
|
||||
* It doesn't need to deal with database errors nor with exceptions.
|
||||
*
|
||||
* @param array $entity_ids Order ids to migrate.
|
||||
* @return void
|
||||
*
|
||||
* @deprecated 8.0.0 Use `fetch_sanitized_migration_data` and `process_migration_data` instead.
|
||||
*/
|
||||
abstract protected function process_migration_batch_for_ids_core( array $entity_ids ): void;
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Internal\Admin\BlockTemplateRegistry;
|
||||
|
||||
use Automattic\WooCommerce\Admin\BlockTemplates\BlockTemplateInterface;
|
||||
|
||||
/**
|
||||
* Block template registry.
|
||||
*/
|
||||
final class BlockTemplateRegistry {
|
||||
|
||||
/**
|
||||
* Class instance.
|
||||
*
|
||||
* @var BlockTemplateRegistry|null
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Templates.
|
||||
*/
|
||||
protected $templates = array();
|
||||
|
||||
/**
|
||||
* Get the instance of the class.
|
||||
*/
|
||||
public static function get_instance(): BlockTemplateRegistry {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a single template.
|
||||
*
|
||||
* @param string $id Template ID.
|
||||
* @param array $template Template layout.
|
||||
*/
|
||||
public function register( BlockTemplateInterface $template ) {
|
||||
$id = $template->get_id();
|
||||
|
||||
if ( isset( $this->templates[ $id ] ) ) {
|
||||
throw new \ValueError( 'A template with the specified ID already exists in the registry.' );
|
||||
}
|
||||
|
||||
$this->templates[ $id ] = $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the registered templates.
|
||||
*/
|
||||
public function get_all_registered(): array {
|
||||
return $this->templates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single registered template.
|
||||
*
|
||||
* @param string $id ID of the template
|
||||
*/
|
||||
public function get_registered( $id ): BlockTemplateInterface {
|
||||
return isset( $this->templates[ $id ] ) ? $this->templates[ $id ] : null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Internal\Admin\BlockTemplateRegistry;
|
||||
|
||||
/**
|
||||
* Block template controller.
|
||||
*/
|
||||
class BlockTemplatesController {
|
||||
|
||||
/**
|
||||
* Block template registry
|
||||
*
|
||||
* @var BlockTemplateRegistry
|
||||
*/
|
||||
private $block_template_registry;
|
||||
|
||||
/**
|
||||
* Block template transformer.
|
||||
*
|
||||
* @var TemplateTransformer
|
||||
*/
|
||||
private $template_transformer;
|
||||
|
||||
/**
|
||||
* Init.
|
||||
*/
|
||||
public function init( $block_template_registry, $template_transformer ) {
|
||||
$this->block_template_registry = $block_template_registry;
|
||||
$this->template_transformer = $template_transformer;
|
||||
add_action( 'rest_api_init', array( $this, 'register_templates' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register templates in the blocks endpoint.
|
||||
*/
|
||||
public function register_templates() {
|
||||
$templates = $this->block_template_registry->get_all_registered();
|
||||
|
||||
foreach ( $templates as $template ) {
|
||||
add_filter( 'pre_get_block_templates', function( $query_result, $query, $template_type ) use( $template ) {
|
||||
if ( ! isset( $query['area'] ) || $query['area'] !== $template->get_area() ) {
|
||||
return $query_result;
|
||||
}
|
||||
|
||||
$wp_block_template = $this->template_transformer->transform( $template );
|
||||
$query_result[] = $wp_block_template;
|
||||
|
||||
return $query_result;
|
||||
}, 10, 3 );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Internal\Admin\BlockTemplateRegistry;
|
||||
|
||||
use Automattic\WooCommerce\Admin\BlockTemplates\BlockTemplateInterface;
|
||||
|
||||
/**
|
||||
* Template transformer.
|
||||
*/
|
||||
class TemplateTransformer {
|
||||
|
||||
/**
|
||||
* Transform the WooCommerceBlockTemplate to a WP_Block_Template.
|
||||
*
|
||||
* @param object $block_template The product template.
|
||||
*/
|
||||
public function transform( BlockTemplateInterface $block_template ): \WP_Block_Template {
|
||||
$template = new \WP_Block_Template();
|
||||
$template->id = $block_template->get_id();
|
||||
$template->theme = 'woocommerce/woocommerce';
|
||||
$template->content = $block_template->get_formatted_template();
|
||||
$template->source = 'plugin';
|
||||
$template->slug = $block_template->get_id();
|
||||
$template->type = 'wp_template';
|
||||
$template->title = $block_template->get_title();
|
||||
$template->description = $block_template->get_description();
|
||||
$template->status = 'publish';
|
||||
$template->has_theme_file = true;
|
||||
$template->origin = 'plugin';
|
||||
$template->is_custom = false; // Templates loaded from the filesystem aren't custom, ones that have been edited and loaded from the DB are.
|
||||
$template->post_types = array(); // Don't appear in any Edit Post template selector dropdown.
|
||||
$template->area = $block_template->get_area();
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Internal\Admin\BlockTemplates;
|
||||
|
||||
use Automattic\WooCommerce\Admin\BlockTemplates\BlockInterface;
|
||||
use Automattic\WooCommerce\Admin\BlockTemplates\BlockTemplateInterface;
|
||||
use Automattic\WooCommerce\Admin\BlockTemplates\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Block configuration used to specify blocks in BlockTemplate.
|
||||
*/
|
||||
class AbstractBlock implements BlockInterface {
|
||||
/**
|
||||
* The block name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* The block ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* The block order.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $order = 10;
|
||||
|
||||
/**
|
||||
* The block attributes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $attributes = [];
|
||||
|
||||
/**
|
||||
* The block template that this block belongs to.
|
||||
*
|
||||
* @var BlockTemplate
|
||||
*/
|
||||
private $root_template;
|
||||
|
||||
/**
|
||||
* The parent container.
|
||||
*
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
private $parent;
|
||||
|
||||
/**
|
||||
* Block constructor.
|
||||
*
|
||||
* @param array $config The block configuration.
|
||||
* @param BlockTemplateInterface $root_template The block template that this block belongs to.
|
||||
* @param BlockContainerInterface|null $parent The parent block container.
|
||||
*
|
||||
* @throws \ValueError If the block configuration is invalid.
|
||||
* @throws \ValueError If the parent block container does not belong to the same template as the block.
|
||||
*/
|
||||
public function __construct( array $config, BlockTemplateInterface &$root_template, ContainerInterface &$parent = null ) {
|
||||
$this->validate( $config, $root_template, $parent );
|
||||
|
||||
$this->root_template = $root_template;
|
||||
$this->parent = is_null( $parent ) ? $root_template : $parent;
|
||||
|
||||
$this->name = $config[ self::NAME_KEY ];
|
||||
|
||||
if ( ! isset( $config[ self::ID_KEY ] ) ) {
|
||||
$this->id = $this->root_template->generate_block_id( $this->get_name() );
|
||||
} else {
|
||||
$this->id = $config[ self::ID_KEY ];
|
||||
}
|
||||
|
||||
if ( isset( $config[ self::ORDER_KEY ] ) ) {
|
||||
$this->order = $config[ self::ORDER_KEY ];
|
||||
}
|
||||
|
||||
if ( isset( $config[ self::ATTRIBUTES_KEY ] ) ) {
|
||||
$this->attributes = $config[ self::ATTRIBUTES_KEY ];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate block configuration.
|
||||
*
|
||||
* @param array $config The block configuration.
|
||||
* @param BlockTemplateInterface $root_template The block template that this block belongs to.
|
||||
* @param ContainerInterface|null $parent The parent block container.
|
||||
*
|
||||
* @throws \ValueError If the block configuration is invalid.
|
||||
* @throws \ValueError If the parent block container does not belong to the same template as the block.
|
||||
*/
|
||||
protected function validate( array $config, BlockTemplateInterface &$root_template, ContainerInterface &$parent = null ) {
|
||||
if ( isset( $parent ) && ( $parent->get_root_template() !== $root_template ) ) {
|
||||
throw new \ValueError( 'The parent block must belong to the same template as the block.' );
|
||||
}
|
||||
|
||||
if ( ! isset( $config[ self::NAME_KEY ] ) || ! is_string( $config[ self::NAME_KEY ] ) ) {
|
||||
throw new \ValueError( 'The block name must be specified.' );
|
||||
}
|
||||
|
||||
if ( isset( $config[ self::ORDER_KEY ] ) && ! is_int( $config[ self::ORDER_KEY ] ) ) {
|
||||
throw new \ValueError( 'The block order must be an integer.' );
|
||||
}
|
||||
|
||||
if ( isset( $config[ self::ATTRIBUTES_KEY ] ) && ! is_array( $config[ self::ATTRIBUTES_KEY ] ) ) {
|
||||
throw new \ValueError( 'The block attributes must be an array.' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the block name.
|
||||
*/
|
||||
public function get_name(): string {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the block ID.
|
||||
*/
|
||||
public function get_id(): string {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the block order.
|
||||
*/
|
||||
public function get_order(): int {
|
||||
return $this->order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the block order.
|
||||
*
|
||||
* @param int $order The block order.
|
||||
*/
|
||||
public function set_order( int $order ) {
|
||||
$this->order = $order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the block attributes.
|
||||
*/
|
||||
public function get_attributes(): array {
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the block attributes.
|
||||
*
|
||||
* @param array $attributes The block attributes.
|
||||
*/
|
||||
public function set_attributes( array $attributes ) {
|
||||
$this->attributes = $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the template that this block belongs to.
|
||||
*/
|
||||
public function &get_root_template(): BlockTemplateInterface {
|
||||
return $this->root_template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent block container.
|
||||
*/
|
||||
public function &get_parent(): ContainerInterface {
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the block configuration as a formatted template.
|
||||
*
|
||||
* @return array The block configuration as a formatted template.
|
||||
*/
|
||||
public function get_formatted_template(): array {
|
||||
$arr = [
|
||||
$this->get_name(),
|
||||
$this->get_attributes(),
|
||||
];
|
||||
|
||||
return $arr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Internal\Admin\BlockTemplates;
|
||||
|
||||
use Automattic\WooCommerce\Admin\BlockTemplates\ContainerInterface;
|
||||
use Automattic\WooCommerce\Admin\BlockTemplates\BlockInterface;
|
||||
use Automattic\WooCommerce\Admin\BlockTemplates\BlockTemplateInterface;
|
||||
|
||||
/**
|
||||
* Block template class.
|
||||
*/
|
||||
abstract class AbstractBlockTemplate implements BlockTemplateInterface {
|
||||
use BlockContainerTrait;
|
||||
|
||||
/**
|
||||
* Get the template ID.
|
||||
*/
|
||||
public abstract function get_id(): string;
|
||||
|
||||
/**
|
||||
* Get the template title.
|
||||
*/
|
||||
public function get_title(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the template description.
|
||||
*/
|
||||
public function get_description(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the template area.
|
||||
*/
|
||||
public function get_area(): string {
|
||||
return 'uncategorized';
|
||||
}
|
||||
|
||||
/**
|
||||
* The block cache.
|
||||
*
|
||||
* @var BlockInterface[]
|
||||
*/
|
||||
private $block_cache = [];
|
||||
|
||||
/**
|
||||
* Get a block by ID.
|
||||
*
|
||||
* @param string $block_id The block ID.
|
||||
*/
|
||||
public function get_block( string $block_id ): ?BlockInterface {
|
||||
return $this->block_cache[ $block_id ] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches a block in the template. This is an internal method and should not be called directly
|
||||
* except for classes that implement BlockContainerInterface, in their add_block() method.
|
||||
*
|
||||
* @param BlockInterface $block The block to cache.
|
||||
*
|
||||
* @throws \ValueError If a block with the specified ID already exists in the template.
|
||||
* @throws \ValueError If the block template that the block belongs to is not this template.
|
||||
*
|
||||
* @ignore
|
||||
*/
|
||||
public function cache_block( BlockInterface &$block ) {
|
||||
$id = $block->get_id();
|
||||
|
||||
if ( isset( $this->block_cache[ $id ] ) ) {
|
||||
throw new \ValueError( 'A block with the specified ID already exists in the template.' );
|
||||
}
|
||||
|
||||
if ( $block->get_root_template() !== $this ) {
|
||||
throw new \ValueError( 'The block template that the block belongs to must be the same as this template.' );
|
||||
}
|
||||
|
||||
$this->block_cache[ $id ] = $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a block ID based on a base.
|
||||
*
|
||||
* @param string $id_base The base to use when generating an ID.
|
||||
* @return string
|
||||
*/
|
||||
public function generate_block_id( string $id_base ): string {
|
||||
$instance_count = 0;
|
||||
|
||||
do {
|
||||
$instance_count++;
|
||||
$block_id = $id_base . '-' . $instance_count;
|
||||
} while ( isset( $this->block_cache[ $block_id ] ) );
|
||||
|
||||
return $block_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the root template.
|
||||
*/
|
||||
public function &get_root_template(): BlockTemplateInterface {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the inner blocks as a formatted template.
|
||||
*/
|
||||
public function get_formatted_template(): array {
|
||||
$inner_blocks = $this->get_inner_blocks_sorted_by_order();
|
||||
|
||||
$inner_blocks_formatted_template = array_map(
|
||||
function( Block $block ) {
|
||||
return $block->get_formatted_template();
|
||||
},
|
||||
$inner_blocks
|
||||
);
|
||||
|
||||
return $inner_blocks_formatted_template;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user