Commit inicial - WordPress Análisis de Precios Unitarios

- WordPress core y plugins
- Tema Twenty Twenty-Four configurado
- Plugin allow-unfiltered-html.php simplificado
- .gitignore configurado para excluir wp-config.php y uploads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-11-03 21:04:30 -06:00
commit a22573bf0b
24068 changed files with 4993111 additions and 0 deletions

View File

@@ -0,0 +1,252 @@
<?php
#[AllowDynamicProperties]
class BWFAN_Action_Controller extends BWFAN_Base_Step_Controller {
/** @var BWFAN_Action $action_ins */
private $action_ins = null;
/** @var BWFAN_Event $event_ins */
private $event_ins = null;
/** Sidebar data of node */
private $step_action_data = array();
private $integration = null;
public function populate_step_data( $db_step = array() ) {
if ( parent::populate_step_data( $db_step ) ) {
if ( isset( $this->step_data['sidebarData'] ) ) {
$this->step_action_data = $this->step_data['sidebarData'];
}
return $this->init_action_instance();
}
return false;
}
private function init_action_instance() {
if ( ! is_array( $this->action_data ) || ! isset( $this->action_data['action'] ) ) {
return false;
}
$action_slug = $this->action_data['action'];
$this->action_ins = BWFAN_Core()->integration->get_action( $action_slug );
return $this->action_ins instanceof BWFAN_Action;
}
public function __get( $key ) {
if ( 'call' === $key ) {
return 'wfco_' . $this->action_ins->get_slug();
}
}
public function execute_action() {
if ( ! $this->action_ins instanceof BWFAN_Action ) {
return array(
'status' => BWFAN_Action::$RESPONSE_FAILED,
'message' => __( 'Action is not defined', 'wp-marketing-automations' ),
);
}
$connector_data = $this->set_connectors_data();
if ( ! empty( $connector_data ) ) {
$this->step_action_data['connector_data'] = $connector_data;
}
if ( empty( $this->automation_id ) ) {
return array(
'status' => BWFAN_Action::$RESPONSE_FAILED,
'message' => __( 'Automation ID is not provided', 'wp-marketing-automations' ),
);
}
$this->set_merge_tags_data();
/** Add Automation ID to Unsubscribe Link */
$this->action_ins->automation_id = $this->automation_id;
$this->action_ins->parse_unsubscribe_link();
$this->automation_data['step_id'] = $this->step_id;
$this->automation_data['automation_id'] = $this->automation_id;
/** Generate Processed data and set it to action's instance */
$processed_data = $this->action_ins->make_v2_data( $this->automation_data, $this->step_action_data );
/** Fallback if not available */
if ( ! isset( $processed_data['automation_id'] ) ) {
$processed_data['automation_id'] = $this->automation_id;
}
if ( ! isset( $processed_data['step_id'] ) ) {
$processed_data['step_id'] = $this->step_id;
}
if ( ! isset( $processed_data['current_language'] ) ) {
$processed_data['current_language'] = isset( $this->automation_data['global'] ) && isset( $this->automation_data['global']['language'] ) ? $this->automation_data['global']['language'] : '';
}
$processed_data['automation_contact_id'] = $this->automation_contact_id;
/** Add automation and track id in Abandoned restore link */
add_filter( 'bwfan_abandoned_cart_restore_link', array( $this, 'add_automation_id_track_link_in_restore_url' ) );
try {
/** Process the action and return [status & message] */
$result = $this->process( $processed_data );
} catch ( Error $e ) {
return $this->action_failed( $e->getMessage() );
} catch ( Exception $e ) {
return $this->action_failed( $e->getMessage() );
}
if ( ! is_array( $result ) || ( isset( $result['status'] ) && BWFAN_Action::$RESPONSE_FAILED === $result['status'] ) ) {
$message = is_array( $result ) ? $result['message'] : __( 'Unknown Error Occurred!', 'wp-marketing-automations' );
return $this->action_failed( $message );
}
// Reset automation id
$this->action_ins->automation_id = 0;
return $result;
}
/** Set Connector Data, if available */
private function set_connectors_data() {
$integration = $this->action_ins->get_integration_type();
$this->integration = BWFAN_Core()->integration->get_integration( $integration );
if ( ! $this->integration instanceof BWFAN_Integration || ! $this->integration->need_connector() ) {
return false;
}
$connector = $this->integration->get_connector_slug();
WFCO_Common::get_connectors_data();
$global_settings = WFCO_Common::$connectors_saved_data;
if ( empty( $connector ) || ! isset( $global_settings[ $connector ] ) ) {
return false;
}
$this->integration->set_settings( $global_settings[ $connector ] );
return $global_settings[ $connector ];
}
public function set_merge_tags_data() {
if ( empty( $this->automation_data ) ) {
return false;
}
BWFAN_Merge_Tag_Loader::reset_data();
/** Set automation contact id */
$this->automation_data['global']['automation_cid'] = $this->automation_contact_id;
BWFAN_Merge_Tag_Loader::set_data( $this->automation_data['global'] );
$this->set_user_language();
}
/** Set User Language in Merge Tags */
private function set_user_language() {
$this->maybe_populate_event_instance();
/** Set language for decode */
if ( ! $this->event_ins->support_lang ) {
return false;
}
$language = BWFAN_Merge_Tag_Loader::get_data( 'language' );
if ( empty( $language ) ) {
$language = array(
'language' => $this->event_ins->get_language_from_event( $this->automation_data['global'] ),
);
BWFAN_Merge_Tag_Loader::set_data( $language );
}
}
/** populate event instance if available */
private function maybe_populate_event_instance() {
if ( $this->event_ins instanceof BWFAN_Event ) {
return true;
}
if ( ! is_array( $this->automation_data ) || ! isset( $this->automation_data['event_data'] ) ) {
return false;
}
$event_data = $this->automation_data['event_data'];
if ( ! is_array( $event_data ) || ! isset( $event_data['event_slug'] ) ) {
return false;
}
$event = $event_data['event_slug'];
$this->event_ins = BWFAN_Core()->sources->get_event( $event );
return true;
}
public function process( $processed_data ) {
/** If connector related action */
if ( ! empty( $this->integration ) && $this->integration->need_connector() ) {
$load_connector = WFCO_Load_Connectors::get_instance();
$call_class = $load_connector->get_call( $this->call );
if ( is_null( $call_class ) ) {
return __( 'Call class not found!', 'wp-marketing-automations' );
}
$call_class->set_data( $processed_data );
try {
$result = $call_class->process();
} catch ( Error $e ) {
return $e;
} catch ( Exception $e ) {
return $e;
}
$result = $this->integration->handle_response( $result, $this->connector, $this->call );
return $this->action_ins->handle_response_v2( $result );
}
/** Direct action execution */
$this->action_ins->reset_data();
$this->action_ins->set_data( $processed_data );
return $this->action_ins->process_v2();
}
/**
* If attempts possible return execution time or failed status
*
* @param $message
*
* @return array
*/
private function action_failed( $message ) {
$this->attempts ++;
$attempt_limit = $this->action_ins->get_action_retry_data();
/** Reset automation id */
$this->action_ins->automation_id = 0;
if ( ! is_array( $attempt_limit ) || ( count( $attempt_limit ) < $this->attempts ) || ! isset( $attempt_limit[ $this->attempts - 1 ] ) ) {
return array(
'status' => BWFAN_Action::$RESPONSE_FAILED,
'message' => $message,
);
}
return array(
'status' => BWFAN_Action::$RESPONSE_REATTEMPT,
'message' => $message,
'e_time' => time() + ( $attempt_limit[ $this->attempts - 1 ] ), // for attempt 2 pass 2nd node element in array
);
}
public function add_automation_id_track_link_in_restore_url( $restore_url ) {
$restore_url = add_query_arg( array(
'automation-id' => $this->automation_id,
), $restore_url );
return $restore_url;
}
}

View File

@@ -0,0 +1,108 @@
<?php
class BWFAN_Add_Contact_To_Automation_Controller {
public $automation_id = '';
public $contact_id = '';
protected $messages = [];
public function __construct( $automation_id, $contact_id ) {
$this->automation_id = intval( $automation_id );
$this->contact_id = intval( $contact_id );
$this->messages = [
'required_data_missing' => __( 'Required data are missing', 'wp-marketing-automations' ),
'automation_data_not_found' => __( 'Automation data not found', 'wp-marketing-automations' ),
'automation_is_not_active' => __( 'Automation is not active', 'wp-marketing-automations' ),
'event_not_found' => __( 'Event not found', 'wp-marketing-automations' ),
'contact_not_found' => __( 'Contact does not exist', 'wp-marketing-automations' ),
'not_added_to_automation' => __( 'Contact not added to automation', 'wp-marketing-automations' ),
'added_to_automation' => __( 'Contact added to automation', 'wp-marketing-automations' ),
'validation_failed' => __( 'Event validation failed', 'wp-marketing-automations' ),
'user_not_found' => __( 'No linked WordPress user for this contact.', 'wp-marketing-automations' )
];
}
public function add_contact_to_automation() {
if ( empty( $this->automation_id ) || empty( $this->contact_id ) ) {
return $this->get_response( 404, 'required_data_missing' );
}
$automation_data = $this->prepare_automation_data();
if ( empty( $automation_data ) ) {
return $this->get_response( 404, 'automation_data_not_found' );
}
if ( isset( $automation_data['code'] ) ) {
return $this->get_response( 404, $automation_data['code'] );
}
/** @var BWFAN_Event $event */
$event = BWFAN_Core()->sources->get_event( $automation_data['event'] );
if ( ! $event instanceof BWFAN_Event ) {
return $this->get_response( 404, 'event_not_found' );
}
$automation_data = $event->get_manually_added_contact_automation_data( $automation_data, $this->contact_id );
if ( isset( $automation_data['status'] ) && 0 === $automation_data['status'] ) {
$message = $automation_data['message'] ?? '';
return $this->get_response( 404, $automation_data['type'], $message );
}
if ( false === $event->validate_v2_event_settings( $automation_data ) ) {
return $this->get_response( 500, 'validation_failed' );
}
$event->global_data = $event->get_event_data( $automation_data );
$event->event_data = $event->get_automation_event_data( $automation_data );
$result = $event->handle_automation_run_v2( $this->automation_id, $automation_data );
return false === $result ? $this->get_response( 500, 'not_added_to_automation' ) : $this->get_response( 200, 'added_to_automation' );
}
public function prepare_automation_data() {
$ins = new BWFAN_Automation_Controller();
$automation_data = $ins->get_automation_data( $this->automation_id );
if ( empty( $automation_data ) ) {
return [];
}
if ( ! isset( $automation_data['status'] ) || 2 === intval( $automation_data['status'] ) ) {
return [ 'code' => 'automation_is_not_active' ];
}
$automation_data = $this->set_or_unset_extra_data( $automation_data );
if ( empty( $automation_data ) ) {
return [];
}
$automation_data = BWFAN_Common::remove_extra_automation_data( $automation_data );
return empty( $automation_data ) ? [] : $automation_data;
}
public function set_or_unset_extra_data( $automation_data ) {
$id = $automation_data['ID'];
$meta = $automation_data['meta'];
$version = $automation_data['v'];
$unset_data = array( 'ID', 'meta', 'v', 'status', 'priority' );
foreach ( $unset_data as $data ) {
if ( isset( $automation_data[ $data ] ) ) {
unset( $automation_data[ $data ] );
}
}
$automation_data['id'] = $id;
$automation_data['version'] = $version;
return array_merge( $automation_data, $meta );
}
protected function get_response( $code, $type = '', $msg = '' ) {
$msg = ! empty( $msg ) ? $msg : ( $this->messages[ $type ] ?? '' );
return [ 'code' => $code, 'message' => $msg ];
}
}

