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()); }
/** * 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; } }
/** * 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; }
?> "> <?php if ($subscription_details['expiry_date'] == 0 && !in_array($subscription_details['status'], array('cancelled', 'switched'))) { ?> <?php _e('When Cancelled', 'woocommerce-subscriptions'); ?> <?php } else { ?> <?php if (in_array($subscription_details['status'], array('cancelled', 'switched'))) { ?> <?php $end_of_prepaid_term = wc_next_scheduled_action('scheduled_subscription_end_of_prepaid_term', array('user_id' => (int) $user_id, 'subscription_key' => $subscription_key)); ?> <?php if (false === $end_of_prepaid_term) { ?> <?php $end_timestamp = strtotime($subscription_details['end_date']); ?> <?php } else { ?> <?php $end_timestamp = $end_of_prepaid_term; ?> <?php }
/** * 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())); } }
/** * Takes a subscription key and returns the date on which the trial for the subscription ended or is going to end, if any. * * @param string $subscription_key A subscription key of the form created by @see self::get_subscription_key() * @param int $user_id The ID of the user who owns the subscriptions. Although this parameter is optional, if you have the User ID you should pass it to improve performance. * @return mixed If the subscription has no trial period, returns 0, otherwise it will return the date the trial period ends or ended in the form specified by $type * @since 1.2 */ public static function get_trial_expiration_date($subscription_key, $user_id = '', $type = 'mysql') { if (empty($user_id)) { $user_id = self::get_user_id_from_subscription_key($subscription_key); } $trial_expiry = wc_next_scheduled_action('scheduled_subscription_trial_end', array('user_id' => (int) $user_id, 'subscription_key' => $subscription_key)); if (false === $trial_expiry) { $subscription = self::get_subscription($subscription_key); $trial_expiry = !empty($subscription['trial_expiry_date']) ? strtotime($subscription['trial_expiry_date']) : 0; } $trial_expiry = 'mysql' == $type && 0 != $trial_expiry ? date('Y-m-d H:i:s', $trial_expiry) : $trial_expiry; return apply_filters('woocommerce_subscription_trial_expiration_date', $trial_expiry, $subscription_key, $user_id, $type); }
/** * 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')); }
function blendercloud_api($atts) { $user_data = array('shop_id' => '0', 'cloud_access' => 0, 'expiration_date' => '1970-01-01 00:00:00'); $last_expiration_date = new DateTime('1970-01-01 00:00:00'); // map blenderid to userid $args = array('search' => $_GET['blenderid'], 'search_columns' => array('user_login')); $user_query = new WP_User_Query($args); // Get the results from the query, returning the first user $users = $user_query->get_results(); if (!empty($users)) { $user_id = $users[0]->ID; $user_data['shop_id'] = $user_id; // process simple products (prepaid subscriptions) $order_ids = bo_get_all_user_orders($user_id, 'completed'); foreach ($order_ids as $order_id) { $order = new WC_Order($order_id); $order_date = $order->order_date; $items = $order->get_items(); foreach ($items as $item) { $tmp = bo_empty_subscription_line(); $product_id = $item['product_id']; $product = get_product($product_id); $sku = $product->get_sku(); $expiry_date = new DateTime($order_date); $tmp['sku'] = $sku; switch ($sku) { case 'cloud-prepaid-3': case 'cloud-prepaid-3-renewal': $expiry_date->modify('+3 month'); break; case 'cloud-prepaid-18': $expiry_date->modify('+18 month'); break; default: continue 2; // skip to next product } $tmp['expiration_date'] = $expiry_date->format('Y-m-d H:i:s'); $tmp['subscription_status'] = 'prepaid'; $now = new DateTime("now"); if ($expiry_date > $now) { $tmp['cloud_access'] = 1; } if ($expiry_date > $last_expiration_date) { $last_expiration_date = $expiry_date; } $user_data['subscriptions'][] = $tmp; } } // process recurring subscriptions $subscriptions = WC_Subscriptions_Manager::get_users_subscriptions($user_id); if (!empty($subscriptions)) { // iterate over all subscriptions. foreach ($subscriptions as $subscription_details) { if ($subscription_details['status'] != 'trash') { $order_id = $subscription_details['order_id']; $product_id = $subscription_details['product_id']; $order = new WC_Order($order_id); // print_r($order); // $next_payment_date = WC_Subscriptions_Manager::get_next_payment_date( $subscription_key, $user_id, 'mysql' ); $subscription_key = WC_Subscriptions_Manager::get_subscription_key($order_id, $product_id); if ($subscription_details['expiry_date'] == 0 && !in_array($subscription_details['status'], array('cancelled', 'switched'))) { $end_time = WC_Subscriptions_Manager::get_next_payment_date($subscription_key, $user_id, 'mysql'); $end_timestamp = strtotime($end_time); } else { if (in_array($subscription_details['status'], array('cancelled', 'switched'))) { $end_of_prepaid_term = wc_next_scheduled_action('scheduled_subscription_end_of_prepaid_term', array('user_id' => (int) $user_id, 'subscription_key' => $subscription_key)); if (false === $end_of_prepaid_term) { $end_timestamp = strtotime($subscription_details['end_date']); } else { $end_timestamp = $end_of_prepaid_term; } } else { $end_timestamp = strtotime($subscription_details['expiry_date']); } } // if( $users[0]->data->user_email == '*****@*****.**' ) { // print_r($subscription_details); // } if ($subscription_details['status'] == 'cancelled') { $end_timestamp = strtotime($subscription_details['trial_expiry_date']); } if ($subscription_details['status'] == 'on-hold') { $end_timestamp = strtotime($subscription_details['last_payment_date']); } $end_time = date("Y-m-d H:i:s", $end_timestamp); $product = get_product($product_id); $sku = $product->get_sku(); $tmp = bo_empty_subscription_line(); $tmp['expiration_date'] = $end_time; $tmp['subscription_status'] = $subscription_details['status']; $expiry_date = new DateTime($end_time); if ($expiry_date > $last_expiration_date) { $last_expiration_date = $expiry_date; } $now = new DateTime("now"); $tmp['cloud_access'] = $expiry_date > $now ? 1 : 0; $tmp['sku'] = $sku; // if order is refunded, stop access if ($order->status == 'refunded') { $tmp['expiration_date'] = $end_time; $tmp['subscription_status'] = 'refunded'; $tmp['cloud_access'] = 0; } switch ($sku) { case 'cloud-subscription-1-renewal': case 'cloud-subscription-3': //$tmp['failed_payments'] = $subscription['failed_payments']; break; case 'cloud-subscription-team': // purchased team size $variation_id = $subscription_details['variation_id']; // find variation info from order to pass on # of seats $order = new WC_Order($order_id); $items = $order->get_items(); $team_members = 0; foreach ($items as $item) { // does product variation id match the current subscription? if ($item['item_meta']['_variation_id'][0] == $variation_id) { $team_members = $item['item_meta']['pa_team-size'][0]; } } $tmp['team_members'] = $team_members; break; } $user_data['subscriptions'][] = $tmp; } } } } // add one grace day to expiration $last_expiration_date->add(DateInterval::createfromdatestring('+1 day')); $user_data['expiration_date'] = $last_expiration_date->format('Y-m-d H:i:s'); $now = new DateTime("now"); if ($last_expiration_date > $now) { $user_data['cloud_access'] = 1; } //echo "<pre>";print_r($user_data); echo json_encode($user_data, JSON_PRETTY_PRINT); die; }
/** * Schedule the daily summary recurring emails */ public function init_daily_summary() { if (!function_exists('wc_next_scheduled_action')) { return; } if (wc_next_scheduled_action('fue_send_summary')) { wc_unschedule_action('fue_send_summary'); } FUE_Sending_Scheduler::queue_daily_summary_email(); delete_option('fue_init_daily_summary'); }
/** * Utility method to check the action scheduler for dates * * @param string $type the type of scheduled action * @param string $subscription_key key of subscription in the format of order_id_item_id * @return string either 0 or mysql date */ private static function maybe_get_date_from_action_scheduler($type, $subscription) { $action_args = array('user_id' => intval($subscription['user_id']), 'subscription_key' => $subscription['subscription_key']); WCS_Upgrade_Logger::add(sprintf('-- For order %d: Repairing date type "%s" from action scheduler...', $subscription['order_id'], $type)); WCS_Upgrade_Logger::add('-- This is the arguments: ' . PHP_EOL . print_r(array($action_args, 'hook' => $type), true) . PHP_EOL); $next_date_timestamp = wc_next_scheduled_action($type, $action_args); if (false === $next_date_timestamp) { // set it to 0 as default $formatted_date = 0; WCS_Upgrade_Logger::add(sprintf('-- For order %d: Repairing date type "%s": fetch of date unsuccessfull: no action present. Date is 0.', $subscription['order_id'], $type)); } else { $formatted_date = date('Y-m-d H:i:s', $next_date_timestamp); WCS_Upgrade_Logger::add(sprintf('-- For order %d: Repairing date type "%s": fetch of date successfull. New date is %s', $subscription['order_id'], $type, $formatted_date)); } return $formatted_date; }
/** * 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; }
/** * get_content_plain function. * * @access public * @return string */ function get_content_plain() { $order = new WC_Order($this->object['order_id']); $item_name = WC_Subscriptions_Order::get_item_name($this->object['order_id'], $this->object['product_id']); $order_item = WC_Subscriptions_Order::get_item_by_product_id($order, $this->object['product_id']); $product = $order->get_product_from_item($order_item); if (isset($product->variation_data)) { $item_name .= ' (' . woocommerce_get_formatted_variation($product->variation_data, true) . ')'; } WC_Subscriptions_Order::$recurring_only_price_strings = true; $recurring_total = WC_Subscriptions_Order::get_formatted_order_total(0, $order); WC_Subscriptions_Order::$recurring_only_price_strings = false; if ($end_of_prepaid_term = wc_next_scheduled_action('scheduled_subscription_end_of_prepaid_term', array('user_id' => (int) $order->user_id, 'subscription_key' => $this->subscription_key))) { $end_of_prepaid_term = date_i18n(woocommerce_date_format(), $end_of_prepaid_term + get_option('gmt_offset') * 3600); } ob_start(); woocommerce_get_template($this->template_plain, array('subscription_key' => $this->subscription_key, 'subscription' => $this->object, 'order' => $order, 'email_heading' => $this->get_heading(), 'item_name' => $item_name, 'recurring_total' => $recurring_total, 'last_payment' => date_i18n(woocommerce_date_format(), WC_Subscriptions_Manager::get_last_payment_date($this->subscription_key, '', 'timestamp')), 'end_of_prepaid_term' => $end_of_prepaid_term), '', $this->template_base); return ob_get_clean(); }