auto-patch 638-dev-dev01-2024-05-14T20_44_36
This commit is contained in:
@@ -51,7 +51,6 @@ class Init {
|
||||
add_filter( 'woocommerce_rest_prepare_shop_order_object', array( __CLASS__, 'add_currency_symbol_to_order_response' ) );
|
||||
|
||||
include_once WC_ABSPATH . 'includes/admin/class-wc-admin-upload-downloadable-product.php';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,6 +104,10 @@ class Init {
|
||||
$product_form_controllers[] = 'Automattic\WooCommerce\Admin\API\ProductForm';
|
||||
}
|
||||
|
||||
if ( Features::is_enabled( 'launch-your-store' ) ) {
|
||||
$controllers[] = 'Automattic\WooCommerce\Admin\API\LaunchYourStore';
|
||||
}
|
||||
|
||||
if ( Features::is_enabled( 'analytics' ) ) {
|
||||
$analytics_controllers = array(
|
||||
'Automattic\WooCommerce\Admin\API\Customers',
|
||||
@@ -134,8 +137,7 @@ class Init {
|
||||
|
||||
// The performance indicators controller must be registered last, after other /stats endpoints have been registered.
|
||||
$analytics_controllers[] = 'Automattic\WooCommerce\Admin\API\Reports\PerformanceIndicators\Controller';
|
||||
|
||||
$controllers = array_merge( $controllers, $analytics_controllers, $product_form_controllers );
|
||||
$controllers = array_merge( $controllers, $analytics_controllers, $product_form_controllers );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,8 +192,8 @@ class Init {
|
||||
* object in REST API responses. For use in formatAmount().
|
||||
*
|
||||
* @internal
|
||||
* @param {WP_REST_Response} $response REST response object.
|
||||
* @returns {WP_REST_Response}
|
||||
* @param WP_REST_Response $response REST response object.
|
||||
* @returns WP_REST_Response
|
||||
*/
|
||||
public static function add_currency_symbol_to_order_response( $response ) {
|
||||
$response_data = $response->get_data();
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
/**
|
||||
* REST API Launch Your Store Controller
|
||||
*
|
||||
* Handles requests to /launch-your-store/*
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\API;
|
||||
|
||||
use Automattic\WooCommerce\Admin\WCAdminHelper;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Launch Your Store controller.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class LaunchYourStore {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc-admin';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'launch-your-store';
|
||||
|
||||
/**
|
||||
* Register routes.
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/initialize-coming-soon',
|
||||
array(
|
||||
array(
|
||||
'methods' => 'POST',
|
||||
'callback' => array( $this, 'initialize_coming_soon' ),
|
||||
'permission_callback' => array( $this, 'must_be_shop_manager_or_admin' ),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* User must be either shop_manager or administrator.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function must_be_shop_manager_or_admin() {
|
||||
// phpcs:ignore
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) && ! current_user_can( 'administrator' ) ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes options for coming soon. Does not override if options exist.
|
||||
*
|
||||
* @return bool|void
|
||||
*/
|
||||
public function initialize_coming_soon() {
|
||||
$current_user_id = get_current_user_id();
|
||||
// Abort if we don't have a user id for some reason.
|
||||
if ( ! $current_user_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$coming_soon = 'yes';
|
||||
$store_pages_only = WCAdminHelper::is_site_fresh() ? 'no' : 'yes';
|
||||
$private_link = 'no';
|
||||
$share_key = wp_generate_password( 32, false );
|
||||
|
||||
add_option( 'woocommerce_coming_soon', $coming_soon );
|
||||
add_option( 'woocommerce_store_pages_only', $store_pages_only );
|
||||
add_option( 'woocommerce_private_link', $private_link );
|
||||
add_option( 'woocommerce_share_key', $share_key );
|
||||
|
||||
wc_admin_record_tracks_event(
|
||||
'launch_your_store_initialize_coming_soon',
|
||||
array(
|
||||
'coming_soon' => $coming_soon,
|
||||
'store_pages_only' => $store_pages_only,
|
||||
'private_link' => $private_link,
|
||||
)
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -132,6 +132,57 @@ class MarketingCampaigns extends WC_REST_Controller {
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get formatted price based on Price type.
|
||||
*
|
||||
* This uses plugins/woocommerce/i18n/currency-info.php and plugins/woocommerce/i18n/locale-info.php to get option object based on $price->currency.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* - When $price->currency is 'USD' and $price->value is '1000', it should return '$1000.00'.
|
||||
* - When $price->currency is 'JPY' and $price->value is '1000', it should return '¥1,000'.
|
||||
* - When $price->currency is 'AED' and $price->value is '1000', it should return '5.000,00 د.إ'.
|
||||
*
|
||||
* @param Price $price Price object.
|
||||
* @return String formatted price.
|
||||
*/
|
||||
private function get_formatted_price( $price ) {
|
||||
// Get $num_decimals to be passed to wc_price.
|
||||
$locale_info_all = include WC()->plugin_path() . '/i18n/locale-info.php';
|
||||
$locale_index = array_search( $price->get_currency(), array_column( $locale_info_all, 'currency_code' ), true );
|
||||
$locale = array_values( $locale_info_all )[ $locale_index ];
|
||||
$num_decimals = $locale['num_decimals'];
|
||||
|
||||
// Get $currency_info based on user locale or default locale.
|
||||
$currency_locales = $locale['locales'];
|
||||
$user_locale = get_user_locale();
|
||||
$currency_info = $currency_locales[ $user_locale ] ?? $currency_locales['default'];
|
||||
|
||||
// Get $price_format to be passed to wc_price.
|
||||
$currency_pos = $currency_info['currency_pos'];
|
||||
$currency_formats = array(
|
||||
'left' => '%1$s%2$s',
|
||||
'right' => '%2$s%1$s',
|
||||
'left_space' => '%1$s %2$s',
|
||||
'right_space' => '%2$s %1$s',
|
||||
);
|
||||
$price_format = $currency_formats[ $currency_pos ] ?? $currency_formats['left'];
|
||||
|
||||
$price_value = wc_format_decimal( $price->get_value() );
|
||||
$price_formatted = wc_price(
|
||||
$price_value,
|
||||
array(
|
||||
'currency' => $price->get_currency(),
|
||||
'decimal_separator' => $currency_info['decimal_sep'],
|
||||
'thousand_separator' => $currency_info['thousand_sep'],
|
||||
'decimals' => $num_decimals,
|
||||
'price_format' => $price_format,
|
||||
)
|
||||
);
|
||||
|
||||
return html_entity_decode( wp_strip_all_tags( $price_formatted ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the item for the REST response.
|
||||
*
|
||||
@@ -150,15 +201,17 @@ class MarketingCampaigns extends WC_REST_Controller {
|
||||
|
||||
if ( $item->get_cost() instanceof Price ) {
|
||||
$data['cost'] = array(
|
||||
'value' => wc_format_decimal( $item->get_cost()->get_value() ),
|
||||
'currency' => $item->get_cost()->get_currency(),
|
||||
'value' => wc_format_decimal( $item->get_cost()->get_value() ),
|
||||
'currency' => $item->get_cost()->get_currency(),
|
||||
'formatted' => $this->get_formatted_price( $item->get_cost() ),
|
||||
);
|
||||
}
|
||||
|
||||
if ( $item->get_sales() instanceof Price ) {
|
||||
$data['sales'] = array(
|
||||
'value' => wc_format_decimal( $item->get_sales()->get_value() ),
|
||||
'currency' => $item->get_sales()->get_currency(),
|
||||
'value' => wc_format_decimal( $item->get_sales()->get_value() ),
|
||||
'currency' => $item->get_sales()->get_currency(),
|
||||
'formatted' => $this->get_formatted_price( $item->get_sales() ),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -257,6 +310,4 @@ class MarketingCampaigns extends WC_REST_Controller {
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ class OnboardingFreeExtensions extends WC_REST_Data_Controller {
|
||||
/**
|
||||
* 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 Woo.com API, then if a plugin with the 'jetpack' slug is found, it is removed.
|
||||
* 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
|
||||
*/
|
||||
|
||||
@@ -409,14 +409,14 @@ class OnboardingProfile extends \WC_REST_Data_Controller {
|
||||
),
|
||||
'wccom_connected' => array(
|
||||
'type' => 'boolean',
|
||||
'description' => __( 'Whether or not the store was connected to Woo.com during the extension flow.', 'woocommerce' ),
|
||||
'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(
|
||||
'type' => 'boolean',
|
||||
'description' => __( 'Whether or not this store agreed to receiving marketing contents from Woo.com.', 'woocommerce' ),
|
||||
'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',
|
||||
|
||||
@@ -740,14 +740,6 @@ class OnboardingTasks extends \WC_REST_Data_Controller {
|
||||
|
||||
$lists = is_array( $task_list_ids ) && count( $task_list_ids ) > 0 ? TaskLists::get_lists_by_ids( $task_list_ids ) : TaskLists::get_lists();
|
||||
|
||||
// We have no use for hidden lists, it's expensive to compute individual tasks completion.
|
||||
$lists = array_filter(
|
||||
$lists,
|
||||
function( $list ) {
|
||||
return ! $list->is_hidden();
|
||||
}
|
||||
);
|
||||
|
||||
$json = array_map(
|
||||
function( $list ) {
|
||||
return $list->sort_tasks()->get_json();
|
||||
|
||||
@@ -249,9 +249,135 @@ class OnboardingThemes extends \WC_REST_Data_Controller {
|
||||
);
|
||||
}
|
||||
|
||||
$in_app_purchase_params = \WC_Admin_Addons::get_in_app_purchase_url_params();
|
||||
$in_app_purchase_params['wccom-back'] = rawurlencode( '/wp-admin/admin.php?page=wc-admin&path=/customize-store' );
|
||||
|
||||
$core_themes = array(
|
||||
array(
|
||||
'name' => 'Twenty Twenty-Four',
|
||||
'price' => 'Free',
|
||||
'color_palettes' => array(
|
||||
array(
|
||||
'title' => 'Black and white',
|
||||
'primary' => '#FEFBF3',
|
||||
'secondary' => '#7F7E7A',
|
||||
),
|
||||
array(
|
||||
'title' => 'Brown Sugar',
|
||||
'primary' => '#EFEBE0',
|
||||
'secondary' => '#AC6239',
|
||||
),
|
||||
array(
|
||||
'title' => 'Midnight',
|
||||
'primary' => '#161514',
|
||||
'secondary' => '#AFADA7',
|
||||
),
|
||||
array(
|
||||
'title' => 'Olive',
|
||||
'primary' => '#FEFBF3',
|
||||
'secondary' => '#7F7E7A',
|
||||
),
|
||||
),
|
||||
'total_palettes' => 0,
|
||||
'slug' => 'twentytwentyfour',
|
||||
'thumbnail_url' => 'https://i0.wp.com/themes.svn.wordpress.org/twentytwentyfour/1.0/screenshot.png',
|
||||
'link_url' => 'https://wordpress.org/themes/twentytwentyfour/',
|
||||
),
|
||||
array(
|
||||
'name' => 'Highline',
|
||||
'price' => '$79/year',
|
||||
'color_palettes' => array(
|
||||
array(
|
||||
'title' => 'Primary',
|
||||
'primary' => '#211f1d',
|
||||
'secondary' => '#211f1d',
|
||||
),
|
||||
array(
|
||||
'title' => 'Additional color',
|
||||
'primary' => '#aaaaaa',
|
||||
'secondary' => '#aaaaaa',
|
||||
),
|
||||
array(
|
||||
'title' => 'Accent Background',
|
||||
'primary' => '#b04b3c',
|
||||
'secondary' => '#b04b3c',
|
||||
),
|
||||
array(
|
||||
'title' => 'Secondary Background',
|
||||
'primary' => '#dabfa1',
|
||||
'secondary' => '#dabfa1',
|
||||
),
|
||||
),
|
||||
'total_palettes' => 9,
|
||||
'slug' => 'highline',
|
||||
'thumbnail_url' => 'https://woocommerce.com/wp-content/uploads/2023/12/Featured-image-538x403-1.png',
|
||||
'link_url' => add_query_arg( $in_app_purchase_params, 'https://woocommerce.com/products/highline/' ),
|
||||
),
|
||||
array(
|
||||
'name' => 'Luminate',
|
||||
'price' => '$79/year',
|
||||
'color_palettes' => array(
|
||||
array(
|
||||
'title' => 'Primary',
|
||||
'primary' => '#242a2e',
|
||||
'secondary' => '#242a2e',
|
||||
),
|
||||
array(
|
||||
'title' => 'Lite',
|
||||
'primary' => '#f6f5f2',
|
||||
'secondary' => '#f6f5f2',
|
||||
),
|
||||
array(
|
||||
'title' => 'Grey',
|
||||
'primary' => '#a5a5a5',
|
||||
'secondary' => '#a5a5a5',
|
||||
),
|
||||
array(
|
||||
'title' => 'Lite Grey',
|
||||
'primary' => '#e4e4e1',
|
||||
'secondary' => '#e4e4e1',
|
||||
),
|
||||
),
|
||||
'total_palettes' => 5,
|
||||
'slug' => 'luminate',
|
||||
'thumbnail_url' => 'https://woocommerce.com/wp-content/uploads/2022/07/Featured-image-538x403-2.png',
|
||||
'link_url' => add_query_arg( $in_app_purchase_params, 'https://woocommerce.com/products/luminate/' ),
|
||||
),
|
||||
array(
|
||||
'name' => 'Nokul',
|
||||
'price' => '$79/year',
|
||||
'color_palettes' => array(
|
||||
array(
|
||||
'title' => 'Foreground and background',
|
||||
'primary' => '#000000',
|
||||
'secondary' => '#f1eee2',
|
||||
),
|
||||
array(
|
||||
'title' => 'Foreground and secondary',
|
||||
'primary' => '#000000',
|
||||
'secondary' => '#999999',
|
||||
),
|
||||
array(
|
||||
'title' => 'Foreground and accent',
|
||||
'primary' => '#000000',
|
||||
'secondary' => '#d82f16',
|
||||
),
|
||||
array(
|
||||
'title' => 'Primary and background',
|
||||
'primary' => '#d9d0bf',
|
||||
'secondary' => '#f1eee2',
|
||||
),
|
||||
),
|
||||
'total_palettes' => 6,
|
||||
'slug' => 'nokul',
|
||||
'thumbnail_url' => 'https://woocommerce.com/wp-content/uploads/2022/11/Product-logo.jpg',
|
||||
'link_url' => add_query_arg( $in_app_purchase_params, 'https://woocommerce.com/products/nokul/' ),
|
||||
),
|
||||
);
|
||||
|
||||
// To be implemented: 1. Fetch themes from the marketplace API. 2. Convert prices to the requested currency.
|
||||
// These are Dotcom themes.
|
||||
$themes = array(
|
||||
$default_themes = array(
|
||||
array(
|
||||
'name' => 'Tsubaki',
|
||||
'price' => 'Free',
|
||||
@@ -269,7 +395,6 @@ class OnboardingThemes extends \WC_REST_Data_Controller {
|
||||
'slug' => 'tazza',
|
||||
'thumbnail_url' => 'https://i0.wp.com/s2.wp.com/wp-content/themes/premium/tazza/screenshot.png',
|
||||
'link_url' => 'https://wordpress.com/theme/tazza/',
|
||||
'total_palettes' => 0,
|
||||
),
|
||||
array(
|
||||
'name' => 'Amulet',
|
||||
@@ -333,6 +458,9 @@ class OnboardingThemes extends \WC_REST_Data_Controller {
|
||||
),
|
||||
);
|
||||
|
||||
$ai_connection_enabled = get_option( 'woocommerce_blocks_allow_ai_connection' );
|
||||
$themes = $ai_connection_enabled ? $default_themes : $core_themes;
|
||||
|
||||
// To be implemented: Filter themes based on industry.
|
||||
if ( $industry ) {
|
||||
$filtered_themes = array_filter(
|
||||
|
||||
@@ -205,6 +205,7 @@ class Options extends \WC_REST_Data_Controller {
|
||||
'woocommerce_product_tour_modal_hidden',
|
||||
'woocommerce_block_product_tour_shown',
|
||||
'woocommerce_revenue_report_date_tour_shown',
|
||||
'woocommerce_show_prepublish_checks_enabled',
|
||||
'woocommerce_date_type',
|
||||
'date_format',
|
||||
'time_format',
|
||||
@@ -219,6 +220,12 @@ class Options extends \WC_REST_Data_Controller {
|
||||
'woocommerce_admin_customize_store_completed',
|
||||
'woocommerce_admin_customize_store_completed_theme_id',
|
||||
'woocommerce_admin_customize_store_survey_completed',
|
||||
'woocommerce_admin_launch_your_store_survey_completed',
|
||||
'woocommerce_coming_soon',
|
||||
'woocommerce_store_pages_only',
|
||||
'woocommerce_private_link',
|
||||
'woocommerce_share_key',
|
||||
'woocommerce_show_lys_tour',
|
||||
// WC Test helper options.
|
||||
'wc-admin-test-helper-rest-api-filters',
|
||||
'wc_admin_helper_feature_values',
|
||||
|
||||
@@ -420,12 +420,12 @@ class Plugins extends \WC_REST_Data_Controller {
|
||||
/**
|
||||
* Kicks off the WCCOM Connect process.
|
||||
*
|
||||
* @return WP_Error|array Connection URL for Woo.com
|
||||
* @return WP_Error|array Connection URL for WooCommerce.com
|
||||
*/
|
||||
public function request_wccom_connect() {
|
||||
include_once WC_ABSPATH . 'includes/admin/helper/class-wc-helper-api.php';
|
||||
if ( ! class_exists( 'WC_Helper_API' ) ) {
|
||||
return new \WP_Error( 'woocommerce_rest_helper_not_active', __( 'There was an error loading the Woo.com Helper API.', 'woocommerce' ), 404 );
|
||||
return new \WP_Error( 'woocommerce_rest_helper_not_active', __( 'There was an error loading the WooCommerce.com Helper API.', 'woocommerce' ), 404 );
|
||||
}
|
||||
|
||||
$redirect_uri = wc_admin_url( '&task=connect&wccom-connected=1' );
|
||||
@@ -442,12 +442,12 @@ class Plugins extends \WC_REST_Data_Controller {
|
||||
|
||||
$code = wp_remote_retrieve_response_code( $request );
|
||||
if ( 200 !== $code ) {
|
||||
return new \WP_Error( 'woocommerce_rest_helper_connect', __( 'There was an error connecting to Woo.com. Please try again.', 'woocommerce' ), 500 );
|
||||
return new \WP_Error( 'woocommerce_rest_helper_connect', __( 'There was an error connecting to WooCommerce.com. Please try again.', 'woocommerce' ), 500 );
|
||||
}
|
||||
|
||||
$secret = json_decode( wp_remote_retrieve_body( $request ) );
|
||||
if ( empty( $secret ) ) {
|
||||
return new \WP_Error( 'woocommerce_rest_helper_connect', __( 'There was an error connecting to Woo.com. Please try again.', 'woocommerce' ), 500 );
|
||||
return new \WP_Error( 'woocommerce_rest_helper_connect', __( 'There was an error connecting to WooCommerce.com. Please try again.', 'woocommerce' ), 500 );
|
||||
}
|
||||
|
||||
do_action( 'woocommerce_helper_connect_start' );
|
||||
@@ -477,7 +477,7 @@ class Plugins extends \WC_REST_Data_Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes connecting to Woo.com.
|
||||
* Finishes connecting to WooCommerce.com.
|
||||
*
|
||||
* @param object $rest_request Request details.
|
||||
* @return WP_Error|array Contains success status.
|
||||
@@ -488,7 +488,7 @@ class Plugins extends \WC_REST_Data_Controller {
|
||||
include_once WC_ABSPATH . 'includes/admin/helper/class-wc-helper-updater.php';
|
||||
include_once WC_ABSPATH . 'includes/admin/helper/class-wc-helper-options.php';
|
||||
if ( ! class_exists( 'WC_Helper_API' ) ) {
|
||||
return new \WP_Error( 'woocommerce_rest_helper_not_active', __( 'There was an error loading the Woo.com Helper API.', 'woocommerce' ), 404 );
|
||||
return new \WP_Error( 'woocommerce_rest_helper_not_active', __( 'There was an error loading the WooCommerce.com Helper API.', 'woocommerce' ), 404 );
|
||||
}
|
||||
|
||||
// Obtain an access token.
|
||||
@@ -504,12 +504,12 @@ class Plugins extends \WC_REST_Data_Controller {
|
||||
|
||||
$code = wp_remote_retrieve_response_code( $request );
|
||||
if ( 200 !== $code ) {
|
||||
return new \WP_Error( 'woocommerce_rest_helper_connect', __( 'There was an error connecting to Woo.com. Please try again.', 'woocommerce' ), 500 );
|
||||
return new \WP_Error( 'woocommerce_rest_helper_connect', __( 'There was an error connecting to WooCommerce.com. Please try again.', 'woocommerce' ), 500 );
|
||||
}
|
||||
|
||||
$access_token = json_decode( wp_remote_retrieve_body( $request ), true );
|
||||
if ( ! $access_token ) {
|
||||
return new \WP_Error( 'woocommerce_rest_helper_connect', __( 'There was an error connecting to Woo.com. Please try again.', 'woocommerce' ), 500 );
|
||||
return new \WP_Error( 'woocommerce_rest_helper_connect', __( 'There was an error connecting to WooCommerce.com. Please try again.', 'woocommerce' ), 500 );
|
||||
}
|
||||
|
||||
\WC_Helper_Options::update(
|
||||
@@ -525,7 +525,7 @@ class Plugins extends \WC_REST_Data_Controller {
|
||||
|
||||
if ( ! \WC_Helper::_flush_authentication_cache() ) {
|
||||
\WC_Helper_Options::update( 'auth', array() );
|
||||
return new \WP_Error( 'woocommerce_rest_helper_connect', __( 'There was an error connecting to Woo.com. Please try again.', 'woocommerce' ), 500 );
|
||||
return new \WP_Error( 'woocommerce_rest_helper_connect', __( 'There was an error connecting to WooCommerce.com. Please try again.', 'woocommerce' ), 500 );
|
||||
}
|
||||
|
||||
delete_transient( '_woocommerce_helper_subscriptions' );
|
||||
@@ -592,25 +592,22 @@ class Plugins extends \WC_REST_Data_Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a URL that can be used to by WCPay to verify business details with Stripe.
|
||||
* Returns a URL that can be used to by WCPay to verify business details.
|
||||
*
|
||||
* @return WP_Error|array Connect URL.
|
||||
*/
|
||||
public function connect_wcpay() {
|
||||
if ( ! class_exists( 'WC_Payments_Account' ) ) {
|
||||
if ( ! class_exists( 'WC_Payments' ) ) {
|
||||
return new \WP_Error( 'woocommerce_rest_helper_connect', __( 'There was an error communicating with the WooPayments plugin.', 'woocommerce' ), 500 );
|
||||
}
|
||||
|
||||
$args = WooCommercePayments::is_account_partially_onboarded() ? [
|
||||
'wcpay-login' => '1',
|
||||
'_wpnonce' => wp_create_nonce( 'wcpay-login' ),
|
||||
] : [
|
||||
'wcpay-connect' => 'WCADMIN_PAYMENT_TASK',
|
||||
'_wpnonce' => wp_create_nonce( 'wcpay-connect' ),
|
||||
];
|
||||
// Redirect to the WooPayments overview page if the merchant started onboarding (aka WooPayments is already connected).
|
||||
// Redirect to the WooPayments connect page if they haven't started onboarding.
|
||||
$path = WooCommercePayments::is_connected() ? '/payments/overview' : '/payments/connect';
|
||||
|
||||
// Point to the WooPayments Connect page rather than straight to the onboarding flow.
|
||||
return( array(
|
||||
'connectUrl' => add_query_arg( $args, admin_url() ),
|
||||
'connectUrl' => add_query_arg( 'from', 'WCADMIN_PAYMENT_TASK', admin_url( 'admin.php?page=wc-admin&path=' . $path ) ),
|
||||
) );
|
||||
}
|
||||
|
||||
|
||||
@@ -1345,6 +1345,7 @@ class DataStore extends SqlQuery {
|
||||
continue;
|
||||
}
|
||||
|
||||
$term_id = '';
|
||||
// If the tuple is numeric, assume these are IDs.
|
||||
if ( is_numeric( $attribute_term[0] ) && is_numeric( $attribute_term[1] ) ) {
|
||||
$attribute_id = intval( $attribute_term[0] );
|
||||
@@ -1374,6 +1375,10 @@ class DataStore extends SqlQuery {
|
||||
// Assume these are a custom attribute slug/value pair.
|
||||
$meta_key = esc_sql( $attribute_term[0] );
|
||||
$meta_value = esc_sql( $attribute_term[1] );
|
||||
$attr_term = get_term_by( 'slug', $meta_value, $meta_key );
|
||||
if ( false !== $attr_term ) {
|
||||
$term_id = $attr_term->term_id;
|
||||
}
|
||||
}
|
||||
|
||||
$join_alias = 'orderitemmeta1';
|
||||
@@ -1391,8 +1396,23 @@ class DataStore extends SqlQuery {
|
||||
$sql_clauses['join'][] = "JOIN {$wpdb->prefix}woocommerce_order_itemmeta as {$join_alias} ON {$join_alias}.order_item_id = {$table_to_join_on}.order_item_id";
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
$sql_clauses['where'][] = $wpdb->prepare( "( {$join_alias}.meta_key = %s AND {$join_alias}.meta_value {$comparator} %s )", $meta_key, $meta_value );
|
||||
$in_comparator = '=' === $comparator ? 'in' : 'not in';
|
||||
|
||||
// Add subquery for products ordered using attributes not used in variations.
|
||||
$term_attribute_subquery = "select product_id from {$wpdb->prefix}wc_product_attributes_lookup where is_variation_attribute=0 and term_id = %s";
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
// phpcs:disable WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
|
||||
$sql_clauses['where'][] = $wpdb->prepare(
|
||||
"
|
||||
( ( {$join_alias}.meta_key = %s AND {$join_alias}.meta_value {$comparator} %s ) or (
|
||||
{$wpdb->prefix}wc_order_product_lookup.variation_id = 0 and {$wpdb->prefix}wc_order_product_lookup.product_id {$in_comparator} ({$term_attribute_subquery})
|
||||
) )",
|
||||
$meta_key,
|
||||
$meta_value,
|
||||
$term_id,
|
||||
);
|
||||
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
// phpcs:enable WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -211,24 +211,30 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'readonly' => true,
|
||||
),
|
||||
'extended_info' => array(
|
||||
'products' => array(
|
||||
'products' => array(
|
||||
'type' => 'array',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'description' => __( 'List of order product IDs, names, quantities.', 'woocommerce' ),
|
||||
),
|
||||
'coupons' => array(
|
||||
'coupons' => array(
|
||||
'type' => 'array',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'description' => __( 'List of order coupons.', 'woocommerce' ),
|
||||
),
|
||||
'customer' => array(
|
||||
'customer' => array(
|
||||
'type' => 'object',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'description' => __( 'Order customer information.', 'woocommerce' ),
|
||||
),
|
||||
'attribution' => array(
|
||||
'type' => 'object',
|
||||
'readonly' => true,
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'description' => __( 'Order attribution information.', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -526,6 +532,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'num_items_sold' => __( 'Items sold', 'woocommerce' ),
|
||||
'coupons' => __( 'Coupon(s)', 'woocommerce' ),
|
||||
'net_total' => __( 'N. Revenue', 'woocommerce' ),
|
||||
'attribution' => __( 'Attribution', 'woocommerce' ),
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -558,6 +565,7 @@ class Controller extends ReportsController implements ExportableInterface {
|
||||
'num_items_sold' => $item['num_items_sold'],
|
||||
'coupons' => isset( $item['extended_info']['coupons'] ) ? $this->get_coupons( $item['extended_info']['coupons'] ) : null,
|
||||
'net_total' => $item['net_total'],
|
||||
'attribution' => $item['extended_info']['attribution']['origin'],
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,9 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Orders;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Internal\Traits\OrderAttributionMeta;
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
|
||||
use Automattic\WooCommerce\Utilities\OrderUtil;
|
||||
use Automattic\WooCommerce\Admin\API\Reports\DataStore as ReportsDataStore;
|
||||
use Automattic\WooCommerce\Admin\API\Reports\DataStoreInterface;
|
||||
use Automattic\WooCommerce\Admin\API\Reports\SqlQuery;
|
||||
@@ -18,6 +21,7 @@ use Automattic\WooCommerce\Admin\API\Reports\TimeInterval;
|
||||
* API\Reports\Orders\DataStore.
|
||||
*/
|
||||
class DataStore extends ReportsDataStore implements DataStoreInterface {
|
||||
use OrderAttributionMeta;
|
||||
|
||||
/**
|
||||
* Dynamically sets the date column name based on configuration
|
||||
@@ -338,13 +342,14 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
|
||||
* @param array $query_args Query parameters.
|
||||
*/
|
||||
protected function include_extended_info( &$orders_data, $query_args ) {
|
||||
$mapped_orders = $this->map_array_by_key( $orders_data, 'order_id' );
|
||||
$related_orders = $this->get_orders_with_parent_id( $mapped_orders );
|
||||
$order_ids = array_merge( array_keys( $mapped_orders ), array_keys( $related_orders ) );
|
||||
$products = $this->get_products_by_order_ids( $order_ids );
|
||||
$coupons = $this->get_coupons_by_order_ids( array_keys( $mapped_orders ) );
|
||||
$customers = $this->get_customers_by_orders( $orders_data );
|
||||
$mapped_customers = $this->map_array_by_key( $customers, 'customer_id' );
|
||||
$mapped_orders = $this->map_array_by_key( $orders_data, 'order_id' );
|
||||
$related_orders = $this->get_orders_with_parent_id( $mapped_orders );
|
||||
$order_ids = array_merge( array_keys( $mapped_orders ), array_keys( $related_orders ) );
|
||||
$products = $this->get_products_by_order_ids( $order_ids );
|
||||
$coupons = $this->get_coupons_by_order_ids( array_keys( $mapped_orders ) );
|
||||
$order_attributions = $this->get_order_attributions_by_order_ids( array_keys( $mapped_orders ) );
|
||||
$customers = $this->get_customers_by_orders( $orders_data );
|
||||
$mapped_customers = $this->map_array_by_key( $customers, 'customer_id' );
|
||||
|
||||
$mapped_data = array();
|
||||
foreach ( $products as $product ) {
|
||||
@@ -384,7 +389,7 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
|
||||
|
||||
foreach ( $coupons as $coupon ) {
|
||||
if ( ! isset( $mapped_data[ $coupon['order_id'] ] ) ) {
|
||||
$mapped_data[ $product['order_id'] ]['coupons'] = array();
|
||||
$mapped_data[ $coupon['order_id'] ]['coupons'] = array();
|
||||
}
|
||||
|
||||
$mapped_data[ $coupon['order_id'] ]['coupons'][] = array(
|
||||
@@ -394,15 +399,22 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
|
||||
}
|
||||
|
||||
foreach ( $orders_data as $key => $order_data ) {
|
||||
$defaults = array(
|
||||
'products' => array(),
|
||||
'coupons' => array(),
|
||||
'customer' => array(),
|
||||
$defaults = array(
|
||||
'products' => array(),
|
||||
'coupons' => array(),
|
||||
'customer' => array(),
|
||||
'attribution' => array(),
|
||||
);
|
||||
$orders_data[ $key ]['extended_info'] = isset( $mapped_data[ $order_data['order_id'] ] ) ? array_merge( $defaults, $mapped_data[ $order_data['order_id'] ] ) : $defaults;
|
||||
$order_id = $order_data['order_id'];
|
||||
|
||||
$orders_data[ $key ]['extended_info'] = isset( $mapped_data[ $order_id ] ) ? array_merge( $defaults, $mapped_data[ $order_id ] ) : $defaults;
|
||||
if ( $order_data['customer_id'] && isset( $mapped_customers[ $order_data['customer_id'] ] ) ) {
|
||||
$orders_data[ $key ]['extended_info']['customer'] = $mapped_customers[ $order_data['customer_id'] ];
|
||||
}
|
||||
|
||||
$source_type = $order_attributions[ $order_id ]['_wc_order_attribution_source_type'] ?? '';
|
||||
$utm_source = $order_attributions[ $order_id ]['_wc_order_attribution_utm_source'] ?? '';
|
||||
$orders_data[ $key ]['extended_info']['attribution']['origin'] = $this->get_origin_label( $source_type, $utm_source );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -534,6 +546,52 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
|
||||
return $coupons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get order attributions data from order IDs.
|
||||
*
|
||||
* @param array $order_ids Array of order IDs.
|
||||
* @return array
|
||||
*/
|
||||
protected function get_order_attributions_by_order_ids( $order_ids ) {
|
||||
global $wpdb;
|
||||
$order_meta_table = OrdersTableDataStore::get_meta_table_name();
|
||||
$included_order_ids = implode( ',', array_map( 'absint', $order_ids ) );
|
||||
|
||||
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
|
||||
/* phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared */
|
||||
$order_attributions_meta = $wpdb->get_results(
|
||||
"SELECT order_id, meta_key, meta_value
|
||||
FROM $order_meta_table
|
||||
WHERE order_id IN ({$included_order_ids})
|
||||
AND meta_key IN ( '_wc_order_attribution_source_type', '_wc_order_attribution_utm_source' )
|
||||
",
|
||||
ARRAY_A
|
||||
);
|
||||
/* phpcs:enable */
|
||||
} else {
|
||||
/* phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared */
|
||||
$order_attributions_meta = $wpdb->get_results(
|
||||
"SELECT post_id as order_id, meta_key, meta_value
|
||||
FROM $wpdb->postmeta
|
||||
WHERE post_id IN ({$included_order_ids})
|
||||
AND meta_key IN ( '_wc_order_attribution_source_type', '_wc_order_attribution_utm_source' )
|
||||
",
|
||||
ARRAY_A
|
||||
);
|
||||
/* phpcs:enable */
|
||||
}
|
||||
|
||||
$order_attributions = array();
|
||||
foreach ( $order_attributions_meta as $meta ) {
|
||||
if ( ! isset( $order_attributions[ $meta['order_id'] ] ) ) {
|
||||
$order_attributions[ $meta['order_id'] ] = array();
|
||||
}
|
||||
$order_attributions[ $meta['order_id'] ][ $meta['meta_key'] ] = $meta['meta_value'];
|
||||
}
|
||||
|
||||
return $order_attributions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all statuses that have been synced.
|
||||
*
|
||||
|
||||
@@ -165,7 +165,7 @@ class Segmenter extends ReportsSegmenter {
|
||||
|
||||
$segments = $this->get_product_related_segments( $type, $segmenting_selections, $segmenting_from, $segmenting_where, $segmenting_groupby, $segmenting_dimension_name, $table_name, $query_params, $unique_orders_table );
|
||||
} elseif ( 'variation' === $this->query_args['segmentby'] ) {
|
||||
if ( ! isset( $this->query_args['product_includes'] ) || ! is_array( count( $this->query_args['product_includes'] ) ) || count( $this->query_args['product_includes'] ) !== 1 ) {
|
||||
if ( ! isset( $this->query_args['product_includes'] ) || ! is_array( $this->query_args['product_includes'] ) || count( $this->query_args['product_includes'] ) !== 1 ) {
|
||||
throw new ParameterException( 'wc_admin_reports_invalid_segmenting_variation', __( 'product_includes parameter need to specify exactly one product when segmenting by variation.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user