Plugin Updates
This commit is contained in:
@@ -11,7 +11,6 @@ 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;
|
||||
@@ -125,30 +124,6 @@ class OnboardingPlugins extends WC_REST_Data_Controller {
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
/*
|
||||
* 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
|
||||
);
|
||||
}
|
||||
|
||||
add_action( 'woocommerce_plugins_install_error', array( $this, 'log_plugins_install_error' ), 10, 4 );
|
||||
add_action( 'woocommerce_plugins_install_api_error', array( $this, 'log_plugins_install_api_error' ), 10, 2 );
|
||||
}
|
||||
|
||||
@@ -365,7 +365,7 @@ class OnboardingTasks extends \WC_REST_Data_Controller {
|
||||
|
||||
$import = self::import_sample_products_from_csv( $template_path );
|
||||
|
||||
if ( is_wp_error( $import ) || 0 === count( $import['imported'] ) ) {
|
||||
if ( is_wp_error( $import ) || ! is_array( $import['imported'] ) || 0 === count( $import['imported'] ) ) {
|
||||
return new \WP_Error(
|
||||
'woocommerce_rest_product_creation_error',
|
||||
/* translators: %s is template name */
|
||||
@@ -740,6 +740,14 @@ 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();
|
||||
|
||||
@@ -548,7 +548,7 @@ class Plugins extends \WC_REST_Data_Controller {
|
||||
if ( ! class_exists( '\WooCommerce\Square\Handlers\Connection' ) ) {
|
||||
return new \WP_Error( 'woocommerce_rest_helper_connect', __( 'There was an error connecting to Square.', 'woocommerce' ), 500 );
|
||||
}
|
||||
|
||||
$has_cbd_industry = false;
|
||||
if ( 'US' === WC()->countries->get_base_country() ) {
|
||||
$profile = get_option( OnboardingProfile::DATA_OPTION, array() );
|
||||
if ( ! empty( $profile['industry'] ) ) {
|
||||
|
||||
@@ -162,7 +162,7 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
|
||||
* Returns an array of ids of included categories, based on query arguments from the user.
|
||||
*
|
||||
* @param array $query_args Parameters supplied by the user.
|
||||
* @return string
|
||||
* @return array
|
||||
*/
|
||||
protected function get_included_categories_array( $query_args ) {
|
||||
if ( isset( $query_args['category_includes'] ) && is_array( $query_args['category_includes'] ) && count( $query_args['category_includes'] ) > 0 ) {
|
||||
|
||||
@@ -62,13 +62,20 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
|
||||
);
|
||||
}
|
||||
|
||||
// This method was already available as non-final, marking it as final now would make it backwards-incompatible.
|
||||
// phpcs:disable WooCommerce.Functions.InternalInjectionMethod.MissingFinal
|
||||
|
||||
/**
|
||||
* Set up all the hooks for maintaining and populating table data.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function init() {
|
||||
add_action( 'woocommerce_analytics_delete_order_stats', array( __CLASS__, 'sync_on_order_delete' ), 5 );
|
||||
}
|
||||
|
||||
// phpcs:enable WooCommerce.Functions.InternalInjectionMethod.MissingFinal
|
||||
|
||||
/**
|
||||
* Returns an array of ids of included coupons, based on query arguments from the user.
|
||||
*
|
||||
@@ -339,6 +346,11 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
|
||||
*/
|
||||
public static function get_coupon_id( \WC_Order_Item_Coupon $coupon_item ) {
|
||||
// First attempt to get coupon ID from order item data.
|
||||
$coupon_info = $coupon_item->get_meta( 'coupon_info', true );
|
||||
if ( $coupon_info ) {
|
||||
return json_decode( $coupon_info, true )[0];
|
||||
}
|
||||
|
||||
$coupon_data = $coupon_item->get_meta( 'coupon_data', true );
|
||||
|
||||
// Normal checkout orders should have this data.
|
||||
|
||||
@@ -258,6 +258,7 @@ class Segmenter extends ReportsSegmenter {
|
||||
return array();
|
||||
}
|
||||
|
||||
$segments = null;
|
||||
$product_segmenting_table = $wpdb->prefix . 'wc_order_product_lookup';
|
||||
$unique_orders_table = '';
|
||||
$segmenting_where = '';
|
||||
@@ -279,7 +280,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'] ) || 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' ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -374,7 +374,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'] ) || 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' ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -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'] ) || count( $this->query_args['product_includes'] ) !== 1 ) {
|
||||
if ( ! isset( $this->query_args['product_includes'] ) || ! is_array( count( $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' ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -368,6 +368,7 @@ class Segmenter {
|
||||
|
||||
if (
|
||||
isset( $this->query_args['product_includes'] ) &&
|
||||
is_array( $this->query_args['product_includes'] ) &&
|
||||
count( $this->query_args['product_includes'] ) === 1
|
||||
) {
|
||||
$args['parent'] = $this->query_args['product_includes'][0];
|
||||
|
||||
@@ -175,7 +175,7 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
|
||||
$this->add_sql_query_params( $query_args );
|
||||
$params = $this->get_limit_params( $query_args );
|
||||
|
||||
if ( isset( $query_args['taxes'] ) && ! empty( $query_args['taxes'] ) ) {
|
||||
if ( isset( $query_args['taxes'] ) && is_array( $query_args['taxes'] ) && ! empty( $query_args['taxes'] ) ) {
|
||||
$total_results = count( $query_args['taxes'] );
|
||||
$total_pages = (int) ceil( $total_results / $params['per_page'] );
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
|
||||
$order_tax_lookup_table = self::get_db_table_name();
|
||||
|
||||
if ( isset( $query_args['taxes'] ) && ! empty( $query_args['taxes'] ) ) {
|
||||
$query_args['taxes'] = (array) $query_args['taxes'];
|
||||
$tax_id_placeholders = implode( ',', array_fill( 0, count( $query_args['taxes'] ), '%d' ) );
|
||||
/* phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared */
|
||||
$taxes_where_clause .= $wpdb->prepare( " AND {$order_tax_lookup_table}.tax_rate_id IN ({$tax_id_placeholders})", $query_args['taxes'] );
|
||||
@@ -115,6 +116,7 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
|
||||
FROM {$wpdb->prefix}woocommerce_tax_rates
|
||||
";
|
||||
if ( ! empty( $args['include'] ) ) {
|
||||
$args['include'] = (array) $args['include'];
|
||||
/* phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared */
|
||||
$tax_placeholders = implode( ',', array_fill( 0, count( $args['include'] ), '%d' ) );
|
||||
$query .= $wpdb->prepare( " WHERE tax_rate_id IN ({$tax_placeholders})", $args['include'] );
|
||||
|
||||
@@ -586,6 +586,7 @@ class TimeInterval {
|
||||
}
|
||||
|
||||
if (
|
||||
! is_array( $value ) ||
|
||||
2 !== count( $value ) ||
|
||||
! is_numeric( $value[0] ) ||
|
||||
! is_numeric( $value[1] )
|
||||
@@ -618,6 +619,7 @@ class TimeInterval {
|
||||
}
|
||||
|
||||
if (
|
||||
! is_array( $value ) ||
|
||||
2 !== count( $value ) ||
|
||||
! rest_parse_date( $value[0] ) ||
|
||||
! rest_parse_date( $value[1] )
|
||||
|
||||
@@ -140,7 +140,7 @@ class Segmenter extends ReportsSegmenter {
|
||||
* @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
|
||||
* @return array|null
|
||||
* @throws \Automattic\WooCommerce\Admin\API\Reports\ParameterException In case of segmenting by variations, when no parent product is specified.
|
||||
*/
|
||||
protected function get_segments( $type, $query_params, $table_name ) {
|
||||
@@ -148,7 +148,7 @@ class Segmenter extends ReportsSegmenter {
|
||||
if ( ! isset( $this->query_args['segmentby'] ) || '' === $this->query_args['segmentby'] ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$segments = null;
|
||||
$product_segmenting_table = $wpdb->prefix . 'wc_order_product_lookup';
|
||||
$unique_orders_table = 'uniq_orders';
|
||||
$segmenting_where = '';
|
||||
|
||||
@@ -192,7 +192,7 @@ class Themes extends \WC_REST_Data_Controller {
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
$params = array( 'context' => $this->get_context_param( array( 'default' => 'view' ) ) );
|
||||
$params['pluginzip'] = array(
|
||||
'description' => __( 'A zip file of the theme to be uploaded.', 'woocommerce' ),
|
||||
'type' => 'file',
|
||||
|
||||
@@ -133,12 +133,12 @@ abstract class DataSourcePoller {
|
||||
$this->merge_specs( $specs_from_data_source, $specs, $url );
|
||||
}
|
||||
|
||||
$specs_group = get_transient( $this->args['transient_name'] ) ?? array();
|
||||
$specs_group = get_transient( $this->args['transient_name'] );
|
||||
$specs_group = is_array( $specs_group ) ? $specs_group : array();
|
||||
$locale = get_user_locale();
|
||||
$specs_group[ $locale ] = $specs;
|
||||
// Persist the specs as a transient.
|
||||
set_transient(
|
||||
$this->args['transient_name'],
|
||||
$this->set_specs_transient(
|
||||
$specs_group,
|
||||
$this->args['transient_expiry']
|
||||
);
|
||||
@@ -154,6 +154,20 @@ abstract class DataSourcePoller {
|
||||
return delete_transient( $this->args['transient_name'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the specs transient.
|
||||
*
|
||||
* @param array $specs The specs to set in the transient.
|
||||
* @param int $expiration The expiration time for the transient.
|
||||
*/
|
||||
public function set_specs_transient( $specs, $expiration = 0 ) {
|
||||
set_transient(
|
||||
$this->args['transient_name'],
|
||||
$specs,
|
||||
$expiration,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a single data source and return the read specs
|
||||
*
|
||||
@@ -183,7 +197,7 @@ abstract class DataSourcePoller {
|
||||
// phpcs:ignore
|
||||
$logger->error( print_r( $response, true ), $logger_context );
|
||||
|
||||
return [];
|
||||
return array();
|
||||
}
|
||||
|
||||
$body = $response['body'];
|
||||
@@ -195,7 +209,7 @@ abstract class DataSourcePoller {
|
||||
$logger_context
|
||||
);
|
||||
|
||||
return [];
|
||||
return array();
|
||||
}
|
||||
|
||||
if ( ! is_array( $specs ) ) {
|
||||
@@ -204,7 +218,7 @@ abstract class DataSourcePoller {
|
||||
$logger_context
|
||||
);
|
||||
|
||||
return [];
|
||||
return array();
|
||||
}
|
||||
|
||||
return $specs;
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\MarketingRecommendations;
|
||||
|
||||
use Automattic\WooCommerce\Admin\RemoteSpecs\RemoteSpecsEngine;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Marketing Recommendations engine.
|
||||
* This goes through the specs and gets marketing recommendations.
|
||||
*/
|
||||
class Init {
|
||||
class Init extends RemoteSpecsEngine {
|
||||
/**
|
||||
* Slug of the category specifying marketing extensions on the Woo.com store.
|
||||
*
|
||||
@@ -54,6 +56,29 @@ class Init {
|
||||
return $specs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process specs.
|
||||
*
|
||||
* @param array|null $specs Marketing recommendations spec array.
|
||||
* @return array
|
||||
*/
|
||||
protected static function evaluate_specs( array $specs = null ) {
|
||||
$suggestions = array();
|
||||
$errors = array();
|
||||
|
||||
foreach ( $specs as $spec ) {
|
||||
try {
|
||||
$suggestions[] = self::object_to_array( $spec );
|
||||
} catch ( \Throwable $e ) {
|
||||
$errors[] = $e;
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'suggestions' => $suggestions,
|
||||
'errors' => $errors,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load recommended plugins from Woo.com
|
||||
@@ -61,14 +86,30 @@ class Init {
|
||||
* @return array
|
||||
*/
|
||||
public static function get_recommended_plugins(): array {
|
||||
$specs = self::get_specs();
|
||||
$result = array();
|
||||
$specs = self::get_specs();
|
||||
$results = self::evaluate_specs( $specs );
|
||||
|
||||
foreach ( $specs as $spec ) {
|
||||
$result[] = self::object_to_array( $spec );
|
||||
$specs_to_return = $results['suggestions'];
|
||||
$specs_to_save = null;
|
||||
|
||||
if ( empty( $specs_to_return ) ) {
|
||||
// When suggestions is empty, replace it with defaults and save for 3 hours.
|
||||
$specs_to_save = DefaultMarketingRecommendations::get_all();
|
||||
$specs_to_return = self::evaluate_specs( $specs_to_save )['suggestions'];
|
||||
} elseif ( count( $results['errors'] ) > 0 ) {
|
||||
// When suggestions is not empty but has errors, save it for 3 hours.
|
||||
$specs_to_save = $specs;
|
||||
}
|
||||
|
||||
return $result;
|
||||
if ( $specs_to_save ) {
|
||||
MarketingRecommendationsDataSourcePoller::get_instance()->set_specs_transient( $specs_to_save, 3 * HOUR_IN_SECONDS );
|
||||
}
|
||||
$errors = $results['errors'];
|
||||
if ( ! empty( $errors ) ) {
|
||||
self::log_errors( $errors );
|
||||
}
|
||||
|
||||
return $specs_to_return;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,16 +180,22 @@ class Init {
|
||||
* This is used to convert the specs to an array so that they can be returned by the API.
|
||||
*
|
||||
* @param mixed $obj Object to convert.
|
||||
* @param array &$visited Reference to an array keeping track of all seen objects to detect circular references.
|
||||
* @return array
|
||||
*/
|
||||
protected static function object_to_array( $obj ) {
|
||||
public static function object_to_array( $obj, &$visited = array() ) {
|
||||
if ( is_object( $obj ) ) {
|
||||
$obj = (array) $obj;
|
||||
if ( in_array( $obj, $visited, true ) ) {
|
||||
// Circular reference detected.
|
||||
return null;
|
||||
}
|
||||
$visited[] = $obj;
|
||||
$obj = (array) $obj;
|
||||
}
|
||||
if ( is_array( $obj ) ) {
|
||||
$new = array();
|
||||
foreach ( $obj as $key => $val ) {
|
||||
$new[ $key ] = self::object_to_array( $val );
|
||||
$new[ $key ] = self::object_to_array( $val, $visited );
|
||||
}
|
||||
} else {
|
||||
$new = $obj;
|
||||
|
||||
@@ -168,7 +168,7 @@ class TaskList {
|
||||
* @return bool
|
||||
*/
|
||||
public function is_visible() {
|
||||
if ( ! $this->visible || ! count( $this->get_viewable_tasks() ) > 0 ) {
|
||||
if ( ! $this->visible || $this->is_hidden() || ! count( $this->get_viewable_tasks() ) > 0 ) {
|
||||
return false;
|
||||
}
|
||||
return ! $this->is_hidden();
|
||||
@@ -199,6 +199,7 @@ class TaskList {
|
||||
'action' => 'remove_card',
|
||||
'completed_task_count' => $completed_count,
|
||||
'incomplete_task_count' => count( $viewable_tasks ) - $completed_count,
|
||||
'tasklist_id' => $this->id,
|
||||
)
|
||||
);
|
||||
|
||||
@@ -324,11 +325,17 @@ class TaskList {
|
||||
* Track list completion of viewable tasks.
|
||||
*/
|
||||
public function possibly_track_completion() {
|
||||
if ( ! $this->is_complete() ) {
|
||||
if ( $this->has_previously_completed() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $this->has_previously_completed() ) {
|
||||
// If it's hidden, completion is tracked via hide method.
|
||||
if ( $this->is_hidden() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Expensive check, do it last.
|
||||
if ( ! $this->is_complete() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -336,7 +343,12 @@ class TaskList {
|
||||
$completed_lists[] = $this->get_list_id();
|
||||
update_option( self::COMPLETED_OPTION, $completed_lists );
|
||||
$this->maybe_set_default_layout( $completed_lists );
|
||||
$this->record_tracks_event( 'tasks_completed' );
|
||||
$this->record_tracks_event(
|
||||
'tasks_completed',
|
||||
array(
|
||||
'tasklist_id' => $this->id,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -407,7 +407,7 @@ class TaskLists {
|
||||
public static function setup_tasks_remaining() {
|
||||
$setup_list = self::get_list( 'setup' );
|
||||
|
||||
if ( ! $setup_list || $setup_list->is_hidden() || $setup_list->is_complete() ) {
|
||||
if ( ! $setup_list || $setup_list->is_hidden() || $setup_list->has_previously_completed() || $setup_list->is_complete() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,4 +30,31 @@ class EvaluateSuggestion {
|
||||
|
||||
return $suggestion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the specs and returns the visible suggestions.
|
||||
*
|
||||
* @param array $specs payment suggestion spec array.
|
||||
* @return array The visible suggestions and errors.
|
||||
*/
|
||||
public static function evaluate_specs( $specs ) {
|
||||
$suggestions = array();
|
||||
$errors = array();
|
||||
|
||||
foreach ( $specs as $spec ) {
|
||||
try {
|
||||
$suggestion = self::evaluate( $spec );
|
||||
if ( ! property_exists( $suggestion, 'is_visible' ) || $suggestion->is_visible ) {
|
||||
$suggestions[] = $suggestion;
|
||||
}
|
||||
} catch ( \Throwable $e ) {
|
||||
$errors[] = $e;
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'suggestions' => $suggestions,
|
||||
'errors' => $errors,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,12 +9,13 @@ defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions\DefaultPaymentGateways;
|
||||
use Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions\PaymentGatewaysController;
|
||||
use Automattic\WooCommerce\Admin\RemoteSpecs\RemoteSpecsEngine;
|
||||
|
||||
/**
|
||||
* Remote Payment Methods engine.
|
||||
* This goes through the specs and gets eligible payment gateways.
|
||||
*/
|
||||
class Init {
|
||||
class Init extends RemoteSpecsEngine {
|
||||
/**
|
||||
* Option name for dismissed payment method suggestions.
|
||||
*/
|
||||
@@ -35,30 +36,31 @@ class Init {
|
||||
* @return array
|
||||
*/
|
||||
public static function get_suggestions( array $specs = null ) {
|
||||
$suggestions = array();
|
||||
if ( null === $specs ) {
|
||||
$specs = self::get_specs();
|
||||
$locale = get_user_locale();
|
||||
|
||||
$specs = is_array( $specs ) ? $specs : self::get_specs();
|
||||
$results = EvaluateSuggestion::evaluate_specs( $specs );
|
||||
$specs_to_return = $results['suggestions'];
|
||||
$specs_to_save = null;
|
||||
|
||||
if ( empty( $specs_to_return ) ) {
|
||||
// When suggestions is empty, replace it with defaults and save for 3 hours.
|
||||
$specs_to_save = DefaultPaymentGateways::get_all();
|
||||
$specs_to_return = EvaluateSuggestion::evaluate_specs( $specs_to_save )['suggestions'];
|
||||
} elseif ( count( $results['errors'] ) > 0 ) {
|
||||
// When suggestions is not empty but has errors, save it for 3 hours.
|
||||
$specs_to_save = $specs;
|
||||
}
|
||||
|
||||
foreach ( $specs as $spec ) {
|
||||
try {
|
||||
$suggestion = EvaluateSuggestion::evaluate( $spec );
|
||||
$suggestions[] = $suggestion;
|
||||
// phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
|
||||
} catch ( \Throwable $e ) {
|
||||
// Ignore errors.
|
||||
}
|
||||
if ( count( $results['errors'] ) > 0 ) {
|
||||
self::log_errors( $results['errors'] );
|
||||
}
|
||||
|
||||
return array_values(
|
||||
array_filter(
|
||||
$suggestions,
|
||||
function( $suggestion ) {
|
||||
return ! property_exists( $suggestion, 'is_visible' ) || $suggestion->is_visible;
|
||||
}
|
||||
)
|
||||
);
|
||||
if ( $specs_to_save ) {
|
||||
PaymentGatewaySuggestionsDataSourcePoller::get_instance()->set_specs_transient( array( $locale => $specs_to_save ), 3 * HOUR_IN_SECONDS );
|
||||
}
|
||||
|
||||
return $specs_to_return;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,11 +31,14 @@ class BlockRegistry {
|
||||
'woocommerce/product-pricing-field',
|
||||
'woocommerce/product-section',
|
||||
'woocommerce/product-section-description',
|
||||
'woocommerce/product-subsection',
|
||||
'woocommerce/product-subsection-description',
|
||||
'woocommerce/product-details-section-description',
|
||||
'woocommerce/product-tab',
|
||||
'woocommerce/product-toggle-field',
|
||||
'woocommerce/product-taxonomy-field',
|
||||
'woocommerce/product-text-field',
|
||||
'woocommerce/product-text-area-field',
|
||||
'woocommerce/product-number-field',
|
||||
'woocommerce/product-linked-list-field',
|
||||
);
|
||||
@@ -60,7 +63,6 @@ class BlockRegistry {
|
||||
'woocommerce/product-tag-field',
|
||||
'woocommerce/product-inventory-quantity-field',
|
||||
'woocommerce/product-variation-items-field',
|
||||
'woocommerce/product-variations-fields',
|
||||
'woocommerce/product-password-field',
|
||||
'woocommerce/product-list-field',
|
||||
'woocommerce/product-has-variations-notice',
|
||||
|
||||
@@ -34,6 +34,14 @@ interface ProductFormTemplateInterface extends BlockTemplateInterface {
|
||||
*/
|
||||
public function get_section_by_id( string $section_id ): ?SectionInterface;
|
||||
|
||||
/**
|
||||
* Gets subsection block by id.
|
||||
*
|
||||
* @param string $subsection_id subsection id.
|
||||
* @return SubsectionInterface|null
|
||||
*/
|
||||
public function get_subsection_by_id( string $subsection_id ): ?SubsectionInterface;
|
||||
|
||||
/**
|
||||
* Gets Block by id.
|
||||
*
|
||||
|
||||
@@ -15,9 +15,9 @@ interface SectionInterface extends BlockContainerInterface {
|
||||
* Adds a new sub-section to the section.
|
||||
*
|
||||
* @param array $block_config block config.
|
||||
* @return SectionInterface new block section.
|
||||
* @return SubsectionInterface new block sub-section.
|
||||
*/
|
||||
public function add_section( array $block_config ): SectionInterface;
|
||||
public function add_subsection( array $block_config ): SubsectionInterface;
|
||||
|
||||
/**
|
||||
* Adds a new block to the section.
|
||||
@@ -25,4 +25,13 @@ interface SectionInterface extends BlockContainerInterface {
|
||||
* @param array $block_config block config.
|
||||
*/
|
||||
public function add_block( array $block_config ): BlockInterface;
|
||||
|
||||
/**
|
||||
* Adds a new sub-section to the section.
|
||||
*
|
||||
* @deprecated 8.6.0
|
||||
*
|
||||
* @param array $block_config The block data.
|
||||
*/
|
||||
public function add_section( array $block_config ): SubsectionInterface;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\ProductBlockEditor\ProductTemplates;
|
||||
|
||||
use Automattic\WooCommerce\Admin\BlockTemplates\BlockContainerInterface;
|
||||
use Automattic\WooCommerce\Admin\BlockTemplates\BlockInterface;
|
||||
|
||||
/**
|
||||
* Interface for subsection containers, which contain sub-sections and blocks.
|
||||
*/
|
||||
interface SubsectionInterface extends BlockContainerInterface {
|
||||
/**
|
||||
* Adds a new block to the sub-section.
|
||||
*
|
||||
* @param array $block_config block config.
|
||||
*/
|
||||
public function add_block( array $block_config ): BlockInterface;
|
||||
}
|
||||
@@ -2,12 +2,13 @@
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\Features\ShippingPartnerSuggestions;
|
||||
|
||||
use Automattic\WooCommerce\Admin\RemoteInboxNotifications\RuleEvaluator;
|
||||
use Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions\EvaluateSuggestion;
|
||||
use Automattic\WooCommerce\Admin\RemoteSpecs\RemoteSpecsEngine;
|
||||
|
||||
/**
|
||||
* Class ShippingPartnerSuggestions
|
||||
*/
|
||||
class ShippingPartnerSuggestions {
|
||||
class ShippingPartnerSuggestions extends RemoteSpecsEngine {
|
||||
|
||||
/**
|
||||
* Go through the specs and run them.
|
||||
@@ -15,31 +16,34 @@ class ShippingPartnerSuggestions {
|
||||
* @param array|null $specs shipping partner suggestion spec array.
|
||||
* @return array
|
||||
*/
|
||||
public static function get_suggestions( $specs = null ) {
|
||||
$suggestions = array();
|
||||
if ( null === $specs ) {
|
||||
$specs = self::get_specs_from_datasource();
|
||||
public static function get_suggestions( array $specs = null ) {
|
||||
$locale = get_user_locale();
|
||||
|
||||
$specs = is_array( $specs ) ? $specs : self::get_specs();
|
||||
$results = EvaluateSuggestion::evaluate_specs( $specs );
|
||||
$specs_to_return = $results['suggestions'];
|
||||
$specs_to_save = null;
|
||||
|
||||
if ( empty( $specs_to_return ) ) {
|
||||
// When suggestions is empty, replace it with defaults and save for 3 hours.
|
||||
$specs_to_save = DefaultShippingPartners::get_all();
|
||||
$specs_to_return = EvaluateSuggestion::evaluate_specs( $specs_to_save )['suggestions'];
|
||||
} elseif ( count( $results['errors'] ) > 0 ) {
|
||||
// When suggestions is not empty but has errors, save it for 3 hours.
|
||||
$specs_to_save = $specs;
|
||||
}
|
||||
|
||||
$rule_evaluator = new RuleEvaluator();
|
||||
foreach ( $specs as &$spec ) {
|
||||
$spec = is_array( $spec ) ? (object) $spec : $spec;
|
||||
if ( isset( $spec->is_visible ) ) {
|
||||
$is_visible = $rule_evaluator->evaluate( $spec->is_visible );
|
||||
if ( $is_visible ) {
|
||||
$spec->is_visible = true;
|
||||
$suggestions[] = $spec;
|
||||
}
|
||||
}
|
||||
if ( $specs_to_save ) {
|
||||
ShippingPartnerSuggestionsDataSourcePoller::get_instance()->set_specs_transient( array( $locale => $specs_to_save ), 3 * HOUR_IN_SECONDS );
|
||||
}
|
||||
|
||||
return $suggestions;
|
||||
return $specs_to_return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get specs or fetch remotely if they don't exist.
|
||||
*/
|
||||
public static function get_specs_from_datasource() {
|
||||
public static function get_specs() {
|
||||
if ( 'no' === get_option( 'woocommerce_show_marketplace_suggestions', 'yes' ) ) {
|
||||
/**
|
||||
* It can be used to modify shipping partner suggestions spec.
|
||||
|
||||
@@ -167,6 +167,7 @@ class PageController {
|
||||
return apply_filters( 'woocommerce_navigation_get_breadcrumbs', array( '' ), $current_page );
|
||||
}
|
||||
|
||||
$current_page['title'] = (array) $current_page['title'];
|
||||
if ( 1 === count( $current_page['title'] ) ) {
|
||||
$breadcrumbs = $current_page['title'];
|
||||
} else {
|
||||
|
||||
@@ -25,7 +25,11 @@ class BaseLocationCountryRuleProcessor implements RuleProcessorInterface {
|
||||
*/
|
||||
public function process( $rule, $stored_state ) {
|
||||
$base_location = wc_get_base_location();
|
||||
if ( ! $base_location ) {
|
||||
if (
|
||||
! is_array( $base_location ) ||
|
||||
! array_key_exists( 'country', $base_location ) ||
|
||||
! array_key_exists( 'state', $base_location )
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ class BaseLocationStateRuleProcessor implements RuleProcessorInterface {
|
||||
*/
|
||||
public function process( $rule, $stored_state ) {
|
||||
$base_location = wc_get_base_location();
|
||||
if ( ! $base_location ) {
|
||||
if ( ! is_array( $base_location ) || ! array_key_exists( 'state', $base_location ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class OnboardingProfileRuleProcessor implements RuleProcessorInterface {
|
||||
public function process( $rule, $stored_state ) {
|
||||
$onboarding_profile = get_option( 'woocommerce_onboarding_profile' );
|
||||
|
||||
if ( empty( $onboarding_profile ) ) {
|
||||
if ( empty( $onboarding_profile ) || ! is_array( $onboarding_profile ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,13 +20,14 @@ class OptionRuleProcessor implements RuleProcessorInterface {
|
||||
* @return bool The result of the operation.
|
||||
*/
|
||||
public function process( $rule, $stored_state ) {
|
||||
$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 = $this->get_option_value( $rule, $default, $is_contains );
|
||||
$is_contains = $rule->operation && strpos( $rule->operation, 'contains' ) !== false;
|
||||
$default_value = $is_contains ? array() : false;
|
||||
$is_default_set = property_exists( $rule, 'default' );
|
||||
$default = $is_default_set ? $rule->default : $default_value;
|
||||
$option_value = $this->get_option_value( $rule, $default, $is_contains );
|
||||
|
||||
if ( isset( $rule->transformers ) && is_array( $rule->transformers ) ) {
|
||||
$option_value = TransformerService::apply( $option_value, $rule->transformers, $default );
|
||||
$option_value = TransformerService::apply( $option_value, $rule->transformers, $is_default_set, $default );
|
||||
}
|
||||
|
||||
return ComparisonOperation::compare(
|
||||
|
||||
@@ -52,7 +52,7 @@ class PluginVersionRuleProcessor implements RuleProcessorInterface {
|
||||
|
||||
$plugin_data = $this->plugins_provider->get_plugin_data( $rule->plugin );
|
||||
|
||||
if ( ! $plugin_data ) {
|
||||
if ( ! is_array( $plugin_data ) || ! array_key_exists( 'Version', $plugin_data ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,13 +41,21 @@ class PluginsActivatedRuleProcessor implements RuleProcessorInterface {
|
||||
* @return bool Whether the rule passes or not.
|
||||
*/
|
||||
public function process( $rule, $stored_state ) {
|
||||
if ( 0 === count( $rule->plugins ) ) {
|
||||
if ( ! is_countable( $rule->plugins ) || 0 === count( $rule->plugins ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$active_plugin_slugs = $this->plugins_provider->get_active_plugin_slugs();
|
||||
|
||||
foreach ( $rule->plugins as $plugin_slug ) {
|
||||
if ( ! is_string( $plugin_slug ) ) {
|
||||
$logger = wc_get_logger();
|
||||
$logger->warning(
|
||||
__( 'Invalid plugin slug provided in the plugins activated rule.', 'woocommerce' )
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! in_array( $plugin_slug, $active_plugin_slugs, true ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -56,6 +56,12 @@ class PublishAfterTimeRuleProcessor implements RuleProcessorInterface {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
new \DateTime( $rule->publish_after );
|
||||
} catch ( \Throwable $e ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,12 @@ class PublishBeforeTimeRuleProcessor implements RuleProcessorInterface {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
new \DateTime( $rule->publish_before );
|
||||
} catch ( \Throwable $e ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,14 @@ defined( 'ABSPATH' ) || exit;
|
||||
use Automattic\WooCommerce\Admin\PluginsProvider\PluginsProvider;
|
||||
use Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProfile;
|
||||
use Automattic\WooCommerce\Admin\Notes\Note;
|
||||
use Automattic\WooCommerce\Admin\RemoteSpecs\RemoteSpecsEngine;
|
||||
|
||||
/**
|
||||
* Remote Inbox Notifications engine.
|
||||
* This goes through the specs and runs (creates admin notes) for those
|
||||
* specs that are able to be triggered.
|
||||
*/
|
||||
class RemoteInboxNotificationsEngine {
|
||||
class RemoteInboxNotificationsEngine extends RemoteSpecsEngine {
|
||||
const STORED_STATE_OPTION_NAME = 'wc_remote_inbox_notifications_stored_state';
|
||||
const WCA_UPDATED_OPTION_NAME = 'wc_remote_inbox_notifications_wca_updated';
|
||||
|
||||
@@ -112,14 +113,22 @@ class RemoteInboxNotificationsEngine {
|
||||
public static function run() {
|
||||
$specs = DataSourcePoller::get_instance()->get_specs_from_data_sources();
|
||||
|
||||
if ( $specs === false || count( $specs ) === 0 ) {
|
||||
if ( false === $specs || ! is_countable( $specs ) || count( $specs ) === 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$stored_state = self::get_stored_state();
|
||||
$errors = array();
|
||||
|
||||
foreach ( $specs as $spec ) {
|
||||
SpecRunner::run_spec( $spec, $stored_state );
|
||||
$error = SpecRunner::run_spec( $spec, $stored_state );
|
||||
if ( isset( $error ) ) {
|
||||
$errors[] = $error;
|
||||
}
|
||||
}
|
||||
|
||||
if ( count( $errors ) > 0 ) {
|
||||
self::log_errors( $errors );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class SpecRunner {
|
||||
|
||||
// Create or update the note.
|
||||
$existing_note_ids = $data_store->get_notes_with_name( $spec->slug );
|
||||
if ( count( $existing_note_ids ) === 0 ) {
|
||||
if ( ! is_countable( $existing_note_ids ) || count( $existing_note_ids ) === 0 ) {
|
||||
$note = new Note();
|
||||
$note->set_status( Note::E_WC_ADMIN_NOTE_PENDING );
|
||||
} else {
|
||||
@@ -45,7 +45,7 @@ class SpecRunner {
|
||||
new RuleEvaluator()
|
||||
);
|
||||
} catch ( \Throwable $e ) {
|
||||
return;
|
||||
return $e;
|
||||
}
|
||||
|
||||
// If the status is changing, update the created date to now.
|
||||
|
||||
@@ -23,17 +23,22 @@ class TotalPaymentsVolumeProcessor implements RuleProcessorInterface {
|
||||
* @return bool The result of the operation.
|
||||
*/
|
||||
public function process( $rule, $stored_state ) {
|
||||
$dates = TimeInterval::get_timeframe_dates( $rule->timeframe );
|
||||
$reports_revenue = new RevenueQuery(
|
||||
$dates = TimeInterval::get_timeframe_dates( $rule->timeframe );
|
||||
$reports_revenue = $this->get_reports_query(
|
||||
array(
|
||||
'before' => $dates['end'],
|
||||
'after' => $dates['start'],
|
||||
'before' => $dates['end'],
|
||||
'after' => $dates['start'],
|
||||
'interval' => 'year',
|
||||
'fields' => array( 'total_sales' ),
|
||||
'fields' => array( 'total_sales' ),
|
||||
)
|
||||
);
|
||||
$report_data = $reports_revenue->get_data();
|
||||
$value = $report_data->totals->total_sales;
|
||||
$report_data = $reports_revenue->get_data();
|
||||
|
||||
if ( ! $report_data || ! isset( $report_data->totals->total_sales ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$value = $report_data->totals->total_sales;
|
||||
|
||||
return ComparisonOperation::compare(
|
||||
$value,
|
||||
@@ -62,7 +67,7 @@ class TotalPaymentsVolumeProcessor implements RuleProcessorInterface {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! isset( $rule->value ) ) {
|
||||
if ( ! isset( $rule->value ) || ! is_numeric( $rule->value ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -72,4 +77,17 @@ class TotalPaymentsVolumeProcessor implements RuleProcessorInterface {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the report query.
|
||||
*
|
||||
* @param array $args The query args.
|
||||
*
|
||||
* @return RevenueQuery The report query.
|
||||
*/
|
||||
protected function get_reports_query( $args ) {
|
||||
return new RevenueQuery(
|
||||
$args
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,12 +36,13 @@ class TransformerService {
|
||||
*
|
||||
* @param mixed $target_value a value to transform.
|
||||
* @param array $transformer_configs transform configuration.
|
||||
* @param bool $is_default_set flag on is default value set.
|
||||
* @param string $default default value.
|
||||
*
|
||||
* @throws InvalidArgumentException Throws when one of the requried arguments is missing.
|
||||
* @return mixed|null
|
||||
*/
|
||||
public static function apply( $target_value, array $transformer_configs, $default ) {
|
||||
public static function apply( $target_value, array $transformer_configs, $is_default_set, $default ) {
|
||||
foreach ( $transformer_configs as $transformer_config ) {
|
||||
if ( ! isset( $transformer_config->use ) ) {
|
||||
throw new InvalidArgumentException( 'Missing required config value: use' );
|
||||
@@ -56,13 +57,25 @@ class TransformerService {
|
||||
throw new InvalidArgumentException( "Unable to find a transformer by name: {$transformer_config->use}" );
|
||||
}
|
||||
|
||||
$transformed_value = $transformer->transform( $target_value, $transformer_config->arguments, $default );
|
||||
// if the transformer returns null, then return the previously transformed value.
|
||||
if ( null === $transformed_value ) {
|
||||
return $target_value;
|
||||
$target_value = $transformer->transform( $target_value, $transformer_config->arguments, $is_default_set ? $default : null );
|
||||
|
||||
// Break early when there's no more value to traverse.
|
||||
if ( null === $target_value ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $is_default_set ) {
|
||||
// Nulls always return the default value.
|
||||
if ( null === $target_value ) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
$target_value = $transformed_value;
|
||||
// When type of the default value is different from the target value, return the default value
|
||||
// to ensure type safety.
|
||||
if ( gettype( $default ) !== gettype( $target_value ) ) {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
return $target_value;
|
||||
|
||||
@@ -23,7 +23,11 @@ class ArrayColumn implements TransformerInterface {
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function transform( $value, stdClass $arguments = null, $default = null ) {
|
||||
public function transform( $value, stdClass $arguments = null, $default = array() ) {
|
||||
if ( ! is_array( $value ) ) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return array_column( $value, $arguments->key );
|
||||
}
|
||||
|
||||
@@ -39,6 +43,14 @@ class ArrayColumn implements TransformerInterface {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
null !== $arguments->key &&
|
||||
! is_string( $arguments->key ) &&
|
||||
! is_int( $arguments->key )
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,11 @@ class ArrayFlatten implements TransformerInterface {
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function transform( $value, stdClass $arguments = null, $default = null ) {
|
||||
public function transform( $value, stdClass $arguments = null, $default = array() ) {
|
||||
if ( ! is_array( $value ) ) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
$return = array();
|
||||
array_walk_recursive(
|
||||
$value,
|
||||
|
||||
@@ -20,7 +20,11 @@ class ArrayKeys implements TransformerInterface {
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function transform( $value, stdClass $arguments = null, $default = null ) {
|
||||
public function transform( $value, stdClass $arguments = null, $default = array() ) {
|
||||
if ( ! is_array( $value ) ) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return array_keys( $value );
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,10 @@ class ArraySearch implements TransformerInterface {
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function transform( $value, stdClass $arguments = null, $default = null ) {
|
||||
if ( ! is_array( $value ) ) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
$key = array_search( $arguments->value, $value, true );
|
||||
if ( false !== $key ) {
|
||||
return $value[ $key ];
|
||||
|
||||
@@ -20,7 +20,11 @@ class ArrayValues implements TransformerInterface {
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function transform( $value, stdClass $arguments = null, $default = null ) {
|
||||
public function transform( $value, stdClass $arguments = null, $default = array() ) {
|
||||
if ( ! is_array( $value ) ) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return array_values( $value );
|
||||
}
|
||||
|
||||
|
||||
@@ -6,21 +6,25 @@ use Automattic\WooCommerce\Admin\RemoteInboxNotifications\TransformerInterface;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Count elements in Array.
|
||||
* Count elements in Array or Countable object.
|
||||
*
|
||||
* @package Automattic\WooCommerce\Admin\RemoteInboxNotifications\Transformers
|
||||
*/
|
||||
class Count implements TransformerInterface {
|
||||
/**
|
||||
* Count elements in Array.
|
||||
* Count elements in Array or Countable object.
|
||||
*
|
||||
* @param array $value an array to count.
|
||||
* @param stdClass|null $arguments arguments.
|
||||
* @param string|null $default default value.
|
||||
* @param array|Countable $value an array to count.
|
||||
* @param stdClass|null $arguments arguments.
|
||||
* @param string|null $default default value.
|
||||
*
|
||||
* @return number
|
||||
*/
|
||||
public function transform( $value, stdClass $arguments = null, $default = null ) {
|
||||
if ( ! is_array( $value ) && ! $value instanceof \Countable ) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return count( $value );
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,10 @@ class DotNotation implements TransformerInterface {
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function get( $array, $path, $default = null ) {
|
||||
if ( ! is_array( $array ) ) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
if ( isset( $array[ $path ] ) ) {
|
||||
return $array[ $path ];
|
||||
}
|
||||
|
||||
@@ -14,15 +14,32 @@ class PrepareUrl implements TransformerInterface {
|
||||
/**
|
||||
* Prepares the site URL by removing the protocol and trailing slash.
|
||||
*
|
||||
* @param mixed $value a value to transform.
|
||||
* @param string $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 ) {
|
||||
if ( ! is_string( $value ) ) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
$url_parts = wp_parse_url( rtrim( $value, '/' ) );
|
||||
return isset( $url_parts['path'] ) ? $url_parts['host'] . $url_parts['path'] : $url_parts['host'];
|
||||
|
||||
if ( ! $url_parts ) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
if ( ! isset( $url_parts['host'] ) ) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
if ( isset( $url_parts['path'] ) ) {
|
||||
return $url_parts['host'] . $url_parts['path'];
|
||||
}
|
||||
|
||||
return $url_parts['host'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,7 +43,12 @@ class WCAdminActiveForRuleProcessor implements RuleProcessorInterface {
|
||||
*/
|
||||
public function process( $rule, $stored_state ) {
|
||||
$active_for_seconds = $this->wcadmin_active_for_provider->get_wcadmin_active_for_in_seconds();
|
||||
$rule_seconds = $rule->days * DAY_IN_SECONDS;
|
||||
|
||||
if ( ! $active_for_seconds || ! is_numeric( $active_for_seconds ) || $active_for_seconds < 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$rule_seconds = $rule->days * DAY_IN_SECONDS;
|
||||
|
||||
return ComparisonOperation::compare(
|
||||
$active_for_seconds,
|
||||
@@ -60,7 +65,8 @@ class WCAdminActiveForRuleProcessor implements RuleProcessorInterface {
|
||||
* @return bool Pass/fail.
|
||||
*/
|
||||
public function validate( $rule ) {
|
||||
if ( ! isset( $rule->days ) ) {
|
||||
// Ensure that 'days' property is set and is a valid numeric value.
|
||||
if ( ! isset( $rule->days ) || ! is_numeric( $rule->days ) || $rule->days < 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Admin\RemoteSpecs;
|
||||
|
||||
/**
|
||||
* RemoteSpecsEngine class.
|
||||
*/
|
||||
abstract class RemoteSpecsEngine {
|
||||
/**
|
||||
* Log errors.
|
||||
*
|
||||
* @param array $errors Array of errors from \Throwable interface.
|
||||
*/
|
||||
public static function log_errors( $errors = array() ) {
|
||||
if (
|
||||
true !== defined( 'WP_ENVIRONMENT_TYPE' ) ||
|
||||
! in_array( constant( 'WP_ENVIRONMENT_TYPE' ), array( 'development', 'local' ), true )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
$logger = wc_get_logger();
|
||||
$error_messages = array();
|
||||
|
||||
foreach ( $errors as $error ) {
|
||||
if ( isset( $error ) && method_exists( $error, 'getMessage' ) ) {
|
||||
$error_messages[] = $error->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
$logger->error(
|
||||
'Error while evaluating specs',
|
||||
array(
|
||||
'source' => 'remotespecsengine-errors',
|
||||
'class' => static::class,
|
||||
'errors' => $error_messages,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user