File: /var/www/html/ielts-store/wp-content/plugins/automatewoo/includes/Rules/Abstract_Date.php
<?php
namespace AutomateWoo\Rules;
use AutomateWoo\DateTime;
defined( 'ABSPATH' ) || exit;
/**
* Abstract date class for rules.
*
* @since 4.4
*/
abstract class Abstract_Date extends Rule {
/**
* The type.
*
* @var string
*/
public $type = 'date';
/**
* We use multiple values to assimilate time frame and time measure.
*
* @var bool
*/
public $has_multiple_value_fields = true;
/**
* Is it future?
*
* @var bool
*/
public $has_is_future_comparision = false;
/**
* Is it past?
*
* @var bool
*/
public $has_is_past_comparision = false;
/**
* Is after specific date?
*
* @var bool
*/
public $has_is_after = true;
/**
* Is before specific date?
*
* @var bool
*/
public $has_is_before = true;
/**
* Is it on a specific date date?
*
* @var bool
*/
public $has_is_on = true;
/**
* Is not on a specific date?
*
* @var bool
*/
public $has_is_not_on = true;
/**
* Only one date per rule.
*
* @var bool
*/
public $is_multi = false;
/**
* Is day of the week.
*
* @var bool
*/
public $has_days_of_the_week = true;
/**
* Is between dates.
*
* @var bool
*/
public $has_is_between_dates = true;
/**
* Is not set?
*
* @var bool
*/
public $has_is_not_set = true;
/**
* Is set?
*
* @var bool
*/
public $has_is_set = true;
/**
* Our rule uses datepicker?
*
* @var bool
*/
public $uses_datepicker = false;
/**
* Rule select options.
*
* @var array
*/
public $select_choices;
/**
* Abstract_Date constructor.
*/
public function __construct() {
if ( $this->has_is_future_comparision ) {
$this->compare_types['is_in_the_next'] = __( 'Is in the next', 'automatewoo' );
$this->compare_types['is_not_in_the_next'] = __( 'Is not in the next', 'automatewoo' );
}
if ( $this->has_is_past_comparision ) {
$this->compare_types['is_in_the_last'] = __( 'Is in the last', 'automatewoo' );
$this->compare_types['is_not_in_the_last'] = __( 'Is not in the last', 'automatewoo' );
}
if ( $this->has_is_after ) {
$this->uses_datepicker = true;
$this->compare_types['is_after'] = __( 'Is after', 'automatewoo' );
}
if ( $this->has_is_before ) {
$this->uses_datepicker = true;
$this->compare_types['is_before'] = __( 'Is before', 'automatewoo' );
}
if ( $this->has_is_on ) {
$this->uses_datepicker = true;
$this->compare_types['is_on'] = __( 'Is on', 'automatewoo' );
}
if ( $this->has_is_not_on ) {
$this->uses_datepicker = true;
$this->compare_types ['is_not_on'] = __( 'Is not on', 'automatewoo' );
}
if ( $this->has_days_of_the_week ) {
$this->compare_types['days_of_the_week'] = __( 'Is on day/s of the week', 'automatewoo' );
}
if ( $this->has_is_between_dates ) {
$this->compare_types['is_between'] = __( 'Is in the range', 'automatewoo' );
}
if ( $this->has_is_not_set ) {
$this->compare_types['is_not_set'] = __( 'Is not set', 'automatewoo' );
}
if ( $this->has_is_set ) {
$this->compare_types['is_set'] = __( 'Is set', 'automatewoo' );
}
$this->select_choices = [
'hours' => __( 'Hours', 'automatewoo' ),
'days' => __( 'Days', 'automatewoo' ),
];
parent::__construct();
}
/**
* Validates a timeframe between dates and that the timeframe is the expected.
*
* @param DateTime $date The date to validate.
* @param string $compare Date compare type: is_in_the_next, is_not_in_the_next, is_in_the_last, is_not_in_the_last
* @param int $timeframe The timeframe we want to validate.
* @param string $measure Days or Hours.
*
* @return bool
*/
protected function validate_date_diff( $date, $compare, $timeframe, $measure ) {
if ( 'days' === $measure ) {
$interval_spec = 'P' . $timeframe . 'D';
} else {
$interval_spec = 'PT' . $timeframe . 'H';
}
try {
$interval = new \DateInterval( $interval_spec );
} catch ( \Exception $e ) {
return false;
}
$now = new DateTime( 'now' );
$comparison_date = clone $now;
switch ( $compare ) {
case 'is_in_the_next':
$comparison_date->add( $interval );
return $this->validate_is_between_dates( $date, $now, $comparison_date );
case 'is_not_in_the_next':
$comparison_date->add( $interval );
return ! $this->validate_is_between_dates( $date, $now, $comparison_date );
case 'is_in_the_last':
$comparison_date->sub( $interval );
return $this->validate_is_between_dates( $date, $comparison_date, $now );
case 'is_not_in_the_last':
$comparison_date->sub( $interval );
return ! $this->validate_is_between_dates( $date, $comparison_date, $now );
}
return false;
}
/**
* Are we running a in/not in the next/last validation?
*
* @param string $compare Compare we want to run.
*
* @return bool
*/
private function is_past_future_validation( $compare ) {
return in_array(
$compare,
[
'is_in_the_next',
'is_not_in_the_next',
'is_in_the_last',
'is_not_in_the_last',
],
true
);
}
/**
* Validates if a date is the same, based on Y-m-d format.
*
* @param DateTime $date1 First date.
* @param DateTime $date2 Second date for comparision.
*
* @return bool
*/
private function validate_same_date( $date1, $date2 ) {
$format = 'Y-m-d';
return ( $date1->format( $format ) === $date2->format( $format ) );
}
/**
* Are we running a same/not same date validation?
*
* @param string $compare Compare we want to run.
*
* @return bool
*/
private function is_same_date_validation( $compare ) {
return in_array(
$compare,
[
'is_on',
'is_not_on',
],
true
);
}
/**
* Validates if a date is after/before a second date.
*
* @param DateTime $date1 Is date1 before/after.
* @param DateTime $date2 Date2?.
* @param string $comparision after/before.
*
* @return bool
*/
private function validate_before_after_date( $date1, $date2, $comparision ) {
if ( 'after' === $comparision ) {
return ( $date1 > $date2 );
}
return ( $date1 < $date2 );
}
/**
* Are we running a before/after validation?
*
* @param string $compare Compare we want to run.
*
* @return bool
*/
private function is_before_after_validation( $compare ) {
return in_array(
$compare,
[
'is_after',
'is_before',
],
true
);
}
/**
* Validates that our day is of the days in the array.
*
* @param DateTime $date Must be UTC.
* @param array $days_of_week Which days of the week we want to search against.
*
* @return bool
*/
private function validate_is_day_of_week( $date, $days_of_week ) {
// days of the week must be compared in the site's timezone
$date->convert_to_site_time();
$days_of_week = array_map( 'absint', $days_of_week );
return in_array( absint( $date->format( 'N' ) ), $days_of_week, true );
}
/**
* Are we running a day_of_week validation?
*
* @param string $compare Compare we want to run.
*
* @return bool
*/
private function is_days_of_week_validation( $compare ) {
return ( 'days_of_the_week' === $compare );
}
/**
* Validates that a date is between two other dates.
*
* All dates must be in the same timezone.
*
* @param DateTime $date Date that we are checking is between $from and $to
* @param DateTime $from Date we are checking from.
* @param DateTime $to Date we are checking up to.
*
* @return bool
*/
private function validate_is_between_dates( $date, $from, $to ) {
if ( $date < $from ) {
return false;
}
if ( $date > $to ) {
return false;
}
return true;
}
/**
* Are we running a between_dates validation?
*
* @param string $compare Compare we want to run.
*
* @return bool
*/
private function is_between_dates_validation( $compare ) {
return ( 'is_between' === $compare );
}
/**
* Validates that we're passing a correct number of days and we're checking more than 0 days.
*
* @param string $compare What variables we're using to compare.
* @param array|int $value The value to compare.
* @param DateTime|false $date The date used for the comparision. Must be UTC.
*
* @return bool
*/
public function validate_date( $compare, $value, $date ) {
// Make sure that our rule still can run this compare type.
if ( ! array_key_exists( $compare, $this->compare_types ) ) {
return false;
}
// If we have no date, pass to separate validation method
if ( empty( $date ) ) {
return $this->validate_logical_empty_date( $compare );
}
// normalize date even though it should already be an AutomateWoo\DateTime instance
$date = aw_normalize_date( $date );
// Validate && sanitize values.
if ( is_array( $value ) ) {
$rule_timeframe = ( ! empty( $value['timeframe'] ) ) ? absint( $value['timeframe'] ) : 0;
$rule_measure = ( ! empty( $value['measure'] ) && 'days' === $value['measure'] ) ? $value['measure'] : 'hours';
$rule_date = ( ! empty( $value['date'] ) ) ? $value['date'] : '';
$rule_days_of_week = ( ! empty( $value['dow'] ) ) ? $value['dow'] : [];
$rule_from = ( ! empty( $value['from'] ) ) ? $value['from'] : '';
$rule_to = ( ! empty( $value['to'] ) ) ? $value['to'] : '';
} else {
$rule_timeframe = absint( $value );
$rule_measure = 'hours';
$rule_date = '';
$rule_days_of_week = [];
$rule_from = '';
$rule_to = '';
}
// Verify that the date is set.
if ( $compare === 'is_set' ) {
return $date !== false;
}
// Date diff. past/future.
if ( $this->is_past_future_validation( $compare ) ) {
if ( ! $rule_timeframe ) {
return false;
}
return $this->validate_date_diff( $date, $compare, $rule_timeframe, $rule_measure );
}
// Before/After date.
if ( $this->is_before_after_validation( $compare ) ) {
if ( ! $rule_date ) {
return false;
}
$rule_date = new DateTime( $rule_date );
$comparative = 'before';
if ( 'is_after' === $compare ) {
$comparative = 'after';
// exclude the current day from after comparisons
$rule_date->set_time_to_day_end();
}
// Because this date value is set in the admin it is logically in site's timezone
// Therefore we must convert it to UTC for the comparison
$rule_date->convert_to_utc_time();
return $this->validate_before_after_date( $date, $rule_date, $comparative );
}
// Is/Is Not on same date.
if ( $this->is_same_date_validation( $compare ) ) {
if ( ! $rule_date ) {
return false;
}
$rule_date = new DateTime( $rule_date );
// We must consider that the dates are from the user perspective in this case
// So do the comparison in the site's timezone
$date->convert_to_site_time();
if ( 'is_on' === $compare ) {
return $this->validate_same_date( $date, $rule_date );
}
if ( 'is_not_on' === $compare ) {
return ! $this->validate_same_date( $date, $rule_date );
}
}
// Handle day of the week.
if ( $this->is_days_of_week_validation( $compare ) ) {
return $this->validate_is_day_of_week( $date, $rule_days_of_week );
}
// Handle between validation
// This validation is inclusive meaning it starts at 00:00:00 on the 'from date' and ends at 23:59:59 on the 'to date'
if ( $this->is_between_dates_validation( $compare ) ) {
// require at least a from or a to date, if one isn't set it can default to now
if ( ! $rule_from && ! $rule_to ) {
return false;
}
$from = new DateTime( $rule_from );
$to = new DateTime( $rule_to );
// include the full 'to' day in the date range
$to->set_time_to_day_end();
if ( $from > $to ) {
return false;
}
// Because the date values are set in the admin it is logically in site's timezone
// Convert the validation date to site time also
$date->convert_to_site_time();
return $this->validate_is_between_dates( $date, $from, $to );
}
return false;
}
/**
* Attempts to do a more logical validation for empty dates.
*
* This method will ALWAYS validate true when using the comparative 'is_not_in_the_last' or 'is_not_in_the_next'.
* This is because if you have the following rule 'workflow has not run for the customer in the last hour',
* and in fact the workflow has NEVER run for the customer, it's more logical for validation to be true.
*
* Logically VALID comparatives for empty dates are:
* - is_not_set Date is empty so 'is not set' is logically true
* - is_not_in_the_next|is_not_in_the_last Date is empty so it has not happened AT ALL
* - is_not_on Date is empty so it's 'not on' all dates, therefore validates true
*
* Logically INVALID comparatives for empty dates are:
* - is_after|is_before It's not before or after any date
* - is_on It's not 'on' any date
* - is_in_the_next|is_in_the_last Date has not ever happened
* - days_of_the_week Date didn't run on any day
* - is_between Date can't be between any dates.
*
* @param string $comparative The type of comparison.
*
* @return bool
*/
public function validate_logical_empty_date( $comparative ) {
$valid_comparatives = [
'is_not_set',
'is_not_in_the_next',
'is_not_in_the_last',
'is_not_on',
];
$is_valid = in_array( $comparative, $valid_comparatives, true );
return apply_filters( 'automatewoo/rules/validate_logical_empty_date', $is_valid, $comparative, $this );
}
}