public function test_unschedule()
 {
     $time = time();
     $hook = md5(rand());
     $action_id = wc_schedule_single_action($time, $hook);
     wc_unschedule_action($hook);
     $next = wc_next_scheduled_action($hook);
     $this->assertFalse($next);
     $store = ActionScheduler::store();
     $action = $store->fetch_action($action_id);
     $this->assertNull($action->get_schedule()->next());
     $this->assertEmpty($action->get_hook());
 }
 /**
  * Update subscription WP-Cron tasks to Action Scheduler.
  *
  * @since 2.0
  */
 public static function upgrade_hooks($number_hooks_to_upgrade)
 {
     $counter = 0;
     $cron = _get_cron_array();
     foreach ($cron as $timestamp => $actions) {
         foreach ($actions as $hook => $details) {
             if ('scheduled_subscription_payment' == $hook || 'scheduled_subscription_expiration' == $hook || 'scheduled_subscription_end_of_prepaid_term' == $hook || 'scheduled_subscription_trial_end' == $hook || 'paypal_check_subscription_payment' == $hook) {
                 foreach ($details as $hook_key => $values) {
                     if (!wc_next_scheduled_action($hook, $values['args'])) {
                         wc_schedule_single_action($timestamp, $hook, $values['args']);
                         unset($cron[$timestamp][$hook][$hook_key]);
                         $counter++;
                     }
                     if ($counter >= $number_hooks_to_upgrade) {
                         break;
                     }
                 }
                 // If there are no other jobs scheduled for this hook at this timestamp, remove the entire hook
                 if (0 == count($cron[$timestamp][$hook])) {
                     unset($cron[$timestamp][$hook]);
                 }
                 if ($counter >= $number_hooks_to_upgrade) {
                     break;
                 }
             }
         }
         // If there are no actions schedued for this timestamp, remove the entire schedule
         if (0 == count($cron[$timestamp])) {
             unset($cron[$timestamp]);
         }
         if ($counter >= $number_hooks_to_upgrade) {
             break;
         }
     }
     // Set the cron with the removed schedule
     _set_cron_array($cron);
     return $counter;
 }
