File: /var/www/vhosts/allende-losmares.com/httpdocs/wp-content/plugins/mailster/classes/queue.class.php
<?php
class MailsterQueue {
private $max_retry_after_error = 3;
public function __construct() {
add_action( 'plugins_loaded', array( &$this, 'init' ), 1 );
}
public function init() {
add_action( 'mailster_cron', array( &$this, 'update_status' ), 10 );
add_action( 'mailster_cron', array( &$this, 'update' ), 20 );
add_action( 'mailster_cron_cleanup', array( &$this, 'cleanup' ), 50 );
add_action( 'mailster_cron_worker', array( &$this, 'update_status' ), 10 );
add_action( 'mailster_cron_worker', array( &$this, 'update' ), 20 );
add_action( 'mailster_cron_worker', array( &$this, 'progress' ), 50 );
add_action( 'mailster_cron_worker', array( &$this, 'finish_campaigns' ), 100 );
add_action( 'mailster_cron_autoresponder', array( &$this, 'autoresponder_timebased' ), 30 );
add_action( 'mailster_cron_autoresponder', array( &$this, 'autoresponder_usertime' ), 30 );
add_action( 'mailster_cron_autoresponder', array( &$this, 'autoresponder' ), 30 );
add_action( 'mailster_update_queue', array( &$this, 'autoresponder' ), 30 );
add_action( 'mailster_update_queue', array( &$this, 'update_status' ), 30 );
add_action( 'mailster_update_queue', array( &$this, 'update' ), 30 );
add_action( 'mailster_bounce', array( &$this, 'update_after_bounce' ), 10, 3 );
add_action( 'mailster_subscriber_change_status', array( &$this, 'subscriber_change_status' ), 10, 3 );
}
/**
*
*
* @param unknown $args
* @return unknown
*/
public function add( $args ) {
global $wpdb;
$now = time();
$args = wp_parse_args(
$args,
array(
'added' => $now,
'timestamp' => $now,
'priority' => 10,
'count' => 1,
'sent' => 0,
)
);
if ( isset( $args['options'] ) ) {
$args['options'] = esc_sql( maybe_serialize( $args['options'] ) );
}
$sql = "INSERT INTO {$wpdb->prefix}mailster_queue (" . implode( ', ', array_keys( $args ) ) . ')';
$sql .= " VALUES ('" . implode( "','", array_values( $args ) ) . "')";
$sql .= ' ON DUPLICATE KEY UPDATE count = count+1, timestamp = values(timestamp), sent = values(sent), priority = values(priority), tags = values(tags)';
return false !== $wpdb->query( $sql );
}
/**
*
*
* @param unknown $subscriber_id
* @param unknown $campaign_id
* @param unknown $hard
* @return unknown
*/
public function update_after_bounce( $subscriber_id, $campaign_id, $hard ) {
// only softbounce
if ( $hard ) {
// remove all from queue
$this->remove_subscribers( $subscriber_id );
return;
}
$now = time();
$delay = mailster_option( 'bounce_delay', 60 ) * 60;
return $this->add(
array(
'campaign_id' => $campaign_id,
'subscriber_id' => $subscriber_id,
'timestamp' => $now + $delay,
'priority' => 15,
'count' => 2,
'requeued' => 1,
)
);
}
/**
*
*
* @param unknown $campaign_id
* @param unknown $subscribers
* @param unknown $timestamp (optional)
* @param unknown $priority (optional)
* @param unknown $clear (optional)
* @param unknown $ignore_status (optional)
* @param unknown $reset (optional)
* @param unknown $options (optional)
* @param unknown $tags (optional)
* @param unknown $index (optional)
* @return unknown
*/
public function bulk_add( $campaign_id, $subscribers, $timestamp = null, $priority = 10, $clear = false, $ignore_status = false, $reset = false, $options = false, $tags = false, $index = false ) {
global $wpdb;
if ( $clear ) {
$this->clear( $campaign_id, $subscribers );
}
if ( empty( $subscribers ) ) {
return;
}
if ( is_null( $timestamp ) ) {
$timestamp = time();
}
$timestamps = ! is_array( $timestamp )
? array_fill( 0, count( $subscribers ), $timestamp )
: $timestamp;
$index = ! is_array( $index )
? array_fill( 0, count( $subscribers ), $index )
: $index;
$now = time();
$campaign_id = (int) $campaign_id;
$subscribers = array_filter( $subscribers, 'is_numeric' );
if ( empty( $subscribers ) ) {
return true;
}
if ( ! empty( $tags ) ) {
$tags = wp_slash( maybe_serialize( $tags ) );
} else {
$tags = '';
}
if ( ! empty( $options ) ) {
$options = wp_slash( maybe_serialize( $options ) );
} else {
$options = '';
}
$inserts = array();
foreach ( $subscribers as $i => $subscriber_id ) {
$inserts[] = "($subscriber_id,$campaign_id,$now," . $timestamps[ $i ] . ",$priority,1,'$ignore_status','$options','$tags'," . ( $index[ $i ] ? $index[ $i ] : 0 ) . ')';
}
$chunks = array_chunk( $inserts, 2000 );
$success = true;
foreach ( $chunks as $insert ) {
$sql = "INSERT INTO {$wpdb->prefix}mailster_queue (subscriber_id, campaign_id, added, timestamp, priority, count, ignore_status, options, tags, i) VALUES";
$sql .= ' ' . implode( ',', $insert );
$sql .= ' ON DUPLICATE KEY UPDATE timestamp = values(timestamp), ignore_status = values(ignore_status), i = values(i)';
if ( $reset ) {
$sql .= ', sent = 0';
}
if ( $options ) {
$sql .= sprintf( ", options = '%s'", $options );
}
if ( $tags ) {
$sql .= sprintf( ", tags = '%s'", $tags );
}
$success = $success && false !== $wpdb->query( $sql );
}
return $success;
}
/**
*
*
* @param unknown $campaign_id (optional)
* @param unknown $subscribers (optional)
* @param unknown $requeued (optional)
* @return unknown
*/
public function remove( $campaign_id = null, $subscribers = null, $requeued = false ) {
global $wpdb;
$sql = "DELETE a FROM {$wpdb->prefix}mailster_queue AS a WHERE 1";
if ( ! is_null( $campaign_id ) ) {
$sql .= $wpdb->prepare( ' AND a.campaign_id = %d', $campaign_id );
}
if ( ! $requeued ) {
$sql .= ' AND a.requeued = 0';
}
if ( ! is_null( $subscribers ) ) {
if ( ! is_array( $subscribers ) ) {
$subscriber = array( $subscribers );
}
$subscribers = array_filter( $subscribers, 'is_numeric' );
if ( empty( $subscribers ) ) {
$subscribers = array( -1 );
}
$sql .= ' AND a.subscriber_id NOT IN (' . implode( ',', $subscribers ) . ')';
}
return false !== $wpdb->query( $sql );
}
/**
*
*
* @param unknown $subscribers (optional)
* @param unknown $campaign_id (optional)
* @return unknown
*/
public function remove_subscribers( $subscribers, $campaign_id = null ) {
global $wpdb;
$sql = "DELETE a FROM {$wpdb->prefix}mailster_queue AS a WHERE 1";
if ( ! is_null( $campaign_id ) ) {
$sql .= $wpdb->prepare( ' AND a.campaign_id = %d', $campaign_id );
}
if ( ! is_array( $subscribers ) ) {
$subscribers = array( (int) $subscribers );
}
$subscribers = array_filter( $subscribers, 'is_numeric' );
if ( empty( $subscribers ) ) {
$subscribers = array( -1 );
}
$sql .= ' AND a.subscriber_id IN (' . implode( ',', $subscribers ) . ')';
return false !== $wpdb->query( $sql );
}
/**
* clear queue from subscribers who where previously in the queue but no longer assigned to the campaign
*
* @param unknown $campaign_id (optional)
* @param unknown $subscribers (optional)
* @return unknown
*/
public function clear( $campaign_id = null, $subscribers = array() ) {
global $wpdb;
$campaign_id = (int) $campaign_id;
$subscribers = array_filter( $subscribers, 'is_numeric' );
if ( empty( $subscribers ) ) {
$subscribers = array( -1 );
}
$sql = "DELETE queue FROM {$wpdb->prefix}mailster_queue AS queue WHERE queue.sent = 0 AND queue.subscriber_id NOT IN (" . implode( ',', $subscribers ) . ')';
if ( ! is_null( $campaign_id ) ) {
$sql .= $wpdb->prepare( ' AND queue.campaign_id = %d', $campaign_id );
}
return false !== $wpdb->query( $sql );
}
public function cleanup() {
global $wpdb;
// remove all entries from the queue where subscribers are hardbounced
$wpdb->query( "DELETE queue FROM {$wpdb->prefix}mailster_queue AS queue LEFT JOIN {$wpdb->prefix}mailster_action_bounces AS b ON queue.subscriber_id = b.subscriber_id AND queue.campaign_id = b.campaign_id WHERE b.hard = 0 AND queue.requeued = 1 AND queue.sent != 0" );
// remove all entries from the queue where subscribers got queue certain autoresponder and are sent already.
$wpdb->query( "DELETE queue FROM {$wpdb->prefix}mailster_queue AS queue LEFT JOIN {$wpdb->posts} AS p ON p.ID = queue.campaign_id AND p.post_status = 'autoresponder' WHERE sent != 0 AND sent < " . ( time() ) );
// remove all entries from the queue where campaign has been removed
$wpdb->query( "DELETE queue FROM {$wpdb->prefix}mailster_queue AS queue LEFT JOIN {$wpdb->posts} AS p ON p.ID = queue.campaign_id AND p.post_type = 'newsletter' WHERE p.ID IS NULL AND queue.campaign_id != 0" );
}
public function update_status() {
$campaigns = mailster( 'campaigns' )->get_campaigns( array( 'post_status' => array( 'queued' ) ) );
$now = time();
foreach ( $campaigns as $campaign ) {
if ( $campaign->post_status != 'queued' ) {
continue;
}
$timestamp = mailster( 'campaigns' )->meta( $campaign->ID, 'timestamp' );
$timezone = mailster( 'campaigns' )->meta( $campaign->ID, 'timezone' );
// change status to active 24h if user based timezone is enabled
if ( $timestamp - $now <= ( $timezone ? 86400 : 0 ) ) {
mailster( 'campaigns' )->change_status( $campaign, 'active' );
}
}
}
public function update() {
global $wpdb;
// update the regular queue
$campaigns = mailster( 'campaigns' )->get_campaigns( array( 'post_status' => array( 'active' ) ) );
$now = time();
foreach ( $campaigns as $campaign ) {
if ( $campaign->post_status != 'active' ) {
continue;
}
// check if subscribers have to get queue
if ( ! mailster( 'campaigns' )->get_unsent_subscribers( $campaign->ID, array( 1 ), true, true ) ) {
continue;
}
$timestamp = mailster( 'campaigns' )->meta( $campaign->ID, 'timestamp' );
$timezone = mailster( 'campaigns' )->meta( $campaign->ID, 'timezone' );
$offset = 0;
$limit = 100000;
// as long we have subscribers
while ( count( $subscribers = mailster( 'campaigns' )->get_unsent_subscribers( $campaign->ID, array( 1 ), true, true, $limit, $offset ) ) ) {
// get users timeoffsets
if ( $timezone ) {
$timestamp = mailster( 'subscribers' )->get_timeoffset_timestamps( $subscribers, $timestamp );
}
$this->bulk_add( $campaign->ID, $subscribers, $timestamp, 10, false );
$offset += $limit;
}
}
}
/**
*
*
* @param unknown $campaign_id (optional)
* @param unknown $force (optional)
*/
public function autoresponder( $campaign_id = null, $force = false ) {
global $wpdb;
static $mailster_autoresponder;
if ( ! isset( $mailster_autoresponder ) ) {
$mailster_autoresponder = array();
}
$campaigns = empty( $campaign_id ) ? mailster( 'campaigns' )->get_autoresponder() : array( mailster( 'campaigns' )->get( $campaign_id ) );
if ( empty( $campaigns ) ) {
return;
}
$now = time();
$timeoffset = mailster( 'helper' )->gmt_offset( true );
foreach ( $campaigns as $campaign ) {
if ( $campaign->post_status != 'autoresponder' ) {
continue;
}
if ( in_array( $campaign->ID, $mailster_autoresponder ) && ! $force ) {
continue;
}
$mailster_autoresponder[] = $campaign->ID;
$meta = mailster( 'campaigns' )->meta( $campaign->ID );
if ( ! $meta['active'] ) {
$this->remove( $campaign->ID );
continue;
}
$autoresponder_meta = $meta['autoresponder'];
if ( is_numeric( $autoresponder_meta['unit'] ) ) {
mailster_notice( sprintf( 'Auto responder campaign %s has been deactivated caused by an old timeformat. Please update your campaign!', '<strong>"<a href="post.php?post=' . $campaign->ID . '&action=edit">' . $campaign->post_title . '</a>"</strong>' ), 'error', false, 'camp_error_' . $campaign->ID, $campaign->post_author );
mailster( 'campaigns' )->update_meta( $campaign->ID, 'active', false );
continue;
}
$grace_period = WEEK_IN_SECONDS;
/**
* Filter the grace period from campaigns to decide the time when user no longer get the campaign.
*
* default: 604800 (one week)
*
* @param int $grace_period The grace period in seconds. set to false to disable
* @param int $campaign_id the campaign id
*/
$grace_period = apply_filters( 'mailster_autoresponder_grace_period', $grace_period, $campaign );
$queue_upfront = 3600;
if ( 'mailster_subscriber_insert' == $autoresponder_meta['action'] ) {
$offset = (int) $autoresponder_meta['amount'] . ' ' . strtoupper( $autoresponder_meta['unit'] );
$list_based = mailster( 'campaigns' )->list_based_opt_out( $campaign->ID );
$ignore_lists = $meta['ignore_lists'];
$lists = ! $ignore_lists ? (array) $meta['lists'] : true;
$conditions = ! empty( $meta['list_conditions'] ) ? $meta['list_conditions'] : null;
$query_args = array(
'select' => array(
'subscribers.ID',
"UNIX_TIMESTAMP ( FROM_UNIXTIME( IFNULL(lists_subscribers.added, IF(subscribers.confirm, subscribers.confirm, subscribers.signup)) ) + INTERVAL $offset ) AS autoresponder_timestamp",
),
'sent__not_in' => $campaign->ID,
'queue__not_in' => $campaign->ID,
'lists' => $lists,
'conditions' => $conditions,
'where' => array(
'(subscribers.confirm != 0 OR subscribers.signup != 0)',
),
'having' => array( 'autoresponder_timestamp <= ' . ( $now + $queue_upfront ) ),
'orderby' => 'autoresponder_timestamp',
);
if ( $grace_period ) {
$query_args['having'][] = 'autoresponder_timestamp >= ' . ( $now - $grace_period );
}
if ( $ignore_lists ) {
$query_args['where'][] = '(subscribers.signup >= ' . (int) $meta['timestamp'] . ')';
} else {
$query_args['where'][] = '(subscribers.signup >= ' . (int) $meta['timestamp'] . ' OR lists_subscribers.added >= ' . (int) $meta['timestamp'] . ')';
$query_args['where'][] = 'lists_subscribers.added != 0';
}
$query_args = apply_filters( 'mailster_autoresponder_hook_args', $query_args, $campaign->ID );
$subscribers = mailster( 'subscribers' )->query( $query_args, $campaign->ID );
if ( ! empty( $subscribers ) ) {
$subscriber_ids = wp_list_pluck( $subscribers, 'ID' );
$timestamps = wp_list_pluck( $subscribers, 'autoresponder_timestamp' );
$this->bulk_add( $campaign->ID, $subscriber_ids, $timestamps, 15 );
$timestamp = min( $timestamps );
// handle instant delivery
if ( $timestamp - time() <= 0 ) {
wp_schedule_single_event( $timestamp, 'mailster_cron_worker', array( $campaign->ID ) );
}
}
} elseif ( 'mailster_subscriber_unsubscribed' == $autoresponder_meta['action'] ) {
$offset = (int) $autoresponder_meta['amount'] . ' ' . strtoupper( $autoresponder_meta['unit'] );
$conditions = ! empty( $meta['list_conditions'] ) ? $meta['list_conditions'] : null;
$query_args = array(
'select' => array( 'subscribers.ID', "UNIX_TIMESTAMP ( FROM_UNIXTIME( actions_unsubscribe.timestamp ) + INTERVAL $offset ) AS autoresponder_timestamp" ),
'status' => array( 1, 2 ),
'unsubscribe' => -1,
'sent__not_in' => $campaign->ID,
'queue__not_in' => $campaign->ID,
'lists' => ( empty( $meta['ignore_lists'] ) && ! empty( $meta['lists'] ) ) ? $meta['lists'] : false,
'conditions' => $conditions,
'having' => array( 'autoresponder_timestamp <= ' . ( $now + $queue_upfront ) ),
'orderby' => 'autoresponder_timestamp',
);
if ( $grace_period ) {
$query_args['having'][] = 'autoresponder_timestamp >= ' . ( $now - $grace_period );
}
$query_args = apply_filters( 'mailster_autoresponder_hook_args', $query_args, $campaign->ID );
$subscribers = mailster( 'subscribers' )->query( $query_args, $campaign->ID );
if ( ! empty( $subscribers ) ) {
$subscriber_ids = wp_list_pluck( $subscribers, 'ID' );
$timestamps = wp_list_pluck( $subscribers, 'autoresponder_timestamp' );
$this->bulk_add( $campaign->ID, $subscriber_ids, $timestamps, 15, false, true );
$timestamp = min( $timestamps );
// handle instant delivery
if ( $timestamp - time() <= 0 ) {
wp_schedule_single_event( $timestamp, 'mailster_cron_worker', array( $campaign->ID ) );
}
}
} elseif ( 'mailster_autoresponder_followup' == $autoresponder_meta['action'] && $campaign->post_parent ) {
$offset = (int) $autoresponder_meta['amount'] . ' ' . strtoupper( $autoresponder_meta['unit'] );
$conditions = ! empty( $meta['list_conditions'] ) ? $meta['list_conditions'] : null;
$query_args = array(
'select' => array( 'subscribers.ID' ),
'sent__not_in' => $campaign->ID,
'queue__not_in' => $campaign->ID,
'lists' => ( empty( $meta['ignore_lists'] ) && ! empty( $meta['lists'] ) ) ? $meta['lists'] : false,
'conditions' => $conditions,
'having' => array( 'autoresponder_timestamp <= ' . ( $now + $queue_upfront ) ),
'orderby' => 'autoresponder_timestamp',
);
switch ( $autoresponder_meta['followup_action'] ) {
case 1:
$query_args['select'][] = "UNIX_TIMESTAMP( FROM_UNIXTIME ( actions_sent_1_0.timestamp) + INTERVAL $offset ) AS autoresponder_timestamp";
$query_args['select'][] = 'actions_sent_1_0.i AS campaign_index';
$query_args['groupby'] = false;
$query_args['sent'] = $campaign->post_parent;
break;
case 2:
$query_args['select'][] = "UNIX_TIMESTAMP( FROM_UNIXTIME ( actions_open_0_0.timestamp) + INTERVAL $offset ) AS autoresponder_timestamp";
$query_args['select'][] = 'actions_open_0_0.i AS campaign_index';
$query_args['groupby'] = false;
$query_args['open'] = $campaign->post_parent;
break;
case 3:
$query_args['select'][] = "UNIX_TIMESTAMP( FROM_UNIXTIME ( actions_click_0_0.timestamp) + INTERVAL $offset ) AS autoresponder_timestamp";
$query_args['select'][] = 'actions_click_0_0.i AS campaign_index';
$query_args['groupby'] = false;
$query_args['click'] = $campaign->post_parent;
break;
}
if ( $grace_period ) {
$query_args['having'][] = 'autoresponder_timestamp >= ' . ( $now - $grace_period );
}
$query_args = apply_filters( 'mailster_autoresponder_hook_args', $query_args, $campaign->ID );
$subscribers = mailster( 'subscribers' )->query( $query_args, $campaign->ID );
if ( ! empty( $subscribers ) ) {
$subscriber_ids = wp_list_pluck( $subscribers, 'ID' );
$timestamps = wp_list_pluck( $subscribers, 'autoresponder_timestamp' );
$index = wp_list_pluck( $subscribers, 'campaign_index' );
$priority = 15;
$clear = false;
$ignore_status = false;
$reset = false;
$options = false;
$tags = false;
$this->bulk_add( $campaign->ID, $subscriber_ids, $timestamps, $priority, $clear, $ignore_status, $reset, $options, $tags, $index );
$timestamp = min( $timestamps );
// handle instant delivery
if ( $timestamp - time() <= 0 ) {
wp_schedule_single_event( $timestamp, 'mailster_cron_worker', array( $campaign->ID ) );
}
}
} elseif ( 'mailster_post_published' == $autoresponder_meta['action'] && $autoresponder_meta['post_type'] == 'rss' ) {
if ( preg_match_all( '#<module[^>]*?data-rss="(.*?)".*?</module>#ms', $campaign->post_content, $hits ) ) {
$feed_urls = array_unique( $hits[1] );
foreach ( $feed_urls as $feed_url ) {
$feeds = mailster( 'helper' )->get_feed_since( $autoresponder_meta['since'], $feed_url );
if ( is_wp_error( $feeds ) || empty( $feeds ) ) {
continue;
}
$count = count( $feeds );
$last = strtotime( $feeds[0]->post_date_gmt );
$created = 0;
foreach ( $feeds as $i => $feed ) {
$autoresponder_meta['post_count_status']++;
if ( ! ( $autoresponder_meta['post_count_status'] % ( $autoresponder_meta['post_count'] + 1 ) ) ) {
$integer = floor( $autoresponder_meta['amount'] );
$decimal = $autoresponder_meta['amount'] - $integer;
$send_offset = ( strtotime( '+' . $integer . ' ' . $autoresponder_meta['unit'], 0 ) + ( strtotime( '+1 ' . $autoresponder_meta['unit'], 0 ) * $decimal ) );
// multiply the offset with the number of created campaigns
$send_offset = $send_offset * ( $created + 1 );
// define an offset to move the "pointer" in the feed
$index_offset = $count - $i - 1;
// sleep one second if multiples are created to prevent the same timestamps
if ( $created ) {
sleep( 1 );
}
if ( $new_id = mailster( 'campaigns' )->autoresponder_to_campaign( $campaign->ID, $send_offset, $autoresponder_meta['issue'], false, $index_offset ) ) {
$created++;
$new_campaign = mailster( 'campaigns' )->get( $new_id );
$autoresponder_meta['issue']++;
mailster_notice( sprintf( __( 'New campaign %1$s has been created and is going to be sent on %2$s.', 'mailster' ), '<strong>"<a href="post.php?post=' . esc_attr( $new_campaign->ID ) . '&action=edit">' . esc_html( $new_campaign->post_title ) . '</a>"</strong>', '<strong>' . date_i18n( mailster( 'helper' )->timeformat(), $now + $send_offset + $timeoffset ) . '</strong>' ), 'info', true );
do_action( 'mailster_autoresponder_post_published', $campaign->ID, $new_id );
}
}
}
if ( $count ) {
$autoresponder_meta['since'] = $last;
// do not create more than one campaign here.
break;
}
}
mailster( 'campaigns' )->update_meta( $campaign->ID, 'autoresponder', $autoresponder_meta );
}
}
}
}
/**
*
*
* @param unknown $campaign_id (optional)
* @param unknown $force (optional)
*/
public function autoresponder_timebased( $campaign_id = null, $force = false ) {
global $wpdb;
static $mailster_autoresponder;
if ( ! isset( $mailster_autoresponder ) ) {
$mailster_autoresponder = array();
}
$campaigns = empty( $campaign_id ) ? mailster( 'campaigns' )->get_autoresponder() : array( mailster( 'campaigns' )->get( $campaign_id ) );
if ( empty( $campaigns ) ) {
return;
}
$now = time();
$timeoffset = mailster( 'helper' )->gmt_offset( true );
foreach ( $campaigns as $campaign ) {
if ( $campaign->post_status != 'autoresponder' ) {
continue;
}
if ( in_array( $campaign->ID, $mailster_autoresponder ) && ! $force ) {
continue;
}
$mailster_autoresponder[] = $campaign->ID;
$meta = mailster( 'campaigns' )->meta( $campaign->ID );
$autoresponder_meta = $meta['autoresponder'];
if ( 'mailster_autoresponder_timebased' != $autoresponder_meta['action'] ) {
continue;
}
if ( ! $meta['active'] ) {
$this->remove( $campaign->ID );
continue;
}
$time_conditions = isset( $autoresponder_meta['time_conditions'] );
$new_content_since = isset( $autoresponder_meta['since'] ) ? (int) $autoresponder_meta['since'] : false;
$starttime = $meta['timestamp'];
$delay = $starttime - $now;
// check if endtime is passed.
if ( isset( $autoresponder_meta['endschedule'] ) && $autoresponder_meta['endtimestamp'] && $autoresponder_meta['endtimestamp'] - $now < 0 ) {
// disable this campaign
mailster( 'campaigns' )->update_meta( $campaign->ID, 'active', false );
mailster_notice( sprintf( esc_html__( 'Auto responder campaign %s has been finished and is deactivated!', 'mailster' ), '<strong>"<a href="post.php?post=' . $campaign->ID . '&action=edit">' . $campaign->post_title . '</a>"</strong>' ), 'success', false, 'autoresponder_' . $campaign_id, $campaign->post_author );
continue;
}
// seconds the campaign should created before the actual send time.
$time_created_before = 3600;
// add a day if timezone based sending is enabled
if ( $meta['timezone'] ) {
// $time_created_before += DAY_IN_SECONDS;
}
// do it if time is over.
$doit = $delay <= $time_created_before;
// do not schedule new by default.
$schedule_new = false;
// check for conditions "only if [time_post_count] [post_type] have been published."
if ( $doit && $time_conditions ) {
if ( 'rss' == $autoresponder_meta['time_post_type'] ) {
if ( preg_match_all( '#<module[^>]*?data-rss="(.*?)".*?</module>#ms', $campaign->post_content, $hits ) ) {
$feed_urls = array_unique( $hits[1] );
foreach ( $feed_urls as $feed_url ) {
if ( $posts = mailster( 'helper' )->get_feed_since( $new_content_since, $feed_url ) ) {
if ( $autoresponder_meta['post_count_status'] = count( $posts ) ) {
mailster( 'campaigns' )->update_meta( $campaign->ID, 'autoresponder', $autoresponder_meta );
break;
}
}
}
}
} else {
}
// if post count is reached
if ( $autoresponder_meta['post_count_status'] >= $autoresponder_meta['time_post_count'] ) {
// reduce counter with required posts counter
$autoresponder_meta['post_count_status'] = $autoresponder_meta['post_count_status'] - $autoresponder_meta['time_post_count'];
} else {
// schedule new event if it's in the past
$schedule_new = $delay < 0;
$doit = false;
}
}
// check if modules with content exist | "only if new content is available."
if ( $doit && $new_content_since ) {
$placeholder = mailster( 'placeholder', $campaign->post_content );
$placeholder->set_campaign( $campaign->ID );
$placeholder->rss_since( $new_content_since );
if ( $placeholder->has_content( true ) ) {
// has content.
} else {
// schedule new event if it's in the past.
$schedule_new = $delay < 0;
$doit = false;
}
}
if ( $doit && $new_id = mailster( 'campaigns' )->autoresponder_to_campaign( $campaign->ID, $delay, $autoresponder_meta['issue']++ ) ) {
$newCamp = mailster( 'campaigns' )->get( $new_id );
mailster_notice( sprintf( esc_html__( 'New campaign %s has been created!', 'mailster' ), '<strong>"<a href="post.php?post=' . $newCamp->ID . '&action=edit">' . $newCamp->post_title . '</a>"</strong>' ), 'info', true, 'autoresponder_' . $campaign->ID, $campaign->post_author );
$schedule_new = true;
if ( $new_content_since ) {
$autoresponder_meta['since'] = $now;
}
do_action( 'mailster_autoresponder_timebased', $campaign->ID, $new_id );
// fix since 3.1.1 where Sunday was stored as "7" (should be "0")
if ( isset( $autoresponder_meta['weekdays'] ) && ( $key = array_search( 7, $autoresponder_meta['weekdays'] ) ) !== false ) {
unset( $autoresponder_meta['weekdays'][ $key ] );
$autoresponder_meta['weekdays'][] = 0;
}
mailster( 'campaigns' )->update_meta( $campaign->ID, 'autoresponder', $autoresponder_meta );
}
if ( $schedule_new ) {
$nextdate = mailster( 'helper' )->get_next_date_in_future( $starttime, $autoresponder_meta['interval'], $autoresponder_meta['time_frame'], $autoresponder_meta['weekdays'] );
mailster( 'campaigns' )->update_meta( $campaign->ID, 'timestamp', $nextdate );
}
}
}
/**
*
*
* @param unknown $campaign_id (optional)
* @param unknown $force (optional)
*/
public function autoresponder_usertime( $campaign_id = null, $force = false ) {
global $wpdb;
static $mailster_autoresponder;
if ( ! isset( $mailster_autoresponder ) ) {
$mailster_autoresponder = array();
}
$campaigns = empty( $campaign_id ) ? mailster( 'campaigns' )->get_autoresponder() : array( mailster( 'campaigns' )->get( $campaign_id ) );
$now = time();
$timeoffset = mailster( 'helper' )->gmt_offset( true );
foreach ( $campaigns as $campaign ) {
if ( $campaign->post_status != 'autoresponder' ) {
continue;
}
if ( in_array( $campaign->ID, $mailster_autoresponder ) && ! $force ) {
continue;
}
$mailster_autoresponder[] = $campaign->ID;
$meta = mailster( 'campaigns' )->meta( $campaign->ID );
$autoresponder_meta = $meta['autoresponder'];
if ( 'mailster_autoresponder_usertime' != $autoresponder_meta['action'] ) {
continue;
}
if ( ! $meta['active'] ) {
$this->remove( $campaign->ID );
continue;
}
$timezone_based = $meta['timezone'];
$date_fields = mailster()->get_custom_date_fields( true );
if ( ! in_array( $autoresponder_meta['uservalue'], $date_fields ) ) {
mailster_notice( sprintf( 'Auto responder campaign %s has been deactivated caused by a missing date field. Please update your campaign!', '<strong>"<a href="post.php?post=' . $campaign->ID . '&action=edit">' . $campaign->post_title . '</a>"</strong>' ), 'error', false, 'camp_error_' . $campaign->ID, $campaign->post_author );
mailster( 'campaigns' )->update_meta( $campaign->ID, 'active', false );
$this->remove( $campaign->ID );
continue;
}
$integer = floor( $autoresponder_meta['amount'] );
$decimal = $autoresponder_meta['amount'] - $integer;
$once = isset( $autoresponder_meta['once'] ) && $autoresponder_meta['once'];
$exact_date = isset( $autoresponder_meta['userexactdate'] ) && $autoresponder_meta['userexactdate'];
$send_offset = ( strtotime( '+' . $integer . ' ' . $autoresponder_meta['unit'], 0 ) + ( strtotime( '+1 ' . $autoresponder_meta['unit'], 0 ) * $decimal ) ) * $autoresponder_meta['before_after'];
$subscriber_ids = array();
$timestamps = array();
$offsettimestamp = strtotime( '+' . ( -1 * $send_offset ) . ' seconds', strtotime( 'tomorrow midnight' ) ) + $timeoffset;
if ( $exact_date ) {
$cond = array(
'field' => $autoresponder_meta['uservalue'],
'operator' => '=',
'value' => date( 'Y-m-d', $offsettimestamp ),
);
} else {
switch ( $autoresponder_meta['userunit'] ) {
case 'year':
$cond = array(
'field' => $autoresponder_meta['uservalue'],
'operator' => '$',
'value' => date( '-m-d', $offsettimestamp ),
);
break;
case 'month':
$cond = array(
'field' => $autoresponder_meta['uservalue'],
'operator' => '$',
'value' => date( '-d', $offsettimestamp ),
);
break;
default:
$cond = array(
'field' => $autoresponder_meta['uservalue'],
'operator' => '!=',
'value' => '',
);
break;
}
}
if ( $meta['ignore_lists'] ) {
$lists = false;
} else {
$lists = $meta['lists'];
}
$conditions = ! empty( $meta['list_conditions'] ) ? $meta['list_conditions'] : array();
$conditions[] = array( $cond );
$query_args = array(
'fields' => array( 'ID', $autoresponder_meta['uservalue'] ),
'lists' => $lists,
'conditions' => $conditions,
'sent__not_in' => $once ? $campaign->ID : false,
'queue__not_in' => $campaign->ID,
'orderby' => $autoresponder_meta['uservalue'],
);
$subscribers = mailster( 'subscribers' )->query( $query_args, $campaign->ID );
foreach ( $subscribers as $subscriber ) {
$nextdate = strtotime( $subscriber->{$autoresponder_meta['uservalue']} ) + $send_offset - $timeoffset;
// in the past already so get next date in future
if ( $nextdate - $now < 0 && ! $exact_date ) {
$nextdate = mailster( 'helper' )->get_next_date_in_future( $nextdate, $autoresponder_meta['useramount'], $autoresponder_meta['userunit'] );
}
$timedelay = $nextdate - $now;
if ( $timedelay < ( $timezone_based ? 86400 : 3600 ) && $timedelay >= 0 ) {
$subscriber_ids[] = $subscriber->ID;
$timestamps[] = $nextdate;
}
}
if ( ! empty( $subscriber_ids ) ) {
if ( $timezone_based ) {
$timestamps = mailster( 'subscribers' )->get_timeoffset_timestamps( $subscriber_ids, $timestamps );
}
$this->bulk_add( $campaign->ID, $subscriber_ids, $timestamps, 15 );
do_action( 'mailster_autoresponder_usertime', $campaign->ID, $subscriber_ids );
}
}
}
public function finish_campaigns() {
global $wpdb;
// remove not sent queues which have a wrong status
$wpdb->query( "DELETE a FROM {$wpdb->prefix}mailster_queue AS a LEFT JOIN {$wpdb->prefix}mailster_subscribers AS b ON a.subscriber_id = b.ID WHERE (a.sent = 0 OR a.ignore_status = 1) AND b.status != 1 AND a.campaign_id != 0" );
// select all active campaigns
$sql = "SELECT posts.ID, queue.sent FROM {$wpdb->prefix}posts AS posts LEFT JOIN {$wpdb->prefix}mailster_queue AS queue ON posts.ID = queue.campaign_id LEFT JOIN {$wpdb->prefix}mailster_action_sent AS actions ON actions.subscriber_id = queue.subscriber_id AND actions.campaign_id = queue.campaign_id WHERE posts.post_status IN ('active') AND posts.post_type = 'newsletter' AND queue.requeued = 0 GROUP BY posts.ID HAVING SUM(queue.sent = 0) = 0 OR queue.sent IS NULL";
$ids = $wpdb->get_col( $sql );
foreach ( $ids as $id ) {
$totals = mailster( 'campaigns' )->get_totals( $id );
$sent = mailster( 'campaigns' )->get_sent( $id );
if ( ! $totals || ! $sent ) {
continue;
}
mailster( 'campaigns' )->finish( $id );
}
// remove notifications which are sent over an hour ago
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}mailster_queue WHERE sent != 0 AND campaign_id = 0 AND sent < %d", ( time() - 3600 ) ) );
}
/**
*
*
* @param unknown $campaign_id (optional)
* @param unknown $process_id (optional)
* @return unknown
*/
public function progress( $campaign_id = null, $process_id = 0 ) {
global $wpdb;
$last_hit = get_option( 'mailster_cron_lasthit' );
$db_version = get_option( 'mailster_dbversion' );
if ( $db_version != MAILSTER_DBVERSION ) {
echo '<h2>' . esc_html__( 'Database update required!', 'mailster' ) . '</h2>';
if ( is_user_logged_in() ) {
$redirectto = admin_url( 'admin.php?page=mailster_update' );
$update_msg = '<p><strong>' . esc_html__( 'An additional update is required for Mailster!', 'mailster' ) . '</strong></p><a class="button button-primary" href="' . $redirectto . '" target="_top">' . esc_html__( 'Progress Update now', 'mailster' ) . '</a>';
echo $update_msg;
}
return false;
}
if ( ( $pid = mailster( 'cron' )->lock( $process_id ) ) !== true ) {
echo '<h2>' . esc_html__( 'Cron Lock Enabled!', 'mailster' ) . '</h2>';
$sec = isset( $last_hit['timestamp'] ) ? ( round( time() - $last_hit['timestamp'] ) ) : 0;
if ( is_user_logged_in() ) {
echo '<p>' . esc_html__( 'Another process is currently running the cron process and you have been temporary blocked to prevent duplicate emails getting sent out.', 'mailster' ) . '</p>';
echo '<p>' . sprintf( esc_html__( 'Read more about Cron Locks %s.', 'mailster' ), '<a href="' . mailster_url( 'https://kb.mailster.co/what-is-a-cron-lock/' ) . '">' . esc_html__( 'here', 'mailster' ) . '</a>' );
if ( $last_hit ) {
echo '<p>' . sprintf(
esc_html__( 'Cron Lock requested %s ago from:', 'mailster' ),
'<strong>' . ( $sec > 60 ? human_time_diff( time() + $sec ) : sprintf( esc_html__( _n( '%d second', '%d seconds', $sec, 'mailster' ) ), $sec ) ) . '</strong>'
) . '</p>';
echo '<p><strong>IP: ' . $last_hit['ip'] . '<br>PID: ' . $pid . '<br>' . $last_hit['user'] . '</strong></p>';
}
}
// unlock?
$unlock = apply_filters( 'mailster_unlock_cron', false );
if ( $last_hit && $last_hit['time'] && mailster_option( 'auto_send_at_once' ) && ! get_transient( 'mailster_cron_lock_triggered_' . $process_id ) ) {
set_transient( 'mailster_cron_lock_triggered_' . $process_id, time() );
}
if ( $unlock && $sec > max( 600, $unlock ) ) {
// unlock automatically but with a minimum of 10 minutes
mailster( 'cron' )->unlock( $process_id );
echo '<h2>' . esc_html__( 'Cron Lock has been released!', 'mailster' ) . '</h2>';
} else {
return false;
}
}
if ( $campaign_id && ! is_array( $campaign_id ) ) {
$campaign_id = array( $campaign_id );
}
$microtime = microtime( true );
$globaltime = isset( $GLOBALS['time_start'] ) ? $GLOBALS['time_start'] : $microtime;
$timeoffset = mailster( 'helper' )->gmt_offset( true );
if ( empty( $last_hit ) ) {
$last_hit = array(
'timestamp' => (int) $microtime,
'time' => 0,
'timemax' => 0,
'mail' => 0,
);
}
$last_hit = array(
'ip' => mailster_get_ip(),
'timestamp' => (int) $microtime,
'user' => isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : 'unknown',
'oldtimestamp' => $last_hit['timestamp'],
'time' => $last_hit['timemax'],
'timemax' => $last_hit['timemax'],
'mail' => $last_hit['mail'],
);
update_option( 'mailster_cron_lasthit', $last_hit );
$memory_limit = ini_get( 'memory_limit' );
$max_execution_time_ini = ini_get( 'max_execution_time' );
ignore_user_abort( true );
set_time_limit( 0 );
$send_at_once = mailster_option( 'send_at_once' );
$max_bounces = mailster_option( 'bounce_attempts' );
$max_execution_time = mailster_option( 'max_execution_time', 0 );
$delay_time = 0;
$in_timeframe = mailster( 'helper' )->in_timeframe();
$sent_this_turn = 0;
$send_delay = mailster_option( 'send_delay', 0 ) / 1000;
$mail_send_time = 0;
$MID = mailster_option( 'ID' );
$unsubscribe_homepage = apply_filters( 'mailster_unsubscribe_link', ( get_page( mailster_option( 'homepage' ) ) ? get_permalink( mailster_option( 'homepage' ) ) : get_bloginfo( 'url' ) ) );
$campaign_errors = array();
$to_send = $this->size( $microtime, $campaign_id );
$queue_update_sql = "UPDATE {$wpdb->prefix}mailster_queue SET sent = %d, error = %d, priority = %d, count = %d WHERE subscriber_id = %d AND campaign_id = %d AND requeued = %d AND options = %s AND i = %d LIMIT 1";
$this->cron_log( 'UTC', '<strong>' . date( 'Y-m-d H:i:s' ) . ' - ' . time() . '</strong>' );
$this->cron_log( 'Local Time', '<strong>' . date( 'Y-m-d H:i:s', time() + $timeoffset ) . '</strong>' );
if ( $memory_limit ) {
$this->cron_log( 'memory limit', '<strong>' . (int) $memory_limit . ' MB</strong>' );
}
$this->cron_log( 'max_execution_time', '<strong>' . $max_execution_time_ini . ' seconds</strong>' );
$this->cron_log( 'queue size', '<strong>' . number_format_i18n( $to_send ) . ' mails</strong>' );
if ( $warmup = mailster_option( 'warmup' ) ) {
$diff_day = ( $warmup - time() ) / DAY_IN_SECONDS;
$p_days = min( 1, 1 - $diff_day / 30 );
if ( $p_days == 1 ) {
mailster_force_update_option( 'warmup', false );
}
$send_at_once_limit = ceil( $send_at_once * $p_days );
$this->cron_log( 'send max at once', '<strong>' . number_format_i18n( $send_at_once_limit ) . '</strong> (' . ( round( 100 * $p_days ) ) . '% of ' . $send_at_once . ') Warmup ends in ' . human_time_diff( $warmup ) );
} else {
$p_days = 1;
$this->cron_log( 'send max at once', '<strong>' . number_format_i18n( $send_at_once ) . '</strong>' );
$send_at_once_limit = $send_at_once;
}
if ( $to_send && ! $in_timeframe ) {
$this->cron_log( 'System Error', '<span class="error">' . esc_html__( 'Not in time frame!', 'mailster' ) . '</span><br><span class="error">' . sprintf( esc_html__( 'Please check the %s on the delivery tab.', 'mailster' ), '<a href="' . admin_url( 'edit.php?post_type=newsletter&page=mailster_settings#delivery' ) . '" target="_blank">' . esc_html__( 'time frame settings', 'mailster' ) . '</a>' ) . '</span>' );
}
if ( $in_timeframe && $to_send ) {
$sql = 'SELECT queue.campaign_id, queue.count AS _count, queue.requeued AS _requeued, queue.options AS _options, queue.tags AS _tags, queue.priority AS _priority, subscribers.ID AS subscriber_id, subscribers.status, subscribers.email, subscribers.rating, queue.i AS _i';
$sql .= " FROM {$wpdb->prefix}mailster_queue AS queue";
$sql .= " LEFT JOIN {$wpdb->posts} AS posts ON posts.ID = queue.campaign_id";
$sql .= " LEFT JOIN {$wpdb->prefix}mailster_subscribers AS subscribers ON subscribers.ID = queue.subscriber_id";
// time is in the past and errors are within the range
$sql .= ' WHERE queue.timestamp <= ' . (int) $microtime . " AND queue.sent = 0 AND queue.error < {$this->max_retry_after_error}";
// post status is important or is '0' (transactional email)
$sql .= " AND (posts.post_status IN ('finished', 'active', 'queued', 'autoresponder', 'notification') OR queue.campaign_id = 0)";
// subscriber status is 1 (subscribed) or ignore_status
$sql .= ' AND (subscribers.status = 1 OR queue.ignore_status = 1)';
// subscriber exists or is not subscriber_id
$sql .= ' AND (subscribers.ID IS NOT NULL OR queue.subscriber_id = 0)';
if ( $campaign_id ) {
$campaign_id = array_filter( $campaign_id, 'is_numeric' );
$sql .= ' AND queue.campaign_id IN (' . implode( ', ', $campaign_id ) . ')';
}
$sql .= ' ORDER BY queue.priority DESC, subscribers.rating DESC, queue.ID';
$sql .= ! mailster_option( 'split_campaigns' ) ? ', queue.campaign_id ASC' : '';
$sql .= " LIMIT $send_at_once_limit";
$queue_result = $wpdb->get_results( $sql );
if ( $wpdb->last_error ) {
$this->cron_log( 'DB Error', ' <span class="error">' . $wpdb->last_error . '</span>' );
mailster( 'upgrade' )->create_primary_keys( 'queue' );
mailster()->dbstructure();
}
$queue_result_count = count( $queue_result );
$this->cron_log( 'subscribers found', '<strong>' . number_format_i18n( $queue_result_count ) . '</strong>' );
$this->cron_log();
$this->cron_log( '#', 'email', 'campaign', 'try', 'time (sec.)' );
foreach ( $queue_result as $i => $data ) {
if ( connection_aborted() ) {
break;
}
if ( $max_execution_time && microtime( true ) - $globaltime > $max_execution_time - 1 ) {
$this->cron_log( '', ' <span class="error">' . esc_html__( 'timeout reached', 'mailster' ) . '</span>', '', '', '' );
if ( ! $sent_this_turn ) {
mailster_notice( sprintf( esc_html__( 'Mailster is not able to send your campaign cause of a server timeout. Please increase the %1$s on the %2$s', 'mailster' ), '<strong>"' . esc_html__( 'Max. Execution Time', 'mailster' ) . '"</strong>', '<a href="edit.php?post_type=newsletter&page=mailster_settings&mailster_remove_notice=max_execution_time#delivery">' . esc_html__( 'settings page', 'mailster' ) . '</a>' ), 'error', false, 'max_execution_time' );
}
break;
}
$send_start_time = microtime( true );
$data = apply_filters( 'mailster_queue_campaign_subscriber_data', $data );
if ( $data->campaign_id ) {
if ( ! $data->_requeued ) {
// prevent to send duplicates within one minute
if ( false && $duplicate = $wpdb->get_results( $wpdb->prepare( "SELECT subscriber_id FROM {$wpdb->prefix}mailster_action_sent WHERE campaign_id = %d AND subscriber_id = %d && `timestamp` > %d", $data->campaign_id, $data->subscriber_id, time() - 60 ) ) ) {
$this->cron_log( '', ' <span class="error">' . $data->subscriber_id . ' ' . $data->email . '<br>' . esc_html__( 'Prevent to send duplicate within one minute.', 'mailster' ) . '</span>', $data->campaign_id, $data->_count, '' );
continue;
}
}
// if ( ! $wpdb->get_var( $wpdb->prepare( "SELECT ID from {$wpdb->posts} WHERE ID = %d AND post_status = %s", $data->campaign_id, 'active' ) ) ) {
// array_push( $campaign_errors, $data->campaign_id );
// }
if ( in_array( $data->campaign_id, $campaign_errors ) ) {
continue;
}
$tags = ! empty( $data->_tags ) ? maybe_unserialize( $data->_tags ) : array();
// regular campaign
$result = mailster( 'campaigns' )->send( $data->campaign_id, $data->subscriber_id, null, false, true, $tags );
$options = false;
} elseif ( $data->_options ) {
if ( $options = maybe_unserialize( $data->_options ) ) {
$options = wp_parse_args( $options, array( 'template' => '' ) );
$result = mailster( 'notification' )->send( $data->subscriber_id, $options );
} else {
continue;
}
} else {
continue;
}
$took = microtime( true ) - $send_start_time;
// success
if ( ! is_wp_error( $result ) ) {
$mail_send_time += $took;
$wpdb->query( $wpdb->prepare( $queue_update_sql, time(), 0, $data->_priority, $data->_count, $data->subscriber_id, $data->campaign_id, $data->_requeued, $data->_options, $data->_i ) );
if ( ! $options ) {
$this->cron_log( $i + 1, $data->subscriber_id . ' ' . $data->email, $data->campaign_id, $data->_count, $took > 2 ? '<span class="error">' . $took . '</span>' : $took );
} else {
$this->cron_log( $i + 1, print_r( $options, true ), $options['template'], $data->_count, $took > 2 ? '<span class="error">' . $took . '</span>' : $took );
}
$sent_this_turn++;
// error
} else {
$this->cron_log( $i + 1, '<span class="error">' . $data->subscriber_id . ' ' . $data->email . '</span>', $data->campaign_id ? $data->campaign_id : $options['template'], $data->_count, $took > 2 ? '<span class="error">' . $took . '</span>' : $took );
$this->cron_log( '', ' <span class="error">[' . $result->get_error_code() . '] ' . $result->get_error_message() . '</span>', '', '', '' );
// user_error
if ( $result->get_error_code() == 'user_error' ) {
$error = $data->_count >= $this->max_retry_after_error;
$wpdb->query( $wpdb->prepare( $queue_update_sql, 0, $data->_count, 15, $data->_count + 1, $data->subscriber_id, $data->campaign_id, $data->_requeued, $data->_options, $data->_i ) );
if ( $error ) {
do_action( 'mailster_subscriber_error', $data->subscriber_id, $data->campaign_id, $result->get_error_message() );
mailster( 'subscribers' )->change_status( $data->subscriber_id, 4 );
}
// notification_error
} elseif ( $result->get_error_code() == 'notification_error' ) {
$error = $data->_count >= $this->max_retry_after_error;
$wpdb->query( $wpdb->prepare( $queue_update_sql, 0, $data->_count, 15, $data->_count + 1, $data->subscriber_id, $data->campaign_id, $data->_requeued, $data->_options, $data->_i ) );
if ( $error ) {
if ( isset( $options['template'] ) && $options['template'] ) {
mailster_notice( sprintf( esc_html__( 'Notification %1$s has thrown an error: %2$s', 'mailster' ), '<strong>"' . $options['template'] . '"</strong>', '<strong>' . implode( '', $result->get_error_messages() ) ) . '</strong>', 'error', 7200, 'notification_error_' . $options['template'] );
}
do_action( 'mailster_notification_error', $data->subscriber_id, $result->get_error_message() );
}
// campaign_error
} elseif ( $result->get_error_code() == 'error' ) {
$campaign = mailster( 'campaigns' )->get( $data->campaign_id );
if ( $campaign->post_status == 'autoresponder' ) {
mailster_notice( sprintf( esc_html__( 'Autoresponder %1$s has caused sending error: %2$s', 'mailster' ), '<a href="post.php?post=' . $campaign->ID . '&action=edit"><strong>' . $campaign->post_title . '</strong></a>', '<strong>' . implode( '', $result->get_error_messages() ) ) . '</strong>', 'success', true, 'camp_error_' . $campaign->ID, $campaign->post_author );
} else {
if ( mailster_option( 'pause_campaigns' ) ) {
mailster( 'campaigns' )->change_status( $campaign, 'paused' );
mailster_notice( sprintf( esc_html__( 'Campaign %1$s has been paused cause of a sending error: %2$s', 'mailster' ), '<a href="post.php?post=' . $campaign->ID . '&action=edit"><strong>' . $campaign->post_title . '</strong></a>', '<strong>' . implode( '', $result->get_error_messages() ) ) . '</strong>', 'error', 7200, 'camp_error_' . $campaign->ID, $campaign->post_author );
} else {
mailster_notice( sprintf( esc_html__( 'Campaign %1$s has some delay cause of a sending error: %2$s', 'mailster' ), '<a href="post.php?post=' . $campaign->ID . '&action=edit"><strong>' . $campaign->post_title . '</strong></a>', '<strong>' . implode( '', $result->get_error_messages() ) ) . '</strong>', 'success', true, 'camp_error_' . $campaign->ID, $campaign->post_author );
}
}
array_push( $campaign_errors, $data->campaign_id );
do_action( 'mailster_campaign_error', $data->subscriber_id, $data->campaign_id, $result->get_error_message() );
// system_error
} elseif ( $result->get_error_code() == 'system_error' ) {
array_push( $campaign_errors, $data->campaign_id );
do_action( 'mailster_system_error', $data->subscriber_id, $data->campaign_id, $result->get_error_message() );
}
}
// pause between mails if defined and not very last email in this batch
if ( $send_delay && $i + 1 < $queue_result_count ) {
$delay = round( ( $send_delay - ( microtime( true ) - $send_start_time ) ), 3 ) * 1000000;
if ( $delay > 0 ) {
$delay_time += $delay;
usleep( $delay );
}
}
}
}
$this->cron_log();
$max_memory_usage = memory_get_peak_usage( true );
if ( $max_memory_usage ) {
$this->cron_log( 'max. memory usage', '<strong>' . size_format( $max_memory_usage, 2 ) . '</strong>' );
}
$this->cron_log( 'sent this turn', $sent_this_turn );
$took = ( microtime( true ) - $microtime );
if ( $sent_this_turn ) {
$mailtook = round( $took / $sent_this_turn, 4 );
mailster_remove_notice( 'max_execution_time' );
$last_hit['timemax'] = max( $last_hit['timemax'], $took );
$last_hit['mail'] = $mailtook;
// if auto is enabled, has been triggered before and send the max amount possible
if ( mailster_option( 'auto_send_at_once' ) && $last_hit['time'] && $sent_this_turn == $send_at_once_limit ) {
$percentage = 0.9;
if ( $cron_lock_timestamp = get_transient( 'mailster_cron_lock_triggered_' . $process_id ) ) {
$percentage = min( 0.5, $percentage );
$interval = abs( $cron_lock_timestamp - $last_hit['timestamp'] );
delete_transient( 'mailster_cron_lock_triggered_' . $process_id );
} else {
$interval = $last_hit['timestamp'] - $last_hit['oldtimestamp'];
}
$possible_mails_per_minute = MINUTE_IN_SECONDS / $last_hit['mail'];
if ( $max_execution_time ) {
$possible_mails_per_interval = $possible_mails_per_minute * ( min( $max_execution_time, $interval ) / MINUTE_IN_SECONDS );
} else {
$possible_mails_per_interval = $possible_mails_per_minute * ( $interval / MINUTE_IN_SECONDS );
}
$possible_mails_per_interval = max( 1, round( $percentage * $possible_mails_per_interval ) );
$ratio = $send_at_once / $possible_mails_per_interval;
$diff = abs( $send_at_once - $possible_mails_per_interval );
$prefix = '';
if ( $ratio < 1 ) {
if ( $to_send == $sent_this_turn ) {
$possible_mails_per_interval = round( $sent_this_turn + ( $diff * $ratio ) );
} else {
$possible_mails_per_interval = round( $send_at_once + ( $diff * $ratio ) );
}
$prefix = '+';
}
$this->cron_log( 'send next turn', $possible_mails_per_interval . ' (' . $prefix . ( round( 100 * $possible_mails_per_interval / $sent_this_turn ) - 100 ) . '%)' );
if ( $diff && mailster_force_option( 'auto_send_at_once' ) ) {
mailster_force_update_option( 'send_at_once', $possible_mails_per_interval );
}
}
$this->cron_log( 'time', round( $took, 2 ) . ' sec., (' . $mailtook . ' sec./mail)' );
}
if ( is_user_logged_in() ) {
$this->show_cron_log();
}
mailster( 'cron' )->unlock( $process_id );
$last_hit['time'] = $took;
update_option( 'mailster_cron_lasthit', $last_hit );
do_action( 'mailster_cron_finished' );
return true;
}
/**
*
*
* @param unknown $microtime (optional)
* @param unknown $campaign_id (optional)
* @return unknown
*/
public function size( $microtime = null, $campaign_id = null ) {
global $wpdb;
if ( is_null( $microtime ) ) {
$microtime = microtime( true );
}
if ( $campaign_id && ! is_array( $campaign_id ) ) {
$campaign_id = array( $campaign_id );
}
$sql = "SELECT COUNT(*) FROM {$wpdb->prefix}mailster_queue AS queue LEFT JOIN {$wpdb->prefix}mailster_subscribers AS subscribers ON subscribers.ID = queue.subscriber_id LEFT JOIN {$wpdb->posts} AS posts ON posts.ID = queue.campaign_id WHERE queue.timestamp <= " . (int) $microtime . " AND queue.sent = 0 AND queue.error < {$this->max_retry_after_error} AND (posts.post_status IN ('finished', 'active', 'queued', 'autoresponder', 'notification') OR queue.campaign_id = 0) AND (subscribers.status = 1 OR queue.ignore_status = 1) AND (subscribers.ID IS NOT NULL OR queue.subscriber_id = 0)";
if ( $campaign_id ) {
$campaign_id = array_filter( $campaign_id, 'is_numeric' );
$sql .= ' AND queue.campaign_id IN (' . implode( ', ', $campaign_id ) . ')';
}
return (int) $wpdb->get_var( $sql );
}
public function cron_log() {
global $mailster_cron_log, $mailster_cron_log_max_fields;
if ( ! $mailster_cron_log ) {
$mailster_cron_log = array();
}
if ( $a = func_get_args() ) {
array_unshift( $a, microtime( true ) );
$mailster_cron_log[] = $a;
$mailster_cron_log_max_fields = max( $mailster_cron_log_max_fields || 0, count( $a ) );
} else {
$mailster_cron_log_max_fields = 0;
$mailster_cron_log[] = array();
}
}
public function show_cron_log() {
global $mailster_cron_log, $mailster_cron_log_max_fields;
$timeoffset = mailster( 'helper' )->gmt_offset( true );
$html = '<table cellpadding="0" cellspacing="0" width="100%">';
$i = 1;
foreach ( $mailster_cron_log as $logs ) {
if ( empty( $logs ) ) {
$i = 1;
$html .= '</table><table cellpadding="0" cellspacing="0" width="100%">';
continue;
}
$time = array_shift( $logs );
$html .= '<tr class="' . ( $i % 2 ? 'odd' : 'even' ) . '">';
foreach ( $logs as $j => $log ) {
$html .= '<td>' . $log . '</td>';
}
$html .= str_repeat( '<td> </td>', max( 0, ( $mailster_cron_log_max_fields + 2 ) - $j - 4 ) );
$html .= '<td width="50">' . date( 'H:i:s', $time + $timeoffset ) . ':' . round( ( $time - floor( $time ) ) * 10000 ) . '</td>';
$html .= '</tr>';
$i++;
}
$html .= '</table>';
echo $html;
}
/**
*
*
* @param unknown $new_status
* @param unknown $old_status
* @param unknown $subscriber
*/
public function subscriber_change_status( $new_status, $old_status, $subscriber ) {
if ( $new_status != 1 ) {
$this->remove_subscriber( $subscriber->ID );
}
}
/**
*
*
* @param unknown $subscribers
* @param unknown $campaign_id (optional)
* @return unknown
*/
public function remove_subscriber( $subscribers, $campaign_id = null ) {
global $wpdb;
$sql = "DELETE a FROM {$wpdb->prefix}mailster_queue AS a WHERE 1";
if ( ! is_null( $campaign_id ) ) {
$sql .= $wpdb->prepare( ' AND a.campaign_id = %d', $campaign_id );
}
if ( ! is_array( $subscribers ) ) {
$subscribers = array( $subscribers );
}
$subscribers = array_filter( $subscribers, 'is_numeric' );
$sql .= ' AND a.subscriber_id IN (' . implode( ',', $subscribers ) . ')';
return false !== $wpdb->query( $sql );
}
/**
*
*
* @param unknown $campaign_id (optional)
* @param unknown $timestamp (optional)
* @return unknown
*/
public function get_job_count( $campaign_id = null, $timestamp = null ) {
global $wpdb;
if ( is_null( $timestamp ) ) {
$timestamp = time();
}
if ( $timestamp === false ) {
$timestamp = 0;
}
$sql = "SELECT COUNT(queue.subscriber_id) AS count FROM {$wpdb->prefix}mailster_queue AS queue WHERE queue.sent = 0 AND queue.timestamp > %d AND queue.campaign_id = %d";
return $wpdb->get_var( $wpdb->prepare( $sql, $timestamp, $campaign_id ) );
}
}