<?php
/**
* Created by Vextras.
*
* Name: Ryan Hungate
* Email: ryan@vextras.com
* Date: 11/14/16
* Time: 9:38 AM
*/
class MailChimp_WooCommerce_User_Submit extends Mailchimp_Woocommerce_Job
{
public static $handling_for = null;
public $id;
public $subscribed;
public $gdpr_fields;
public $updated_data;
public $language;
public $should_ignore = false;
public $submit_transactional = true;
/**
* MailChimp_WooCommerce_User_Submit constructor.
*
* @param null $id
* @param null $subscribed
* @param null $updated_data
* @param null $language
* @param null $gdpr_fields
*/
public function __construct($id = null, $subscribed = null, $updated_data = null, $language = null, $gdpr_fields = null)
{
if (!empty($id)) {
// if we're passing in another user with the same id during the same php process we need to ignore it.
if (static::$handling_for === $id) {
$this->should_ignore = true;
}
// set the user id and the current 'handling_for' to this user id so we don't duplicate jobs.
static::$handling_for = $this->id = $id;
}
if (is_bool($subscribed)) {
$this->subscribed = $subscribed;
if ($subscribed && !empty($gdpr_fields)) {
foreach ($gdpr_fields as $id => $value) {
$gdpr_field['marketing_permission_id'] = $id;
$gdpr_field['enabled'] = (bool) $value;
$this->gdpr_fields[] = $gdpr_field;
}
}
}
if (!empty($updated_data)) {
$this->updated_data = $updated_data->to_array();
}
if (!empty($language)) {
$this->language = $language;
}
}
/**
* @param bool $bool
* @return $this
*/
public function submittingTransactional($bool = true)
{
$this->submit_transactional = (bool) $bool;
return $this;
}
/**
* @return bool
*/
public function handle()
{
if (!mailchimp_is_configured()) {
mailchimp_debug(get_called_class(), 'Mailchimp is not configured properly');
static::$handling_for = null;
return false;
}
if ($this->should_ignore) {
mailchimp_debug(get_called_class(), "{$this->id} is currently in motion - skipping this one.");
static::$handling_for = null;
return false;
}
$options = get_option('mailchimp-woocommerce', array());
$store_id = mailchimp_get_store_id();
// load up the user.
$user = new WP_User($this->id);
// we need a valid user, a valid store id and options to continue
if ($user->ID <= 0 || empty($store_id) || !is_array($options)) {
// seems as if the database records are not being set by the time this queue job is fired,
// just a precautionary to make sure it's available during
sleep(1);
$options = get_option('mailchimp-woocommerce', array());
$store_id = mailchimp_get_store_id();
// load up the user.
$user = new WP_User($this->id);
if ($user->ID <= 0 || empty($store_id) || !is_array($options)) {
mailchimp_log('member.sync', "Invalid Data For Submission :: {$user->ID}");
static::$handling_for = null;
return false;
}
}
$email = $user->user_email;
// make sure we don't need to skip this email
if (!mailchimp_email_is_allowed($email)) {
static::$handling_for = null;
return false;
}
$user_subscribed = get_user_meta($this->id, 'mailchimp_woocommerce_is_subscribed', true);
$unsaved = '' === $user_subscribed || null === $user_subscribed;
// if we have a null value, we need to grab the correct user meta for is_subscribed
if (is_null($this->subscribed)) {
if ( $unsaved ) {
mailchimp_log('member.sync', "Skipping sync for {$email} because no subscriber status has been set");
static::$handling_for = null;
return false;
}
$this->subscribed = (bool) $user_subscribed;
}
// if the meta we've stored on the user is not equal to the value being passed to Mailchimp
// let's update that value here.
if ( $unsaved || ( (bool) $this->subscribed && (bool) $user_subscribed !== (bool) $this->subscribed ) ) {
update_user_meta(
$this->id,
'mailchimp_woocommerce_is_subscribed',
$this->subscribed ? '1' : '0'
);
}
$api_key = isset($options['mailchimp_api_key']) ? $options['mailchimp_api_key'] : false;
$list_id = isset($options['mailchimp_list']) ? $options['mailchimp_list'] : false;
// we need a valid api key and list id to continue
if (empty($api_key) || empty($list_id)) {
mailchimp_log('member.sync', "Invalid Api Key or ListID :: {$email}");
static::$handling_for = null;
return false;
}
// don't let anyone be unsubscribed from the list - that should only happen on email campaigns
// and someone clicking the unsubscribe linkage.
if (!$this->subscribed && !$this->submit_transactional) {
static::$handling_for = null;
return false;
}
$api = new MailChimp_WooCommerce_MailChimpApi($api_key);
$merge_fields_system = array();
$fn = trim($user->first_name);
$ln = trim($user->last_name);
if (!empty($fn)) $merge_fields_system['FNAME'] = $fn;
if (!empty($ln)) $merge_fields_system['LNAME'] = $ln;
// allow users to hook into the merge field submission
$merge_fields = apply_filters('mailchimp_sync_user_mergetags', $merge_fields_system, $user);
// for whatever reason if this isn't an array we need to skip it.
if (!is_array($merge_fields)) {
mailchimp_error("custom.merge_fields", "The filter for mailchimp_sync_user_mergetags needs to return an array, using the default setup instead.");
$merge_fields = $merge_fields_system;
}
// language
$language = $this->language;
// GDPR
$gdpr_fields = $this->gdpr_fields;
// pull the transient key for this job.
$transient_id = mailchimp_get_transient_email_key($email);
$status_meta = mailchimp_get_subscriber_status_options($this->subscribed);
try {
// check to see if the status meta has changed when a false response is given
if (mailchimp_check_serialized_transient_changed($transient_id, $status_meta) === false) {
mailchimp_debug(get_called_class(), "Skipping sync for {$email} because it was just pushed less than a minute ago.");
static::$handling_for = null;
return false;
}
// see if we have a member.
$member_data = $api->member($list_id, $email);
// if we're updating a member and the email is different, we need to delete the old person
if (is_array($this->updated_data) && isset($this->updated_data['user_email'])) {
if ($this->updated_data['user_email'] !== $email) {
// delete the old
$api->deleteMember($list_id, $this->updated_data['user_email']);
// subscribe the new
$api->subscribe($list_id, $email, $status_meta['created'], $merge_fields, null, $language, $gdpr_fields);
// update the member tags but fail silently just in case.
$api->updateMemberTags(mailchimp_get_list_id(), $email, true);
mailchimp_tell_system_about_user_submit($email, $status_meta);
if ($status_meta['created']) {
mailchimp_log('member.sync', 'Subscriber Swap '.$this->updated_data['user_email'].' to '.$email, array(
'status' => $status_meta['created'],
'merge_fields' => $merge_fields
));
} else {
mailchimp_log('member.sync', 'Subscriber Swap '.$this->updated_data['user_email'].' to '.$email.' Pending Double OptIn', array(
'status' => $status_meta['created'],
'merge_fields' => $merge_fields
));
}
static::$handling_for = null;
return false;
}
}
// if the member is unsubscribed or pending, we really can't do anything here.
if (isset($member_data['status']) && in_array($member_data['status'], array('unsubscribed', 'pending'))) {
if ($this->subscribed && $member_data['status'] !== 'pending') {
mailchimp_log('member.sync', "pushing {$email} status as pending because they were previously unsubscribed, and must use the double opt in to make it back on the list.");
$member_data['status'] = 'pending';
} else {
mailchimp_log('member.sync', "Skipped Member Sync For {$email} because the current status is {$member_data['status']}", $merge_fields);
static::$handling_for = null;
return false;
}
}
// if the status is not === 'transactional' we can update them to subscribed or pending now.
if (isset($member_data['status']) && $member_data['status'] === 'transactional' || $member_data['status'] === 'cleaned') {
// ok let's update this member
$api->update($list_id, $email, $status_meta['updated'], $merge_fields, null, $language, $gdpr_fields);
// update the member tags but fail silently just in case.
$api->updateMemberTags(mailchimp_get_list_id(), $email, true);
mailchimp_tell_system_about_user_submit($email, $status_meta);
mailchimp_log('member.sync', "Updated Member {$email}", array(
'previous_status' => $member_data['status'],
'status' => $status_meta['updated'],
'language' => $language,
'merge_fields' => $merge_fields,
'gdpr_fields' => $gdpr_fields,
));
static::$handling_for = null;
return true;
}
if (isset($member_data['status'])) {
if ($member_data['status'] === 'subscribed' && !$this->subscribed) {
$member_data['status'] = 'transactional';
}
// ok let's update this member
$api->update($list_id, $email, $member_data['status'], $merge_fields, null, $language, $gdpr_fields);
// delete this admin transient if there was one
mailchimp_delete_transient("updating_subscriber_status.{$this->id}" );
// update the member tags but fail silently just in case.
$api->updateMemberTags(mailchimp_get_list_id(), $email, true);
mailchimp_tell_system_about_user_submit($email, $status_meta);
mailchimp_log('member.sync', "Updated Member {$email}", array(
'status' => $member_data['status'],
'language' => $language,
'merge_fields' => $merge_fields,
'gdpr_fields' => $gdpr_fields,
));
static::$handling_for = null;
return true;
}
static::$handling_for = null;
} catch (MailChimp_WooCommerce_RateLimitError $e) {
sleep(3);
mailchimp_error('member.sync.error', mailchimp_error_trace($e, "RateLimited :: user #{$this->id}"));
$this->retry();
} catch (Exception $e) {
// if we have a 404 not found, we can create the member
if ($e->getCode() == 404) {
try {
$uses_doi = isset($status_meta['requires_double_optin']) && $status_meta['requires_double_optin'];
$status_if_new = $uses_doi && $this->subscribed ? 'pending' : (bool) $this->subscribed;
$api->subscribe($list_id, $user->user_email, $status_if_new, $merge_fields, null, $language, $gdpr_fields);
// delete this admin transient if there was one
mailchimp_delete_transient("updating_subscriber_status.{$this->id}" );
// update the member tags but fail silently just in case.
$api->updateMemberTags(mailchimp_get_list_id(), $email, true);
mailchimp_tell_system_about_user_submit($email, $status_meta);
if ($status_meta['created']) {
mailchimp_log('member.sync', "Subscribed Member {$user->user_email}", array('status_if_new' => $status_if_new, 'merge_fields' => $merge_fields));
} else {
mailchimp_log('member.sync', "{$user->user_email} is Pending Double OptIn");
}
} catch (Exception $e) {
mailchimp_log('member.sync', $e->getMessage());
}
static::$handling_for = null;
return false;
}
mailchimp_error('member.sync', mailchimp_error_trace($e, $user->user_email));
}
static::$handling_for = null;
return false;
}
}