Example #3
0
 /**
  * Migrate the trial expiration, next payment and expiration/end dates to a new subscription.
  *
  * @since 2.0
  */
 private static function migrate_dates($new_subscription, $old_subscription)
 {
     global $wpdb;
     $dates_to_update = array();
     // old hook => new hook
     $date_keys = array('trial_end' => array('old_subscription_key' => 'trial_expiry_date', 'old_scheduled_hook' => 'scheduled_subscription_trial_end'), 'end' => array('old_subscription_key' => 'expiry_date', 'old_scheduled_hook' => 'scheduled_subscription_expiration'), 'end_date' => array('old_subscription_key' => '_subscription_end_date', 'old_scheduled_hook' => ''), 'next_payment' => array('old_subscription_key' => '', 'old_scheduled_hook' => 'scheduled_subscription_payment'), 'end_of_prepaid_term' => array('old_subscription_key' => '', 'old_scheduled_hook' => 'scheduled_subscription_end_of_prepaid_term'));
     $old_hook_args = array('user_id' => $old_subscription['user_id'], 'subscription_key' => $old_subscription['subscription_key']);
     foreach ($date_keys as $new_key => $old_keys) {
         // First check if there is a date stored on the subscription, and if so, use that
         if (!empty($old_keys['old_subscription_key']) && (isset($old_subscription[$old_keys['old_subscription_key']]) && 0 !== $old_subscription[$old_keys['old_subscription_key']])) {
             $dates_to_update[$new_key] = $old_subscription[$old_keys['old_subscription_key']];
         } elseif (!empty($old_keys['old_scheduled_hook'])) {
             // Now check if there is a scheduled date, this is for next payment and end of prepaid term dates
             $next_scheduled = wc_next_scheduled_action($old_keys['old_scheduled_hook'], $old_hook_args);
             if ($next_scheduled > 0) {
                 if ('end_of_prepaid_term' == $new_key) {
                     wc_schedule_single_action($next_scheduled, 'woocommerce_scheduled_subscription_end_of_prepaid_term', array('subscription_id' => $new_subscription->id));
                 } else {
                     $dates_to_update[$new_key] = date('Y-m-d H:i:s', $next_scheduled);
                 }
             }
         }
     }
     // Trash all the hooks in one go to save write requests
     $wpdb->update($wpdb->posts, array('post_status' => 'trash'), array('post_type' => ActionScheduler_wpPostStore::POST_TYPE, 'post_content' => json_encode($old_hook_args)), array('%s', '%s'));
     $dates_to_update['start'] = $new_subscription->post->post_date_gmt;
     // v2.0 enforces new rules for dates when they are being set, so we need to massage the old data to conform to these new rules
     foreach ($dates_to_update as $date_type => $date) {
         if (0 == $date) {
             continue;
         }
         switch ($date_type) {
             case 'end':
                 if (array_key_exists('next_payment', $dates_to_update) && $date <= $dates_to_update['next_payment']) {
                     $dates_to_update[$date_type] = $date;
                 }
             case 'next_payment':
                 if (array_key_exists('trial_end', $dates_to_update) && $date < $dates_to_update['trial_end']) {
                     $dates_to_update[$date_type] = $date;
                 }
             case 'trial_end':
                 if (array_key_exists('start', $dates_to_update) && $date <= $dates_to_update['start']) {
                     $dates_to_update[$date_type] = $date;
                 }
         }
     }
     try {
         if (!empty($dates_to_update)) {
             $new_subscription->update_dates($dates_to_update);
         }
         WCS_Upgrade_Logger::add(sprintf('For subscription %d: updated dates = %s', $new_subscription->id, str_replace(array('{', '}', '"'), '', json_encode($dates_to_update))));
     } catch (Exception $e) {
         WCS_Upgrade_Logger::add(sprintf('For subscription %d: unable to update dates, exception "%s"', $new_subscription->id, $e->getMessage()));
     }
 }
 public function test_filtering_of_get_comments()
 {
     $post_id = $this->factory->post->create_object(array('post_title' => __FUNCTION__));
     $comment_id = $this->factory->comment->create_object(array('comment_post_ID' => $post_id, 'comment_author' => __CLASS__, 'comment_content' => __FUNCTION__));
     // Verify that we're getting the expected comment before we add logging comments
     $comments = get_comments();
     $this->assertCount(1, $comments);
     $this->assertEquals($comment_id, $comments[0]->comment_ID);
     $action_id = wc_schedule_single_action(time(), 'a hook');
     $logger = ActionScheduler::logger();
     $message = 'Logging that something happened';
     $log_id = $logger->log($action_id, $message);
     // Verify that logging comments are excluded from general comment queries
     $comments = get_comments();
     $this->assertCount(1, $comments);
     $this->assertEquals($comment_id, $comments[0]->comment_ID);
     // Verify that logging comments are returned when asking for them specifically
     $comments = get_comments(array('type' => ActionScheduler_wpCommentLogger::TYPE));
     // Expecting two: one when the action is created, another when we added our custom log
     $this->assertCount(2, $comments);
     $this->assertContains($log_id, wp_list_pluck($comments, 'comment_ID'));
 }
 /**
  * When a subscription's status is updated, maybe schedule an event
  *
  * @param object $subscription An instance of a WC_Subscription object
  * @param string $date_type Can be 'start', 'trial_end', 'next_payment', 'last_payment', 'end', 'end_of_prepaid_term' or a custom date type
  * @param string $datetime A MySQL formated date/time string in the GMT/UTC timezone.
  */
 public function update_status($subscription, $new_status, $old_status)
 {
     $action_args = array('subscription_id' => $subscription->id);
     switch ($new_status) {
         case 'active':
             foreach ($this->action_hooks as $action_hook => $date_type) {
                 $next_scheduled = wc_next_scheduled_action($action_hook, $action_args);
                 $event_time = $subscription->get_time($date_type);
                 // Maybe clear the existing schedule for this hook
                 if (false !== $next_scheduled && $next_scheduled != $event_time) {
                     wc_unschedule_action($action_hook, $action_args);
                 }
                 if (0 != $event_time && $event_time > current_time('timestamp', true) && $next_scheduled != $event_time) {
                     wc_schedule_single_action($event_time, $action_hook, $action_args);
                 }
             }
             break;
         case 'pending-cancel':
             $end_time = $subscription->get_time('end');
             // This will have been set to the correct date already
             // Now that we have the current times, clear the scheduled hooks
             foreach ($this->action_hooks as $action_hook => $date_type) {
                 wc_unschedule_action($action_hook, $action_args);
             }
             $next_scheduled = wc_next_scheduled_action('woocommerce_scheduled_subscription_end_of_prepaid_term', $action_args);
             if (false !== $next_scheduled && $next_scheduled != $end_time) {
                 wc_unschedule_action('woocommerce_scheduled_subscription_end_of_prepaid_term', $action_args);
             }
             // The end date was set in WC_Subscriptions::update_dates() to the appropriate value, so we can schedule our action for that time
             if ($end_time > current_time('timestamp', true) && $next_scheduled != $end_time) {
                 wc_schedule_single_action($end_time, 'woocommerce_scheduled_subscription_end_of_prepaid_term', $action_args);
             }
             break;
         case 'on-hold':
         case 'cancelled':
         case 'switched':
         case 'expired':
         case 'trash':
             foreach ($this->action_hooks as $action_hook => $date_type) {
                 wc_unschedule_action($action_hook, $action_args);
             }
             wc_unschedule_action('woocommerce_scheduled_subscription_end_of_prepaid_term', $action_args);
             break;
     }
 }
 /**
  * Updates the trial expiration date as scheduled in WP-Cron and in the subscription details array.
  *
  * @param string $subscription_key A subscription key of the form created by @see self::get_subscription_key()
  * @param int $user_id (optional) The ID of the user who owns the subscription. Although this parameter is optional, if you have the User ID you should pass it to improve performance.
  * @param (optional) $next_payment string | int The date and time the next payment is due, either as MySQL formatted datetime string or a Unix timestamp. If empty, @see self::calculate_next_payment_date() will be called.
  * @return mixed If the trial expiration does not get set, returns false, otherwise it will return a MySQL datetime formatted string for the new date when the trial will expire
  * @since 1.2.4
  */
 public static function set_trial_expiration_date($subscription_key, $user_id = '', $trial_expiration_date = '')
 {
     $is_set = false;
     if (empty($user_id)) {
         $user_id = self::get_user_id_from_subscription_key($subscription_key);
     }
     if (empty($trial_expiration_date)) {
         $trial_expiration_date = self::calculate_trial_expiration_date($subscription_key, $user_id, 'timestamp');
     } elseif (is_string($trial_expiration_date)) {
         $trial_expiration_date = strtotime($trial_expiration_date);
     }
     // Update the date stored on the subscription
     $date_string = $trial_expiration_date != 0 ? date('Y-m-d H:i:s', $trial_expiration_date) : 0;
     self::update_users_subscriptions($user_id, array($subscription_key => array('trial_expiry_date' => $date_string)));
     $hook_args = array('user_id' => (int) $user_id, 'subscription_key' => $subscription_key);
     // Clear the existing schedule for this hook
     wc_unschedule_action('scheduled_subscription_trial_end', $hook_args);
     if ($trial_expiration_date != 0 && $trial_expiration_date > gmdate('U')) {
         wc_schedule_single_action($trial_expiration_date, 'scheduled_subscription_trial_end', array('user_id' => (int) $user_id, 'subscription_key' => $subscription_key));
         $is_set = true;
     }
     return apply_filters('woocommerce_subscriptions_set_trial_expiration_date', $is_set, $trial_expiration_date, $subscription_key, $user_id);
 }