View File

@@ -0,0 +1,71 @@
<?php
#[AllowDynamicProperties]
class BWFAN_Conditional_Controller extends BWFAN_Base_Step_Controller {
protected $rules = array();
public function populate_step_data( $db_step = array() ) {
if ( parent::populate_step_data( $db_step ) ) {
return $this->populate_filters();
}
return false;
}
protected function populate_filters() {
if ( empty( $this->step_data ) ) {
return false;
}
$this->rules = $this->step_data['sidebarData'];
}
public function is_match() {
if ( empty( $this->rules ) || empty( $this->automation_data ) ) {
return false;
}
foreach ( $this->rules as $rule_set ) {
if ( ! is_array( $rule_set ) || empty( $rule_set ) ) {
continue;
}
$rule_set_passed = true;
foreach ( $rule_set as $rule ) {
if ( ! is_array( $rule ) || ! isset( $rule['filter'] ) ) {
continue;
}
$rule_data = $rule;
$rule_class = BWFAN_Core()->rules->get_rule( $rule['filter'] );
if ( empty( $rule_class ) ) {
BWFAN_Core()->rules->load_rules_classes();
$rule_class = BWFAN_Core()->rules->get_rule( $rule['filter'] );
}
if ( ! $rule_class instanceof BWFAN_Rule_Base || ! $rule_class->is_match_v2( $this->automation_data, $rule_data ) ) {
$rule_set_passed = false;
break;
}
}
if ( $rule_set_passed ) {
return true;
}
}
return false;
}
/**
* Get retry options
*
* @return array
*/
public function get_retry_data() {
return array(
HOUR_IN_SECONDS, // 1 hr
6 * HOUR_IN_SECONDS, // 6 hrs
18 * HOUR_IN_SECONDS, // 18 hrs
);
}
}

View File

@@ -0,0 +1,328 @@
<?php
#[AllowDynamicProperties]
class BWFAN_Delay_Controller extends BWFAN_Base_Step_Controller {
public static $DELAY_DURATION = 1;
public static $DELAY_FIXED = 2;
public static $DELAY_CUSTOM_FIELD = 3;
public static $DELAY_VARIABLE = 4;
private $type = 0;
private $data = array();
private $is_contact_timezone = null;
public function populate_step_data( $db_step = array() ) {
if ( ! parent::populate_step_data( $db_step ) || ! isset( $this->step_data['sidebarData']['data'] ) || ! is_array( $this->step_data['sidebarData']['data'] ) || ! isset( $this->step_data['sidebarData']['type'] ) ) {
return false;
}
$this->data = $this->step_data['sidebarData']['data'];
$this->type = absint( $this->step_data['sidebarData']['type'] );
$this->is_contact_timezone = false;
if ( ! isset( $this->data['enable_time_delay'] ) || empty( $this->data['enable_time_delay'] ) ) {
return true;
}
if ( ! isset( $this->data['time_delay'] ) || ! isset( $this->data['time_delay']['time'] ) || empty( $this->data['time_delay']['time'] ) ) {
return true;
}
if ( ! isset( $this->data['time_delay']['in_contact_timezone'] ) || 1 !== absint( $this->data['time_delay']['in_contact_timezone'] ) ) {
return true;
}
$this->is_contact_timezone = true;
return true;
}
public function get_time( $last_time = '' ) {
switch ( $this->type ) {
case self::$DELAY_DURATION:
return $this->get_delay_duration_time( $last_time );
case self::$DELAY_FIXED:
return $this->get_fixed_time();
case self::$DELAY_CUSTOM_FIELD:
return $this->get_custom_field_time();
case self::$DELAY_VARIABLE:
return $this->get_delay_variable_time();
}
return false;
}
public function get_delay_duration_time( $last_time = '' ) {
if ( ! isset( $this->data['day_delay'] ) || ! isset( $this->data['day_delay']['text'] ) ) {
return false;
}
$current_timestamp = current_time( 'timestamp', 1 );
$current_store_timestamp = current_time( 'timestamp' );
/** If diff is negative then UTC is + & diff is positive then UTC is - */
$diff = $current_timestamp - $current_store_timestamp;
$current_timestamp = ! empty( $last_time ) ? $last_time : $current_timestamp;
/** Set general delay */
switch ( $this->data['day_delay']['unit'] ) {
case 'min':
$current_timestamp += MINUTE_IN_SECONDS * (int) $this->data['day_delay']['text'];
break;
case 'hours':
$current_timestamp += HOUR_IN_SECONDS * (int) $this->data['day_delay']['text'];
break;
case 'days':
$current_timestamp += DAY_IN_SECONDS * (int) $this->data['day_delay']['text'];
break;
case 'weeks':
$current_timestamp += WEEK_IN_SECONDS * (int) $this->data['day_delay']['text'];
break;
case 'months':
$current_timestamp += MONTH_IN_SECONDS * (int) $this->data['day_delay']['text'];
break;
}
if ( false === bwfan_is_autonami_pro_active() ) {
return $current_timestamp;
}
/** If no time & day delay set */
if ( empty( $this->data['enable_time_delay'] ) && empty( $this->data['enable_week_delay'] ) ) {
return $current_timestamp;
}
if ( ( ! isset( $this->data['time_delay']['time'] ) || empty( $this->data['time_delay']['time'] ) ) && empty( $this->data['week_delay'] ) ) {
return $current_timestamp;
}
$delay_time = isset( $this->data['time_delay']['time'] ) && ! empty( $this->data['time_delay']['time'] ) ? $this->data['time_delay']['time'] : 0;
$current_modified_real_time = $current_timestamp;
/** timestamp modified to local time */
$current_timestamp = $current_timestamp - $diff;
/** Get time according to contact's timezone */
$datetime = $this->get_contact_time( $current_timestamp, $delay_time );
/** No days delay found */
if ( empty( $this->data['week_delay'] ) ) {
/** If older time */
if ( $datetime->getTimestamp() - $current_modified_real_time < 0 ) {
return $datetime->getTimestamp() + DAY_IN_SECONDS;
}
return $datetime->getTimestamp();
}
/** Days delay functioning */
if ( is_array( $this->data['week_delay'] ) && count( $this->data['week_delay'] ) > 0 ) {
for ( $h = 0; $h < 7; $h ++ ) {
/** 1 - Sunday | 2 - Monday */
$current_day = ( intval( $datetime->format( "N" ) ) === 7 ) ? 1 : ( intval( $datetime->format( "N" ) ) + 1 );
if ( in_array( $current_day, $this->data['week_delay'] ) ) {
/** If future time then return else +1 day */
if ( $current_modified_real_time < $datetime->getTimestamp() ) {
return $datetime->getTimestamp();
}
}
$datetime->modify( '+1 days' );
}
}
return $datetime->getTimestamp();
}
public function get_contact_time( $current_timestamp, $delay_time ) {
$timezone = new DateTimeZone( wp_timezone_string() );
$datetime = new DateTime( date( 'Y-m-d H:i:s', $current_timestamp ), $timezone );
/** If time type is not full date time and delay time is empty */
if ( empty( $delay_time ) && ( ! isset( $this->data['occurrence'] ) || 'fulldatetime' !== $this->data['occurrence'] ) ) {
return $datetime;
}
/** Time delay functioning */
if ( $this->is_contact_timezone && ! empty( $this->contact_id ) ) {
$contact = new WooFunnels_Contact( '', '', '', absint( $this->contact_id ) );
if ( absint( $contact->get_id() ) > 0 && ! empty( $contact->get_timezone() ) ) {
try {
$timezone = new DateTimeZone( $contact->get_timezone() );
} catch ( Exception $e ) {
BWFAN_Common::log_test_data( 'Contact timezone error for contact #' . $contact->get_id() . '. Timezone . ' . $contact->get_timezone() . '. Error: ' . $e->getMessage(), 'delay-time', true );
}
}
$datetime = new DateTime( date( 'Y-m-d H:i:s', $current_timestamp ), $timezone );
}
/** If time type is full date time or delay time is empty */
if ( ( isset( $this->data['occurrence'] ) && 'fulldatetime' === $this->data['occurrence'] ) || empty( $delay_time ) ) {
return $datetime;
}
sscanf( $delay_time, '%d:%d', $hours, $minutes );
$datetime->setTime( $hours, $minutes, 0 );
return $datetime;
}
public function get_fixed_time() {
if ( false === bwfan_is_autonami_pro_active() ) {
return current_time( 'timestamp', 1 );
}
/** Store timezone */
$timezone = new DateTimeZone( wp_timezone_string() );
$enable_user_timezone = ( isset( $this->data['time_delay']['in_contact_timezone'] ) && 1 === intval( $this->data['time_delay']['in_contact_timezone'] ) );
if ( true === $enable_user_timezone && ! empty( $this->contact_id ) ) {
/** User timezone */
$contact = new WooFunnels_Contact( '', '', '', absint( $this->contact_id ) );
if ( absint( $contact->get_id() ) > 0 && ! empty( $contact->get_timezone() ) ) {
try {
$timezone = new DateTimeZone( $contact->get_timezone() );
} catch ( Exception $e ) {
BWFAN_Common::log_test_data( [ 'cid' => $contact->get_id(), 'timezone' => $contact->get_timezone(), 'msg' => $e->getMessage() ], 'contact-timezone-error' );
}
}
}
$date = new DateTime();
$date->setTimezone( $timezone );
sscanf( $this->data['date'], '%d/%d/%d', $months, $days, $years );
$date->setDate( $years, $months, $days );
sscanf( $this->data['time_delay']['time'], '%d:%d', $hours, $minutes );
$date->setTime( $hours, $minutes, 0 );
return $date->getTimestamp();
}
public function get_custom_field_time() {
$current_timestamp = current_time( 'timestamp', 1 );
if ( false === bwfan_is_autonami_pro_active() ) {
return $current_timestamp;
}
if ( empty( $this->contact_id ) || ! class_exists( 'BWFCRM_Contact' ) ) {
return false;
}
if ( ! isset( $this->data['custom_field'] ) || empty( $this->data['custom_field'] ) ) {
return $current_timestamp;
}
$contact = new BWFCRM_Contact( $this->contact_id );
if ( ! $contact->is_contact_exists() || empty( $contact->fields ) ) {
return false;
}
$field_id = isset( $this->data['custom_field']['field'] ) ? $this->data['custom_field']['field'] : 0;
$field_val = isset( $contact->fields[ absint( $field_id ) ] ) ? $contact->fields[ absint( $field_id ) ] : 0;
if ( empty( $field_val ) ) {
return $current_timestamp;
}
if ( isset( $this->data['custom_field']['in_contact_timezone'] ) && ! empty( $this->data['custom_field']['in_contact_timezone'] ) ) {
$this->is_contact_timezone = true;
}
$delay_time = '';
if ( $this->data['occurrence'] !== 'fulldatetime' ) {
$delay_time = empty( $this->data['custom_field']['time'] ) ? '10:00' : $this->data['custom_field']['time'];
}
/** Get time according to contact's timezone */
$datetime = $this->get_contact_time( strtotime( $field_val ), $delay_time );
/** Get delay setting for certain days from field date */
$enable_delay = isset( $this->data['enable_time_delay'] ) ? absint( $this->data['enable_time_delay'] ) : 0;
if ( ! empty( $enable_delay ) && isset( $this->data['time_delay']['timing'] ) && isset( $this->data['time_delay']['type'] ) && isset( $this->data['time_delay']['duration'] ) ) {
$operator = $this->data['time_delay']['timing'];
$operator = ( 'before' === $operator ) ? '-' : '+';
$type = $this->data['time_delay']['type'];
$duration = absint( $this->data['time_delay']['duration'] );
if ( ! empty( $operator ) && ! empty( $duration ) && ! empty( $type ) ) {
$datetime->modify( "$operator $duration $type" );
}
}
/** checking for daymonth and then adding 1 year to date if time has passed */
if ( isset( $this->data['occurrence'] ) && 'daymonth' === $this->data['occurrence'] ) {
$datetime->setDate( date( 'Y', $current_timestamp ), $datetime->format( 'm' ), $datetime->format( 'd' ) );
if ( $datetime->getTimestamp() < $current_timestamp ) {
$datetime->modify( '+1 year' );
}
}
return $datetime->getTimestamp();
}
public function get_delay_variable_time() {
$current_timestamp = current_time( 'timestamp', 1 );
if ( false === bwfan_is_autonami_pro_active() ) {
return $current_timestamp;
}
if ( ! isset( $this->data['variable_field'] ) || empty( $this->data['variable_field'] ) ) {
BWFAN_Common::log_test_data( 'Delay variable field is not set ' . $current_timestamp, 'delay-time', true );
BWFAN_Common::log_test_data( $this->data, 'delay-time', true );
return $current_timestamp;
}
if ( ! isset( $this->data['variable_field']['variable'] ) || empty( $this->data['variable_field']['variable'] ) ) {
BWFAN_Common::log_test_data( 'Delay variable is empty ' . $current_timestamp, 'delay-time', true );
BWFAN_Common::log_test_data( $this->data, 'delay-time', true );
return $current_timestamp;
}
/** Reset the old merge tag data if any and set the current one and decoding accordingly */
BWFAN_Merge_Tag_Loader::reset_data();
BWFAN_Merge_Tag_Loader::set_data( $this->automation_data['global'] );
/** Set type and output format(o_format) in mergetag */
$delay_variable = str_replace( '}}', " from='delay' type='date'}}", $this->data['variable_field']['variable'] );
$delay_variable_date = BWFAN_Common::decode_merge_tags( $delay_variable );
/** Return current time if date value is empty */
if ( empty( $delay_variable_date ) || false === strtotime( $delay_variable_date ) ) {
BWFAN_Common::log_test_data( 'Date value is empty ' . $current_timestamp, 'delay-time', true );
BWFAN_Common::log_test_data( $this->data, 'delay-time', true );
return $current_timestamp;
}
if ( isset( $this->data['variable_field']['in_contact_timezone'] ) && ! empty( $this->data['variable_field']['in_contact_timezone'] ) ) {
$this->is_contact_timezone = true;
}
$delay_time = empty( $this->data['variable_field']['time'] ) ? '10:00' : $this->data['variable_field']['time'];
/** Get time according to contact's timezone */
$datetime = $this->get_contact_time( strtotime( $delay_variable_date ), $delay_time );
/** Get delay setting for certain days from delay variable date */
$enable_delay = isset( $this->data['enable_time_delay'] ) ? absint( $this->data['enable_time_delay'] ) : 0;
if ( ! empty( $enable_delay ) && isset( $this->data['time_delay']['timing'] ) && isset( $this->data['time_delay']['type'] ) && isset( $this->data['time_delay']['duration'] ) ) {
$operator = $this->data['time_delay']['timing'];
$operator = ( 'before' === $operator ) ? '-' : '+';
$type = $this->data['time_delay']['type'];
$duration = absint( $this->data['time_delay']['duration'] );
if ( ! empty( $operator ) && ! empty( $duration ) && ! empty( $type ) ) {
$datetime->modify( "$operator $duration $type" );
}
}
/** checking for daymonth and then adding 1 year to date if time has passed */
if ( isset( $this->data['occurrence'] ) && 'daymonth' === $this->data['occurrence'] ) {
$datetime->setDate( date( 'Y', $current_timestamp ), $datetime->format( 'm' ), $datetime->format( 'd' ) );
if ( $datetime->getTimestamp() < $current_timestamp ) {
$datetime->modify( '+1 year' );
}
}
return $datetime->getTimestamp();
}
}

