/**
  * 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">&nbsp;</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>';
 }