Example #7
0
 /**
  * AJAX handler for toggling an email queue's status
  */
 public static function toggle_queue_status()
 {
     global $wpdb;
     $id = $_POST['id'];
     $status = $wpdb->get_var($wpdb->prepare("SELECT status FROM {$wpdb->prefix}followup_email_orders WHERE id = %d", $id));
     $resp = array('ack' => 'OK');
     if ($status == 0) {
         // activate
         $wpdb->update($wpdb->prefix . 'followup_email_orders', array('status' => 1), array('id' => $id));
         // re-create the task
         $param = array('email_order_id' => $id);
         $send_time = $wpdb->get_var($wpdb->prepare("SELECT send_on FROM {$wpdb->prefix}followup_email_orders WHERE id = %d", $id));
         wc_schedule_single_action($send_time, 'sfn_followup_emails', $param, 'fue');
         $resp['new_status'] = __('Queued', 'follow_up_emails');
         $resp['new_action'] = __('Do not send', 'follow_up_emails');
     } else {
         // deactivate
         $wpdb->update($wpdb->prefix . 'followup_email_orders', array('status' => 0), array('id' => $id));
         // if using action-scheduler, delete the task
         $param = array('email_order_id' => $id);
         wc_unschedule_action('sfn_followup_emails', $param, 'fue');
         $resp['new_status'] = __('Suspended', 'follow_up_emails');
         $resp['new_action'] = __('Re-enable', 'follow_up_emails');
     }
     die(json_encode($resp));
 }
 /**
  * In typical PayPal style, there are a couple of important limitations we need to work around:
  *
  * 1. PayPal does not support subscriptions with a $0 recurring total. As a result, we treat it
  * as a normal purchase and then handle the subscription renewals here.
  *
  * 2. PayPal make no guarantee about when a recurring payment will be charged. This creates issues for
  * suspending a subscription until the payment is processed. Specifically, if PayPal processed a payment
  * *before* it was due, we can't suspend the subscription when it is due because it will remain suspended
  * until the next payment. As a result, subscriptions for PayPal are not suspended. However, if there was
  * an issue with the subscription sign-up or payment that was not correctly reported to the store, then the
  * subscription would remain active. No renewal order would be generated, because no payments are completed,
  * so physical subscriptions would not be affected, however, subscriptions to digital goods would be affected.
  *
  * @since 1.4.3
  */
 public static function scheduled_subscription_payment($amount_to_charge, $order, $product_id)
 {
     $hook_args = array('subscription_key' => WC_Subscriptions_Manager::get_subscription_key($order->id, $product_id));
     $one_day_from_now = gmdate('U') + 60 * 60 * 24;
     wc_schedule_single_action($one_day_from_now, 'paypal_check_subscription_payment', $hook_args);
 }
 /**
  * Schedule unsent emails to use action-scheduler
  *
  * @param int $pos
  * @param int $length
  *
  * @return bool|int
  */
 public static function action_scheduler_import($pos = 0, $length = 0)
 {
     $wpdb = Follow_Up_Emails::instance()->wpdb;
     if ($length > 0) {
         $rows = $wpdb->get_results("SELECT id, send_on FROM {$wpdb->prefix}followup_email_orders WHERE is_sent = 0 ORDER BY id ASC LIMIT {$pos}, {$length}");
     } else {
         $rows = $wpdb->get_results("SELECT id, send_on FROM {$wpdb->prefix}followup_email_orders WHERE is_sent = 0 ORDER BY id ASC");
     }
     if (!$rows) {
         return false;
     }
     foreach ($rows as $row) {
         $data = array('email_order_id' => $row->id);
         $job_id = wc_schedule_single_action($row->send_on, 'sfn_followup_emails', $data, 'fue');
         $pos++;
     }
     return $pos;
 }
 /**
  * Return the scheduled date/time
  * @param array $item
  * @return string
  */
 public function get_date_value($item)
 {
     $scheduler = Follow_Up_Emails::instance()->scheduler;
     $param = $scheduler->get_scheduler_parameters($item['id']);
     $send_on = wc_next_scheduled_action('sfn_followup_emails', $param, 'fue');
     if (false === $send_on) {
         // attempt to schedule the email again
         $send_on = strtotime(get_gmt_from_date(date('Y-m-d H:i:s', $item['send_on'])));
         wc_schedule_single_action($send_on, 'sfn_followup_emails', $param, 'fue');
     }
     return get_date_from_gmt(date('Y-m-d H:i:s', $send_on), get_option('date_format') . ' ' . get_option('time_format'));
 }
 /**
  * Move scheduled subscription hooks out of wp-cron and into the new Action Scheduler.
  *
  * Also set all existing subscriptions to "sold individually" to maintain previous behavior
  * for existing subscription products before the subscription quantities feature was enabled..
  *
  * @since 1.5
  */
 public static function ajax_upgrade()
 {
     global $wpdb;
     @set_time_limit(600);
     @ini_set('memory_limit', apply_filters('admin_memory_limit', WP_MAX_MEMORY_LIMIT));
     set_transient('wc_subscriptions_is_upgrading', 'true', 60 * 2);
     if ('really_old_version' == $_POST['upgrade_step']) {
         $database_updates = '';
         if ('0' != self::$active_version && version_compare(self::$active_version, '1.2', '<')) {
             self::upgrade_database_to_1_2();
             self::generate_renewal_orders();
             update_option(WC_Subscriptions_Admin::$option_prefix . '_active_version', '1.2');
             $database_updates = '1.2, ';
         }
         // Add Variable Subscription product type term
         if ('0' != self::$active_version && version_compare(self::$active_version, '1.3', '<')) {
             self::upgrade_database_to_1_3();
             update_option(WC_Subscriptions_Admin::$option_prefix . '_active_version', '1.3');
             $database_updates .= '1.3 & ';
         }
         // Moving subscription meta out of user meta and into item meta
         if ('0' != self::$active_version && version_compare(self::$active_version, '1.4', '<')) {
             self::upgrade_database_to_1_4();
             update_option(WC_Subscriptions_Admin::$option_prefix . '_active_version', '1.4');
             $database_updates .= '1.4.';
         }
         $results = array('message' => sprintf(__('Database updated to version %s', 'woocommerce-subscriptions'), $database_updates));
     } elseif ('products' == $_POST['upgrade_step']) {
         // Set status to 'sold individually' for all existing subscriptions that haven't already been updated
         $sql = "SELECT DISTINCT ID FROM {$wpdb->posts} as posts\n\t\t\t\tJOIN {$wpdb->postmeta} as postmeta\n\t\t\t\t\tON posts.ID = postmeta.post_id\n\t\t\t\t\tAND (postmeta.meta_key LIKE '_subscription%')\n\t\t\t\tJOIN  {$wpdb->postmeta} AS soldindividually\n\t\t\t\t\tON posts.ID = soldindividually.post_id\n\t\t\t\t\tAND ( soldindividually.meta_key LIKE '_sold_individually' AND soldindividually.meta_value !=  'yes' )\n\t\t\t\tWHERE posts.post_type = 'product'";
         $subscription_product_ids = $wpdb->get_results($sql);
         foreach ($subscription_product_ids as $product_id) {
             update_post_meta($product_id->ID, '_sold_individually', 'yes');
         }
         $results = array('message' => sprintf(__('Marked %s subscription products as "sold individually".', 'woocommerce-subscriptions'), count($subscription_product_ids)));
     } else {
         $counter = 0;
         $before_cron_update = microtime(true);
         // update all of the current Subscription cron tasks to the new Action Scheduler
         $cron = _get_cron_array();
         foreach ($cron as $timestamp => $actions) {
             foreach ($actions as $hook => $details) {
                 if ($hook == 'scheduled_subscription_payment' || $hook == 'scheduled_subscription_expiration' || $hook == 'scheduled_subscription_end_of_prepaid_term' || $hook == 'scheduled_subscription_trial_end' || $hook == 'paypal_check_subscription_payment') {
                     foreach ($details as $hook_key => $values) {
                         if (!wc_next_scheduled_action($hook, $values['args'])) {
                             wc_schedule_single_action($timestamp, $hook, $values['args']);
                             unset($cron[$timestamp][$hook][$hook_key]);
                             $counter++;
                         }
                         if ($counter >= self::$upgrade_limit) {
                             break;
                         }
                     }
                     // If there are no other jobs scheduled for this hook at this timestamp, remove the entire hook
                     if (0 == count($cron[$timestamp][$hook])) {
                         unset($cron[$timestamp][$hook]);
                     }
                     if ($counter >= self::$upgrade_limit) {
                         break;
                     }
                 }
             }
             // If there are no actions schedued for this timestamp, remove the entire schedule
             if (0 == count($cron[$timestamp])) {
                 unset($cron[$timestamp]);
             }
             if ($counter >= self::$upgrade_limit) {
                 break;
             }
         }
         // Set the cron with the removed schedule
         _set_cron_array($cron);
         $results = array('upgraded_count' => $counter, 'message' => sprintf(__('Migrated %s subscription related hooks to the new scheduler (in {execution_time} seconds).', 'woocommerce-subscriptions'), $counter));
     }
     if (isset($counter) && $counter < self::$upgrade_limit) {
         self::upgrade_complete();
     }
     delete_transient('wc_subscriptions_is_upgrading');
     header('Content-Type: application/json; charset=utf-8');
     echo json_encode($results);
     exit;
 }
 /**
  * Schedule a hook to automatically clear the log after 8 weeks
  */
 public static function schedule_cleanup()
 {
     $time_to_cleanup = gmdate('U') + self::$weeks_until_cleanup * WEEK_IN_SECONDS;
     self::add(sprintf('Upgrade complete. Scheduling log cleanup for %s GMT/UTC', date('Y-m-d H:i:s', $time_to_cleanup)));
     wc_schedule_single_action($time_to_cleanup, 'woocommerce_subscriptions_clear_upgrade_log');
 }