View File

@@ -0,0 +1,73 @@
<?php
class BWFAN_Generic_Rule_Controller {
protected $data = [];
protected $rules = [];
public function __construct( $rules, $data = [] ) {
$this->set_data( $data );
$this->rules = $rules;
}
public function set_data( $data ) {
if ( empty( $data ) ) {
$data = array_merge( BWFAN_Merge_Tag_Loader::get_data() );
$data = [ 'global' => $data ];
}
/** If cid is not available in data */
if ( ! isset( $data['global']['cid'] ) && isset( $data['global']['contact_id'] ) ) {
$data['global']['cid'] = intval( $data['global']['contact_id'] );
}
/** Set email if not available */
if ( ! isset( $data['global']['email'] ) && isset( $data['global']['contact_id'] ) ) {
$contact = new BWFCRM_Contact( $data['global']['contact_id'] );
$data['global']['email'] = $contact->get_id() > 0 ? $contact->contact->get_email() : '';
}
$this->data = $data;
}
/**
* Validate rules
*
* @return bool
*/
public function is_match() {
if ( empty( $this->rules ) || empty( $this->data ) ) {
return false;
}
/** No need to validate rule if preview or if is broadcast */
if ( ( isset( $this->data['global']['is_preview'] ) && 1 === intval( $this->data['global']['is_preview'] ) ) || ( isset( $this->data['global']['broadcast_id'] ) && intval( $this->data['global']['broadcast_id'] ) > 0 ) ) {
return true;
}
foreach ( $this->rules as $rule_set ) {
if ( ! is_array( $rule_set ) || empty( $rule_set ) ) {
continue;
}
$rule_set_passed = true;
foreach ( $rule_set as $rule ) {
if ( ! is_array( $rule ) || ! isset( $rule['filter'] ) ) {
continue;
}
$rule_data = $rule;
$rule = BWFAN_Core()->rules->get_rule( $rule['filter'] );
if ( ! $rule instanceof BWFAN_Rule_Base || ! $rule->is_match_v2( $this->data, $rule_data ) ) {
$rule_set_passed = false;
break;
}
}
if ( $rule_set_passed ) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,303 @@
<?php
#[AllowDynamicProperties]
class BWFAN_Goal_Controller extends BWFAN_Base_Step_Controller {
/** Props - Global usage */
public static $TRAVERSE_SETTING_CONTINUE = 'continue';
public static $TRAVERSE_SETTING_WAIT = 'wait';
public static $TRAVERSE_SETTING_END = 'end';
/** Captured goal event data */
public static $captured_data = [];
public $traverse_setting = 'continue';
public $data = null;
public $goal_steps = [];
public $update_status = false;
/**
* Goal related events, end controller function
* Used by BWFAN_Common extend_async_capture method
*
* @param BWFAN_Event $event
* @param array $post_parameters
*/
public static function capture_async_goal( $event, $post_parameters ) {
/** @todo maybe create AS action and run async */
self::$captured_data = $post_parameters;
/** Get Contact ID */
$contact_id = $event->get_contact_id_for_goal( $post_parameters );
if ( empty( $contact_id ) ) {
return false;
}
/** Get Active Automations for current goal */
$automations = $event->get_current_goal_automations();
if ( empty( $automations ) ) {
return false;
}
/** Check if contact is in those automations */
$automation_contact_rows = self::filter_goal_automations_contact( $automations, $contact_id );
if ( empty( $automation_contact_rows ) ) {
return false;
}
/** Process each automation */
foreach ( $automation_contact_rows as $automation_contact ) {
$automation = BWFAN_Model_Automations::get_automation_with_data( $automation_contact['aid'] );
if ( empty( $automation ) ) {
continue;
}
$controller = new BWFAN_Goal_Controller();
$controller->automation_id = absint( $automation_contact['aid'] );
$controller->contact_id = absint( $contact_id );
$controller->process_goal( $automation, $event, $automation_contact, self::$captured_data );
}
self::$captured_data = array();
}
/**
* Filter goal automations if contact is active in them
*
* @param $automations
* @param $contact_id
*
* @return array automation contact row
*/
public static function filter_goal_automations_contact( $automations, $contact_id ) {
global $wpdb;
if ( empty( $automations ) || empty( $contact_id ) ) {
return [];
}
$string_placeholder = array_fill( 0, count( $automations ), '%d' );
$placeholder = implode( ', ', $string_placeholder );
$data = array_merge( [ $contact_id ], $automations );
$query = $wpdb->prepare( "SELECT * FROM `{$wpdb->prefix}bwfan_automation_contact` WHERE `cid` = %d AND `aid` IN ($placeholder) AND `status` IN (1,4,6)", $data );
return $wpdb->get_results( $query, ARRAY_A ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
}
/**
* @param array $automation Automation data
* @param BWFAN_Event $event
* @param array $automation_contact Automation contact row
* @param array $post_parameters
*
* @return void
*/
public function process_goal( $automation, $event, $automation_contact, $post_parameters ) {
/** Set default values */
$this->traverse_setting = 'continue';
$this->data = null;
$this->goal_steps = [];
$this->update_status = false;
/** Fetching steps trail */
$trail_id = $automation_contact['trail'];
if ( empty( $trail_id ) ) {
$trail_id = md5( $automation_contact['ID'] . $automation_contact['cid'] . $automation_contact['c_date'] );
$automation_contact['trail'] = $trail_id;
BWFAN_Model_Automation_Contact::update( array(
'trail' => $trail_id,
), array(
'ID' => $automation_contact['ID'],
) );
}
/** Steps and Links not found */
if ( ! is_array( $automation['meta'] ) || ! is_array( $automation['meta']['steps'] ) || ! is_array( $automation['meta']['links'] ) ) {
return;
}
BWFAN_Common::log_l2_data( 'Trail id: ' . $trail_id, 'goal-check' );
/** Again Checking if the goal and it's step_id exists */
$goal_step_ids = $this->get_goal_step_ids( $this->automation_id, $event->get_slug() );
if ( empty( $goal_step_ids ) ) {
BWFAN_Common::log_l2_data( 'no goal step ids found', 'goal-check' );
return;
}
$this->step_id = ( 0 === absint( $automation_contact['last'] ) ) ? $automation['start'] : $automation_contact['last'];
BWFAN_Common::log_l2_data( 'Last run: ' . $this->step_id, 'goal-check' );
BWFAN_Common::log_l2_data( 'Goal step ids: ' . implode( ', ', $goal_step_ids ), 'goal-check' );
$traverse_ins = new BWFAN_Traversal_Controller();
$traverse_ins->set_steps( $automation['meta']['steps'] );
$traverse_ins->set_links( $automation['meta']['links'] );
$traverse_ins->set_node_id_from_step_id( $this->step_id );
$traverse_ins->automation_id = $this->automation_id;
$traverse_ins->contact_id = $this->contact_id;
/** Try to traverse the goal, if unable to traverse, then return false */
$traversed_goal_node_ids = $traverse_ins->try_traverse_to_goal( $goal_step_ids, $this, $automation_contact );
if ( empty( $traversed_goal_node_ids ) ) {
return;
}
$steps_trail = BWFAN_Model_Automation_Contact_Trail::get_trail( $trail_id );
/** Validate found goal settings */
foreach ( $traversed_goal_node_ids as $node_id ) {
$step_id = array_search( $node_id, $this->goal_steps );
if ( empty( $step_id ) || 1 > intval( $step_id ) ) {
continue;
}
$step_data = BWFAN_Model_Automation_Step::get_step_data_by_id( $step_id );
/** Get Benchmark Step Data */
$this->step_id = $step_id;
$this->populate_step_data( $step_data );
if ( ! $event->validate_goal_settings( $this->data, $post_parameters ) ) {
/** Unable to pass the goal settings */
BWFAN_Common::log_l2_data( 'Goal settings failed for step id ' . $step_id, 'goal-check' );
continue;
}
BWFAN_Common::log_l2_data( 'Goal achieved: step id ' . $step_id, 'goal-check' );
/** Update existing steps trail status if needed or insert */
$this->maybe_update_trail_status( $steps_trail );
$this->maybe_add_goal_step_trail( $step_id, $steps_trail, $automation_contact );
/** Goal step passed, update automation contact status to active */
BWFAN_Model_Automation_Contact::update( array(
'last' => $step_id,
'attempts' => 0,
'status' => BWFAN_Automation_Controller::$STATUS_ACTIVE,
'last_time' => current_time( 'timestamp', 1 ),
'e_time' => current_time( 'timestamp', 1 ),
), array(
'ID' => $automation_contact['ID'],
) );
}
}
/**
* Get goals step ids in an automation
*
* @param $automation_id
* @param $event_slug
*
* @return array|int
*/
public function get_goal_step_ids( $automation_id, $event_slug ) {
if ( empty( $automation_id ) || empty( $event_slug ) ) {
return 0;
}
global $wpdb;
$query = $wpdb->prepare( "SELECT `ID` FROM `{$wpdb->prefix}bwfan_automation_step` WHERE `aid` = %d AND `type` = 3 AND `status` IN (0, 1) AND `action` LIKE %s", $automation_id, "%{$event_slug}%" );
$step_ids = $wpdb->get_col( $query ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
if ( empty( $step_ids ) ) {
return [];
}
return ! empty( $step_ids ) ? $step_ids : [];
}
/**
* Set step data
*
* @param array $db_step step data
*
* @return bool
*/
public function populate_step_data( $db_step = array() ) {
if ( parent::populate_step_data( $db_step ) && isset( $this->step_data['sidebarData'] ) && is_array( $this->step_data['sidebarData'] ) ) {
$this->data = $this->step_data['sidebarData'];
$this->traverse_setting = $this->data['bwfan_goal_run'];
return true;
}
return false;
}
/**
* Maybe steps trail contains a step with status 2. Update that to 1.
* Run once only
*
* array $steps_trail current contact steps trail
*
* @return void
*/
public function maybe_update_trail_status( $steps_trail ) {
if ( true === $this->update_status ) {
return;
}
/** Filter steps with status 2 */
$filtered = array_filter( $steps_trail, function ( $row ) {
if ( 2 === absint( $row['status'] ) ) {
return true;
}
return false;
} );
/** If empty */
if ( ! is_array( $filtered ) || 0 === count( $filtered ) ) {
$this->update_status = true;
return;
}
$filtered = array_column( $filtered, 'ID' );
BWFAN_Common::log_l2_data( __FUNCTION__ . ' filtered', 'goal-update-trail-status' );
BWFAN_Common::log_l2_data( implode( ', ', $filtered ), 'goal-update-trail-status' );
global $wpdb;
$string_placeholder = array_fill( 0, count( $filtered ), '%d' );
$placeholder = implode( ', ', $string_placeholder );
/** Update status and time */
$data = array_merge( [ 1, time() ], $filtered );
$query = $wpdb->prepare( "UPDATE `{$wpdb->prefix}bwfan_automation_contact_trail` SET `status` = %d, `c_time` = %d WHERE `ID` IN ($placeholder)", $data );
$wpdb->query( $query ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$this->update_status = true;
}
/**
* Maybe add the goal step trail
*
* @param int $goal_step_id
* @param array $steps_trail current contact steps trail
* @param array $automation_contact automation contact row
*
* @return int|void
*/
public function maybe_add_goal_step_trail( $goal_step_id, $steps_trail, $automation_contact ) {
/** Filter steps if a goal step is already in wait */
$filtered = array_filter( $steps_trail, function ( $row ) use ( $goal_step_id ) {
$data = ! empty( $row['data'] ) ? json_decode( $row['data'], true ) : [];
return ( ! empty( $data ) && isset( $data['msg'] ) && 'wait' === $data['msg'] && $goal_step_id === intval( $row['sid'] ) );
} );
if ( is_array( $filtered ) && count( $filtered ) > 0 ) {
return;
}
/** Add a goal step trail */
$arr = array(
'tid' => $automation_contact['trail'],
'cid' => $automation_contact['cid'],
'aid' => $automation_contact['aid'],
'sid' => $goal_step_id,
'c_time' => time(),
'status' => BWFAN_Automation_Controller::$STATUS_ACTIVE,
);
BWFAN_Model_Automation_Contact_Trail::insert( $arr );
return BWFAN_Model_Automation_Contact_Trail::insert_id();
}
}

View File

@@ -0,0 +1,25 @@
<?php
#[AllowDynamicProperties]
class BWFAN_Jump_Controller extends BWFAN_Base_Step_Controller {
private $jump_step_id = '';
private $jump_step_name = '';
public function populate_step_data( $db_step = array() ) {
if ( ! parent::populate_step_data( $db_step ) || ! isset( $this->step_data['sidebarData']['jump_to']['step'] ) ) {
return false;
}
$this->jump_step_id = $this->step_data['sidebarData']['jump_to']['step'];
$this->jump_step_name = $this->step_data['sidebarData']['jump_to']['name'];
}
public function get_jump_step_id() {
return $this->jump_step_id;
}
public function get_jump_step_name() {
return $this->jump_step_name;
}
}

View File

@@ -0,0 +1,551 @@
<?php
#[AllowDynamicProperties]
class BWFAN_Recipe_Dependency {
protected $data = "Required data is missing ";
protected $err_msg = null;
public function set_data( $data ) {
$this->data = $data;
}
public function get_error_message() {
return $this->err_msg;
}
public function validate() {
$response = $this->check_version();
if ( false === $response ) {
return $this->get_error_message();
}
if ( ! isset( $this->data['function'] ) || empty( $this->data['function'] ) ) {
return true;
}
$functions = $this->data['function'];
foreach ( $functions as $function ) {
$response = call_user_func( [ $this, $function ] );
if ( false === $response ) {
return $this->get_error_message();
}
}
return true;
}
public function check_version() {
$version = isset( $this->data['version'] ) ? $this->data['version'] : [];
if ( empty( $version ) ) {
return true;
}
if ( isset( $version['lite'] ) && version_compare( BWFAN_VERSION, $version['lite'], "<" ) ) {
$this->err_msg = sprintf( __( 'FunnelKit Automations version should be greater than or equal to %s.', 'wp-marketing-automations' ), $version['lite'] ); // phpcs:ignore WordPress.WP.I18n.MissingTranslatorsComment
return false;
}
if ( ! isset( $version['pro'] ) ) {
return true;
}
if ( ! bwfan_is_autonami_pro_active() ) {
$this->err_msg = __( 'FunnelKit Automations Pro plugin is not active.', 'wp-marketing-automations' );
return false;
}
if ( isset( $version['pro'] ) && version_compare( BWFAN_PRO_VERSION, $version['pro'], "<" ) ) {
$this->err_msg = sprintf( __( 'FunnelKit Automations Pro version should be greater than or equal to %s.', 'wp-marketing-automations' ), $version['pro'] ); // phpcs:ignore WordPress.WP.I18n.MissingTranslatorsComment
return false;
}
return true;
}
/** Autonami Pro */
public function plugin_autonami_pro_active() {
if ( ! bwfan_is_autonami_pro_active() ) {
$this->err_msg = __( 'FunnelKit Automations Pro plugin is not active.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** WooCommerce */
public function plugin_wc_active() {
if ( ! bwfan_is_woocommerce_active() ) {
$this->err_msg = __( 'WooCommerce plugin is not active.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** WooCommerce Subscription */
public function plugin_wcs_active() {
if ( ! bwfan_is_woocommerce_subscriptions_active() ) {
$this->err_msg = __( 'WooCommerce Subscription plugin is not active.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** Learndash */
public function plugin_ld_active() {
if ( ! bwfan_is_learndash_active() ) {
$this->err_msg = __( 'Learndash plugin is not active.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** Paid Membership Pro */
public function plugin_pm_pro_active() {
if ( ! bwfan_is_paid_membership_pro_active() ) {
$this->err_msg = __( 'Paid Membership Pro plugin is not active.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** Tutor LMS */
public function plugin_tutor_lms_active() {
if ( ! bwfan_is_tutorlms_active() ) {
$this->err_msg = __( 'Tutor LMS plugin is not active.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** MemberPress */
public function plugin_mepr_active() {
if ( ! bwfan_is_mepr_active() ) {
$this->err_msg = __( 'MemberPress plugin is not active.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** Wishlist Member */
public function plugin_wlm_active() {
if ( ! bwfan_is_wlm_active() ) {
$this->err_msg = __( 'Wishlist Member plugin is not active.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** AffiliateWP */
public function plugin_affwp_active() {
if ( ! bwfan_is_affiliatewp_active() ) {
$this->err_msg = __( 'AffiliateWP plugin is not active.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** WPForms */
public function plugin_wpforms_active() {
if ( ! bwfan_is_wpforms_active() ) {
$this->err_msg = __( 'WPForms plugin is not active.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** Elementor Forms */
public function plugin_elementor_forms_active() {
if ( ! bwfan_is_elementorpro_active() ) {
$this->err_msg = __( 'Elementor plugin is not active.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** Gravity Forms */
public function plugin_gforms_active() {
if ( ! bwfan_is_gforms_active() ) {
$this->err_msg = __( 'Gravity Forms plugin is not active.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** Thrive Leads */
public function plugin_tve_active() {
if ( ! bwfan_is_tve_active() ) {
$this->err_msg = __( 'Thrive Leads plugin is not active.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** Divi Forms */
public function plugin_divi_forms_active() {
if ( ! bwfan_is_divi_forms_active() ) {
$this->err_msg = __( 'Divi theme or plugin is not active.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** Formidable Forms */
public function plugin_formidable_form_active() {
if ( ! bwfan_is_formidable_forms_active() ) {
$this->err_msg = __( 'Formidable Forms plugin is not active.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** WooCommerce Membership */
public function plugin_wc_membership_active() {
if ( ! bwfan_is_woocommerce_membership_active() ) {
$this->err_msg = __( 'WooCommerce Membership plugin is not active.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** FunnelKit Funnel Builder */
public function plugin_woofunnels_fb_active() {
if ( ! function_exists( 'bwfan_is_funnel_optin_forms_active' ) || ! bwfan_is_funnel_optin_forms_active() ) {
$this->err_msg = __( 'FunnelKit Funnel Builder plugin is not active.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** FunnelKit Funnel Builder Pro */
public function plugin_woofunnels_fb_pro_active() {
if ( ! defined( 'WFFN_PRO_BUILD_VERSION' ) ) {
$this->err_msg = __( 'FunnelKit Funnel Builder Pro plugin is not active.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** Autonami Cart tracking */
public function setting_cart_tracking_enabled() {
$global_settings = BWFAN_Common::get_global_settings();
if ( ! isset( $global_settings['bwfan_ab_enable'] ) || empty( $global_settings['bwfan_ab_enable'] ) ) {
$this->err_msg = __( 'Cart tracking is not active.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** Slack Connector */
public function connector_slack_active() {
if ( ! bwfan_is_autonami_connector_active() ) {
$this->err_msg = __( 'FunnelKit Automations Connector plugin is not active.', 'wp-marketing-automations' );
return false;
}
$is_connected = BWFAN_Core()->connectors->is_connected( 'bwfco_slack' );
if ( empty( $is_connected ) ) {
$this->err_msg = __( 'Slack connector is not integrated.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** ActiveCampaign Connector */
public function connector_ac_active() {
if ( ! bwfan_is_autonami_connector_active() ) {
$this->err_msg = __( 'FunnelKit Automations Connector plugin is not active.', 'wp-marketing-automations' );
return false;
}
$is_connected = BWFAN_Core()->connectors->is_connected( 'bwfco_activecampaign' );
if ( empty( $is_connected ) ) {
$this->err_msg = __( 'ActiveCampaign connector is not integrated.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** Mailchimp Connector */
public function connector_mailchimp_active() {
if ( ! bwfan_is_autonami_connector_active() ) {
$this->err_msg = __( 'FunnelKit Automations Connector plugin is not active.', 'wp-marketing-automations' );
return false;
}
$is_connected = BWFAN_Core()->connectors->is_connected( 'bwfco_mailchimp' );
if ( empty( $is_connected ) ) {
$this->err_msg = __( 'Mailchimp connector is not integrated.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** GetResponse Connector */
public function connector_getresponse_active() {
if ( ! bwfan_is_autonami_connector_active() ) {
$this->err_msg = __( 'FunnelKit Automations Connector plugin is not active.', 'wp-marketing-automations' );
return false;
}
$is_connected = BWFAN_Core()->connectors->is_connected( 'bwfco_getresponse' );
if ( empty( $is_connected ) ) {
$this->err_msg = __( 'GetResponse connector is not integrated.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** Ontraport Connector */
public function connector_ontraport_active() {
if ( ! bwfan_is_autonami_connector_active() ) {
$this->err_msg = __( 'FunnelKit Automations Connector plugin is not active.', 'wp-marketing-automations' );
return false;
}
$is_connected = BWFAN_Core()->connectors->is_connected( 'bwfco_ontraport' );
if ( empty( $is_connected ) ) {
$this->err_msg = __( 'Ontraport connector is not integrated.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** Hubspot Connector */
public function connector_hubspot_active() {
if ( ! bwfan_is_autonami_connector_active() ) {
$this->err_msg = __( 'FunnelKit Automations Connector plugin is not active.', 'wp-marketing-automations' );
return false;
}
$is_connected = BWFAN_Core()->connectors->is_connected( 'bwfco_hubspot' );
if ( empty( $is_connected ) ) {
$this->err_msg = __( 'Hubspot connector is not integrated.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** Klaviyo Connector */
public function connector_klaviyo_active() {
if ( ! bwfan_is_autonami_connector_active() ) {
$this->err_msg = __( 'FunnelKit Automations Connector plugin is not active.', 'wp-marketing-automations' );
return false;
}
$is_connected = BWFAN_Core()->connectors->is_connected( 'bwfco_klaviyo' );
if ( empty( $is_connected ) ) {
$this->err_msg = __( 'Klaviyo connector is not integrated.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** ConvertKit Connector */
public function connector_convertkit_active() {
if ( ! bwfan_is_autonami_connector_active() ) {
$this->err_msg = __( 'FunnelKit Automations Connector plugin is not active.', 'wp-marketing-automations' );
return false;
}
$is_connected = BWFAN_Core()->connectors->is_connected( 'bwfco_convertkit' );
if ( empty( $is_connected ) ) {
$this->err_msg = __( 'Kit ( Formerly ConvertKit ) connector is not integrated.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** Drip Connector */
public function connector_drip_active() {
if ( ! bwfan_is_autonami_connector_active() ) {
$this->err_msg = __( 'FunnelKit Automations Connector plugin is not active.', 'wp-marketing-automations' );
return false;
}
$is_connected = BWFAN_Core()->connectors->is_connected( 'bwfco_drip' );
if ( empty( $is_connected ) ) {
$this->err_msg = __( 'Drip connector is not integrated.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** Keap Connector */
public function connector_keap_active() {
if ( ! bwfan_is_autonami_connector_active() ) {
$this->err_msg = __( 'FunnelKit Automations Connector plugin is not active.', 'wp-marketing-automations' );
return false;
}
$is_connected = BWFAN_Core()->connectors->is_connected( 'bwfco_keap' );
if ( empty( $is_connected ) ) {
$this->err_msg = __( 'Infusionsoft by Keap connector is not integrated.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** MailerLite Connector */
public function connector_mailerlite_active() {
if ( ! bwfan_is_autonami_connector_active() ) {
$this->err_msg = __( 'FunnelKit Automations Connector plugin is not active.', 'wp-marketing-automations' );
return false;
}
$is_connected = BWFAN_Core()->connectors->is_connected( 'bwfco_mailerlite' );
if ( empty( $is_connected ) ) {
$this->err_msg = __( 'MailerLite connector is not integrated.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** Mautic Connector */
public function connector_mautic_active() {
if ( ! bwfan_is_autonami_connector_active() ) {
$this->err_msg = __( 'FunnelKit Automations Connector plugin is not active.', 'wp-marketing-automations' );
return false;
}
$is_connected = BWFAN_Core()->connectors->is_connected( 'bwfco_mautic' );
if ( empty( $is_connected ) ) {
$this->err_msg = __( 'Mautic connector is not integrated.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** Twilio Connector */
public function connector_twilio_active() {
if ( ! bwfan_is_autonami_connector_active() ) {
$this->err_msg = __( 'FunnelKit Automations Connector plugin is not active.', 'wp-marketing-automations' );
return false;
}
$is_connected = BWFAN_Core()->connectors->is_connected( 'bwfco_twilio' );
if ( empty( $is_connected ) ) {
$this->err_msg = __( 'Twilio connector is not integrated.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** Google Sheets Connector */
public function connector_google_sheets_active() {
if ( ! bwfan_is_autonami_connector_active() ) {
$this->err_msg = __( 'FunnelKit Automations Connector plugin is not active.', 'wp-marketing-automations' );
return false;
}
$is_connected = BWFAN_Core()->connectors->is_connected( 'bwfco_google_sheets' );
if ( empty( $is_connected ) ) {
$this->err_msg = __( 'Google Sheets connector is not integrated.', 'wp-marketing-automations' );
return false;
}
return true;
}
/** BulkGate Connector */
public function connector_bulkgate_active() {
if ( ! bwfan_is_autonami_connector_active() ) {
$this->err_msg = __( 'FunnelKit Automations Connector plugin is not active.', 'wp-marketing-automations' );
return false;
}
$is_connected = BWFAN_Core()->connectors->is_connected( 'bwfco_bulkgate' );
if ( empty( $is_connected ) ) {
$this->err_msg = __( 'BulkGate connector is not integrated.', 'wp-marketing-automations' );
return false;
}
return true;
}
}

View File

@@ -0,0 +1,187 @@
<?php
class BWFAN_Split_Path_Traversal_Controller extends BWFAN_Base_Step_Controller {
public $current_step_type = null;
public $current_node_id = 0;
public $links = null;
public $steps = null;
public $db_links = null;
public $step_iteration = null;
public $get_nodes = false;
public $formatted_links = [];
public $path_target_nodes = [];
/**
* Set steps in the traverser
*
* @param array $db_steps
*
* @return void
*/
public function set_steps( $db_steps ) {
if ( ! is_array( $db_steps ) || empty( $db_steps ) ) {
return;
}
foreach ( $db_steps as $step ) {
$this->steps[ $step['id'] ] = $step;
}
if ( ! isset( $this->steps[ $this->current_node_id ] ) ) {
return;
}
$this->current_step_type = $this->steps[ $this->current_node_id ]['type'];
$this->step_id = $this->steps[ $this->current_node_id ]['stepId'];
}
/**
* Get Step branches
*
* @param $step_id
*
* @return array
*/
public function get_all_branches_of_step( $step_id ) {
$iteration_array = empty( $this->step_iteration ) ? BWFAN_Model_Automationmeta::get_meta( $this->automation_id, 'step_iteration_array' ) : $this->step_iteration;
if ( ! isset( $iteration_array[ $step_id ] ) ) {
return [];
}
$branches = [];
foreach ( $iteration_array[ $step_id ] as $data ) {
if ( ! isset( $data['next'] ) ) {
continue;
}
$branches[] = $data['next'];
}
return $branches;
}
/**
* Get split/conditional step's all node & step ids
*
* @param $node_id
*
* @return array
*/
public function get_steps_all_node( $node_id ) {
$this->formatted_links = $this->get_formatted_links();
$node_id = empty( $node_id ) ? $this->current_node_id : $node_id;
if ( ! isset( $this->steps[ $node_id ] ) ) {
return [];
}
$step_id = $this->steps[ $node_id ]['stepId'];
$type = $this->steps[ $node_id ]['type'];
$branches = $this->get_all_branches_of_step( $step_id );
if ( empty( $branches ) ) {
return [];
}
$split_steps = [];
$first_branch = '';
$first_path_node_ids = $first_path_sids = $last_path_sids = $merger_points = [];
foreach ( $branches as $index => $branch ) {
$branch_name = str_replace( $node_id . '-path-', 'p-', $branch );
/** Assign first branch name */
if ( empty( $first_branch ) ) {
$first_branch = $branch_name;
}
$this->path_target_nodes = [];
/** Fetch node ids and step ids */
$path_nodes = $this->get_connected_nodes( $branch );
/** Assign first path to get first path's steps */
if ( empty( $first_path_node_ids ) ) {
$first_path_node_ids = $path_nodes['node_ids'];
$first_path_sids = $path_nodes['step_ids'];
if ( 'split' === $type ) {
$split_steps[ $branch_name ] = $path_nodes['step_ids'];
}
continue;
}
$merger_points = array_values( array_unique( array_intersect( $path_nodes['node_ids'], $first_path_node_ids ) ) );
if ( 'split' === $type ) {
$split_steps[ $branch_name ] = array_diff( $path_nodes['step_ids'], array_intersect( $path_nodes['step_ids'], $first_path_sids ) );
}
$last_path_sids = $path_nodes['step_ids'];
}
if ( 'split' === $type ) {
$split_steps[ $first_branch ] = array_diff( $split_steps[ $first_branch ], array_intersect( $split_steps[ $first_branch ], $last_path_sids ) );
}
return [
'merger_points' => isset( $merger_points[0] ) ? $merger_points[0] : 0,
'split_steps' => $split_steps
];
}
/**
* Get connected nodes
*
* @param $element
*
* @return array
*/
public function get_connected_nodes( $element ) {
$node_ids = [];
$step_ids = [];
if ( ! empty( $this->formatted_links[ $element ] ) && ! in_array( $element, $this->path_target_nodes, true ) ) {
foreach ( $this->formatted_links[ $element ] as $targetElem ) {
/** For merger points we need node ids */
$node_ids[] = isset( $this->steps[ $targetElem['target'] ]['id'] ) ? $this->steps[ $targetElem['target'] ]['id'] : '';
$step_ids[] = isset( $this->steps[ $targetElem['target'] ]['stepId'] ) ? $this->steps[ $targetElem['target'] ]['stepId'] : $this->steps[ $targetElem['target'] ]['id'];
$subTargetElem = $this->get_connected_nodes( $targetElem['target'] );
$res_node_ids = $subTargetElem['node_ids'];
if ( count( $res_node_ids ) > 0 ) {
$node_ids = array_merge( $node_ids, $res_node_ids );
$step_ids = array_merge( $step_ids, $subTargetElem['step_ids'] );
}
}
}
$this->path_target_nodes = $node_ids;
return [
'node_ids' => array_unique( $node_ids ),
'step_ids' => array_unique( $step_ids ),
];
}
/**
* Get formatted links
*
* @param $db_links
*
* @return array
*/
public function get_formatted_links() {
$links = empty( $this->db_links ) ? BWFAN_Model_Automationmeta::get_meta( $this->automation_id, 'links' ) : $this->db_links;
if ( empty( $links ) ) {
return [];
}
$data = [];
foreach ( $links as $link ) {
if ( isset( $link['source'] ) && isset( $link['target'] ) && isset( $mapArr[ $link['source'] ] ) ) {
$data[ $link['source'] ][] = [
'target' => $link['target'],
];
continue;
}
if ( isset( $link['target'] ) ) {
$data[ $link['source'] ][] = [
'target' => $link['target'],
];
}
}
return $data;
}
}

View File

@@ -0,0 +1,54 @@
<?php
class BWFAN_Split_Test_Controller extends BWFAN_Base_Step_Controller {
public $last_run_path = 0;
public $total_paths = 3;
public $automation = [];
public $automation_data = [];
public $current_node_id = 0;
public $winner_path = 0;
/**
* @var BWFAN_Split_Path_Traversal_Controller
*/
public $split_traverse_ins = null;
/**
* Populate step data
*
* @param $db_step
*
* @return bool
*/
public function populate_step_data( $db_step = array() ) {
if ( ! parent::populate_step_data( $db_step ) ) {
return false;
}
$this->winner_path = isset( $this->step_data['sidebarData']['winner'] ) ? intval( $this->step_data['sidebarData']['winner'] ) : 0;
/** If winner path is declared then no need to populate other data */
if ( $this->winner_path > 0 ) {
return true;
}
$this->total_paths = isset( $this->step_data['sidebarData']['split_path'] ) ? $this->step_data['sidebarData']['split_path'] : $this->total_paths;
$this->last_run_path = isset( $this->step_data['sidebarData']['last_run'] ) ? intval( $this->step_data['sidebarData']['last_run'] ) : 0;
return true;
}
/**
* Get next run path
*
* @return int|mixed
*/
public function get_next_path() {
/** If winner path is declared */
if ( $this->winner_path > 0 ) {
return $this->winner_path;
}
$next_path = $this->last_run_path + 1;
return ( intval( $this->total_paths ) >= intval( $next_path ) ) ? $next_path : 1;
}
}

View File

@@ -0,0 +1,11 @@
<?php
abstract class BWFAN_Step_Run_Controller {
public $step_id = 0;
public $step_data = array();
public $action_data = array();
public $automation_id = 0;
public $contact_id = 0;
}

View File

@@ -0,0 +1,322 @@
<?php
class BWFAN_Table_Validation_Controller {
public static $tables = [
'bwfan_abandonedcarts',
'bwfan_automation_complete_contact',
'bwfan_automationmeta',
'bwfan_automation_contact',
'bwfan_automation_contact_trail',
'bwfan_automation_events',
'bwfan_automation_step',
'bwfan_automations',
'bwf_contact_fields',
'bwfan_contact_note',
'bwfan_conversions',
'bwfan_engagement_tracking',
'bwfan_engagement_trackingmeta',
'bwfan_field_groups',
'bwfan_fields',
'bwfan_message',
'bwfan_message_unsubscribe',
'bwfan_templates',
'bwfan_terms',
'bwf_options',
'bwf_actions',
'bwf_action_claim',
'bwf_contact',
'bwf_contact_meta',
'bwf_wc_customers',
'bwfan_automation_contact_claim'
];
public static $pro_tables = [
'bwfan_api_keys',
'bwfan_broadcast',
'bwfan_bulk_action',
'bwfan_form_feeds',
'bwfan_import_export',
'bwfan_link_triggers',
];
public static $v1_tables = [
'bwfan_contact_automations',
'bwfan_logs',
'bwfan_logmeta',
'bwfan_tasks',
'bwfan_taskmeta',
'bwfan_task_claim',
];
public static $core_tables = [
'bwf_actions',
'bwf_action_claim',
'bwf_contact',
'bwf_contact_meta',
'bwf_wc_customers',
];
/**
* Validate database tables and return missing ones
*
* @param array $tables
*
* @return array
*/
public static function bwfan_validate_db_tables( $tables ) {
global $wpdb;
$db_name = ! empty( $wpdb->dbname ) ? $wpdb->dbname : ( defined( 'DB_NAME' ) ? DB_NAME : '' );
if ( empty( $db_name ) ) {
return array( 'error' => __( "Unable to find the Database name", "wp-marketing-automations" ) );
}
$table_names = array_map( function ( $table ) use ( $wpdb ) {
return $wpdb->prefix . $table;
}, $tables );
$placeholders = implode( ',', array_fill( 0, count( $table_names ), '%s' ) );
$query = $wpdb->prepare( "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = %s AND TABLE_NAME IN ($placeholders)", array_merge( [ $db_name ], $table_names ) );
$existing_tables = $wpdb->get_col( $query ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$missing_tables = array_diff( $table_names, $existing_tables );
return ! empty( $missing_tables ) ? array_map( function ( $table ) use ( $wpdb ) {
return str_replace( $wpdb->prefix, '', $table );
}, $missing_tables ) : [];
}
/**
* @param $missing_tables
*
* @return bool
*/
public static function create_missing_tables( $missing_tables ) {
self::load_table_classes();
$db_errors = [];
global $wpdb;
foreach ( $missing_tables as $table ) {
/** Check for core tables */
if ( in_array( $table, self::$core_tables ) ) {
$method_name = 'create_' . $table;
if ( ! method_exists( __CLASS__, $method_name ) ) {
continue;
}
$sql = call_user_func_array( [ __CLASS__, $method_name ], [] );
$result = $wpdb->query( $sql ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
if ( $result === false ) {
$db_errors[] = $wpdb->last_error;
}
continue;
}
$class_name = str_replace( [ 'bwf_', 'bwfan_' ], '', $table );
$class_name = 'BWFAN_DB_Table_' . $class_name;
if ( ! class_exists( $class_name ) ) {
continue;
}
/** @var BWFAN_DB_Tables_Base $table_instance */
$table_instance = new $class_name();
$table_instance->create_table();
if ( ! empty( $table_instance->db_errors ) ) {
$db_errors[] = $table_instance->db_errors;
}
}
if ( empty( $db_errors ) ) {
bwf_options_update( 'bwfan_table_validation_error', 0 );
return true;
}
BWFAN_Common::log_test_data( array( 'table validation logs ' => $db_errors ), 'base_table_validation', true );
return false;
}
/**
* This function checks for any missing required database tables and returns their names in an array.
*
* @return array An array of missing table names.
*/
public static function check_missing_tables() {
$missing_tables = self::bwfan_validate_db_tables( self::$tables );
if ( isset( $missing_tables['error'] ) ) {
return $missing_tables;
}
if ( BWFAN_Common::is_automation_v1_active() ) {
$v1_tables = self::bwfan_validate_db_tables( self::$v1_tables );
$missing_tables = array_merge( $missing_tables, $v1_tables );
}
if ( bwfan_is_autonami_pro_active() ) {
$pro_tables = self::bwfan_validate_db_tables( self::$pro_tables );
$missing_tables = array_merge( $missing_tables, $pro_tables );
}
return $missing_tables;
}
public static function load_table_classes() {
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
$dir = BWFAN_PLUGIN_DIR . '/includes/db/tables';
/** Load base class of verify tables */
include_once( $dir . "/bwfan-db-tables-base.php" );
self::load_class_files( $dir );
/** Load pro tables class files */
if ( bwfan_is_autonami_pro_active() ) {
$pro_dir = BWFAN_PRO_PLUGIN_DIR . '/includes/db/tables';
self::load_class_files( $pro_dir );
}
}
public static function load_class_files( $dir ) {
foreach ( glob( $dir . '/class-*.php' ) as $_field_filename ) {
$file_data = pathinfo( $_field_filename );
if ( isset( $file_data['basename'] ) && 'index.php' === $file_data['basename'] ) {
continue;
}
require_once( $_field_filename );
}
}
public static function get_collation() {
global $wpdb;
$collate = '';
if ( $wpdb->has_cap( 'collation' ) ) {
$collate = $wpdb->get_charset_collate();
}
return $collate;
}
/**
* This function validates if any required tables are missing and updates the option accordingly.
*
* @return int Returns 1 if there are missing tables, 0 otherwise.
*/
public static function get_table_validate_option() {
if ( bwf_options_get( 'bwfan_table_validation_error' ) ) {
return bwf_options_get( 'bwfan_table_validation_error' );
}
$missing_tables = self::check_missing_tables();
$table_validate = ! empty( $missing_tables ) ? 1 : 0;
bwf_options_update( 'bwfan_table_validation_error', $table_validate );
return $table_validate;
}
public static function create_bwf_contact() {
global $wpdb;
$collate = self::get_collation();
return "CREATE TABLE `{$wpdb->prefix}bwf_contact` (
`id` int(12) unsigned NOT NULL AUTO_INCREMENT,
`wpid` int(12) NOT NULL,
`uid` varchar(35) NOT NULL DEFAULT '',
`email` varchar(100) NOT NULL,
`f_name` varchar(100),
`l_name` varchar(100),
`contact_no` varchar(20),
`country` char(2),
`state` varchar(100),
`timezone` varchar(50) DEFAULT '',
`type` varchar(20) DEFAULT 'lead',
`source` varchar(100) DEFAULT '',
`points` bigint(20) unsigned NOT NULL DEFAULT '0',
`tags` longtext,
`lists` longtext,
`last_modified` DateTime NOT NULL,
`creation_date` DateTime NOT NULL,
`status` int(2) NOT NULL DEFAULT 1,
PRIMARY KEY (`id`),
KEY `id` (`id`),
KEY `wpid` (`wpid`),
KEY `uid` (`uid`),
UNIQUE KEY (`email`)
)$collate;";
}
public static function create_bwf_contact_meta() {
global $wpdb;
$collate = self::get_collation();
return "CREATE TABLE `{$wpdb->prefix}bwf_contact_meta` (
`meta_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`contact_id` bigint(20) unsigned NOT NULL DEFAULT '0',
`meta_key` varchar(50) DEFAULT NULL,
`meta_value` longtext,
PRIMARY KEY (`meta_id`)
) $collate;";
}
public static function create_bwf_actions() {
global $wpdb;
$collate = self::get_collation();
return "CREATE TABLE `{$wpdb->prefix}bwf_actions` (
id bigint(20) unsigned NOT NULL auto_increment,
c_date datetime NOT NULL default '0000-00-00 00:00:00',
e_time int(12) NOT NULL default 0,
hook varchar(255) not null,
args longtext null,
status int(1) not null default 0 COMMENT '0 - Pending | 1 - Running',
recurring_interval int(10) not null default 0,
group_slug varchar(255) not null default 'woofunnels',
claim_id bigint(20) unsigned NOT NULL default 0,
PRIMARY KEY (id),
KEY id (id),
KEY e_time (e_time),
KEY hook (hook(191)),
KEY status (status),
KEY group_slug (group_slug(191)),
KEY claim_id (claim_id)
) $collate;";
}
public static function create_bwf_action_claim() {
global $wpdb;
$collate = self::get_collation();
return "CREATE TABLE `{$wpdb->prefix}bwf_action_claim` (
id bigint(20) unsigned NOT NULL auto_increment,
date datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (id),
KEY date (date)
) $collate;";
}
public static function create_bwf_wc_customers() {
global $wpdb;
$collate = self::get_collation();
return "CREATE TABLE `{$wpdb->prefix}bwf_wc_customers` (
`id` int(12) unsigned NOT NULL AUTO_INCREMENT,
`cid` int(12) NOT NULL,
`l_order_date` DateTime NOT NULL,
`f_order_date` DateTime NOT NULL,
`total_order_count` int(7) NOT NULL,
`total_order_value` double NOT NULL,
`aov` double NOT NULL,
`purchased_products` longtext,
`purchased_products_cats` longtext,
`purchased_products_tags` longtext,
`used_coupons` longtext,
PRIMARY KEY (`id`),
KEY `id` (`id`),
UNIQUE KEY`cid` (`cid`)
) $collate;";
}
}

View File

@@ -0,0 +1,461 @@
<?php
#[AllowDynamicProperties]
class BWFAN_Traversal_Controller {
public $current_node_id = 0;
/** For processing of conditional controller */
public $automation_id = 0;
public $contact_id = 0;
public $trail_id = 0;
/** Conditional traversal Result */
public $conditional_result_node_id = null;
/** Split test traversal result */
public $split_result_node_id = null;
protected $steps = array();
protected $links = array();
/** Logging */
protected $step_log = null;
public $automation_contact_ins = null;
/**
* Set steps in the traverser
*
* @param array $db_steps
*
* @return void
*/
public function set_steps( $db_steps ) {
if ( ! is_array( $db_steps ) || empty( $db_steps ) ) {
return;
}
foreach ( $db_steps as $step ) {
$this->steps[ $step['id'] ] = $step;
}
}
/**
* Set links in the traverser
*
* @param array $db_links
*
* @return void
*/
public function set_links( $db_links ) {
if ( ! is_array( $db_links ) || empty( $db_links ) ) {
return;
}
foreach ( $db_links as $link ) {
if ( empty( $link ) || ! isset( $link['source'] ) || ! isset( $link['target'] ) ) {
continue;
}
$this->links[ $link['source'] ] = $link['target'];
}
}
/**
* Get current step id
*
* @return false|mixed
*/
public function get_current_step_id() {
if ( empty( $this->steps ) || empty( $this->current_node_id ) || ! isset( $this->steps[ $this->current_node_id ] ) || ! isset( $this->steps[ $this->current_node_id ]['stepId'] ) ) {
return false;
}
return $this->steps[ $this->current_node_id ]['stepId'];
}
/**
* Check if the current step is an end step
*
* @return bool
*/
public function is_end() {
return $this->current_node_id === 'end';
}
/**
* @param array $step_ids Goal step IDs
* @param $goal_ins BWFAN_Goal_Controller
* @param array $automation_contact
*
* @return array|false
*/
public function try_traverse_to_goal( $step_ids, $goal_ins, $automation_contact ) {
if ( empty( $this->current_node_id ) || empty( $step_ids ) ) {
return false;
}
$goal_ins->goal_steps = $this->get_step_nodes( $step_ids );
$run = 0;
$goal_found_nodes = [];
while ( $run < count( $this->steps ) ) {
$run ++;
$current_step = $this->steps[ $this->current_node_id ];
$this->log( 'current step', 'goal-check' );
$this->log( [ 'id' => $current_step['id'], 'type' => $current_step['type'] ], 'goal-check' );
switch ( $current_step['type'] ) {
case 'action':
case 'wait':
$this->traverse_to_next_step();
$this->log( 'traversed to next action/ wait step', 'goal-check' );
break;
case 'conditional':
$this->process_goal_conditional( $goal_ins, $automation_contact );
$this->traverse_to_next_step();
$this->log( 'traversed to next conditional step', 'goal-check' );
break;
case 'jump':
$this->process_jump( $goal_ins, $automation_contact );
/** No need to traverse to next step as traversing already from process_jump function */
$this->log( 'traversed to next jump step', 'goal-check' );
break;
case 'end':
$this->log( 'end step', 'goal-check' );
$run = count( $this->steps );
break;
case 'benchmark':
if ( in_array( $current_step['id'], $goal_ins->goal_steps ) ) {
$goal_found_nodes[] = $current_step['id'];
$this->log( 'goal step found during traverse. goal step id: ' . $current_step['id'], 'goal-check' );
}
$this->traverse_to_next_step();
$this->log( 'traversed to next step from goal step', 'goal-check' );
break;
case 'split':
if ( in_array( $current_step['id'], $goal_ins->goal_steps ) ) {
$goal_found_nodes[] = $current_step['id'];
$this->log( 'split step found during traverse. split step id: ' . $current_step['id'], 'goal-check' );
}
$this->process_split_path( $automation_contact );
$this->traverse_to_next_step();
/** No need to traverse to next step as traversing already from process_jump function */
$this->log( 'traversed to next split step', 'goal-check' );
break;
default:
break;
}
}
$goal_found_nodes = array_unique( $goal_found_nodes );
sort( $goal_found_nodes );
return $goal_found_nodes;
}
/**
* Return current step data by step id
*
* @param int $step_id
*
* @return false|mixed
*/
public function get_step_by_step_id( $step_id ) {
if ( empty( $step_id ) ) {
return false;
}
foreach ( $this->steps as $step ) {
if ( isset( $step['stepId'] ) && ( absint( $step['stepId'] ) === absint( $step_id ) ) ) {
return $step;
}
}
return false;
}
/**
* Get node ids from step ids
*
* @param $step_ids
*
* @return array|false
*/
public function get_step_nodes( $step_ids ) {
if ( empty( $step_ids ) ) {
return false;
}
$nodes = [];
foreach ( $this->steps as $step ) {
if ( 'benchmark' !== $step['type'] ) {
continue;
}
if ( isset( $step['stepId'] ) && in_array( $step['stepId'], $step_ids ) ) {
$nodes[ $step['stepId'] ] = $step['id'];
}
}
return empty( $nodes ) ? false : $nodes;
}
/**
* Traverse to next step node
*
* @return bool
*/
public function traverse_to_next_step() {
$current_step = $this->get_current_step();
$this->log( $this->trail_id . ' - current step id: ' . $current_step['stepId'] . ' and node id: ' . $current_step['id'] . ' and type: ' . $current_step['type'] );
if ( empty( $current_step ) || empty( $this->links ) ) {
return false;
}
if ( 'conditional' === $current_step['type'] ) {
/** Traverse conditional step */
$next_step = $this->traverse_conditional_step();
if ( empty( $next_step ) ) {
return false;
}
$this->current_node_id = $next_step;
$this->log( $this->trail_id . ' - traverse to next step from conditional step. New node id: ' . $this->current_node_id );
return true;
}
/** If current step is split */
if ( 'split' === $current_step['type'] ) {
/** Traverse split step */
$next_step = $this->traverse_split_step();
if ( empty( $next_step ) ) {
return false;
}
$this->current_node_id = $next_step;
$this->log( $this->trail_id . ' - traverse to next path from split step. New node id: ' . $this->current_node_id );
return true;
}
if ( ! isset( $this->links[ $this->current_node_id ] ) ) {
return false;
}
$this->current_node_id = $this->links[ $this->current_node_id ];
$this->log( $this->trail_id . ' - traverse to next step. New node id: ' . $this->current_node_id );
return true;
}
/**
* Get current step data
*
* @return false|mixed
*/
public function get_current_step() {
return ! empty( $this->steps ) && ! empty( $this->current_node_id ) && isset( $this->steps[ $this->current_node_id ] ) ? $this->steps[ $this->current_node_id ] : false;
}
/**
* Return step id after processing conditional step result
*
* @return false|mixed
*/
public function traverse_conditional_step() {
$conditional_node_id = $this->conditional_result_node_id;
if ( isset( $this->links[ $conditional_node_id ] ) ) {
return $this->links[ $conditional_node_id ];
}
/** Maybe check if set in the database */
if ( $this->automation_contact_ins instanceof BWFAN_Automation_Controller ) {
$data = $this->automation_contact_ins->automation_contact_data;
if ( isset( $data['node_id'] ) && isset( $this->links[ $data['node_id'] ] ) ) {
/** Removing saved node id from the DB */
$this->automation_contact_ins->update_conditional_step_result();
return $this->links[ $data['node_id'] ];
}
}
/** If somehow conditional step is deleted from table but its step node id exists in links then return target step of NO node */
if ( isset( $this->links[ $this->links[ $this->current_node_id ] ] ) ) {
return $this->links[ $this->links[ $this->current_node_id ] ];
}
BWFAN_Common::log_test_data( 'not able to traverse to condition next step ' . $this->current_node_id, 'traverse_conditional_failed' );
BWFAN_Common::log_test_data( 'conditional_node_id: ' . $conditional_node_id, 'traverse_conditional_failed' );
BWFAN_Common::log_test_data( $this->links, 'traverse_conditional_failed' );
return false;
}
/**
* Return step id after processing split next path result
*
* @return false|mixed
*/
public function traverse_split_step() {
$path_node_id = $this->split_result_node_id;
if ( isset( $this->links[ $path_node_id ] ) ) {
return $this->links[ $path_node_id ];
}
BWFAN_Common::log_test_data( 'not able to traverse to split next path ' . $this->current_node_id, 'traverse_split_failed' );
BWFAN_Common::log_test_data( 'split_path_node_id: ' . $path_node_id, 'traverse_split_failed' );
BWFAN_Common::log_test_data( $this->links, 'traverse_split_failed' );
return false;
}
/**
* Process conditional step data for goal only
*
* @param $goal_ins BWFAN_Goal_Controller
* @param array $automation_contact automation contact row
*
* @return void
*/
public function process_goal_conditional( $goal_ins, $automation_contact ) {
$current_step = $this->get_current_step();
$ins = new BWFAN_Conditional_Controller();
$ins->contact_id = $goal_ins->contact_id;
$ins->automation_id = $goal_ins->automation_id;
$ins->step_id = absint( $current_step['stepId'] );
$ins->populate_step_data( $current_step );
$ins->populate_automation_contact_data( $automation_contact );
$result = $ins->is_match();
$this->conditional_result_node_id = $current_step['id'] . ( ( true === $result ) ? 'yes' : 'no' );
}
/**
* Process jump step
*
* @param $goal_ins
* @param $automation_contact
*
* @return void
*/
public function process_jump( $goal_ins, $automation_contact ) {
$current_step = $this->get_current_step();
$ins = new BWFAN_Jump_Controller();
$ins->step_id = absint( $current_step['stepId'] );
$ins->populate_step_data( $current_step );
$jump_step_id = $ins->get_jump_step_id();
if ( empty( $jump_step_id ) ) {
return;
}
/** Set current node to jump step */
$this->set_node_id_from_step_id( $jump_step_id );
}
/**
* Maybe traverse back to valid step
*
* @return void
*/
public function maybe_traverse_back() {
global $wpdb;
/** @var BWFAN_Automation_Controller $ins */
$ins = $this->automation_contact_ins;
$step_id = $ins->step_id;
$query = $wpdb->prepare( "SELECT `ID` FROM `{$wpdb->prefix}bwfan_automation_contact_trail` WHERE `tid` = %s AND `sid` = %d LIMIT 0,1;", $ins->automation_contact['trail'], $step_id );
$result = $wpdb->get_var( $query ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$valid_step_id = 0;
if ( ! empty( $result ) ) {
$query = $wpdb->prepare( "SELECT t.`sid` FROM `{$wpdb->prefix}bwfan_automation_contact_trail` as t INNER JOIN `{$wpdb->prefix}bwfan_automation_step` as s ON s.`ID` = t.`sid` WHERE t.`tid` = %s AND t.`ID` < %d AND s.`status` IN (%d, %d) ORDER BY t.ID DESC LIMIT 0,1;", $ins->automation_contact['trail'], $result, 0, 1 );
$result = $wpdb->get_var( $query ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
if ( ! empty( $result ) ) {
$valid_step_id = strval( $result );
}
}
if ( empty( $valid_step_id ) ) {
/** Step not found, start from the first node */
$ins->step_id = $ins->automation['start'];
$this->set_node_id_from_step_id( $ins->step_id );
$ins->automation_contact['last'] = 0;
return;
}
$this->set_node_id_from_step_id( $valid_step_id );
}
/**
* Set step node ID from the step ID
*
* @param int $step_id
*
* @return bool
*/
public function set_node_id_from_step_id( $step_id ) {
$step = $this->get_step_by_step_id( $step_id );
if ( empty( $step ) || ! isset( $step['id'] ) ) {
return false;
}
$this->current_node_id = $step['id'];
return true;
}
/**
* Process split step for goal only
*
* @param $automation_contact
*
* @return void
*/
public function process_split_path( $automation_contact ) {
$current_step = $this->get_current_step();
$ins = new BWFAN_Split_Test_Controller();
$ins->current_node_id = $this->current_node_id;
$ins->automation_id = $this->automation_id;
$ins->step_id = absint( $current_step['stepId'] );
$ins->populate_step_data( $current_step );
$ins->populate_automation_contact_data( $automation_contact );
$path = $ins->get_next_path();
$this->split_result_node_id = $current_step['id'] . '-path-' . $path;
}
/**
* Special log function for step execution
*
* @param $log
* @param $name
*
* @return void
*/
public function log( $log, $name = 'fka-automation-step-id' ) {
if ( empty( $log ) ) {
return;
}
if ( false === $this->step_log ) {
return;
}
if ( is_null( $this->step_log ) && false === apply_filters( 'bwfan_allow_automation_step_logging', BWFAN_Common::is_log_enabled( 'bwfan_step_logging' ) ) ) {
$this->step_log = false;
return;
}
$this->step_log = true;
BWFAN_Common::log_test_data( $log, $name, true );
}
}