/** * Send email message. * * Delete history of sent messages after max is reached. * * @since 1.0.0 * * @param mixed $reference A reference to identify the member/subscription. * @return bool True if successfully sent email. */ public function send_message($reference) { $user_id = 0; $subscription = null; $member = null; if ($reference instanceof MS_Model_Relationship) { $user_id = $reference->user_id; $subscription = $reference; $member = $subscription->get_member(); } elseif ($reference instanceof MS_Model_Member) { $user_id = $reference->id; $member = $reference; $subscription = null; } /** * Documented in process_queue() * * @since 1.0.1.0 */ if (MS_Plugin::get_modifier('MS_STOP_EMAILS')) { do_action('lib2_debug_log', sprintf('Following Email was not sent: "%s" to user "%s".', $this->type, $user_id)); return false; } do_action('ms_model_communication_send_message_before', $member, $subscription, $this); $sent = false; if ($this->enabled) { if (!is_email($member->email)) { do_action('lib2_debug_log', sprintf('Invalid user email. User_id: %1$s, email: %2$s', $user_id, $member->email)); return false; } $comm_vars = $this->get_comm_vars($subscription, $member); // Replace the email variables. $message = str_replace(array_keys($comm_vars), array_values($comm_vars), stripslashes($this->message)); $subject = str_replace(array_keys($comm_vars), array_values($comm_vars), stripslashes($this->subject)); $html_message = wpautop($message); $text_message = strip_tags(preg_replace('/\\<a .*?href="(.*?)".*?\\>.*?\\<\\/a\\>/is', '$0 [$1]', $message)); $subject = strip_tags(preg_replace('/\\<a .*?href="(.*?)".*?\\>.*?\\<\\/a\\>/is', '$0 [$1]', $subject)); $message = $text_message; if ('text/html' == $this->get_mail_content_type()) { $this->add_filter('wp_mail_content_type', 'get_mail_content_type'); $message = $html_message; } $recipients = array($member->email); if ($this->cc_enabled) { $recipients[] = $this->cc_email; } $admin_emails = MS_Model_Member::get_admin_user_emails(); $headers = ''; if (!empty($admin_emails[0])) { $headers = array(sprintf('From: %s <%s> ', get_option('blogname'), $admin_emails[0])); } $recipients = apply_filters('ms_model_communication_send_message_recipients', $recipients, $this, $subscription); $html_message = apply_filters('ms_model_communication_send_message_html_message', $html_message, $this, $subscription); $text_message = apply_filters('ms_model_communication_send_message_text_message', $text_message, $this, $subscription); $subject = apply_filters('ms_model_communication_send_message_subject', $subject, $this, $subscription); $headers = apply_filters('ms_model_communication_send_message_headers', $headers, $this, $subscription); /* * Send the mail. * wp_mail will not throw an error, so no error-suppression/handling * is required here. On error the function response is FALSE. */ $sent = wp_mail($recipients, $subject, $message, $headers); // Log the outgoing email. do_action('lib2_debug_log', sprintf('Sent email [%s] to <%s>: %s', $this->type, implode('>, <', $recipients), $sent ? 'OK' : 'ERR')); /* // -- Debugging code -- MS_Helper_Debug::log( sprintf( "Variables:\n%s", print_r( $comm_vars, true ) ) ); //*/ if ('text/html' == $this->get_mail_content_type()) { $this->remove_filter('wp_mail_content_type', 'get_mail_content_type'); } } do_action('ms_model_communication_send_message', $member, $subscription, $this); return $sent; }
/** * Check membership status. * * Execute actions when time/period condition are met. * E.g. change membership status, add communication to queue, create invoices. * * This check is called via a cron job. * * @since 1.0.0 * @internal Used by Cron * @see MS_Model_Plugin::check_membership_status() */ public function check_membership_status() { do_action('ms_model_relationship_check_membership_status_before', $this); /** * Use `define( 'MS_LOCK_SUBSCRIPTIONS', true );` in wp-config.php to prevent * Membership2 from sending *any* emails to users. * Also any currently enqueued message is removed from the queue * * @since 1.0.0 */ if (MS_Plugin::get_modifier('MS_LOCK_SUBSCRIPTIONS')) { return false; } $membership = $this->get_membership(); $remaining_days = $this->get_remaining_period(); $remaining_trial_days = $this->get_remaining_trial_period(); $comms = MS_Model_Communication::get_communications($membership); $invoice_before_days = 5; //@todo create a setting to configure this period. $deactivate_expired_after_days = 30; //@todo create a setting to configure this period. $deactivate_pending_after_days = 30; //@todo create a setting to configure this period. $deactivate_trial_expired_after_days = 5; //@todo create a setting to configure this period. //@todo: Add a flag to subscriptions with sent communications. Then improve the conditions below to prevent multiple emails. do_action('ms_check_membership_status-' . $this->status, $this, $remaining_days, $remaining_trial_days); // Update the Subscription status. $next_status = $this->calculate_status(null); switch ($next_status) { case self::STATUS_TRIAL: if (MS_Model_Addon::is_enabled(MS_Model_Addon::ADDON_TRIAL) && MS_Model_Addon::is_enabled(MS_Model_Addon::ADDON_AUTO_MSGS_PLUS)) { // Send trial end communication. $comm = $comms[MS_Model_Communication::COMM_TYPE_BEFORE_TRIAL_FINISHES]; if ($comm->enabled) { $days = MS_Helper_Period::get_period_in_days($comm->period['period_unit'], $comm->period['period_type']); //@todo: This will send out the reminder multiple times on the reminder-day (4 times or more often) if ($days == $remaining_trial_days) { $comm->add_to_queue($this->id); MS_Model_Event::save_event(MS_Model_Event::TYPE_MS_BEFORE_TRIAL_FINISHES, $this); } } } // Check for card expiration $gateway = $this->get_gateway(); $gateway->check_card_expiration($this); break; case self::STATUS_TRIAL_EXPIRED: if (MS_Model_Addon::is_enabled(MS_Model_Addon::ADDON_TRIAL)) { // Mark the trial period as completed. $this->save() is below. $this->trial_period_completed = true; // Request payment to the gateway (for gateways that allows it). $gateway = $this->get_gateway(); /* * The subscription will be either automatically activated * or set to pending. * * Important: Set trial_period_completed=true before calling * request_payment()! */ if ($gateway->request_payment($this)) { $next_status = self::STATUS_ACTIVE; $this->status = $next_status; $this->config_period(); // Needed because of status change. } // Check for card expiration $gateway->check_card_expiration($this); // Deactivate expired memberships after a period of time. if ($deactivate_trial_expired_after_days < -$remaining_trial_days) { $this->deactivate_membership(); } } break; case self::STATUS_ACTIVE: case self::STATUS_EXPIRED: case self::STATUS_CANCELED: /* * Make sure the expire date has a correct value, in case the user * changed the payment_type of the parent membership after this * subscription was created. */ if ($this->payment_type != $membership->payment_type) { $this->payment_type = $membership->payment_type; switch ($this->payment_type) { case MS_Model_Membership::PAYMENT_TYPE_PERMANENT: $this->expire_date = false; break; default: // Either keep the current expire date (if valid) or // calculate a new expire date, based on current date. if (!$this->expire_date) { $this->expire_date = $this->calc_expire_date(MS_Helper_Period::current_date()); } break; } // Recalculate the days until the subscription expires. $remaining_days = $this->get_remaining_period(); // Recalculate the new Subscription status. $next_status = $this->calculate_status(); } /* * Only "Recurring" memberships will ever try to automatically * renew the subscription. All other types will expire when the * end date is reached. */ $auto_renew = $membership->payment_type == MS_Model_Membership::PAYMENT_TYPE_RECURRING; $deactivate = false; $invoice = null; if ($auto_renew && $membership->pay_cycle_repetitions > 0) { /* * The membership has a payment-repetition limit. * When this limit is reached then we do not auto-renew the * subscription but expire it. */ $payments = $this->get_payments(); if (count($payments) >= $membership->pay_cycle_repetitions) { $auto_renew = false; } } if ($auto_renew) { if ($remaining_days < $invoice_before_days) { // Create a new invoice. $invoice = $this->get_next_invoice(); } else { $invoice = $this->get_current_invoice(); } } else { $invoice = $this->get_current_invoice(); } // Advanced communications Add-on. if (MS_Model_Addon::is_enabled(MS_Model_Addon::ADDON_AUTO_MSGS_PLUS)) { // Before finishes communication. $comm = $comms[MS_Model_Communication::COMM_TYPE_BEFORE_FINISHES]; $days = MS_Helper_Period::get_period_in_days($comm->period['period_unit'], $comm->period['period_type']); if ($days == $remaining_days) { $comm->add_to_queue($this->id); MS_Model_Event::save_event(MS_Model_Event::TYPE_MS_BEFORE_FINISHES, $this); } // After finishes communication. $comm = $comms[MS_Model_Communication::COMM_TYPE_AFTER_FINISHES]; $days = MS_Helper_Period::get_period_in_days($comm->period['period_unit'], $comm->period['period_type']); if ($remaining_days < 0 && $days == abs($remaining_days)) { $comm->add_to_queue($this->id); MS_Model_Event::save_event(MS_Model_Event::TYPE_MS_AFTER_FINISHES, $this); } // Before payment due. $comm = $comms[MS_Model_Communication::COMM_TYPE_BEFORE_PAYMENT_DUE]; $days = MS_Helper_Period::get_period_in_days($comm->period['period_unit'], $comm->period['period_type']); $invoice_days = MS_Helper_Period::subtract_dates($invoice->due_date, MS_Helper_Period::current_date()); if (MS_Model_Invoice::STATUS_BILLED == $invoice->status && $days == $invoice_days) { $comm->add_to_queue($this->id); MS_Model_Event::save_event(MS_Model_Event::TYPE_PAYMENT_BEFORE_DUE, $this); } // After payment due event $comm = $comms[MS_Model_Communication::COMM_TYPE_AFTER_PAYMENT_DUE]; $days = MS_Helper_Period::get_period_in_days($comm->period['period_unit'], $comm->period['period_type']); $invoice_days = MS_Helper_Period::subtract_dates($invoice->due_date, MS_Helper_Period::current_date()); if (MS_Model_Invoice::STATUS_BILLED == $invoice->status && $days == $invoice_days) { $comm->add_to_queue($this->id); MS_Model_Event::save_event(MS_Model_Event::TYPE_PAYMENT_AFTER_DUE, $this); } } // -- End of advanced communications Add-on // Subscription ended. See if we can renew it. if ($remaining_days <= 0) { if ($auto_renew) { /* * The membership can be renewed. Try to renew it * automatically by requesting the next payment from the * payment gateway (only works if gateway supports this) */ $gateway = $this->get_gateway(); $gateway->check_card_expiration($this); $gateway->request_payment($this); // Check if the payment was successful. $remaining_days = $this->get_remaining_period(); } /* * User did not renew the membership. Give him some time to * react before restricting his access. */ if ($deactivate_expired_after_days < -$remaining_days) { $deactivate = true; } } $next_status = $this->calculate_status(null); /* * When the subscription expires the first time then create a * new event that triggers the "Expired" email. */ if (self::STATUS_EXPIRED == $next_status && $next_status != $this->status) { MS_Model_Event::save_event(MS_Model_Event::TYPE_MS_EXPIRED, $this); } elseif ($deactivate) { $this->deactivate_membership(); $next_status = $this->status; // Move membership to configured membership. $membership = $this->get_membership(); $new_membership = MS_Factory::load('MS_Model_Membership', $membership->on_end_membership_id); if ($new_membership->is_valid()) { $member = MS_Factory::load('MS_Model_Member', $this->user_id); $new_subscription = $member->add_membership($membership->on_end_membership_id, $this->gateway_id); MS_Model_Event::save_event(MS_Model_Event::TYPE_MS_MOVED, $new_subscription); /* * If the new membership is paid we want that the user * confirms the payment in his account. So we set it * to "Pending" first. */ if (!$new_membership->is_free()) { $new_subscription->status = self::STATUS_PENDING; } } } break; case self::STATUS_DEACTIVATED: /* * A subscription was finally deactivated. * Lets check if the member has any other active subscriptions, * or (if not) his account should be deactivated. * * First get a list of all subscriptions that do not have status * Pending / Deactivated. */ $subscriptions = self::get_subscriptions(array('user_id' => $this->user_id)); // Check if there is a subscription that keeps the user active. $deactivate = true; foreach ($subscriptions as $item) { if ($item->id == $this->id) { continue; } $deactivate = false; } if ($deactivate) { $member = $this->get_member(); $member->is_member = false; $member->save(); } break; case self::STATUS_PENDING: default: // Do nothing. break; } // Save the new status. $this->status = $next_status; $this->save(); // Save the changed email queue. foreach ($comms as $comm) { $comm->save(); } do_action('ms_model_relationship_check_membership_status_after', $this); }
/** * Display a footer below the Settings box. * The footer will show information on the next scheduled cron jobs and also * allow the user to run these jobs instantly. * * @since 1.0.0 * @param string $tab_name Name of the currently open settings-tab. */ protected function render_settings_footer($tab_name) { if ('general' != $tab_name) { return; } $status_stamp = wp_next_scheduled('ms_cron_check_membership_status') - time(); $email_stamp = wp_next_scheduled('ms_cron_process_communications') - time(); if ($status_stamp > 0) { $status_delay = sprintf(__('in %s hrs %s min', 'membership2'), floor(($status_stamp - 1) / 3600), date('i', $status_stamp)); } else { $status_delay = __('(now...)', 'membership2'); } if ($email_stamp > 0) { $email_delay = sprintf(__('in %s hrs %s min', 'membership2'), floor(($email_stamp - 1) / 3600), date('i', $email_stamp)); } else { $email_delay = __('(now...)', 'membership2'); } $status_url = esc_url_raw(add_query_arg(array('run_cron' => 'ms_cron_check_membership_status'))); $email_url = esc_url_raw(add_query_arg(array('run_cron' => 'ms_cron_process_communications'))); $lbl_run = __('Run now!', 'membership2'); echo '<div class="cf ms-settings-footer"><div class="ms-tab-container"> </div>'; echo '<div>'; if (MS_Plugin::get_modifier('MS_LOCK_SUBSCRIPTIONS')) { _e('Membership Status Checks are disabled.', 'membership2'); echo ' '; } else { printf(__('Check Membership Status changes %s.') . ' ', '<a href="' . $status_url . '" title="' . $lbl_run . '">' . $status_delay . '</a>'); } if (MS_Plugin::get_modifier('MS_STOP_EMAILS')) { _e('Sending Email Responses is disabled.', 'membership2'); } else { $count = MS_Model_Communication::get_queue_count(); if (!$count) { $msg = __('No pending Email Responses found', 'membership2'); } elseif (1 == $count) { $msg = __('Send 1 pending Email Response %1$s', 'membership2'); } else { $msg = __('Send %2$s pending Email Responses %1$s', 'membership2'); } printf($msg, '<a href="' . $email_url . '"title="' . $lbl_run . '">' . $email_delay . '</a>', $count); } echo '</div></div>'; }