Refactor remote specs structure and naming (#45547)

* Deprecate DataSourcePoller

* Deprecate and move all rule processors and transformers

* Lint

* More deprecation

* Remove extra line

* Update deprecated class to not produce too many messages by limiting to unique messages

* Changelog

* Update all dependency uses, move TransformerService and TransformerInterface to Transformers package

* More dependency update

* Changelog

* Fix wrong file reference

* Lint markdown

* Lint markdown

* Add unsaved file

* Delete unused file and more lint

* More lint

* Ugh ignore faulty lint rule

* Rename variables for lint
This commit is contained in:
Ilyas Foo 2024-03-19 22:15:05 +08:00 committed by GitHub
parent 7214a1ffee
commit 5485665727
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
135 changed files with 4285 additions and 2502 deletions

View File

@ -2,7 +2,7 @@
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\RemoteInboxNotifications\RuleEvaluator;
use Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\RuleEvaluator;
register_woocommerce_admin_test_helper_rest_route(
'/remote-spec-validator/validate',

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update import path

View File

@ -0,0 +1,4 @@
Significance: minor
Type: dev
Refactor remote specs structure and naming

View File

@ -8,6 +8,7 @@
use Automattic\Jetpack\Constants;
use Automattic\WooCommerce\Admin\RemoteInboxNotifications as PromotionRuleEngine;
use Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\RuleEvaluator;
if ( ! defined( 'ABSPATH' ) ) {
exit;
@ -1075,7 +1076,7 @@ class WC_Admin_Addons {
// Check for existence of promotions and evaluate out if we should show them.
if ( ! empty( $promotions ) ) {
foreach ( $promotions as $promo_id => $promotion ) {
$evaluator = new PromotionRuleEngine\RuleEvaluator();
$evaluator = new RuleEvaluator();
$passed = $evaluator->evaluate( $promotion->rules );
if ( ! $passed ) {
unset( $promotions[ $promo_id ] );

View File

@ -2,55 +2,29 @@
namespace Automattic\WooCommerce\Admin;
use Automattic\WooCommerce\Admin\RemoteSpecs\DataSourcePoller as RemoteSpecsDataSourcePoller;
/**
* Specs data source poller class.
* This handles polling specs from JSON endpoints, and
* stores the specs in to the database as an option.
*
* @deprecated since 8.8.0
*/
abstract class DataSourcePoller {
abstract class DataSourcePoller extends RemoteSpecsDataSourcePoller {
/**
* Get class instance.
* Log a deprecation to the error log.
*/
abstract public static function get_instance();
/**
* Name of data sources filter.
*/
const FILTER_NAME = 'data_source_poller_data_sources';
/**
* Name of data source specs filter.
*/
const FILTER_NAME_SPECS = 'data_source_poller_specs';
/**
* Id of DataSourcePoller.
*
* @var string
*/
protected $id = array();
/**
* Default data sources array.
*
* @var array
*/
protected $data_sources = array();
/**
* Default args.
*
* @var array
*/
protected $args = array();
/**
* The logger instance.
*
* @var WC_Logger|null
*/
protected static $logger = null;
private static function log_deprecation() {
error_log( // phpcs:ignore
sprintf(
'%1$s is deprecated since version %2$s! Use %3$s instead.',
self::class,
'8.8.0',
'Automattic\WooCommerce\Admin\RemoteSpecs\DataSourcePoller'
)
);
}
/**
* Constructor.
@ -60,98 +34,41 @@ abstract class DataSourcePoller {
* @param array $args Options for DataSourcePoller.
*/
public function __construct( $id, $data_sources = array(), $args = array() ) {
$this->data_sources = $data_sources;
$this->id = $id;
$arg_defaults = array(
'spec_key' => 'id',
'transient_name' => 'woocommerce_admin_' . $id . '_specs',
'transient_expiry' => 7 * DAY_IN_SECONDS,
);
$this->args = wp_parse_args( $args, $arg_defaults );
}
/**
* Get the logger instance.
*
* @return WC_Logger
*/
protected static function get_logger() {
if ( is_null( self::$logger ) ) {
self::$logger = wc_get_logger();
}
return self::$logger;
}
/**
* Returns the key identifier of spec, this can easily be overwritten. Defaults to id.
*
* @param mixed $spec a JSON parsed spec coming from the JSON feed.
* @return string|boolean
*/
protected function get_spec_key( $spec ) {
$key = $this->args['spec_key'];
if ( isset( $spec->$key ) ) {
return $spec->$key;
}
return false;
self::log_deprecation();
parent::__construct( $id, $data_sources, $args );
}
/**
* Reads the data sources for specs and persists those specs.
*
* @deprecated 8.8.0
* @return array list of specs.
*/
public function get_specs_from_data_sources() {
$locale = get_user_locale();
$specs_group = get_transient( $this->args['transient_name'] ) ?? array();
$specs = isset( $specs_group[ $locale ] ) ? $specs_group[ $locale ] : array();
if ( ! is_array( $specs ) || empty( $specs ) ) {
$this->read_specs_from_data_sources();
$specs_group = get_transient( $this->args['transient_name'] );
$specs = isset( $specs_group[ $locale ] ) ? $specs_group[ $locale ] : array();
}
$specs = apply_filters( self::FILTER_NAME_SPECS, $specs, $this->id );
return $specs !== false ? $specs : array();
self::log_deprecation();
return parent::get_specs_from_data_sources();
}
/**
* Reads the data sources for specs and persists those specs.
*
* @deprecated 8.8.0
* @return bool Whether any specs were read.
*/
public function read_specs_from_data_sources() {
$specs = array();
$data_sources = apply_filters( self::FILTER_NAME, $this->data_sources, $this->id );
// Note that this merges the specs from the data sources based on the
// id - last one wins.
foreach ( $data_sources as $url ) {
$specs_from_data_source = self::read_data_source( $url );
$this->merge_specs( $specs_from_data_source, $specs, $url );
}
$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.
$this->set_specs_transient(
$specs_group,
$this->args['transient_expiry']
);
return count( $specs ) !== 0;
self::log_deprecation();
return parent::read_specs_from_data_sources();
}
/**
* Delete the specs transient.
*
* @deprecated 8.8.0
* @return bool success of failure of transient deletion.
*/
public function delete_specs_transient() {
return delete_transient( $this->args['transient_name'] );
self::log_deprecation();
return parent::delete_specs_transient();
}
/**
@ -159,112 +76,11 @@ abstract class DataSourcePoller {
*
* @param array $specs The specs to set in the transient.
* @param int $expiration The expiration time for the transient.
*
* @deprecated 8.8.0
*/
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
*
* @param string $url The URL to read the specs from.
*
* @return array The specs that have been read from the data source.
*/
protected static function read_data_source( $url ) {
$logger_context = array( 'source' => $url );
$logger = self::get_logger();
$response = wp_remote_get(
add_query_arg(
'locale',
get_user_locale(),
$url
),
array(
'user-agent' => 'WooCommerce/' . WC_VERSION . '; ' . home_url( '/' ),
)
);
if ( is_wp_error( $response ) || ! isset( $response['body'] ) ) {
$logger->error(
'Error getting data feed',
$logger_context
);
// phpcs:ignore
$logger->error( print_r( $response, true ), $logger_context );
return array();
}
$body = $response['body'];
$specs = json_decode( $body );
if ( $specs === null ) {
$logger->error(
'Empty response in data feed',
$logger_context
);
return array();
}
if ( ! is_array( $specs ) ) {
$logger->error(
'Data feed is not an array',
$logger_context
);
return array();
}
return $specs;
}
/**
* Merge the specs.
*
* @param Array $specs_to_merge_in The specs to merge in to $specs.
* @param Array $specs The list of specs being merged into.
* @param string $url The url of the feed being merged in (for error reporting).
*/
protected function merge_specs( $specs_to_merge_in, &$specs, $url ) {
foreach ( $specs_to_merge_in as $spec ) {
if ( ! $this->validate_spec( $spec, $url ) ) {
continue;
}
$id = $this->get_spec_key( $spec );
$specs[ $id ] = $spec;
}
}
/**
* Validate the spec.
*
* @param object $spec The spec to validate.
* @param string $url The url of the feed that provided the spec.
*
* @return bool The result of the validation.
*/
protected function validate_spec( $spec, $url ) {
$logger = self::get_logger();
$logger_context = array( 'source' => $url );
if ( ! $this->get_spec_key( $spec ) ) {
$logger->error(
'Spec is invalid because the id is missing in feed',
$logger_context
);
// phpcs:ignore
$logger->error( print_r( $spec, true ), $logger_context );
return false;
}
return true;
self::log_deprecation();
return parent::set_specs_transient( $specs, $expiration );
}
}

View File

@ -47,6 +47,12 @@ class DeprecatedClassFacade {
*/
protected static $deprecated_in_version = '';
/**
* Static array of logged messages.
*
* @var array
*/
private static $logged_messages = array();
/**
* Constructor.
@ -61,14 +67,18 @@ class DeprecatedClassFacade {
* @param string $function The name of the deprecated function being called.
*/
private static function log_deprecation( $function ) {
error_log( // phpcs:ignore
sprintf(
'%1$s is deprecated since version %2$s! Use %3$s instead.',
static::class . '::' . $function,
static::$deprecated_in_version,
static::$facade_over_classname . '::' . $function
)
$message = sprintf(
'%1$s is deprecated since version %2$s! Use %3$s instead.',
static::class . '::' . $function,
static::$deprecated_in_version,
static::$facade_over_classname . '::' . $function
);
// Only log when the message has not been logged before.
if ( ! in_array( $message, self::$logged_messages, true ) ) {
error_log( $message ); // phpcs:ignore
self::$logged_messages[] = $message;
}
}
/**

View File

@ -2,7 +2,7 @@
namespace Automattic\WooCommerce\Admin\Features\MarketingRecommendations;
use Automattic\WooCommerce\Admin\DataSourcePoller;
use Automattic\WooCommerce\Admin\RemoteSpecs\DataSourcePoller;
/**
* Specs data source poller class for marketing recommendations.

View File

@ -7,7 +7,7 @@ namespace Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\RemoteInboxNotifications\RuleEvaluator;
use Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\RuleEvaluator;
/**
* Evaluates the spec and returns the evaluated suggestion.

View File

@ -2,7 +2,7 @@
namespace Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions;
use Automattic\WooCommerce\Admin\DataSourcePoller;
use Automattic\WooCommerce\Admin\RemoteSpecs\DataSourcePoller;
/**
* Specs data source poller class for payment gateway suggestions.

View File

@ -2,7 +2,7 @@
namespace Automattic\WooCommerce\Admin\Features\ShippingPartnerSuggestions;
use Automattic\WooCommerce\Admin\DataSourcePoller;
use Automattic\WooCommerce\Admin\RemoteSpecs\DataSourcePoller;
/**
* Specs data source poller class for shipping partner suggestions.

View File

@ -6,65 +6,28 @@
namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
use Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProfile;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
defined( 'ABSPATH' ) || exit;
/**
* Rule processor that performs a comparison operation against the base
* location - country.
*
* @deprecated 8.8.0
*/
class BaseLocationCountryRuleProcessor implements RuleProcessorInterface {
class BaseLocationCountryRuleProcessor extends DeprecatedClassFacade {
/**
* Performs a comparison operation against the base location - country.
* The name of the non-deprecated class that this facade covers.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
* @var string
*/
public function process( $rule, $stored_state ) {
$base_location = wc_get_base_location();
if (
! is_array( $base_location ) ||
! array_key_exists( 'country', $base_location ) ||
! array_key_exists( 'state', $base_location )
) {
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 ( $is_address_default && OnboardingProfile::needs_completion() && ! $is_store_country_set ) {
return false;
}
return ComparisonOperation::compare(
$base_location['country'],
$rule->value,
$rule->operation
);
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\BaseLocationCountryRuleProcessor';
/**
* Validates the rule.
* The version that this class was deprecated in.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
* @var string
*/
public function validate( $rule ) {
if ( ! isset( $rule->value ) ) {
return false;
}
if ( ! isset( $rule->operation ) ) {
return false;
}
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -6,50 +6,28 @@
namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
defined( 'ABSPATH' ) || exit;
/**
* Rule processor that performs a comparison operation against the base
* location - state.
*
* @deprecated 8.8.0
*/
class BaseLocationStateRuleProcessor implements RuleProcessorInterface {
class BaseLocationStateRuleProcessor extends DeprecatedClassFacade {
/**
* Performs a comparison operation against the base location - state.
* The name of the non-deprecated class that this facade covers.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
* @var string
*/
public function process( $rule, $stored_state ) {
$base_location = wc_get_base_location();
if ( ! is_array( $base_location ) || ! array_key_exists( 'state', $base_location ) ) {
return false;
}
return ComparisonOperation::compare(
$base_location['state'],
$rule->value,
$rule->operation
);
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\BaseLocationStateRuleProcessor';
/**
* Validates the rule.
* The version that this class was deprecated in.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
* @var string
*/
public function validate( $rule ) {
if ( ! isset( $rule->value ) ) {
return false;
}
if ( ! isset( $rule->operation ) ) {
return false;
}
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -7,70 +7,25 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Compare two operands using the specified operation.
*
* @deprecated 8.8.0
*/
class ComparisonOperation {
class ComparisonOperation extends DeprecatedClassFacade {
/**
* Compare two operands using the specified operation.
* The name of the non-deprecated class that this facade covers.
*
* @param object $left_operand The left hand operand.
* @param object $right_operand The right hand operand -- 'value' from the rule definition.
* @param string $operation The operation used to compare the operands.
* @var string
*/
public static function compare( $left_operand, $right_operand, $operation ) {
switch ( $operation ) {
case '=':
return $left_operand === $right_operand;
case '<':
return $left_operand < $right_operand;
case '<=':
return $left_operand <= $right_operand;
case '>':
return $left_operand > $right_operand;
case '>=':
return $left_operand >= $right_operand;
case '!=':
return $left_operand !== $right_operand;
case 'contains':
if ( is_array( $left_operand ) && is_string( $right_operand ) ) {
return in_array( $right_operand, $left_operand, true );
}
if ( is_string( $right_operand ) && is_string( $left_operand ) ) {
return strpos( $right_operand, $left_operand ) !== false;
}
break;
case '!contains':
if ( is_array( $left_operand ) && is_string( $right_operand ) ) {
return ! in_array( $right_operand, $left_operand, true );
}
if ( is_string( $right_operand ) && is_string( $left_operand ) ) {
return strpos( $right_operand, $left_operand ) === false;
}
break;
case 'in':
if ( is_array( $right_operand ) && is_string( $left_operand ) ) {
return in_array( $left_operand, $right_operand, true );
}
if ( is_string( $left_operand ) && is_string( $right_operand ) ) {
return strpos( $left_operand, $right_operand ) !== false;
}
break;
case '!in':
if ( is_array( $right_operand ) && is_string( $left_operand ) ) {
return ! in_array( $left_operand, $right_operand, true );
}
if ( is_string( $left_operand ) && is_string( $right_operand ) ) {
return strpos( $left_operand, $right_operand ) === false;
}
break;
case 'range':
if ( ! is_array( $right_operand ) || count( $right_operand ) !== 2 ) {
return false;
}
return $left_operand >= $right_operand[0] && $left_operand <= $right_operand[1];
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\ComparisonOperation';
return false;
}
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -7,53 +7,25 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Evaluates the spec and returns a status.
*
* @deprecated 8.8.0
*/
class EvaluateAndGetStatus {
class EvaluateAndGetStatus extends DeprecatedClassFacade {
/**
* Evaluates the spec and returns a status.
* The name of the non-deprecated class that this facade covers.
*
* @param array $spec The spec to evaluate.
* @param string $current_status The note's current status.
* @param object $stored_state Stored state.
* @param object $rule_evaluator Evaluates rules into true/false.
*
* @return string The evaluated status.
* @var string
*/
public static function evaluate( $spec, $current_status, $stored_state, $rule_evaluator ) {
// No rules should leave the note alone.
if ( ! isset( $spec->rules ) ) {
return $current_status;
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\EvaluateAndGetStatus';
$evaluated_result = $rule_evaluator->evaluate(
$spec->rules,
$stored_state,
array(
'slug' => $spec->slug,
'source' => 'remote-inbox-notifications',
)
);
// Pending notes should be the spec status if the spec passes,
// left alone otherwise.
if ( Note::E_WC_ADMIN_NOTE_PENDING === $current_status ) {
return $evaluated_result
? $spec->status
: Note::E_WC_ADMIN_NOTE_PENDING;
}
// When allow_redisplay isn't set, just leave the note alone.
if ( ! isset( $spec->allow_redisplay ) || ! $spec->allow_redisplay ) {
return $current_status;
}
// allow_redisplay is set, unaction the note if eval to true.
return $evaluated_result
? Note::E_WC_ADMIN_NOTE_UNACTIONED
: $current_status;
}
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -2,89 +2,26 @@
namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Class EvaluationLogger
*
* @package Automattic\WooCommerce\Admin\RemoteInboxNotifications
*
* @deprecated 8.8.0
*/
class EvaluationLogger {
class EvaluationLogger extends DeprecatedClassFacade {
/**
* Slug of the spec.
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
private $slug;
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\EvaluationLogger';
/**
* Results of rules in the given spec.
* The version that this class was deprecated in.
*
* @var array
* @var string
*/
private $results = array();
/**
* Logger class to use.
*
* @var WC_Logger_Interface|null
*/
private $logger;
/**
* Logger source.
*
* @var string logger source.
*/
private $source = '';
/**
* EvaluationLogger constructor.
*
* @param string $slug Slug of a spec that is being evaluated.
* @param null $source Logger source.
* @param \WC_Logger_Interface $logger Logger class to use.
*/
public function __construct( $slug, $source = null, \WC_Logger_Interface $logger = null ) {
$this->slug = $slug;
if ( null === $logger ) {
$logger = wc_get_logger();
}
if ( $source ) {
$this->source = $source;
}
$this->logger = $logger;
}
/**
* Add evaluation result of a rule.
*
* @param string $rule_type name of the rule being tested.
* @param boolean $result result of a given rule.
*/
public function add_result( $rule_type, $result ) {
array_push(
$this->results,
array(
'rule' => $rule_type,
'result' => $result ? 'passed' : 'failed',
)
);
}
/**
* Log the results.
*/
public function log() {
if ( false === defined( 'WC_ADMIN_DEBUG_RULE_EVALUATOR' ) || true !== constant( 'WC_ADMIN_DEBUG_RULE_EVALUATOR' ) ) {
return;
}
foreach ( $this->results as $result ) {
$this->logger->debug(
"[{$this->slug}] {$result['rule']}: {$result['result']}",
array( 'source' => $this->source )
);
}
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -7,30 +7,25 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Rule processor that fails.
*
* @deprecated 8.8.0
*/
class FailRuleProcessor implements RuleProcessorInterface {
class FailRuleProcessor extends DeprecatedClassFacade {
/**
* Fails the rule.
* The name of the non-deprecated class that this facade covers.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool Always false.
* @var string
*/
public function process( $rule, $stored_state ) {
return false;
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\FailRuleProcessor';
/**
* Validates the rule.
* The version that this class was deprecated in.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
* @var string
*/
public function validate( $rule ) {
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -7,63 +7,25 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Class encapsulating getting the processor for a given rule type.
*
* @deprecated 8.8.0
*/
class GetRuleProcessor {
class GetRuleProcessor extends DeprecatedClassFacade {
/**
* Get the processor for the specified rule type.
* The name of the non-deprecated class that this facade covers.
*
* @param string $rule_type The rule type.
*
* @return RuleProcessorInterface The matching processor for the specified rule type, or a FailRuleProcessor if no matching processor is found.
* @var string
*/
public static function get_processor( $rule_type ) {
switch ( $rule_type ) {
case 'plugins_activated':
return new PluginsActivatedRuleProcessor();
case 'publish_after_time':
return new PublishAfterTimeRuleProcessor();
case 'publish_before_time':
return new PublishBeforeTimeRuleProcessor();
case 'not':
return new NotRuleProcessor();
case 'or':
return new OrRuleProcessor();
case 'fail':
return new FailRuleProcessor();
case 'pass':
return new PassRuleProcessor();
case 'plugin_version':
return new PluginVersionRuleProcessor();
case 'stored_state':
return new StoredStateRuleProcessor();
case 'order_count':
return new OrderCountRuleProcessor();
case 'wcadmin_active_for':
return new WCAdminActiveForRuleProcessor();
case 'product_count':
return new ProductCountRuleProcessor();
case 'onboarding_profile':
return new OnboardingProfileRuleProcessor();
case 'is_ecommerce':
return new IsEcommerceRuleProcessor();
case 'is_woo_express':
return new IsWooExpressRuleProcessor();
case 'base_location_country':
return new BaseLocationCountryRuleProcessor();
case 'base_location_state':
return new BaseLocationStateRuleProcessor();
case 'note_status':
return new NoteStatusRuleProcessor();
case 'option':
return new OptionRuleProcessor();
case 'wca_updated':
return new WooCommerceAdminUpdatedRuleProcessor();
case 'total_payments_value':
return new TotalPaymentsVolumeProcessor();
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\GetRuleProcessor';
return new FailRuleProcessor();
}
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -10,40 +10,26 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Rule processor that passes (or fails) when the site is on the eCommerce
* plan.
*
* @deprecated 8.8.0
*/
class IsEcommerceRuleProcessor implements RuleProcessorInterface {
class IsEcommerceRuleProcessor extends DeprecatedClassFacade {
/**
* Passes (or fails) based on whether the site is on the eCommerce plan or
* not.
* The name of the non-deprecated class that this facade covers.
*
* @param object $rule The rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
* @var string
*/
public function process( $rule, $stored_state ) {
if ( ! function_exists( 'wc_calypso_bridge_is_ecommerce_plan' ) ) {
return false === $rule->value;
}
return (bool) wc_calypso_bridge_is_ecommerce_plan() === $rule->value;
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\IsEcommerceRuleProcessor';
/**
* Validate the rule.
* The version that this class was deprecated in.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
* @var string
*/
public function validate( $rule ) {
if ( ! isset( $rule->value ) ) {
return false;
}
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -9,61 +9,26 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Rule processor that passes (or fails) when the site is on a Woo Express plan.
* You may optionally pass a plan name to target a specific Woo Express plan.
*
* @deprecated 8.8.0
*/
class IsWooExpressRuleProcessor implements RuleProcessorInterface {
class IsWooExpressRuleProcessor extends DeprecatedClassFacade {
/**
* Passes (or fails) based on whether the site is a Woo Express plan.
* The name of the non-deprecated class that this facade covers.
*
* @param object $rule The rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
* @var string
*/
public function process( $rule, $stored_state ) {
if ( ! function_exists( 'wc_calypso_bridge_is_woo_express_plan' ) ) {
return false === $rule->value;
}
// If the plan is undefined, only check if it's a Woo Express plan.
if ( ! isset( $rule->plan ) ) {
return wc_calypso_bridge_is_woo_express_plan() === $rule->value;
}
// If a plan name is defined, only evaluate the plan if we're on the Woo Express plan.
if ( wc_calypso_bridge_is_woo_express_plan() ) {
$fn = 'wc_calypso_bridge_is_woo_express_' . (string) $rule->plan . '_plan';
if ( function_exists( $fn ) ) {
return $fn() === $rule->value;
}
// If an invalid plan name is given, only evaluate the rule if we're targeting all plans other than the specified (invalid) one.
return false === $rule->value;
}
return false;
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\IsWooExpressRuleProcessor';
/**
* Validate the rule.
* The version that this class was deprecated in.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
* @var string
*/
public function validate( $rule ) {
if ( ! isset( $rule->value ) ) {
return false;
}
if ( isset( $rule->plan ) ) {
if ( ! function_exists( 'wc_calypso_bridge_is_woo_express_plan' ) ) {
return false;
}
}
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -7,58 +7,25 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Rule processor that negates the rules in the rule's operand.
*
* @deprecated 8.8.0
*/
class NotRuleProcessor implements RuleProcessorInterface {
class NotRuleProcessor extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\NotRuleProcessor';
/**
* The rule evaluator to use.
* The version that this class was deprecated in.
*
* @var RuleEvaluator
* @var string
*/
protected $rule_evaluator;
/**
* Constructor.
*
* @param RuleEvaluator $rule_evaluator The rule evaluator to use.
*/
public function __construct( $rule_evaluator = null ) {
$this->rule_evaluator = null === $rule_evaluator
? new RuleEvaluator()
: $rule_evaluator;
}
/**
* Evaluates the rules in the operand and negates the result.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
*/
public function process( $rule, $stored_state ) {
$evaluated_operand = $this->rule_evaluator->evaluate(
$rule->operand,
$stored_state
);
return ! $evaluated_operand;
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->operand ) ) {
return false;
}
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -9,53 +9,25 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Notes;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Rule processor that compares against the status of another note.
*
* @deprecated 8.8.0
*/
class NoteStatusRuleProcessor implements RuleProcessorInterface {
class NoteStatusRuleProcessor extends DeprecatedClassFacade {
/**
* Compare against the status of another note.
* The name of the non-deprecated class that this facade covers.
*
* @param object $rule The rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
* @var string
*/
public function process( $rule, $stored_state ) {
$status = Notes::get_note_status( $rule->note_name );
if ( ! $status ) {
return false;
}
return ComparisonOperation::compare(
$status,
$rule->status,
$rule->operation
);
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\NoteStatusRuleProcessor';
/**
* Validates the rule.
* The version that this class was deprecated in.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
* @var string
*/
public function validate( $rule ) {
if ( ! isset( $rule->note_name ) ) {
return false;
}
if ( ! isset( $rule->status ) ) {
return false;
}
if ( ! isset( $rule->operation ) ) {
return false;
}
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -8,58 +8,26 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Rule processor that performs a comparison operation against a value in the
* onboarding profile.
*
* @deprecated 8.8.0
*/
class OnboardingProfileRuleProcessor implements RuleProcessorInterface {
class OnboardingProfileRuleProcessor extends DeprecatedClassFacade {
/**
* Performs a comparison operation against a value in the onboarding
* profile.
* The name of the non-deprecated class that this facade covers.
*
* @param object $rule The rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
* @var string
*/
public function process( $rule, $stored_state ) {
$onboarding_profile = get_option( 'woocommerce_onboarding_profile' );
if ( empty( $onboarding_profile ) || ! is_array( $onboarding_profile ) ) {
return false;
}
if ( ! isset( $onboarding_profile[ $rule->index ] ) ) {
return false;
}
return ComparisonOperation::compare(
$onboarding_profile[ $rule->index ],
$rule->value,
$rule->operation
);
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\OnboardingProfileRuleProcessor';
/**
* Validates the rule.
* The version that this class was deprecated in.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
* @var string
*/
public function validate( $rule ) {
if ( ! isset( $rule->index ) ) {
return false;
}
if ( ! isset( $rule->value ) ) {
return false;
}
if ( ! isset( $rule->operation ) ) {
return false;
}
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -7,97 +7,25 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Rule processor that performs a comparison operation against an option value.
*
* @deprecated 8.8.0
*/
class OptionRuleProcessor implements RuleProcessorInterface {
class OptionRuleProcessor extends DeprecatedClassFacade {
/**
* Performs a comparison operation against the option value.
* The name of the non-deprecated class that this facade covers.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
* @var string
*/
public function process( $rule, $stored_state ) {
$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, $is_default_set, $default );
}
return ComparisonOperation::compare(
$option_value,
$rule->value,
$rule->operation
);
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\OptionRuleProcessor';
/**
* Retrieves the option value and handles logging if necessary.
* The version that this class was deprecated in.
*
* @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.
* @var string
*/
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(
'ComparisonOperation "%s" option value "%s" is not an array, defaulting to empty array.',
$rule->operation,
$rule->option_name
),
array(
'option_value' => $option_value,
'rule' => $rule,
)
);
$option_value = array();
}
return $option_value;
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->option_name ) ) {
return false;
}
if ( ! isset( $rule->value ) ) {
return false;
}
if ( ! isset( $rule->operation ) ) {
return false;
}
if ( isset( $rule->transformers ) && is_array( $rule->transformers ) ) {
foreach ( $rule->transformers as $transform_args ) {
$transformer = TransformerService::create_transformer( $transform_args->use );
if ( ! $transformer->validate( $transform_args->arguments ) ) {
return false;
}
}
}
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -8,66 +8,26 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Rule processor that performs an OR operation on the rule's left and right
* operands.
*
* @deprecated 8.8.0
*/
class OrRuleProcessor implements RuleProcessorInterface {
class OrRuleProcessor extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\OrRuleProcessor';
/**
* Rule evaluator to use.
* The version that this class was deprecated in.
*
* @var RuleEvaluator
* @var string
*/
private $rule_evaluator;
/**
* Constructor.
*
* @param RuleEvaluator $rule_evaluator The rule evaluator to use.
*/
public function __construct( $rule_evaluator = null ) {
$this->rule_evaluator = null === $rule_evaluator
? new RuleEvaluator()
: $rule_evaluator;
}
/**
* Performs an OR operation on the rule's left and right operands.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
*/
public function process( $rule, $stored_state ) {
foreach ( $rule->operands as $operand ) {
$evaluated_operand = $this->rule_evaluator->evaluate(
$operand,
$stored_state
);
if ( $evaluated_operand ) {
return true;
}
}
return false;
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->operands ) || ! is_array( $rule->operands ) ) {
return false;
}
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -7,63 +7,25 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Rule processor for publishing based on the number of orders.
*
* @deprecated 8.8.0
*/
class OrderCountRuleProcessor implements RuleProcessorInterface {
class OrderCountRuleProcessor extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\OrderCountRuleProcessor';
/**
* The orders provider.
* The version that this class was deprecated in.
*
* @var OrdersProvider
* @var string
*/
protected $orders_provider;
/**
* Constructor.
*
* @param object $orders_provider The orders provider.
*/
public function __construct( $orders_provider = null ) {
$this->orders_provider = null === $orders_provider
? new OrdersProvider()
: $orders_provider;
}
/**
* Process the rule.
*
* @param object $rule The rule to process.
* @param object $stored_state Stored state.
*
* @return bool Whether the rule passes or not.
*/
public function process( $rule, $stored_state ) {
$count = $this->orders_provider->get_order_count();
return ComparisonOperation::compare(
$count,
$rule->value,
$rule->operation
);
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->value ) ) {
return false;
}
if ( ! isset( $rule->operation ) ) {
return false;
}
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -7,30 +7,25 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Provider for order-related queries and operations.
*
* @deprecated 8.8.0
*/
class OrdersProvider {
class OrdersProvider extends DeprecatedClassFacade {
/**
* Allowed order statuses for calculating milestones.
* The name of the non-deprecated class that this facade covers.
*
* @var array
* @var string
*/
protected $allowed_statuses = array(
'pending',
'processing',
'completed',
);
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\OrdersProvider';
/**
* Returns the number of orders.
* The version that this class was deprecated in.
*
* @return integer The number of orders.
* @var string
*/
public function get_order_count() {
$status_counts = array_map( 'wc_orders_count', $this->allowed_statuses );
$orders_count = array_sum( $status_counts );
return $orders_count;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -8,30 +8,25 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Rule processor that passes.
*
* @deprecated 8.8.0
*/
class PassRuleProcessor implements RuleProcessorInterface {
class PassRuleProcessor extends DeprecatedClassFacade {
/**
* Passes the rule.
* The name of the non-deprecated class that this facade covers.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool Always true.
* @var string
*/
public function process( $rule, $stored_state ) {
return true;
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\PassRuleProcessor';
/**
* Validates the rule.
* The version that this class was deprecated in.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
* @var string
*/
public function validate( $rule ) {
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -8,79 +8,26 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\PluginsProvider\PluginsProvider;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Rule processor for sending when the provided plugin is activated and
* matches the specified version.
*
* @deprecated 8.8.0
*/
class PluginVersionRuleProcessor implements RuleProcessorInterface {
class PluginVersionRuleProcessor extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\PluginVersionRuleProcessor';
/**
* Plugins provider instance.
* The version that this class was deprecated in.
*
* @var PluginsProviderInterface
* @var string
*/
private $plugins_provider;
/**
* Constructor.
*
* @param PluginsProviderInterface $plugins_provider The plugins provider.
*/
public function __construct( $plugins_provider = null ) {
$this->plugins_provider = null === $plugins_provider
? new PluginsProvider()
: $plugins_provider;
}
/**
* Process the rule.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool Whether the rule passes or not.
*/
public function process( $rule, $stored_state ) {
$active_plugin_slugs = $this->plugins_provider->get_active_plugin_slugs();
if ( ! in_array( $rule->plugin, $active_plugin_slugs, true ) ) {
return false;
}
$plugin_data = $this->plugins_provider->get_plugin_data( $rule->plugin );
if ( ! is_array( $plugin_data ) || ! array_key_exists( 'Version', $plugin_data ) ) {
return false;
}
$plugin_version = $plugin_data['Version'];
return version_compare( $plugin_version, $rule->version, $rule->operator );
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->plugin ) ) {
return false;
}
if ( ! isset( $rule->version ) ) {
return false;
}
if ( ! isset( $rule->operator ) ) {
return false;
}
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -7,75 +7,25 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\PluginsProvider\PluginsProvider;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Rule processor for sending when the provided plugins are activated.
*
* @deprecated 8.8.0
*/
class PluginsActivatedRuleProcessor implements RuleProcessorInterface {
class PluginsActivatedRuleProcessor extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\PluginsActivatedRuleProcessor';
/**
* The plugins provider.
* The version that this class was deprecated in.
*
* @var PluginsProviderInterface
* @var string
*/
protected $plugins_provider;
/**
* Constructor.
*
* @param PluginsProviderInterface $plugins_provider The plugins provider.
*/
public function __construct( $plugins_provider = null ) {
$this->plugins_provider = null === $plugins_provider
? new PluginsProvider()
: $plugins_provider;
}
/**
* Process the rule.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool Whether the rule passes or not.
*/
public function process( $rule, $stored_state ) {
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;
}
}
return true;
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->plugins ) || ! is_array( $rule->plugins ) ) {
return false;
}
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -8,71 +8,26 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Rule processor that performs a comparison operation against the number of
* products.
*
* @deprecated 8.8.0
*/
class ProductCountRuleProcessor implements RuleProcessorInterface {
class ProductCountRuleProcessor extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\ProductCountRuleProcessor';
/**
* The product query.
* The version that this class was deprecated in.
*
* @var WC_Product_Query
* @var string
*/
protected $product_query;
/**
* Constructor.
*
* @param object $product_query The product query.
*/
public function __construct( $product_query = null ) {
$this->product_query = null === $product_query
? new \WC_Product_Query(
array(
'limit' => 1,
'paginate' => true,
'return' => 'ids',
'status' => array( 'publish' ),
)
)
: $product_query;
}
/**
* Performs a comparison operation against the number of products.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
*/
public function process( $rule, $stored_state ) {
$products = $this->product_query->get_products();
return ComparisonOperation::compare(
$products->total,
$rule->value,
$rule->operation
);
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->value ) ) {
return false;
}
if ( ! isset( $rule->operation ) ) {
return false;
}
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -7,61 +7,25 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\DateTimeProvider\CurrentDateTimeProvider;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Rule processor for sending after a specified date/time.
*
* @deprecated 8.8.0
*/
class PublishAfterTimeRuleProcessor implements RuleProcessorInterface {
class PublishAfterTimeRuleProcessor extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\PublishAfterTimeRuleProcessor';
/**
* The DateTime provider.
* The version that this class was deprecated in.
*
* @var DateTimeProviderInterface
* @var string
*/
protected $date_time_provider;
/**
* Constructor.
*
* @param DateTimeProviderInterface $date_time_provider The DateTime provider.
*/
public function __construct( $date_time_provider = null ) {
$this->date_time_provider = null === $date_time_provider
? new CurrentDateTimeProvider()
: $date_time_provider;
}
/**
* Process the rule.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool Whether the rule passes or not.
*/
public function process( $rule, $stored_state ) {
return $this->date_time_provider->get_now() >= new \DateTime( $rule->publish_after );
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->publish_after ) ) {
return false;
}
try {
new \DateTime( $rule->publish_after );
} catch ( \Throwable $e ) {
return false;
}
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -7,61 +7,25 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\DateTimeProvider\CurrentDateTimeProvider;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Rule processor for sending before a specified date/time.
*
* @deprecated 8.8.0
*/
class PublishBeforeTimeRuleProcessor implements RuleProcessorInterface {
class PublishBeforeTimeRuleProcessor extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\PublishBeforeTimeRuleProcessor';
/**
* The DateTime provider.
* The version that this class was deprecated in.
*
* @var DateTimeProviderInterface
* @var string
*/
protected $date_time_provider;
/**
* Constructor.
*
* @param DateTimeProviderInterface $date_time_provider The DateTime provider.
*/
public function __construct( $date_time_provider = null ) {
$this->date_time_provider = null === $date_time_provider
? new CurrentDateTimeProvider()
: $date_time_provider;
}
/**
* Process the rule.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool Whether the rule passes or not.
*/
public function process( $rule, $stored_state ) {
return $this->date_time_provider->get_now() <= new \DateTime( $rule->publish_before );
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->publish_before ) ) {
return false;
}
try {
new \DateTime( $rule->publish_before );
} catch ( \Throwable $e ) {
return false;
}
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -136,480 +136,4 @@ The `status` is what the status of the created note will be set to after interac
## Rule
Rules in an array are executed as an AND operation. If there are no rules in the array the result is false and the specified notification is not shown.
### Operations
Some rule types support an `operation` value, which is used to compare two
values. The following operations are implemented:
- `=`
- `<`
- `<=`
- `>`
- `>=`
- `!=`
- `contains`
- `!contains`
- `in` (Added in WooCommerce 8.2.0)
- `!in` (Added in WooCommerce 8.2.0)
- `range` (Added in WooCommerce 8.8.0)
#### contains and !contains
`contains` and `!contains` allow checking if the provided value is present (or
not present) in the haystack value. An example of this is using the
`onboarding_profile` rule to match on a value in the `product_types` array -
this rule matches if `physical` was selected as a product type in the
onboarding profile:
```json
{
"type": "onboarding_profile",
"index": "product_types",
"operation": "contains",
"value": "physical"
}
```
#### in and !in
`in` and `!in` allow checking if a value is found (or not found) in a provided array. For example, using the `in` comparison operator to check if the base country location value is found in a given array, as below. This rule matches if the `base_location_country` is `US`, `NZ`, or `ZA`. **NOTE:** These comparisons were added in **WooCommerce 8.2.0**. If the spec is read by an older version of WooCommerce, the rule will evaluate to `false`.
```json
{
"type": "base_location_country",
"value": [
"US",
"NZ",
"ZA"
],
"operation": "in"
}
```
#### range
`range` operator performs an inclusive check to determine if a number falls within a certain range.
This means that both the 'from' and 'to' values of the specified range are included in the check.
The following rule returns true when `woocommerce_remote_variant_assignment` value is between 1 and 10.
```json
{
"type": "option",
"value": [ 1, 10 ],
"default": 0,
"operation": "range",
"option_name": "woocommerce_remote_variant_assignment",
}
```
### Plugins activated
This passes if all of the listed plugins are installed and activated.
`plugins` is required.
```json
{
"type": "plugins_activated",
"plugins": [
"plugin-slug-1',
"plugin-slug-2"
]
}
```
### Publish after time
This passes if the system time is after the specified date/time.
Note that using both `publish_after_time` and `publish_before_time` allows timeboxing a note which could be useful for promoting a sale.
`publish_after` is required.
```json
{
"type": "publish_after_time",
"publish_after": "2020-04-22 00:00:00"
}
```
### Publish before time
This passes if the system time is before the specified date/time.
Note that using both `publish_after_time` and `publish_before_time` allows timeboxing a note which could be useful for promoting a sale.
`publish_before` is required.
```json
{
"type": "publish_before_time",
"publish_before": "2020-04-22 00:00:00"
}
```
### Not
This negates the rules in the provided set of rules. Note that the rules in `operand` get ANDed together into a single boolean.
`operand` is required.
```json
{
"type": "not",
"operand": [
<Rule>,
...
]
}
```
### Or
This performs an OR operation on the operands, passing if any of the operands evaluates to true. Note that if the operands are an array of `Rule`s (as in the first example), each operand is treated as an AND operation.
`operands` is required.
```json
{
"type": "or",
"operands": [
[
<Rule>,
...
],
[
<Rule>,
...
]
]
}
```
Alternatively:
```json
{
"type": "or",
"operands": [
<Rule>,
...
]
}
```
### Fail
This just returns a false value. This is useful if you want to keep a specification around, but don't want it displayed.
```json
{
"type": "fail"
}
```
### Plugin version
This compares the installed version of the plugin to the required version, using the comparison operator. If the plugin isnt activated this returns false.
`plugin`, `version`, and `operator` are required.
This example passes if Jetpack 8.4.1 is installed and activated.
```json
{
"type": "plugin_version",
"plugin": "jetpack",
"version": "8.4.1",
"operator": "="
}
```
### Stored state
This allows access to a stored state containing calculated values that otherwise would be impossible to reproduce using other rules. It performs the comparison operation against the stored state value.
This example passes if the `there_were_no_products` index is equal to `true`.
```json
{
"type": "stored_state",
"index": "there_were_no_products",
"operation": "=",
"value": true
}
```
There are only a limited amount of indices available to this rule, and new indices will need a new version of the WC Admin plugin to be installed.
The currently available indices are:
```json
there_were_no_products
there_are_now_products
new_product_count
```
`index`, `operation`, and `value` are required.
### Product count
This passes if the number of products currently in the system match the comparison operation.
This example passes if there are more than 10 products currently in the system.
```json
{
"type": "product_count",
"operation": ">",
"value": 10
}
```
`operation` and `value` are required.
### Order count
This passes if the number of orders currently in the system match the comparison operation.
This example passes if there are more than 10 orders currently in the system.
```json
{
"type": "order_count",
"operation": ">",
"value": 10
}
```
`operation` and `value` are required.
### WooCommerce Admin active for
This passes if the time WooCommerce Admin has been active for (in days) matches the comparison operation.
This is used as a proxy indicator of the age of the shop.
This example passes if it has been active for more than 8 days.
```json
{
"type": "wcadmin_active_for",
"operation": ">",
"days": 8
}
```
`operation` and `days` are required.
### Onboarding profile
This allows access to the onboarding profile that was built up in the onboarding wizard. The below example passes when the current revenue selected was "none".
```json
{
"type": "onboarding_profile",
"index": "revenue",
"operation": "=",
"value": "none"
}
```
`index`, `operation`, and `value` are all required.
### Is eCommerce
This passes when the store is on a WordPress.com site with the eCommerce plan.
```json
{
"type": "is_ecommerce",
"value": true
}
```
`value` is required.
### Is Woo Express
This passes when the store is on a WordPress.com site with a Woo Express plan active.
You can optionally pass the `plan` name to target a specific Woo Express plan, e.g. `performance`.
```json
{
"type": "is_woo_express",
"plan": "trial|essential|performance",
"value": true
}
```
`value` is required.
`plan` is optional, e.g. `trial`, `essential`, `performance`.
### Base location - country
This passes when the store is located in the specified country.
```json
{
"type": "base_location_country",
"value": "US",
"operation": "="
}
```
`value` and `operation` are both required.
### Base location - state
This passes when the store is located in the specified state.
```json
{
"type": "base_location_state",
"value": "TX",
"operation": "="
}
```
`value` and `operation` are both required.
### Note status
This passes when the status of the specified note matches the specified status.
The below example passes when the `wc-admin-mobile-app` note has not been
actioned.
```json
{
"type": "note_status",
"note_name": "wc-admin-mobile-app",
"status": "actioned",
"operation": "!="
}
```
### Total Payments Value
This passes when the total value of all payments for a given timeframe
compared to the provided value pass the operation test.
`timeframe` can be one of `last_week`, `last_month`, `last_quarter`, `last_6_months`, `last_year`
```json
{
"type": "total_payments_value",
"timeframe": "last_month",
"value": 2300,
"operation": ">"
}
```
`timeframe`, `value`, and `operation` are all required.
### Option
This passes when the option value matches the value using the operation.
```json
{
"type": "option",
"option_name": "woocommerce_currency",
"value": "USD",
"default": "USD",
"operation": "="
}
```
`option_name`, `value`, and `operation` are all required. `default` is not required and allows a default value to be used if the option does not exist.
#### Option Transformer
This transforms the given option value into a different value by a series of transformers.
Example option value:
```php
Array
(
[setup_client] =>
[industry] => Array
(
[0] => Array
(
[slug] => food-drink
)
[1] => Array
(
[slug] => fashion-apparel-accessories
)
)
)
```
If you want to ensure that the industry array contains `fashion-apparel-accessories`, you can use the following Option definition with transformers.
```json
{
"type": "option",
"transformers": [
{
"use": "dot_notation",
"arguments": {
"path": "industry"
}
},
{
"use": "array_column",
"arguments": {
"key": "slug"
}
},
{
"use": "array_search",
"arguments": {
"value": "fashion-apparel-accessories"
}
}
],
"option_name": "woocommerce_onboarding_profile",
"value": "fashion-apparel-accessories",
"default": "USD",
"operation": "="
}
```
You can find a list of transformers and examples in the transformer [README](./Transformers/README.md).
### WCA updated
This passes when WooCommerce Admin has just been updated. The specs will be run
on update. Note that this doesn't provide a way to check the version number as
the `plugin_version` rule can be used to check for a specific version of the
WooCommerce Admin plugin.
```json
{
type: "wca_updated"
}
```
No other values are needed.
## Debugging Specification
You can see the evaluation of each specification by turning on an optional evaluation logger.
1. Define `WC_ADMIN_DEBUG_RULE_EVALUATOR` constant in `wp-config.php`. `define('WC_ADMIN_DEBUG_RULE_EVALUATOR', true);`
2. Run `wc_admin_daily`
3. cd to `wp-content/uploads/wc-logs/` and check a log file in `remote-inbox-notifications-:date-hash.log` format.
You can tail the log file with a slug name to see the evaluation of a rule that you are testing.
Example:
`tail -f remote-inbox-notifications-2021-06-15-128.log | grep 'wcpay-promo-2021-6-incentive-2'`
You can find the readme for RuleProcessors and Transformers in [this README](../RemoteSpecs/RuleProcessors/README.md)

View File

@ -7,12 +7,15 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\RemoteSpecs\DataSourcePoller;
use Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\GetRuleProcessor;
/**
* Specs data source poller class.
* This handles polling specs from JSON endpoints, and
* stores the specs in to the database as an option.
*/
class DataSourcePoller extends \Automattic\WooCommerce\Admin\DataSourcePoller {
class RemoteInboxNotificationsDataSourcePoller extends DataSourcePoller {
const ID = 'remote_inbox_notifications';
const DATA_SOURCES = array(
'https://woocommerce.com/wp-json/wccom/inbox-notifications/2.0/notifications.json',

View File

@ -11,6 +11,7 @@ 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;
use Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\StoredStateSetupForProducts;
/**
* Remote Inbox Notifications engine.
@ -111,7 +112,7 @@ class RemoteInboxNotificationsEngine extends RemoteSpecsEngine {
* Go through the specs and run them.
*/
public static function run() {
$specs = DataSourcePoller::get_instance()->get_specs_from_data_sources();
$specs = RemoteInboxNotificationsDataSourcePoller::get_instance()->get_specs_from_data_sources();
if ( false === $specs || ! is_countable( $specs ) || count( $specs ) === 0 ) {
return;
@ -204,7 +205,7 @@ class RemoteInboxNotificationsEngine extends RemoteSpecsEngine {
if ( ! $note_from_db instanceof Note || get_user_locale() === $note_from_db->get_locale() ) {
return $note_from_db;
}
$specs = DataSourcePoller::get_instance()->get_specs_from_data_sources();
$specs = RemoteInboxNotificationsDataSourcePoller::get_instance()->get_specs_from_data_sources();
foreach ( $specs as $spec ) {
if ( $spec->slug !== $note_from_db->get_name() ) {
continue;

View File

@ -8,85 +8,26 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Evaluate the given rules as an AND operation - return false early if a
* rule evaluates to false.
*
* @deprecated 8.8.0
*/
class RuleEvaluator {
class RuleEvaluator extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\RuleEvaluator';
/**
* GetRuleProcessor to use.
* The version that this class was deprecated in.
*
* @var GetRuleProcessor
* @var string
*/
private $get_rule_processor;
/**
* Constructor.
*
* @param GetRuleProcessor $get_rule_processor The GetRuleProcessor to use.
*/
public function __construct( $get_rule_processor = null ) {
$this->get_rule_processor = null === $get_rule_processor
? new GetRuleProcessor()
: $get_rule_processor;
}
/**
* Evaluate the given rules as an AND operation - return false early if a
* rule evaluates to false.
*
* @param array|object $rules The rule or rules being processed.
* @param object|null $stored_state Stored state.
* @param array $logger_args Arguments for the event logger. `slug` is required.
*
* @throws \InvalidArgumentException Thrown when $logger_args is missing slug.
*
* @return bool The result of the operation.
*/
public function evaluate( $rules, $stored_state = null, $logger_args = array() ) {
if ( is_bool( $rules ) ) {
return $rules;
}
if ( ! is_array( $rules ) ) {
$rules = array( $rules );
}
if ( 0 === count( $rules ) ) {
return false;
}
$evaluation_logger = null;
if ( count( $logger_args ) ) {
if ( ! array_key_exists( 'slug', $logger_args ) ) {
throw new \InvalidArgumentException( 'Missing required field: slug in $logger_args.' );
}
array_key_exists( 'source', $logger_args ) ? $source = $logger_args['source'] : $source = null;
$evaluation_logger = new EvaluationLogger( $logger_args['slug'], $source );
}
foreach ( $rules as $rule ) {
if ( ! is_object( $rule ) ) {
return false;
}
$processor = $this->get_rule_processor->get_processor( $rule->type );
$processor_result = $processor->process( $rule, $stored_state );
$evaluation_logger && $evaluation_logger->add_result( $rule->type, $processor_result );
if ( ! $processor_result ) {
$evaluation_logger && $evaluation_logger->log();
return false;
}
}
$evaluation_logger && $evaluation_logger->log();
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -9,6 +9,8 @@ defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\Notes;
use Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\EvaluateAndGetStatus;
use Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\RuleEvaluator;
/**
* Runs a single spec.

View File

@ -8,51 +8,26 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Rule processor that performs a comparison operation against a value in the
* stored state object.
*
* @deprecated 8.8.0
*/
class StoredStateRuleProcessor implements RuleProcessorInterface {
class StoredStateRuleProcessor extends DeprecatedClassFacade {
/**
* Performs a comparison operation against a value in the stored state object.
* The name of the non-deprecated class that this facade covers.
*
* @param object $rule The rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
* @var string
*/
public function process( $rule, $stored_state ) {
if ( ! isset( $stored_state->{$rule->index} ) ) {
return false;
}
return ComparisonOperation::compare(
$stored_state->{$rule->index},
$rule->value,
$rule->operation
);
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\StoredStateRuleProcessor';
/**
* Validates the rule.
* The version that this class was deprecated in.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
* @var string
*/
public function validate( $rule ) {
if ( ! isset( $rule->index ) ) {
return false;
}
if ( ! isset( $rule->value ) ) {
return false;
}
if ( ! isset( $rule->operation ) ) {
return false;
}
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -7,125 +7,25 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\PluginsProvider\PluginsProvider;
use Automattic\WooCommerce\Admin\RemoteInboxNotifications\SpecRunner;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Handles stored state setup for products.
*
* @deprecated 8.8.0
*/
class StoredStateSetupForProducts {
const ASYNC_RUN_REMOTE_NOTIFICATIONS_ACTION_NAME =
'woocommerce_admin/stored_state_setup_for_products/async/run_remote_notifications';
class StoredStateSetupForProducts extends DeprecatedClassFacade {
/**
* Initialize the class via the admin_init hook.
*/
public static function admin_init() {
add_action( 'product_page_product_importer', array( __CLASS__, 'run_on_product_importer' ) );
add_action( 'transition_post_status', array( __CLASS__, 'run_on_transition_post_status' ), 10, 3 );
}
/**
* Initialize the class via the init hook.
*/
public static function init() {
add_action( self::ASYNC_RUN_REMOTE_NOTIFICATIONS_ACTION_NAME, array( __CLASS__, 'run_remote_notifications' ) );
}
/**
* Run the remote notifications engine. This is triggered by
* action-scheduler after a product is added. It also cleans up from
* setting the product count increment.
*/
public static function run_remote_notifications() {
RemoteInboxNotificationsEngine::run();
}
/**
* Set initial stored state values.
* The name of the non-deprecated class that this facade covers.
*
* @param object $stored_state The stored state.
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\StoredStateSetupForProducts';
/**
* The version that this class was deprecated in.
*
* @return object The stored state.
* @var string
*/
public static function init_stored_state( $stored_state ) {
$stored_state->there_were_no_products = ! self::are_there_products();
$stored_state->there_are_now_products = ! $stored_state->there_were_no_products;
return $stored_state;
}
/**
* Are there products query.
*
* @return bool
*/
private static function are_there_products() {
$query = new \WC_Product_Query(
array(
'limit' => 1,
'paginate' => true,
'return' => 'ids',
'status' => array( 'publish' ),
)
);
$products = $query->get_products();
$count = $products->total;
return $count > 0;
}
/**
* Runs on product importer steps.
*/
public static function run_on_product_importer() {
// We're only interested in when the importer completes.
// phpcs:disable WordPress.Security.NonceVerification.Recommended
if ( ! isset( $_REQUEST['step'] ) ) {
return;
}
if ( 'done' !== $_REQUEST['step'] ) {
return;
}
// phpcs:enable
self::update_stored_state_and_possibly_run_remote_notifications();
}
/**
* Runs when a post status transitions, but we're only interested if it is
* a product being published.
*
* @param string $new_status The new status.
* @param string $old_status The old status.
* @param Post $post The post.
*/
public static function run_on_transition_post_status( $new_status, $old_status, $post ) {
if (
'product' !== $post->post_type ||
'publish' !== $new_status
) {
return;
}
self::update_stored_state_and_possibly_run_remote_notifications();
}
/**
* Enqueues an async action (using action-scheduler) to run remote
* notifications.
*/
private static function update_stored_state_and_possibly_run_remote_notifications() {
$stored_state = RemoteInboxNotificationsEngine::get_stored_state();
// If the stored_state is the same, we don't need to run remote notifications to avoid unnecessary action scheduling.
if ( true === $stored_state->there_are_now_products ) {
return;
}
$stored_state->there_are_now_products = true;
RemoteInboxNotificationsEngine::update_stored_state( $stored_state );
// Run self::run_remote_notifications asynchronously.
as_enqueue_async_action( self::ASYNC_RUN_REMOTE_NOTIFICATIONS_ACTION_NAME );
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -7,87 +7,25 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\API\Reports\Revenue\Query as RevenueQuery;
use Automattic\WooCommerce\Admin\API\Reports\TimeInterval;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Rule processor that passes when a store's payments volume exceeds a provided amount.
*
* @deprecated 8.8.0
*/
class TotalPaymentsVolumeProcessor implements RuleProcessorInterface {
class TotalPaymentsVolumeProcessor extends DeprecatedClassFacade {
/**
* Compare against the store's total payments volume.
* The name of the non-deprecated class that this facade covers.
*
* @param object $rule The rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
* @var string
*/
public function process( $rule, $stored_state ) {
$dates = TimeInterval::get_timeframe_dates( $rule->timeframe );
$reports_revenue = $this->get_reports_query(
array(
'before' => $dates['end'],
'after' => $dates['start'],
'interval' => 'year',
'fields' => array( '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,
$rule->value,
$rule->operation
);
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\TotalPaymentsVolumeProcessor';
/**
* Validates the rule.
* The version that this class was deprecated in.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
* @var string
*/
public function validate( $rule ) {
$allowed_timeframes = array(
'last_week',
'last_month',
'last_quarter',
'last_6_months',
'last_year',
);
if ( ! isset( $rule->timeframe ) || ! in_array( $rule->timeframe, $allowed_timeframes, true ) ) {
return false;
}
if ( ! isset( $rule->value ) || ! is_numeric( $rule->value ) ) {
return false;
}
if ( ! isset( $rule->operation ) ) {
return false;
}
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
);
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -17,11 +17,11 @@ interface TransformerInterface {
*
* @param mixed $value a value to transform.
* @param stdClass|null $arguments arguments.
* @param string|null $default default value.
* @param string|null $default_value default value.
*
* @return mixed|null
*/
public function transform( $value, stdClass $arguments = null, $default = null);
public function transform( $value, stdClass $arguments = null, $default_value = null );
/**
* Validate Transformer arguments.

View File

@ -2,8 +2,7 @@
namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
use InvalidArgumentException;
use stdClass;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* A simple service class for the Transformer classes.
@ -11,73 +10,21 @@ use stdClass;
* Class TransformerService
*
* @package Automattic\WooCommerce\Admin\RemoteInboxNotifications
*
* @deprecated 8.8.0
*/
class TransformerService {
class TransformerService extends DeprecatedClassFacade {
/**
* Create a transformer object by name.
* The name of the non-deprecated class that this facade covers.
*
* @param string $name name of the transformer.
*
* @return TransformerInterface|null
* @var string
*/
public static function create_transformer( $name ) {
$camel_cased = str_replace( ' ', '', ucwords( str_replace( '_', ' ', $name ) ) );
$classname = __NAMESPACE__ . '\\Transformers\\' . $camel_cased;
if ( ! class_exists( $classname ) ) {
return null;
}
return new $classname();
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers\TransformerService';
/**
* Apply transformers to the given value.
* The version that this class was deprecated in.
*
* @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
* @var string
*/
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' );
}
if ( ! isset( $transformer_config->arguments ) ) {
$transformer_config->arguments = null;
}
$transformer = self::create_transformer( $transformer_config->use );
if ( null === $transformer ) {
throw new InvalidArgumentException( "Unable to find a transformer by name: {$transformer_config->use}" );
}
$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;
}
// 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;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -2,55 +2,27 @@
namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications\Transformers;
use Automattic\WooCommerce\Admin\RemoteInboxNotifications\TransformerInterface;
use InvalidArgumentException;
use stdClass;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Search array value by one of its key.
*
* @package Automattic\WooCommerce\Admin\RemoteInboxNotifications\Transformers
*
* @deprecated 8.8.0
*/
class ArrayColumn implements TransformerInterface {
class ArrayColumn extends DeprecatedClassFacade {
/**
* Search array value by one of its key.
* The name of the non-deprecated class that this facade covers.
*
* @param mixed $value a value to transform.
* @param stdClass|null $arguments required arguments 'key'.
* @param string|null $default default value.
*
* @throws InvalidArgumentException Throws when the required argument 'key' is missing.
*
* @return mixed
* @var string
*/
public function transform( $value, stdClass $arguments = null, $default = array() ) {
if ( ! is_array( $value ) ) {
return $default;
}
return array_column( $value, $arguments->key );
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers\ArrayColumn';
/**
* Validate Transformer arguments.
* The version that this class was deprecated in.
*
* @param stdClass|null $arguments arguments to validate.
*
* @return mixed
* @var string
*/
public function validate( stdClass $arguments = null ) {
if ( ! isset( $arguments->key ) ) {
return false;
}
if (
null !== $arguments->key &&
! is_string( $arguments->key ) &&
! is_int( $arguments->key )
) {
return false;
}
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -2,48 +2,27 @@
namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications\Transformers;
use Automattic\WooCommerce\Admin\RemoteInboxNotifications\TransformerInterface;
use stdClass;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Flatten nested array.
*
* @package Automattic\WooCommerce\Admin\RemoteInboxNotifications\Transformers
*
* @deprecated 8.8.0
*/
class ArrayFlatten implements TransformerInterface {
class ArrayFlatten extends DeprecatedClassFacade {
/**
* Search a given value in the array.
* The name of the non-deprecated class that this facade covers.
*
* @param mixed $value a value to transform.
* @param stdClass|null $arguments arguments.
* @param string|null $default default value.
*
* @return mixed|null
* @var string
*/
public function transform( $value, stdClass $arguments = null, $default = array() ) {
if ( ! is_array( $value ) ) {
return $default;
}
$return = array();
array_walk_recursive(
$value,
function( $item ) use ( &$return ) {
$return[] = $item;
}
);
return $return;
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers\ArrayColumn';
/**
* Validate Transformer arguments.
* The version that this class was deprecated in.
*
* @param stdClass|null $arguments arguments to validate.
*
* @return mixed
* @var string
*/
public function validate( stdClass $arguments = null ) {
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -2,40 +2,27 @@
namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications\Transformers;
use Automattic\WooCommerce\Admin\RemoteInboxNotifications\TransformerInterface;
use stdClass;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Search array value by one of its key.
*
* @package Automattic\WooCommerce\Admin\RemoteInboxNotifications\Transformers
*
* @deprecated 8.8.0
*/
class ArrayKeys implements TransformerInterface {
class ArrayKeys extends DeprecatedClassFacade {
/**
* Search array value by one of its key.
* The name of the non-deprecated class that this facade covers.
*
* @param mixed $value a value to transform.
* @param stdClass|null $arguments arguments.
* @param string|null $default default value.
*
* @return mixed
* @var string
*/
public function transform( $value, stdClass $arguments = null, $default = array() ) {
if ( ! is_array( $value ) ) {
return $default;
}
return array_keys( $value );
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers\ArrayColumn';
/**
* Validate Transformer arguments.
* The version that this class was deprecated in.
*
* @param stdClass|null $arguments arguments to validate.
*
* @return mixed
* @var string
*/
public function validate( stdClass $arguments = null ) {
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -2,52 +2,27 @@
namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications\Transformers;
use Automattic\WooCommerce\Admin\RemoteInboxNotifications\TransformerInterface;
use InvalidArgumentException;
use stdClass;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Searches a given a given value in the array.
*
* @package Automattic\WooCommerce\Admin\RemoteInboxNotifications\Transformers
*
* @deprecated 8.8.0
*/
class ArraySearch implements TransformerInterface {
class ArraySearch extends DeprecatedClassFacade {
/**
* Search a given value in the array.
* The name of the non-deprecated class that this facade covers.
*
* @param mixed $value a value to transform.
* @param stdClass|null $arguments required argument 'value'.
* @param string|null $default default value.
*
* @throws InvalidArgumentException Throws when the required 'value' is missing.
*
* @return mixed|null
* @var string
*/
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 ];
}
return null;
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers\ArrayColumn';
/**
* Validate Transformer arguments.
* The version that this class was deprecated in.
*
* @param stdClass|null $arguments arguments to validate.
*
* @return mixed
* @var string
*/
public function validate( stdClass $arguments = null ) {
if ( ! isset( $arguments->value ) ) {
return false;
}
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -2,40 +2,27 @@
namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications\Transformers;
use Automattic\WooCommerce\Admin\RemoteInboxNotifications\TransformerInterface;
use stdClass;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Search array value by one of its key.
*
* @package Automattic\WooCommerce\Admin\RemoteInboxNotifications\Transformers
*
* @deprecated 8.8.0
*/
class ArrayValues implements TransformerInterface {
class ArrayValues extends DeprecatedClassFacade {
/**
* Search array value by one of its key.
* The name of the non-deprecated class that this facade covers.
*
* @param mixed $value a value to transform.
* @param stdClass|null $arguments arguments.
* @param string|null $default default value.
*
* @return mixed
* @var string
*/
public function transform( $value, stdClass $arguments = null, $default = array() ) {
if ( ! is_array( $value ) ) {
return $default;
}
return array_values( $value );
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers\ArrayColumn';
/**
* Validate Transformer arguments.
* The version that this class was deprecated in.
*
* @param stdClass|null $arguments arguments to validate.
*
* @return mixed
* @var string
*/
public function validate( stdClass $arguments = null ) {
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -2,40 +2,27 @@
namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications\Transformers;
use Automattic\WooCommerce\Admin\RemoteInboxNotifications\TransformerInterface;
use stdClass;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Count elements in Array or Countable object.
*
* @package Automattic\WooCommerce\Admin\RemoteInboxNotifications\Transformers
*
* @deprecated 8.8.0
*/
class Count implements TransformerInterface {
class Count extends DeprecatedClassFacade {
/**
* Count elements in Array or Countable object.
* The name of the non-deprecated class that this facade covers.
*
* @param array|Countable $value an array to count.
* @param stdClass|null $arguments arguments.
* @param string|null $default default value.
*
* @return number
* @var string
*/
public function transform( $value, stdClass $arguments = null, $default = null ) {
if ( ! is_array( $value ) && ! $value instanceof \Countable ) {
return $default;
}
return count( $value );
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers\ArrayColumn';
/**
* Validate Transformer arguments.
* The version that this class was deprecated in.
*
* @param stdClass|null $arguments arguments to validate.
*
* @return mixed
* @var string
*/
public function validate( stdClass $arguments = null ) {
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -2,78 +2,27 @@
namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications\Transformers;
use Automattic\WooCommerce\Admin\RemoteInboxNotifications\TransformerInterface;
use InvalidArgumentException;
use stdClass;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Find an array value by dot notation.
*
* @package Automattic\WooCommerce\Admin\RemoteInboxNotifications\Transformers
*
* @deprecated 8.8.0
*/
class DotNotation implements TransformerInterface {
class DotNotation extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers\ArrayColumn';
/**
* Find given path from the given value.
* The version that this class was deprecated in.
*
* @param mixed $value a value to transform.
* @param stdClass|null $arguments required argument 'path'.
* @param string|null $default default value.
*
* @throws InvalidArgumentException Throws when the required 'path' is missing.
*
* @return mixed
* @var string
*/
public function transform( $value, stdclass $arguments = null, $default = null ) {
if ( is_object( $value ) ) {
// if the value is an object, convert it to an array.
$value = json_decode( wp_json_encode( $value ), true );
}
return $this->get( $value, $arguments->path, $default );
}
/**
* Find the given $path in $array by dot notation.
*
* @param array $array an array to search in.
* @param string $path a path in the given array.
* @param null $default default value to return if $path was not found.
*
* @return mixed|null
*/
public function get( $array, $path, $default = null ) {
if ( ! is_array( $array ) ) {
return $default;
}
if ( isset( $array[ $path ] ) ) {
return $array[ $path ];
}
foreach ( explode( '.', $path ) as $segment ) {
if ( ! is_array( $array ) || ! array_key_exists( $segment, $array ) ) {
return $default;
}
$array = $array[ $segment ];
}
return $array;
}
/**
* Validate Transformer arguments.
*
* @param stdClass|null $arguments arguments to validate.
*
* @return mixed
*/
public function validate( stdClass $arguments = null ) {
if ( ! isset( $arguments->path ) ) {
return false;
}
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -2,54 +2,27 @@
namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications\Transformers;
use Automattic\WooCommerce\Admin\RemoteInboxNotifications\TransformerInterface;
use stdClass;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Prepare site URL for comparison.
*
* @package Automattic\WooCommerce\Admin\RemoteInboxNotifications\Transformers
*
* @deprecated 8.8.0
*/
class PrepareUrl implements TransformerInterface {
class PrepareUrl extends DeprecatedClassFacade {
/**
* Prepares the site URL by removing the protocol and trailing slash.
* The name of the non-deprecated class that this facade covers.
*
* @param string $value a value to transform.
* @param stdClass|null $arguments arguments.
* @param string|null $default default value.
*
* @return mixed|null
* @var string
*/
public function transform( $value, stdClass $arguments = null, $default = null ) {
if ( ! is_string( $value ) ) {
return $default;
}
$url_parts = wp_parse_url( rtrim( $value, '/' ) );
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'];
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers\ArrayColumn';
/**
* Validate Transformer arguments.
* The version that this class was deprecated in.
*
* @param stdClass|null $arguments arguments to validate.
*
* @return mixed
* @var string
*/
public function validate( stdClass $arguments = null ) {
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -5,20 +5,27 @@
namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
use Automattic\WooCommerce\Admin\WCAdminHelper;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* WCAdminActiveForProvider class
*
* @deprecated 8.8.0
*/
class WCAdminActiveForProvider {
class WCAdminActiveForProvider extends DeprecatedClassFacade {
/**
* Get the number of seconds that the store has been active.
* The name of the non-deprecated class that this facade covers.
*
* @return number Number of seconds.
* @var string
*/
public function get_wcadmin_active_for_in_seconds() {
return WCAdminHelper::get_wcadmin_active_for_in_seconds();
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\WCAdminActiveForProvider';
/**
* The version that this class was deprecated in.
*
* @var string
*/
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -8,72 +8,26 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Rule processor for publishing if wc-admin has been active for at least the
* given number of seconds.
*
* @deprecated 8.8.0
*/
class WCAdminActiveForRuleProcessor implements RuleProcessorInterface {
class WCAdminActiveForRuleProcessor extends DeprecatedClassFacade {
/**
* The name of the non-deprecated class that this facade covers.
*
* @var string
*/
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\WCAdminActiveForRuleProcessor';
/**
* Provides the amount of time wcadmin has been active for.
* The version that this class was deprecated in.
*
* @var WCAdminActiveForProvider
* @var string
*/
protected $wcadmin_active_for_provider;
/**
* Constructor
*
* @param object $wcadmin_active_for_provider Provides the amount of time wcadmin has been active for.
*/
public function __construct( $wcadmin_active_for_provider = null ) {
$this->wcadmin_active_for_provider = null === $wcadmin_active_for_provider
? new WCAdminActiveForProvider()
: $wcadmin_active_for_provider;
}
/**
* Performs a comparison operation against the amount of time wc-admin has
* been active for in days.
*
* @param object $rule The rule being processed.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
*/
public function process( $rule, $stored_state ) {
$active_for_seconds = $this->wcadmin_active_for_provider->get_wcadmin_active_for_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,
$rule_seconds,
$rule->operation
);
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
// 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;
}
if ( ! isset( $rule->operation ) ) {
return false;
}
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -7,30 +7,25 @@ namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\DeprecatedClassFacade;
/**
* Rule processor for sending when WooCommerce Admin has been updated.
*
* @deprecated 8.8.0
*/
class WooCommerceAdminUpdatedRuleProcessor implements RuleProcessorInterface {
class WooCommerceAdminUpdatedRuleProcessor extends DeprecatedClassFacade {
/**
* Process the rule.
* The name of the non-deprecated class that this facade covers.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool Whether the rule passes or not.
* @var string
*/
public function process( $rule, $stored_state ) {
return get_option( RemoteInboxNotificationsEngine::WCA_UPDATED_OPTION_NAME, false );
}
protected static $facade_over_classname = 'Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\WooCommerceAdminUpdatedRuleProcessor';
/**
* Validates the rule.
* The version that this class was deprecated in.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
* @var string
*/
public function validate( $rule ) {
return true;
}
protected static $deprecated_in_version = '8.8.0';
}

View File

@ -0,0 +1,288 @@
<?php
namespace Automattic\WooCommerce\Admin\RemoteSpecs;
/**
* Specs data source poller class.
* This handles polling specs from JSON endpoints, and
* stores the specs in to the database as an option.
*/
abstract class DataSourcePoller {
/**
* Get class instance.
*/
abstract public static function get_instance();
/**
* Name of data sources filter.
*/
const FILTER_NAME = 'data_source_poller_data_sources';
/**
* Name of data source specs filter.
*/
const FILTER_NAME_SPECS = 'data_source_poller_specs';
/**
* Id of DataSourcePoller.
*
* @var string
*/
protected $id = array();
/**
* Default data sources array.
*
* @var array
*/
protected $data_sources = array();
/**
* Default args.
*
* @var array
*/
protected $args = array();
/**
* The logger instance.
*
* @var WC_Logger|null
*/
protected static $logger = null;
/**
* Constructor.
*
* @param string $id id of DataSourcePoller.
* @param array $data_sources urls for data sources.
* @param array $args Options for DataSourcePoller.
*/
public function __construct( $id, $data_sources = array(), $args = array() ) {
$this->data_sources = $data_sources;
$this->id = $id;
$arg_defaults = array(
'spec_key' => 'id',
'transient_name' => 'woocommerce_admin_' . $id . '_specs',
'transient_expiry' => 7 * DAY_IN_SECONDS,
);
$this->args = wp_parse_args( $args, $arg_defaults );
}
/**
* Get the logger instance.
*
* @return WC_Logger
*/
protected static function get_logger() {
if ( is_null( self::$logger ) ) {
self::$logger = wc_get_logger();
}
return self::$logger;
}
/**
* Returns the key identifier of spec, this can easily be overwritten. Defaults to id.
*
* @param mixed $spec a JSON parsed spec coming from the JSON feed.
* @return string|boolean
*/
protected function get_spec_key( $spec ) {
$key = $this->args['spec_key'];
if ( isset( $spec->$key ) ) {
return $spec->$key;
}
return false;
}
/**
* Reads the data sources for specs and persists those specs.
*
* @return array list of specs.
*/
public function get_specs_from_data_sources() {
$locale = get_user_locale();
$specs_group = get_transient( $this->args['transient_name'] ) ?? array();
$specs = isset( $specs_group[ $locale ] ) ? $specs_group[ $locale ] : array();
if ( ! is_array( $specs ) || empty( $specs ) ) {
$this->read_specs_from_data_sources();
$specs_group = get_transient( $this->args['transient_name'] );
$specs = isset( $specs_group[ $locale ] ) ? $specs_group[ $locale ] : array();
}
/**
* Filter specs.
*
* @param array $specs List of specs.
* @param string $this->id Spec identifier.
*
* @since 8.8.0
*/
$specs = apply_filters( self::FILTER_NAME_SPECS, $specs, $this->id );
return false !== $specs ? $specs : array();
}
/**
* Reads the data sources for specs and persists those specs.
*
* @return bool Whether any specs were read.
*/
public function read_specs_from_data_sources() {
$specs = array();
/**
* Filter data sources.
*
* @param array $this->data_sources List of data sources.
* @param string $this->id Spec identifier.
*
* @since 8.8.0
*/
$data_sources = apply_filters( self::FILTER_NAME, $this->data_sources, $this->id );
// Note that this merges the specs from the data sources based on the
// id - last one wins.
foreach ( $data_sources as $url ) {
$specs_from_data_source = self::read_data_source( $url );
$this->merge_specs( $specs_from_data_source, $specs, $url );
}
$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.
$this->set_specs_transient(
$specs_group,
$this->args['transient_expiry']
);
return count( $specs ) !== 0;
}
/**
* Delete the specs transient.
*
* @return bool success of failure of transient deletion.
*/
public function delete_specs_transient() {
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
*
* @param string $url The URL to read the specs from.
*
* @return array The specs that have been read from the data source.
*/
protected static function read_data_source( $url ) {
$logger_context = array( 'source' => $url );
$logger = self::get_logger();
$response = wp_remote_get(
add_query_arg(
'locale',
get_user_locale(),
$url
),
array(
'user-agent' => 'WooCommerce/' . WC_VERSION . '; ' . home_url( '/' ),
)
);
if ( is_wp_error( $response ) || ! isset( $response['body'] ) ) {
$logger->error(
'Error getting data feed',
$logger_context
);
// phpcs:ignore
$logger->error( print_r( $response, true ), $logger_context );
return array();
}
$body = $response['body'];
$specs = json_decode( $body );
if ( null === $specs ) {
$logger->error(
'Empty response in data feed',
$logger_context
);
return array();
}
if ( ! is_array( $specs ) ) {
$logger->error(
'Data feed is not an array',
$logger_context
);
return array();
}
return $specs;
}
/**
* Merge the specs.
*
* @param Array $specs_to_merge_in The specs to merge in to $specs.
* @param Array $specs The list of specs being merged into.
* @param string $url The url of the feed being merged in (for error reporting).
*/
protected function merge_specs( $specs_to_merge_in, &$specs, $url ) {
foreach ( $specs_to_merge_in as $spec ) {
if ( ! $this->validate_spec( $spec, $url ) ) {
continue;
}
$id = $this->get_spec_key( $spec );
$specs[ $id ] = $spec;
}
}
/**
* Validate the spec.
*
* @param object $spec The spec to validate.
* @param string $url The url of the feed that provided the spec.
*
* @return bool The result of the validation.
*/
protected function validate_spec( $spec, $url ) {
$logger = self::get_logger();
$logger_context = array( 'source' => $url );
if ( ! $this->get_spec_key( $spec ) ) {
$logger->error(
'Spec is invalid because the id is missing in feed',
$logger_context
);
// phpcs:ignore
$logger->error( print_r( $spec, true ), $logger_context );
return false;
}
return true;
}
}

View File

@ -0,0 +1,70 @@
<?php
/**
* Rule processor that performs a comparison operation against the base
* location - country.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
use Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProfile;
defined( 'ABSPATH' ) || exit;
/**
* Rule processor that performs a comparison operation against the base
* location - country.
*/
class BaseLocationCountryRuleProcessor implements RuleProcessorInterface {
/**
* Performs a comparison operation against the base location - country.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
*/
public function process( $rule, $stored_state ) {
$base_location = wc_get_base_location();
if (
! is_array( $base_location ) ||
! array_key_exists( 'country', $base_location ) ||
! array_key_exists( 'state', $base_location )
) {
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 ( $is_address_default && OnboardingProfile::needs_completion() && ! $is_store_country_set ) {
return false;
}
return ComparisonOperation::compare(
$base_location['country'],
$rule->value,
$rule->operation
);
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->value ) ) {
return false;
}
if ( ! isset( $rule->operation ) ) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,55 @@
<?php
/**
* Rule processor that performs a comparison operation against the base
* location - state.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
/**
* Rule processor that performs a comparison operation against the base
* location - state.
*/
class BaseLocationStateRuleProcessor implements RuleProcessorInterface {
/**
* Performs a comparison operation against the base location - state.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
*/
public function process( $rule, $stored_state ) {
$base_location = wc_get_base_location();
if ( ! is_array( $base_location ) || ! array_key_exists( 'state', $base_location ) ) {
return false;
}
return ComparisonOperation::compare(
$base_location['state'],
$rule->value,
$rule->operation
);
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->value ) ) {
return false;
}
if ( ! isset( $rule->operation ) ) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,76 @@
<?php
/**
* Compare two operands using the specified operation.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
/**
* Compare two operands using the specified operation.
*/
class ComparisonOperation {
/**
* Compare two operands using the specified operation.
*
* @param object $left_operand The left hand operand.
* @param object $right_operand The right hand operand -- 'value' from the rule definition.
* @param string $operation The operation used to compare the operands.
*/
public static function compare( $left_operand, $right_operand, $operation ) {
switch ( $operation ) {
case '=':
return $left_operand === $right_operand;
case '<':
return $left_operand < $right_operand;
case '<=':
return $left_operand <= $right_operand;
case '>':
return $left_operand > $right_operand;
case '>=':
return $left_operand >= $right_operand;
case '!=':
return $left_operand !== $right_operand;
case 'contains':
if ( is_array( $left_operand ) && is_string( $right_operand ) ) {
return in_array( $right_operand, $left_operand, true );
}
if ( is_string( $right_operand ) && is_string( $left_operand ) ) {
return strpos( $right_operand, $left_operand ) !== false;
}
break;
case '!contains':
if ( is_array( $left_operand ) && is_string( $right_operand ) ) {
return ! in_array( $right_operand, $left_operand, true );
}
if ( is_string( $right_operand ) && is_string( $left_operand ) ) {
return strpos( $right_operand, $left_operand ) === false;
}
break;
case 'in':
if ( is_array( $right_operand ) && is_string( $left_operand ) ) {
return in_array( $left_operand, $right_operand, true );
}
if ( is_string( $left_operand ) && is_string( $right_operand ) ) {
return strpos( $left_operand, $right_operand ) !== false;
}
break;
case '!in':
if ( is_array( $right_operand ) && is_string( $left_operand ) ) {
return ! in_array( $left_operand, $right_operand, true );
}
if ( is_string( $left_operand ) && is_string( $right_operand ) ) {
return strpos( $left_operand, $right_operand ) === false;
}
break;
case 'range':
if ( ! is_array( $right_operand ) || count( $right_operand ) !== 2 ) {
return false;
}
return $left_operand >= $right_operand[0] && $left_operand <= $right_operand[1];
}
return false;
}
}

View File

@ -0,0 +1,59 @@
<?php
/**
* Evaluates the spec and returns a status.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
/**
* Evaluates the spec and returns a status.
*/
class EvaluateAndGetStatus {
/**
* Evaluates the spec and returns a status.
*
* @param array $spec The spec to evaluate.
* @param string $current_status The note's current status.
* @param object $stored_state Stored state.
* @param object $rule_evaluator Evaluates rules into true/false.
*
* @return string The evaluated status.
*/
public static function evaluate( $spec, $current_status, $stored_state, $rule_evaluator ) {
// No rules should leave the note alone.
if ( ! isset( $spec->rules ) ) {
return $current_status;
}
$evaluated_result = $rule_evaluator->evaluate(
$spec->rules,
$stored_state,
array(
'slug' => $spec->slug,
'source' => 'remote-inbox-notifications',
)
);
// Pending notes should be the spec status if the spec passes,
// left alone otherwise.
if ( Note::E_WC_ADMIN_NOTE_PENDING === $current_status ) {
return $evaluated_result
? $spec->status
: Note::E_WC_ADMIN_NOTE_PENDING;
}
// When allow_redisplay isn't set, just leave the note alone.
if ( ! isset( $spec->allow_redisplay ) || ! $spec->allow_redisplay ) {
return $current_status;
}
// allow_redisplay is set, unaction the note if eval to true.
return $evaluated_result
? Note::E_WC_ADMIN_NOTE_UNACTIONED
: $current_status;
}
}

View File

@ -0,0 +1,90 @@
<?php
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
/**
* Class EvaluationLogger
*
* @package Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors
*/
class EvaluationLogger {
/**
* Slug of the spec.
*
* @var string
*/
private $slug;
/**
* Results of rules in the given spec.
*
* @var array
*/
private $results = array();
/**
* Logger class to use.
*
* @var WC_Logger_Interface|null
*/
private $logger;
/**
* Logger source.
*
* @var string logger source.
*/
private $source = '';
/**
* EvaluationLogger constructor.
*
* @param string $slug Slug of a spec that is being evaluated.
* @param null $source Logger source.
* @param \WC_Logger_Interface $logger Logger class to use.
*/
public function __construct( $slug, $source = null, \WC_Logger_Interface $logger = null ) {
$this->slug = $slug;
if ( null === $logger ) {
$logger = wc_get_logger();
}
if ( $source ) {
$this->source = $source;
}
$this->logger = $logger;
}
/**
* Add evaluation result of a rule.
*
* @param string $rule_type name of the rule being tested.
* @param boolean $result result of a given rule.
*/
public function add_result( $rule_type, $result ) {
array_push(
$this->results,
array(
'rule' => $rule_type,
'result' => $result ? 'passed' : 'failed',
)
);
}
/**
* Log the results.
*/
public function log() {
if ( false === defined( 'WC_ADMIN_DEBUG_RULE_EVALUATOR' ) || true !== constant( 'WC_ADMIN_DEBUG_RULE_EVALUATOR' ) ) {
return;
}
foreach ( $this->results as $result ) {
$this->logger->debug(
"[{$this->slug}] {$result['rule']}: {$result['result']}",
array( 'source' => $this->source )
);
}
}
}

View File

@ -0,0 +1,36 @@
<?php
/**
* Rule processor that fails.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
/**
* Rule processor that fails.
*/
class FailRuleProcessor implements RuleProcessorInterface {
/**
* Fails the rule.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool Always false.
*/
public function process( $rule, $stored_state ) {
return false;
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
return true;
}
}

View File

@ -0,0 +1,69 @@
<?php
/**
* Gets the processor for the specified rule type.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
/**
* Class encapsulating getting the processor for a given rule type.
*/
class GetRuleProcessor {
/**
* Get the processor for the specified rule type.
*
* @param string $rule_type The rule type.
*
* @return RuleProcessorInterface The matching processor for the specified rule type, or a FailRuleProcessor if no matching processor is found.
*/
public static function get_processor( $rule_type ) {
switch ( $rule_type ) {
case 'plugins_activated':
return new PluginsActivatedRuleProcessor();
case 'publish_after_time':
return new PublishAfterTimeRuleProcessor();
case 'publish_before_time':
return new PublishBeforeTimeRuleProcessor();
case 'not':
return new NotRuleProcessor();
case 'or':
return new OrRuleProcessor();
case 'fail':
return new FailRuleProcessor();
case 'pass':
return new PassRuleProcessor();
case 'plugin_version':
return new PluginVersionRuleProcessor();
case 'stored_state':
return new StoredStateRuleProcessor();
case 'order_count':
return new OrderCountRuleProcessor();
case 'wcadmin_active_for':
return new WCAdminActiveForRuleProcessor();
case 'product_count':
return new ProductCountRuleProcessor();
case 'onboarding_profile':
return new OnboardingProfileRuleProcessor();
case 'is_ecommerce':
return new IsEcommerceRuleProcessor();
case 'is_woo_express':
return new IsWooExpressRuleProcessor();
case 'base_location_country':
return new BaseLocationCountryRuleProcessor();
case 'base_location_state':
return new BaseLocationStateRuleProcessor();
case 'note_status':
return new NoteStatusRuleProcessor();
case 'option':
return new OptionRuleProcessor();
case 'wca_updated':
return new WooCommerceAdminUpdatedRuleProcessor();
case 'total_payments_value':
return new TotalPaymentsVolumeProcessor();
}
return new FailRuleProcessor();
}
}

View File

@ -0,0 +1,49 @@
<?php
/**
* Rule processor that passes (or fails) when the site is on the eCommerce
* plan.
*
* @package WooCommerce\Admin\Classes
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
/**
* Rule processor that passes (or fails) when the site is on the eCommerce
* plan.
*/
class IsEcommerceRuleProcessor implements RuleProcessorInterface {
/**
* Passes (or fails) based on whether the site is on the eCommerce plan or
* not.
*
* @param object $rule The rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
*/
public function process( $rule, $stored_state ) {
if ( ! function_exists( 'wc_calypso_bridge_is_ecommerce_plan' ) ) {
return false === $rule->value;
}
return (bool) wc_calypso_bridge_is_ecommerce_plan() === $rule->value;
}
/**
* Validate the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->value ) ) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,69 @@
<?php
/**
* Rule processor that passes (or fails) when the site is on a Woo Express plan.
*
* @package WooCommerce\Admin\Classes
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
/**
* Rule processor that passes (or fails) when the site is on a Woo Express plan.
* You may optionally pass a plan name to target a specific Woo Express plan.
*/
class IsWooExpressRuleProcessor implements RuleProcessorInterface {
/**
* Passes (or fails) based on whether the site is a Woo Express plan.
*
* @param object $rule The rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
*/
public function process( $rule, $stored_state ) {
if ( ! function_exists( 'wc_calypso_bridge_is_woo_express_plan' ) ) {
return false === $rule->value;
}
// If the plan is undefined, only check if it's a Woo Express plan.
if ( ! isset( $rule->plan ) ) {
return wc_calypso_bridge_is_woo_express_plan() === $rule->value;
}
// If a plan name is defined, only evaluate the plan if we're on the Woo Express plan.
if ( wc_calypso_bridge_is_woo_express_plan() ) {
$fn = 'wc_calypso_bridge_is_woo_express_' . (string) $rule->plan . '_plan';
if ( function_exists( $fn ) ) {
return $fn() === $rule->value;
}
// If an invalid plan name is given, only evaluate the rule if we're targeting all plans other than the specified (invalid) one.
return false === $rule->value;
}
return false;
}
/**
* Validate the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->value ) ) {
return false;
}
if ( isset( $rule->plan ) ) {
if ( ! function_exists( 'wc_calypso_bridge_is_woo_express_plan' ) ) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,64 @@
<?php
/**
* Rule processor that negates the rules in the rule's operand.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
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.
*
* @param RuleEvaluator $rule_evaluator The rule evaluator to use.
*/
public function __construct( $rule_evaluator = null ) {
$this->rule_evaluator = null === $rule_evaluator
? new RuleEvaluator()
: $rule_evaluator;
}
/**
* Evaluates the rules in the operand and negates the result.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
*/
public function process( $rule, $stored_state ) {
$evaluated_operand = $this->rule_evaluator->evaluate(
$rule->operand,
$stored_state
);
return ! $evaluated_operand;
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->operand ) ) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,61 @@
<?php
/**
* Rule processor that compares against the status of another note. For
* example, this could be used to conditionally create a note only if another
* note has not been actioned.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Notes;
/**
* Rule processor that compares against the status of another note.
*/
class NoteStatusRuleProcessor implements RuleProcessorInterface {
/**
* Compare against the status of another note.
*
* @param object $rule The rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
*/
public function process( $rule, $stored_state ) {
$status = Notes::get_note_status( $rule->note_name );
if ( ! $status ) {
return false;
}
return ComparisonOperation::compare(
$status,
$rule->status,
$rule->operation
);
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->note_name ) ) {
return false;
}
if ( ! isset( $rule->status ) ) {
return false;
}
if ( ! isset( $rule->operation ) ) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,65 @@
<?php
/**
* Rule processor that performs a comparison operation against a value in the
* onboarding profile.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
/**
* Rule processor that performs a comparison operation against a value in the
* onboarding profile.
*/
class OnboardingProfileRuleProcessor implements RuleProcessorInterface {
/**
* Performs a comparison operation against a value in the onboarding
* profile.
*
* @param object $rule The rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
*/
public function process( $rule, $stored_state ) {
$onboarding_profile = get_option( 'woocommerce_onboarding_profile' );
if ( empty( $onboarding_profile ) || ! is_array( $onboarding_profile ) ) {
return false;
}
if ( ! isset( $onboarding_profile[ $rule->index ] ) ) {
return false;
}
return ComparisonOperation::compare(
$onboarding_profile[ $rule->index ],
$rule->value,
$rule->operation
);
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->index ) ) {
return false;
}
if ( ! isset( $rule->value ) ) {
return false;
}
if ( ! isset( $rule->operation ) ) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,105 @@
<?php
/**
* Rule processor that performs a comparison operation against an option value.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
use Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers\TransformerService;
defined( 'ABSPATH' ) || exit;
/**
* Rule processor that performs a comparison operation against an option value.
*/
class OptionRuleProcessor implements RuleProcessorInterface {
/**
* Performs a comparison operation against the option value.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
*/
public function process( $rule, $stored_state ) {
$is_contains = $rule->operation && strpos( $rule->operation, 'contains' ) !== false;
$value_when_default_not_provided = $is_contains ? array() : false;
$is_default_set = property_exists( $rule, 'default' );
$default_value = $is_default_set ? $rule->default : $value_when_default_not_provided;
$option_value = $this->get_option_value( $rule, $default_value, $is_contains );
if ( isset( $rule->transformers ) && is_array( $rule->transformers ) ) {
$option_value = TransformerService::apply( $option_value, $rule->transformers, $is_default_set, $default_value );
}
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_value 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_value, $is_contains ) {
$option_value = get_option( $rule->option_name, $default_value );
$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(
'ComparisonOperation "%s" option value "%s" is not an array, defaulting to empty array.',
$rule->operation,
$rule->option_name
),
array(
'option_value' => $option_value,
'rule' => $rule,
)
);
$option_value = array();
}
return $option_value;
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->option_name ) ) {
return false;
}
if ( ! isset( $rule->value ) ) {
return false;
}
if ( ! isset( $rule->operation ) ) {
return false;
}
if ( isset( $rule->transformers ) && is_array( $rule->transformers ) ) {
foreach ( $rule->transformers as $transform_args ) {
$transformer = TransformerService::create_transformer( $transform_args->use );
if ( ! $transformer->validate( $transform_args->arguments ) ) {
return false;
}
}
}
return true;
}
}

View File

@ -0,0 +1,73 @@
<?php
/**
* Rule processor that performs an OR operation on the rule's left and right
* operands.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
/**
* Rule processor that performs an OR operation on the rule's left and right
* operands.
*/
class OrRuleProcessor implements RuleProcessorInterface {
/**
* Rule evaluator to use.
*
* @var RuleEvaluator
*/
private $rule_evaluator;
/**
* Constructor.
*
* @param RuleEvaluator $rule_evaluator The rule evaluator to use.
*/
public function __construct( $rule_evaluator = null ) {
$this->rule_evaluator = null === $rule_evaluator
? new RuleEvaluator()
: $rule_evaluator;
}
/**
* Performs an OR operation on the rule's left and right operands.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
*/
public function process( $rule, $stored_state ) {
foreach ( $rule->operands as $operand ) {
$evaluated_operand = $this->rule_evaluator->evaluate(
$operand,
$stored_state
);
if ( $evaluated_operand ) {
return true;
}
}
return false;
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->operands ) || ! is_array( $rule->operands ) ) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,69 @@
<?php
/**
* Rule processor for publishing based on the number of orders.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
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.
*
* @param object $orders_provider The orders provider.
*/
public function __construct( $orders_provider = null ) {
$this->orders_provider = null === $orders_provider
? new OrdersProvider()
: $orders_provider;
}
/**
* Process the rule.
*
* @param object $rule The rule to process.
* @param object $stored_state Stored state.
*
* @return bool Whether the rule passes or not.
*/
public function process( $rule, $stored_state ) {
$count = $this->orders_provider->get_order_count();
return ComparisonOperation::compare(
$count,
$rule->value,
$rule->operation
);
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->value ) ) {
return false;
}
if ( ! isset( $rule->operation ) ) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,36 @@
<?php
/**
* Provider for order-related queries and operations.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
/**
* Provider for order-related queries and operations.
*/
class OrdersProvider {
/**
* Allowed order statuses for calculating milestones.
*
* @var array
*/
protected $allowed_statuses = array(
'pending',
'processing',
'completed',
);
/**
* Returns the number of orders.
*
* @return integer The number of orders.
*/
public function get_order_count() {
$status_counts = array_map( 'wc_orders_count', $this->allowed_statuses );
$orders_count = array_sum( $status_counts );
return $orders_count;
}
}

View File

@ -0,0 +1,37 @@
<?php
/**
* Rule processor that passes. This is required because an empty set of rules
* (or predicate) evaluates to false.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
/**
* Rule processor that passes.
*/
class PassRuleProcessor implements RuleProcessorInterface {
/**
* Passes the rule.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool Always true.
*/
public function process( $rule, $stored_state ) {
return true;
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
return true;
}
}

View File

@ -0,0 +1,86 @@
<?php
/**
* Rule processor for sending when the provided plugin is activated and
* matches the specified version.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\PluginsProvider\PluginsProvider;
/**
* Rule processor for sending when the provided plugin is activated and
* matches the specified version.
*/
class PluginVersionRuleProcessor implements RuleProcessorInterface {
/**
* Plugins provider instance.
*
* @var PluginsProviderInterface
*/
private $plugins_provider;
/**
* Constructor.
*
* @param PluginsProviderInterface $plugins_provider The plugins provider.
*/
public function __construct( $plugins_provider = null ) {
$this->plugins_provider = null === $plugins_provider
? new PluginsProvider()
: $plugins_provider;
}
/**
* Process the rule.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool Whether the rule passes or not.
*/
public function process( $rule, $stored_state ) {
$active_plugin_slugs = $this->plugins_provider->get_active_plugin_slugs();
if ( ! in_array( $rule->plugin, $active_plugin_slugs, true ) ) {
return false;
}
$plugin_data = $this->plugins_provider->get_plugin_data( $rule->plugin );
if ( ! is_array( $plugin_data ) || ! array_key_exists( 'Version', $plugin_data ) ) {
return false;
}
$plugin_version = $plugin_data['Version'];
return version_compare( $plugin_version, $rule->version, $rule->operator );
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->plugin ) ) {
return false;
}
if ( ! isset( $rule->version ) ) {
return false;
}
if ( ! isset( $rule->operator ) ) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,81 @@
<?php
/**
* Rule processor for sending when the provided plugins are activated.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
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.
*
* @param PluginsProviderInterface $plugins_provider The plugins provider.
*/
public function __construct( $plugins_provider = null ) {
$this->plugins_provider = null === $plugins_provider
? new PluginsProvider()
: $plugins_provider;
}
/**
* Process the rule.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool Whether the rule passes or not.
*/
public function process( $rule, $stored_state ) {
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;
}
}
return true;
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->plugins ) || ! is_array( $rule->plugins ) ) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,78 @@
<?php
/**
* Rule processor that performs a comparison operation against the number of
* products.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
/**
* Rule processor that performs a comparison operation against the number of
* products.
*/
class ProductCountRuleProcessor implements RuleProcessorInterface {
/**
* The product query.
*
* @var WC_Product_Query
*/
protected $product_query;
/**
* Constructor.
*
* @param object $product_query The product query.
*/
public function __construct( $product_query = null ) {
$this->product_query = null === $product_query
? new \WC_Product_Query(
array(
'limit' => 1,
'paginate' => true,
'return' => 'ids',
'status' => array( 'publish' ),
)
)
: $product_query;
}
/**
* Performs a comparison operation against the number of products.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
*/
public function process( $rule, $stored_state ) {
$products = $this->product_query->get_products();
return ComparisonOperation::compare(
$products->total,
$rule->value,
$rule->operation
);
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->value ) ) {
return false;
}
if ( ! isset( $rule->operation ) ) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,67 @@
<?php
/**
* Rule processor for sending after a specified date/time.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
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.
*
* @param DateTimeProviderInterface $date_time_provider The DateTime provider.
*/
public function __construct( $date_time_provider = null ) {
$this->date_time_provider = null === $date_time_provider
? new CurrentDateTimeProvider()
: $date_time_provider;
}
/**
* Process the rule.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool Whether the rule passes or not.
*/
public function process( $rule, $stored_state ) {
return $this->date_time_provider->get_now() >= new \DateTime( $rule->publish_after );
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->publish_after ) ) {
return false;
}
try {
new \DateTime( $rule->publish_after );
} catch ( \Throwable $e ) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,67 @@
<?php
/**
* Rule processor for sending before a specified date/time.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
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.
*
* @param DateTimeProviderInterface $date_time_provider The DateTime provider.
*/
public function __construct( $date_time_provider = null ) {
$this->date_time_provider = null === $date_time_provider
? new CurrentDateTimeProvider()
: $date_time_provider;
}
/**
* Process the rule.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool Whether the rule passes or not.
*/
public function process( $rule, $stored_state ) {
return $this->date_time_provider->get_now() <= new \DateTime( $rule->publish_before );
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->publish_before ) ) {
return false;
}
try {
new \DateTime( $rule->publish_before );
} catch ( \Throwable $e ) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,479 @@
# Rule
Rules in an array are executed as an AND operation. If there are no rules in the array the result is false and the specified notification is not shown.
## Operations
Some rule types support an `operation` value, which is used to compare two
values. The following operations are implemented:
- `=`
- `<`
- `<=`
- `>`
- `>=`
- `!=`
- `contains`
- `!contains`
- `in` (Added in WooCommerce 8.2.0)
- `!in` (Added in WooCommerce 8.2.0)
- `range` (Added in WooCommerce 8.8.0)
### contains and !contains
`contains` and `!contains` allow checking if the provided value is present (or
not present) in the haystack value. An example of this is using the
`onboarding_profile` rule to match on a value in the `product_types` array -
this rule matches if `physical` was selected as a product type in the
onboarding profile:
```json
{
"type": "onboarding_profile",
"index": "product_types",
"operation": "contains",
"value": "physical"
}
```
### in and !in
`in` and `!in` allow checking if a value is found (or not found) in a provided array. For example, using the `in` comparison operator to check if the base country location value is found in a given array, as below. This rule matches if the `base_location_country` is `US`, `NZ`, or `ZA`. **NOTE:** These comparisons were added in **WooCommerce 8.2.0**. If the spec is read by an older version of WooCommerce, the rule will evaluate to `false`.
```json
{
"type": "base_location_country",
"value": [
"US",
"NZ",
"ZA"
],
"operation": "in"
}
```
### range
`range` operator performs an inclusive check to determine if a number falls within a certain range.
This means that both the 'from' and 'to' values of the specified range are included in the check.
The following rule returns true when `woocommerce_remote_variant_assignment` value is between 1 and 10.
```json
{
"type": "option",
"value": [ 1, 10 ],
"default": 0,
"operation": "range",
"option_name": "woocommerce_remote_variant_assignment",
}
```
## Plugins activated
This passes if all of the listed plugins are installed and activated.
`plugins` is required.
```json
{
"type": "plugins_activated",
"plugins": [
"plugin-slug-1',
"plugin-slug-2"
]
}
```
## Publish after time
This passes if the system time is after the specified date/time.
Note that using both `publish_after_time` and `publish_before_time` allows timeboxing a note which could be useful for promoting a sale.
`publish_after` is required.
```json
{
"type": "publish_after_time",
"publish_after": "2020-04-22 00:00:00"
}
```
## Publish before time
This passes if the system time is before the specified date/time.
Note that using both `publish_after_time` and `publish_before_time` allows timeboxing a note which could be useful for promoting a sale.
`publish_before` is required.
```json
{
"type": "publish_before_time",
"publish_before": "2020-04-22 00:00:00"
}
```
## Not
This negates the rules in the provided set of rules. Note that the rules in `operand` get ANDed together into a single boolean.
`operand` is required.
```json
{
"type": "not",
"operand": [
<Rule>,
...
]
}
```
## Or
This performs an OR operation on the operands, passing if any of the operands evaluates to true. Note that if the operands are an array of `Rule`s (as in the first example), each operand is treated as an AND operation.
`operands` is required.
```json
{
"type": "or",
"operands": [
[
<Rule>,
...
],
[
<Rule>,
...
]
]
}
```
Alternatively:
```json
{
"type": "or",
"operands": [
<Rule>,
...
]
}
```
## Fail
This just returns a false value. This is useful if you want to keep a specification around, but don't want it displayed.
```json
{
"type": "fail"
}
```
## Plugin version
This compares the installed version of the plugin to the required version, using the comparison operator. If the plugin isnt activated this returns false.
`plugin`, `version`, and `operator` are required.
This example passes if Jetpack 8.4.1 is installed and activated.
```json
{
"type": "plugin_version",
"plugin": "jetpack",
"version": "8.4.1",
"operator": "="
}
```
## Stored state
This allows access to a stored state containing calculated values that otherwise would be impossible to reproduce using other rules. It performs the comparison operation against the stored state value.
This example passes if the `there_were_no_products` index is equal to `true`.
```json
{
"type": "stored_state",
"index": "there_were_no_products",
"operation": "=",
"value": true
}
```
There are only a limited amount of indices available to this rule, and new indices will need a new version of the WC Admin plugin to be installed.
The currently available indices are:
```json
there_were_no_products
there_are_now_products
new_product_count
```
`index`, `operation`, and `value` are required.
## Product count
This passes if the number of products currently in the system match the comparison operation.
This example passes if there are more than 10 products currently in the system.
```json
{
"type": "product_count",
"operation": ">",
"value": 10
}
```
`operation` and `value` are required.
## Order count
This passes if the number of orders currently in the system match the comparison operation.
This example passes if there are more than 10 orders currently in the system.
```json
{
"type": "order_count",
"operation": ">",
"value": 10
}
```
`operation` and `value` are required.
## WooCommerce Admin active for
This passes if the time WooCommerce Admin has been active for (in days) matches the comparison operation.
This is used as a proxy indicator of the age of the shop.
This example passes if it has been active for more than 8 days.
```json
{
"type": "wcadmin_active_for",
"operation": ">",
"days": 8
}
```
`operation` and `days` are required.
## Onboarding profile
This allows access to the onboarding profile that was built up in the onboarding wizard. The below example passes when the current revenue selected was "none".
```json
{
"type": "onboarding_profile",
"index": "revenue",
"operation": "=",
"value": "none"
}
```
`index`, `operation`, and `value` are all required.
## Is eCommerce
This passes when the store is on a WordPress.com site with the eCommerce plan.
```json
{
"type": "is_ecommerce",
"value": true
}
```
`value` is required.
## Is Woo Express
This passes when the store is on a WordPress.com site with a Woo Express plan active.
You can optionally pass the `plan` name to target a specific Woo Express plan, e.g. `performance`.
```json
{
"type": "is_woo_express",
"plan": "trial|essential|performance",
"value": true
}
```
`value` is required.
`plan` is optional, e.g. `trial`, `essential`, `performance`.
## Base location - country
This passes when the store is located in the specified country.
```json
{
"type": "base_location_country",
"value": "US",
"operation": "="
}
```
`value` and `operation` are both required.
## Base location - state
This passes when the store is located in the specified state.
```json
{
"type": "base_location_state",
"value": "TX",
"operation": "="
}
```
`value` and `operation` are both required.
## Note status
This passes when the status of the specified note matches the specified status.
The below example passes when the `wc-admin-mobile-app` note has not been
actioned.
```json
{
"type": "note_status",
"note_name": "wc-admin-mobile-app",
"status": "actioned",
"operation": "!="
}
```
## Total Payments Value
This passes when the total value of all payments for a given timeframe
compared to the provided value pass the operation test.
`timeframe` can be one of `last_week`, `last_month`, `last_quarter`, `last_6_months`, `last_year`
```json
{
"type": "total_payments_value",
"timeframe": "last_month",
"value": 2300,
"operation": ">"
}
```
`timeframe`, `value`, and `operation` are all required.
## Option
This passes when the option value matches the value using the operation.
```json
{
"type": "option",
"option_name": "woocommerce_currency",
"value": "USD",
"default": "USD",
"operation": "="
}
```
`option_name`, `value`, and `operation` are all required. `default` is not required and allows a default value to be used if the option does not exist.
### Option Transformer
This transforms the given option value into a different value by a series of transformers.
Example option value:
```php
Array
(
[setup_client] =>
[industry] => Array
(
[0] => Array
(
[slug] => food-drink
)
[1] => Array
(
[slug] => fashion-apparel-accessories
)
)
)
```
If you want to ensure that the industry array contains `fashion-apparel-accessories`, you can use the following Option definition with transformers.
```json
{
"type": "option",
"transformers": [
{
"use": "dot_notation",
"arguments": {
"path": "industry"
}
},
{
"use": "array_column",
"arguments": {
"key": "slug"
}
},
{
"use": "array_search",
"arguments": {
"value": "fashion-apparel-accessories"
}
}
],
"option_name": "woocommerce_onboarding_profile",
"value": "fashion-apparel-accessories",
"default": "USD",
"operation": "="
}
```
You can find a list of transformers and examples in the transformer [README](./Transformers/README.md).
## WCA updated
This passes when WooCommerce Admin has just been updated. The specs will be run
on update. Note that this doesn't provide a way to check the version number as
the `plugin_version` rule can be used to check for a specific version of the
WooCommerce Admin plugin.
```json
{
type: "wca_updated"
}
```
No other values are needed.
## Debugging Specification
You can see the evaluation of each specification by turning on an optional evaluation logger.
1. Define `WC_ADMIN_DEBUG_RULE_EVALUATOR` constant in `wp-config.php`. `define('WC_ADMIN_DEBUG_RULE_EVALUATOR', true);`
2. Run `wc_admin_daily`
3. cd to `wp-content/uploads/wc-logs/` and check a log file in `remote-inbox-notifications-:date-hash.log` format.
You can tail the log file with a slug name to see the evaluation of a rule that you are testing.
Example:
`tail -f remote-inbox-notifications-2021-06-15-128.log | grep 'wcpay-promo-2021-6-incentive-2'`

View File

@ -0,0 +1,92 @@
<?php
/**
* Evaluate the given rules as an AND operation - return false early if a
* rule evaluates to false.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
/**
* Evaluate the given rules as an AND operation - return false early if a
* rule evaluates to false.
*/
class RuleEvaluator {
/**
* GetRuleProcessor to use.
*
* @var GetRuleProcessor
*/
private $get_rule_processor;
/**
* Constructor.
*
* @param GetRuleProcessor $get_rule_processor The GetRuleProcessor to use.
*/
public function __construct( $get_rule_processor = null ) {
$this->get_rule_processor = null === $get_rule_processor
? new GetRuleProcessor()
: $get_rule_processor;
}
/**
* Evaluate the given rules as an AND operation - return false early if a
* rule evaluates to false.
*
* @param array|object $rules The rule or rules being processed.
* @param object|null $stored_state Stored state.
* @param array $logger_args Arguments for the event logger. `slug` is required.
*
* @throws \InvalidArgumentException Thrown when $logger_args is missing slug.
*
* @return bool The result of the operation.
*/
public function evaluate( $rules, $stored_state = null, $logger_args = array() ) {
if ( is_bool( $rules ) ) {
return $rules;
}
if ( ! is_array( $rules ) ) {
$rules = array( $rules );
}
if ( 0 === count( $rules ) ) {
return false;
}
$evaluation_logger = null;
if ( count( $logger_args ) ) {
if ( ! array_key_exists( 'slug', $logger_args ) ) {
throw new \InvalidArgumentException( 'Missing required field: slug in $logger_args.' );
}
array_key_exists( 'source', $logger_args ) ? $source = $logger_args['source'] : $source = null;
$evaluation_logger = new EvaluationLogger( $logger_args['slug'], $source );
}
foreach ( $rules as $rule ) {
if ( ! is_object( $rule ) ) {
return false;
}
$processor = $this->get_rule_processor->get_processor( $rule->type );
$processor_result = $processor->process( $rule, $stored_state );
$evaluation_logger && $evaluation_logger->add_result( $rule->type, $processor_result );
if ( ! $processor_result ) {
$evaluation_logger && $evaluation_logger->log();
return false;
}
}
$evaluation_logger && $evaluation_logger->log();
return true;
}
}

View File

@ -0,0 +1,32 @@
<?php
/**
* Interface for a rule processor.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
/**
* Rule processor interface
*/
interface RuleProcessorInterface {
/**
* Processes a rule, returning the boolean result of the processing.
*
* @param object $rule The rule to process.
* @param object $stored_state Stored state.
*
* @return bool The result of the processing.
*/
public function process( $rule, $stored_state );
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule );
}

View File

@ -0,0 +1,58 @@
<?php
/**
* Rule processor that performs a comparison operation against a value in the
* stored state object.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
/**
* Rule processor that performs a comparison operation against a value in the
* stored state object.
*/
class StoredStateRuleProcessor implements RuleProcessorInterface {
/**
* Performs a comparison operation against a value in the stored state object.
*
* @param object $rule The rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
*/
public function process( $rule, $stored_state ) {
if ( ! isset( $stored_state->{$rule->index} ) ) {
return false;
}
return ComparisonOperation::compare(
$stored_state->{$rule->index},
$rule->value,
$rule->operation
);
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
if ( ! isset( $rule->index ) ) {
return false;
}
if ( ! isset( $rule->value ) ) {
return false;
}
if ( ! isset( $rule->operation ) ) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,132 @@
<?php
/**
* Handles stored state setup for products.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\RemoteInboxNotifications\RemoteInboxNotificationsEngine;
/**
* Handles stored state setup for products.
*/
class StoredStateSetupForProducts {
const ASYNC_RUN_REMOTE_NOTIFICATIONS_ACTION_NAME =
'woocommerce_admin/stored_state_setup_for_products/async/run_remote_notifications';
/**
* Initialize the class via the admin_init hook.
*/
public static function admin_init() {
add_action( 'product_page_product_importer', array( __CLASS__, 'run_on_product_importer' ) );
add_action( 'transition_post_status', array( __CLASS__, 'run_on_transition_post_status' ), 10, 3 );
}
/**
* Initialize the class via the init hook.
*
* @internal
*/
final public static function init() {
add_action( self::ASYNC_RUN_REMOTE_NOTIFICATIONS_ACTION_NAME, array( __CLASS__, 'run_remote_notifications' ) );
}
/**
* Run the remote notifications engine. This is triggered by
* action-scheduler after a product is added. It also cleans up from
* setting the product count increment.
*/
public static function run_remote_notifications() {
RemoteInboxNotificationsEngine::run();
}
/**
* Set initial stored state values.
*
* @param object $stored_state The stored state.
*
* @return object The stored state.
*/
public static function init_stored_state( $stored_state ) {
$stored_state->there_were_no_products = ! self::are_there_products();
$stored_state->there_are_now_products = ! $stored_state->there_were_no_products;
return $stored_state;
}
/**
* Are there products query.
*
* @return bool
*/
private static function are_there_products() {
$query = new \WC_Product_Query(
array(
'limit' => 1,
'paginate' => true,
'return' => 'ids',
'status' => array( 'publish' ),
)
);
$products = $query->get_products();
$count = $products->total;
return $count > 0;
}
/**
* Runs on product importer steps.
*/
public static function run_on_product_importer() {
// We're only interested in when the importer completes.
// phpcs:disable WordPress.Security.NonceVerification.Recommended
if ( ! isset( $_REQUEST['step'] ) ) {
return;
}
if ( 'done' !== $_REQUEST['step'] ) {
return;
}
// phpcs:enable
self::update_stored_state_and_possibly_run_remote_notifications();
}
/**
* Runs when a post status transitions, but we're only interested if it is
* a product being published.
*
* @param string $new_status The new status.
* @param string $old_status The old status.
* @param Post $post The post.
*/
public static function run_on_transition_post_status( $new_status, $old_status, $post ) {
if (
'product' !== $post->post_type ||
'publish' !== $new_status
) {
return;
}
self::update_stored_state_and_possibly_run_remote_notifications();
}
/**
* Enqueues an async action (using action-scheduler) to run remote
* notifications.
*/
private static function update_stored_state_and_possibly_run_remote_notifications() {
$stored_state = RemoteInboxNotificationsEngine::get_stored_state();
// If the stored_state is the same, we don't need to run remote notifications to avoid unnecessary action scheduling.
if ( true === $stored_state->there_are_now_products ) {
return;
}
$stored_state->there_are_now_products = true;
RemoteInboxNotificationsEngine::update_stored_state( $stored_state );
// Run self::run_remote_notifications asynchronously.
as_enqueue_async_action( self::ASYNC_RUN_REMOTE_NOTIFICATIONS_ACTION_NAME );
}
}

View File

@ -0,0 +1,93 @@
<?php
/**
* Rule processor that passes when a store's payments volume exceeds a provided amount.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\API\Reports\Revenue\Query as RevenueQuery;
use Automattic\WooCommerce\Admin\API\Reports\TimeInterval;
/**
* Rule processor that passes when a store's payments volume exceeds a provided amount.
*/
class TotalPaymentsVolumeProcessor implements RuleProcessorInterface {
/**
* Compare against the store's total payments volume.
*
* @param object $rule The rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
*/
public function process( $rule, $stored_state ) {
$dates = TimeInterval::get_timeframe_dates( $rule->timeframe );
$reports_revenue = $this->get_reports_query(
array(
'before' => $dates['end'],
'after' => $dates['start'],
'interval' => 'year',
'fields' => array( '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,
$rule->value,
$rule->operation
);
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
$allowed_timeframes = array(
'last_week',
'last_month',
'last_quarter',
'last_6_months',
'last_year',
);
if ( ! isset( $rule->timeframe ) || ! in_array( $rule->timeframe, $allowed_timeframes, true ) ) {
return false;
}
if ( ! isset( $rule->value ) || ! is_numeric( $rule->value ) ) {
return false;
}
if ( ! isset( $rule->operation ) ) {
return false;
}
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
);
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers;
use Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers\TransformerInterface;
use InvalidArgumentException;
use stdClass;
/**
* Search array value by one of its key.
*
* @package Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers
*/
class ArrayColumn implements TransformerInterface {
/**
* Search array value by one of its key.
*
* @param mixed $value a value to transform.
* @param stdClass|null $arguments required arguments 'key'.
* @param string|null $default_value default value.
*
* @throws InvalidArgumentException Throws when the required argument 'key' is missing.
*
* @return mixed
*/
public function transform( $value, stdClass $arguments = null, $default_value = array() ) {
if ( ! is_array( $value ) ) {
return $default_value;
}
return array_column( $value, $arguments->key );
}
/**
* Validate Transformer arguments.
*
* @param stdClass|null $arguments arguments to validate.
*
* @return mixed
*/
public function validate( stdClass $arguments = null ) {
if ( ! isset( $arguments->key ) ) {
return false;
}
if (
null !== $arguments->key &&
! is_string( $arguments->key ) &&
! is_int( $arguments->key )
) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers;
use Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers\TransformerInterface;
use stdClass;
/**
* Flatten nested array.
*
* @package Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers
*/
class ArrayFlatten implements TransformerInterface {
/**
* Search a given value in the array.
*
* @param mixed $value a value to transform.
* @param stdClass|null $arguments arguments.
* @param string|null $default_value default value.
*
* @return mixed|null
*/
public function transform( $value, stdClass $arguments = null, $default_value = array() ) {
if ( ! is_array( $value ) ) {
return $default_value;
}
$return = array();
array_walk_recursive(
$value,
function ( $item ) use ( &$return ) {
$return[] = $item;
}
);
return $return;
}
/**
* Validate Transformer arguments.
*
* @param stdClass|null $arguments arguments to validate.
*
* @return mixed
*/
public function validate( stdClass $arguments = null ) {
return true;
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers;
use Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers\TransformerInterface;
use stdClass;
/**
* Search array value by one of its key.
*
* @package Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers
*/
class ArrayKeys implements TransformerInterface {
/**
* Search array value by one of its key.
*
* @param mixed $value a value to transform.
* @param stdClass|null $arguments arguments.
* @param string|null $default_value default value.
*
* @return mixed
*/
public function transform( $value, stdClass $arguments = null, $default_value = array() ) {
if ( ! is_array( $value ) ) {
return $default_value;
}
return array_keys( $value );
}
/**
* Validate Transformer arguments.
*
* @param stdClass|null $arguments arguments to validate.
*
* @return mixed
*/
public function validate( stdClass $arguments = null ) {
return true;
}
}

View File

@ -0,0 +1,53 @@
<?php
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers;
use Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers\TransformerInterface;
use InvalidArgumentException;
use stdClass;
/**
* Searches a given a given value in the array.
*
* @package Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers
*/
class ArraySearch implements TransformerInterface {
/**
* Search a given value in the array.
*
* @param mixed $value a value to transform.
* @param stdClass|null $arguments required argument 'value'.
* @param string|null $default_value default value.
*
* @throws InvalidArgumentException Throws when the required 'value' is missing.
*
* @return mixed|null
*/
public function transform( $value, stdClass $arguments = null, $default_value = null ) {
if ( ! is_array( $value ) ) {
return $default_value;
}
$key = array_search( $arguments->value, $value, true );
if ( false !== $key ) {
return $value[ $key ];
}
return null;
}
/**
* Validate Transformer arguments.
*
* @param stdClass|null $arguments arguments to validate.
*
* @return mixed
*/
public function validate( stdClass $arguments = null ) {
if ( ! isset( $arguments->value ) ) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers;
use Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers\TransformerInterface;
use stdClass;
/**
* Search array value by one of its key.
*
* @package Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers
*/
class ArrayValues implements TransformerInterface {
/**
* Search array value by one of its key.
*
* @param mixed $value a value to transform.
* @param stdClass|null $arguments arguments.
* @param string|null $default_value default value.
*
* @return mixed
*/
public function transform( $value, stdClass $arguments = null, $default_value = array() ) {
if ( ! is_array( $value ) ) {
return $default_value;
}
return array_values( $value );
}
/**
* Validate Transformer arguments.
*
* @param stdClass|null $arguments arguments to validate.
*
* @return mixed
*/
public function validate( stdClass $arguments = null ) {
return true;
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers;
use Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers\TransformerInterface;
use stdClass;
/**
* Count elements in Array or Countable object.
*
* @package Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers
*/
class Count implements TransformerInterface {
/**
* Count elements in Array or Countable object.
*
* @param array|Countable $value an array to count.
* @param stdClass|null $arguments arguments.
* @param string|null $default_value default value.
*
* @return number
*/
public function transform( $value, stdClass $arguments = null, $default_value = null ) {
if ( ! is_array( $value ) && ! $value instanceof \Countable ) {
return $default_value;
}
return count( $value );
}
/**
* Validate Transformer arguments.
*
* @param stdClass|null $arguments arguments to validate.
*
* @return mixed
*/
public function validate( stdClass $arguments = null ) {
return true;
}
}

View File

@ -0,0 +1,79 @@
<?php
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers;
use Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers\TransformerInterface;
use InvalidArgumentException;
use stdClass;
/**
* Find an array value by dot notation.
*
* @package Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers
*/
class DotNotation implements TransformerInterface {
/**
* Find given path from the given value.
*
* @param mixed $value a value to transform.
* @param stdClass|null $arguments required argument 'path'.
* @param string|null $default_value default value.
*
* @throws InvalidArgumentException Throws when the required 'path' is missing.
*
* @return mixed
*/
public function transform( $value, stdclass $arguments = null, $default_value = null ) {
if ( is_object( $value ) ) {
// if the value is an object, convert it to an array.
$value = json_decode( wp_json_encode( $value ), true );
}
return $this->get( $value, $arguments->path, $default_value );
}
/**
* Find the given $path in $array_to_search by dot notation.
*
* @param array $array_to_search an array to search in.
* @param string $path a path in the given array.
* @param null $default_value default value to return if $path was not found.
*
* @return mixed|null
*/
public function get( $array_to_search, $path, $default_value = null ) {
if ( ! is_array( $array_to_search ) ) {
return $default_value;
}
if ( isset( $array_to_search[ $path ] ) ) {
return $array_to_search[ $path ];
}
foreach ( explode( '.', $path ) as $segment ) {
if ( ! is_array( $array_to_search ) || ! array_key_exists( $segment, $array_to_search ) ) {
return $default_value;
}
$array_to_search = $array_to_search[ $segment ];
}
return $array_to_search;
}
/**
* Validate Transformer arguments.
*
* @param stdClass|null $arguments arguments to validate.
*
* @return mixed
*/
public function validate( stdClass $arguments = null ) {
if ( ! isset( $arguments->path ) ) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers;
use Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers\TransformerInterface;
use stdClass;
/**
* Prepare site URL for comparison.
*
* @package Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers
*/
class PrepareUrl implements TransformerInterface {
/**
* Prepares the site URL by removing the protocol and trailing slash.
*
* @param string $value a value to transform.
* @param stdClass|null $arguments arguments.
* @param string|null $default_value default value.
*
* @return mixed|null
*/
public function transform( $value, stdClass $arguments = null, $default_value = null ) {
if ( ! is_string( $value ) ) {
return $default_value;
}
$url_parts = wp_parse_url( rtrim( $value, '/' ) );
if ( ! $url_parts ) {
return $default_value;
}
if ( ! isset( $url_parts['host'] ) ) {
return $default_value;
}
if ( isset( $url_parts['path'] ) ) {
return $url_parts['host'] . $url_parts['path'];
}
return $url_parts['host'];
}
/**
* Validate Transformer arguments.
*
* @param stdClass|null $arguments arguments to validate.
*
* @return mixed
*/
public function validate( stdClass $arguments = null ) {
return true;
}
}

View File

@ -0,0 +1,404 @@
<!-- markdownlint-disable MD024 -->
# Option Transformer
An option transformer is a class that transforms the given option value into a different value for the comparison operation.
Transformers run in the order in which they are defined, and each transformer passes down the value it transformed to the next transformer for consumption.
**Definition example**: transformers are always used with `option` rule.
```js
{
"slug": "test-note",
...
],
"rules": [
{
"type": "option",
"transformers": [
{
"use": "dot_notation",
"arguments": {
"path": "industry"
}
},
{
"use": "array_column",
"arguments": {
"key": "slug"
}
}
],
"option_name": "woocommerce_onboarding_profile",
"operation": "!=",
"value": "fashion-apparel-accessories",
"default": []
}
]
}
```
## array_column
PHP's built-in `array_column` to select values from a single column. For more information about how array_column works, please see PHP's [official documentation](https://www.php.net/manual/en/function.array-column.php).
### Arguments
| name | description |
| ---- | -------------- |
| key | array key name |
### Definition
```php
"transformers": [
{
"use": "array_column",
"arguments": {
"key": "industry"
}
}
],
```
### Example
Given the following data
```php
array(
array("industry" => "media" ),
array("industry" => "software" )
);
```
Use `array_column` to extract `array("media", "software")` then choose the first element with `dot_notation`.
```php
"transformers": [
{
"use": "array_column",
"arguments": {
"key": "industry"
}
},
{
"use": "dot_notation",
"arguments": {
"key": "0"
}
}
],
```
**Output**: "media"
## array_flatten
Flattens a nested array.
### Arguments: N/A
### Definition
```php
"transformers": [
{
"use": "array_flatten"
}
],
```
### Example
Given the following data
```php
array(
array(
'member1',
),
array(
'member2',
),
array(
'member3',
),
);
```
Use `array_flatten` to extract `array("member1", "member2", "member3")` then use `array_search` to make sure it has `member2`
```php
"transformers": [
{
"use": "array_flatten",
},
{
"use": "array_search",
"arguments": {
"key": "member2"
}
}
],
```
**Output**: true
## array_keys
PHP's built-in `array_keys` to return keys from an array. For more information about how `array_keys` works, please see PHPs [official documentation](https://www.php.net/manual/en/function.array-column.php).
### Arguments: N/A
### Definition
```php
"transformers": [
{
"use": "array_keys"
}
],
```
### Example
Given the following data
```php
array(
"name" => "tester",
"address" => "test",
"supports_version_2" => true
)
```
Use `array_keys` to extract `array("name", "address", "supports_version_2")` and then use `array_search` to make sure it has `supports_version_2`
```php
"transformers": [
{
"use": "array_keys",
},
{
"use": "array_search",
"arguments": {
"key": "member2"
}
}
],
```
**Output**: true
## array_search
PHP's built-in `array_search` to search a value in an array. For more information about how `array_search` works, please see PHPs [official documentation](https://www.php.net/manual/en/function.array-search.php).
### Arguments
|name|description|
|----|---------|
| value | a value to search in the given array |
### Definition
```php
"transformers": [
{
"use": "array_search",
"arguments": {
"value": "test"
}
}
],
```
### Examples
See examples from [array_flatten](#array_flatten) and [array_keys](#array_keys)
## array_values
PHP's built-in array_values to return values from an array. For more information about how `array_values` works, please see PHPs [official documentation](https://www.php.net/manual/en/function.array-values).
### Arguments: N/A
### Definition
```php
"transformers": [
{
"use": "array_values"
}
],
```
### Example
Given the following data
```php
array (
"size" => "x-large"
)
```
Use `array_values` to extract `array("x-large")`
```php
"transformers": [
{
"use": "array_values",
}
],
```
**Output:** "x-large"
## dot_notation
Uses dot notation to select a value in an array. Dot notation lets you access an array as if it is an object.
### Arguments: N/A
### Definition
```php
"transformers": [
{
"use": "dot_notation",
"arguments": {
"path": "name"
}
}
],
```
### Example
Given the following data
```php
array(
'name' => 'john',
'members' => ['member1', 'member2']
);
```
Select `name` field.
```php
"transformers": [
{
"use": "dot_notation",
"arguments": {
"path": "name"
}
}
],
```
**Output:** "john"
Select `member2`. You can access array items with an index.
```php
"transformers": [
{
"use": "dot_notation",
"arguments": {
"path": "members.1"
}
}
],
```
**Output:**: "member2"
## count
PHP's built-in count to return the number of values from a countable, such as an array.
### Arguments: N/A
### Definition
```php
"transformers": [
{
"use": "count"
}
],
```
### Example
Given the following list of usernames
```php
array(
"username1",
"username2",
"username3"
)
```
Let's count # of users with `count`
```php
"transformers": [
{
"use": "count",
}
],
```
**Output:** 3
## prepare_url
This prepares the site URL by removing the protocol and the last slash.
### Arguments: N/A
### Definition
```php
"transformers": [
{
"use": "prepare_url"
}
],
```
### Example
Given the following data
```php
$siteurl = "https://mysite.com/"
```
Removes the protocol and the last slash.
```php
"transformers": [
{
"use": "prepare_url",
}
],
```
**Output:** "mysite.com"

View File

@ -0,0 +1,34 @@
<?php
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers;
use stdClass;
/**
* An interface to define a transformer.
*
* Interface TransformerInterface
*
* @package Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers
*/
interface TransformerInterface {
/**
* Transform given value to a different value.
*
* @param mixed $value a value to transform.
* @param stdClass|null $arguments arguments.
* @param string|null $default_value default value.
*
* @return mixed|null
*/
public function transform( $value, stdClass $arguments = null, $default_value = null );
/**
* Validate Transformer arguments.
*
* @param stdClass|null $arguments arguments to validate.
*
* @return mixed
*/
public function validate( stdClass $arguments = null );
}

View File

@ -0,0 +1,83 @@
<?php
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers;
use InvalidArgumentException;
use stdClass;
/**
* A simple service class for the Transformer classes.
*
* Class TransformerService
*
* @package Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\Transformers
*/
class TransformerService {
/**
* Create a transformer object by name.
*
* @param string $name name of the transformer.
*
* @return TransformerInterface|null
*/
public static function create_transformer( $name ) {
$camel_cased = str_replace( ' ', '', ucwords( str_replace( '_', ' ', $name ) ) );
$classname = __NAMESPACE__ . '\\' . $camel_cased;
if ( ! class_exists( $classname ) ) {
return null;
}
return new $classname();
}
/**
* Apply transformers to the given value.
*
* @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_value 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, $is_default_set, $default_value ) {
foreach ( $transformer_configs as $transformer_config ) {
if ( ! isset( $transformer_config->use ) ) {
throw new InvalidArgumentException( 'Missing required config value: use' );
}
if ( ! isset( $transformer_config->arguments ) ) {
$transformer_config->arguments = null;
}
$transformer = self::create_transformer( $transformer_config->use );
if ( null === $transformer ) {
throw new InvalidArgumentException( "Unable to find a transformer by name: {$transformer_config->use}" ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
}
$target_value = $transformer->transform( $target_value, $transformer_config->arguments, $is_default_set ? $default_value : 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_value;
}
// When type of the default value is different from the target value, return the default value
// to ensure type safety.
if ( gettype( $default_value ) !== gettype( $target_value ) ) {
return $default_value;
}
}
return $target_value;
}
}

View File

@ -0,0 +1,24 @@
<?php
/**
* WCAdmin active for provider.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
use Automattic\WooCommerce\Admin\WCAdminHelper;
defined( 'ABSPATH' ) || exit;
/**
* WCAdminActiveForProvider class
*/
class WCAdminActiveForProvider {
/**
* Get the number of seconds that the store has been active.
*
* @return number Number of seconds.
*/
public function get_wcadmin_active_for_in_seconds() {
return WCAdminHelper::get_wcadmin_active_for_in_seconds();
}
}

View File

@ -0,0 +1,79 @@
<?php
/**
* Rule processor for publishing if wc-admin has been active for at least the
* given number of seconds.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
defined( 'ABSPATH' ) || exit;
/**
* Rule processor for publishing if wc-admin has been active for at least the
* 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
*
* @param object $wcadmin_active_for_provider Provides the amount of time wcadmin has been active for.
*/
public function __construct( $wcadmin_active_for_provider = null ) {
$this->wcadmin_active_for_provider = null === $wcadmin_active_for_provider
? new WCAdminActiveForProvider()
: $wcadmin_active_for_provider;
}
/**
* Performs a comparison operation against the amount of time wc-admin has
* been active for in days.
*
* @param object $rule The rule being processed.
* @param object $stored_state Stored state.
*
* @return bool The result of the operation.
*/
public function process( $rule, $stored_state ) {
$active_for_seconds = $this->wcadmin_active_for_provider->get_wcadmin_active_for_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,
$rule_seconds,
$rule->operation
);
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
// 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;
}
if ( ! isset( $rule->operation ) ) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,38 @@
<?php
/**
* Rule processor for sending when WooCommerce Admin has been updated.
*/
namespace Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors;
use Automattic\WooCommerce\Admin\RemoteInboxNotifications\RemoteInboxNotificationsEngine;
defined( 'ABSPATH' ) || exit;
/**
* Rule processor for sending when WooCommerce Admin has been updated.
*/
class WooCommerceAdminUpdatedRuleProcessor implements RuleProcessorInterface {
/**
* Process the rule.
*
* @param object $rule The specific rule being processed by this rule processor.
* @param object $stored_state Stored state.
*
* @return bool Whether the rule passes or not.
*/
public function process( $rule, $stored_state ) {
return get_option( RemoteInboxNotificationsEngine::WCA_UPDATED_OPTION_NAME, false );
}
/**
* Validates the rule.
*
* @param object $rule The rule to validate.
*
* @return bool Pass/fail.
*/
public function validate( $rule ) {
return true;
}
}

View File

@ -8,7 +8,7 @@ namespace Automattic\WooCommerce\Internal\Admin;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Features\Features;
use Automattic\WooCommerce\Admin\RemoteInboxNotifications\DataSourcePoller;
use Automattic\WooCommerce\Admin\RemoteInboxNotifications\RemoteInboxNotificationsDataSourcePoller;
use Automattic\WooCommerce\Admin\RemoteInboxNotifications\RemoteInboxNotificationsEngine;
use Automattic\WooCommerce\Internal\Admin\Notes\AddFirstProduct;
use Automattic\WooCommerce\Internal\Admin\Notes\ChoosingTheme;
@ -146,7 +146,7 @@ class Events {
$this->possibly_refresh_data_source_pollers();
if ( $this->is_remote_inbox_notifications_enabled() ) {
DataSourcePoller::get_instance()->read_specs_from_data_sources();
RemoteInboxNotificationsDataSourcePoller::get_instance()->read_specs_from_data_sources();
RemoteInboxNotificationsEngine::run();
}

View File

@ -8,7 +8,7 @@ namespace Automattic\WooCommerce\Internal\Admin\RemoteFreeExtensions;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\PluginsHelper;
use Automattic\WooCommerce\Admin\RemoteInboxNotifications\RuleEvaluator;
use Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\RuleEvaluator;
/**
* Evaluates the extension and returns it.

View File

@ -2,10 +2,11 @@
namespace Automattic\WooCommerce\Internal\Admin\RemoteFreeExtensions;
use Automattic\WooCommerce\Admin\RemoteSpecs\DataSourcePoller;
/**
* Specs data source poller class for remote free extensions.
*/
class RemoteFreeExtensionsDataSourcePoller extends \Automattic\WooCommerce\Admin\DataSourcePoller {
class RemoteFreeExtensionsDataSourcePoller extends DataSourcePoller {
const ID = 'remote_free_extensions';

View File

@ -1,7 +1,7 @@
<?php
namespace Automattic\WooCommerce\Internal\Admin\WCPayPromotion;
use Automattic\WooCommerce\Admin\DataSourcePoller;
use Automattic\WooCommerce\Admin\RemoteSpecs\DataSourcePoller;
/**
* Specs data source poller class for WooCommerce Payment Promotion.
@ -20,7 +20,7 @@ class WCPayPromotionDataSourcePoller extends DataSourcePoller {
/**
* Class instance.
*
* @var Analytics instance
* @var WCPayPromotionDataSourcePoller instance
*/
protected static $instance = null;

Some files were not shown because too many files have changed in this diff Show More