/** * Add the simulated relationship to the current users memberships. * * @since 1.0.0 */ public function add_simulation_membership($subscriptions) { $subscription = false; if (!isset($subscriptions[$this->membership_id])) { $this->start_simulation(); $subscription = MS_Model_Relationship::create_ms_relationship($this->membership_id, 0, 'simulation'); } if (is_a($subscription, 'MS_Model_Relationship')) { $membership = $subscription->get_membership(); if (MS_Model_Membership::PAYMENT_TYPE_PERMANENT == $membership->payment_type) { $subscription->expire_date = '2999-12-31'; } $key = 'ms_model_relationship--1'; MS_Factory::set_singleton($subscription, $key); $this->_subscription = $subscription; $subscriptions[$this->membership_id] = $subscription; } return $subscriptions; }
/** * Export specific data. * * @since 1.0.0 * @param MS_Model_Relationship $src * @return object Export data */ protected function export_relationship($src) { $obj = (object) array(); $obj->id = $this->exp_id('relationship', $src->id); $obj->membership = $this->exp_id('membership', $src->membership_id); $obj->status = $src->status; $obj->gateway = $src->gateway_id; $obj->start = $src->start_date; $obj->end = $src->expire_date; $obj->trial_finished = $src->trial_period_completed; if (!$obj->trial_finished) { $obj->trial_end = $src->trial_expire_date; } $obj->invoices = array(); $invoices = $src->get_invoices(); foreach ($invoices as $invoice) { $obj->invoices[] = $this->export_invoice($invoice); } return $obj; }
/** * Save invoices using the invoices model. * * @since 1.0.0 * * @param mixed $fields Transaction fields */ private function save_invoice($fields) { $msg = MS_Helper_Billing::BILLING_MSG_NOT_UPDATED; if ($this->is_admin_user() && is_array($fields) && !empty($fields['user_id']) && !empty($fields['membership_id'])) { $member = MS_Factory::load('MS_Model_Member', $fields['user_id']); $membership_id = $fields['membership_id']; $gateway_id = 'admin'; $subscription = MS_Model_Relationship::get_subscription($member->id, $membership_id); if (empty($subscription)) { $subscription = MS_Model_Relationship::create_ms_relationship($membership_id, $member->id, $gateway_id); } else { $subscription->gateway_id = $gateway_id; $subscription->save(); } $invoice_id = intval($fields['invoice_id']); $invoice = MS_Factory::load('MS_Model_Invoice', $invoice_id); if (!$invoice->is_valid()) { $invoice = $subscription->get_current_invoice(); $msg = MS_Helper_Billing::BILLING_MSG_ADDED; } else { $msg = MS_Helper_Billing::BILLING_MSG_UPDATED; } foreach ($fields as $field => $value) { $invoice->{$field} = $value; } $invoice->save(); if (!empty($fields['execute'])) { $invoice->changed(); } } return apply_filters('ms_controller_billing_save_invoice', $msg, $fields, $this); }
/** * Returns the contens of the dialog * * @since 1.0.0 * * @return object */ public function get_contents($data) { $member = $data['model']; $currency = MS_Plugin::instance()->settings->currency; $show_trial = MS_Model_Addon::is_enabled(MS_Model_Addon::ADDON_TRIAL); $all_subscriptions = MS_Model_Relationship::get_subscriptions(array('user_id' => $member->id, 'status' => 'all', 'meta_key' => 'expire_date', 'orderby' => 'meta_value', 'order' => 'DESC')); // Prepare the form fields. $inp_dialog = array('type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'name' => 'dialog', 'value' => 'View_Member_Dialog'); $inp_id = array('type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'name' => 'member_id', 'value' => $member->id); $inp_nonce = array('type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'name' => '_wpnonce', 'value' => wp_create_nonce(self::ACTION_SAVE)); $inp_action = array('type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'name' => 'dialog_action', 'value' => self::ACTION_SAVE); $inp_save = array('type' => MS_Helper_Html::INPUT_TYPE_SUBMIT, 'value' => __('Save', 'membership2'), 'class' => 'ms-submit-form', 'data' => array('form' => 'ms-edit-member')); $inp_cancel = array('type' => MS_Helper_Html::INPUT_TYPE_BUTTON, 'value' => __('Close', 'membership2'), 'class' => 'close'); ob_start(); ?> <div> <form class="ms-form wpmui-ajax-update ms-edit-member" data-wpmui-ajax="<?php echo esc_attr('save'); ?> "> <div class="ms-form wpmui-form wpmui-grid-8"> <table class="widefat"> <thead> <tr> <th class="column-membership"> <?php _e('Membership', 'membership2'); ?> </th> <th class="column-status"> <?php _e('Status', 'membership2'); ?> </th> <th class="column-start"> <?php _e('Subscribed on', 'membership2'); ?> </th> <th class="column-expire"> <?php _e('Expires on', 'membership2'); ?> </th> <?php if ($show_trial) { ?> <th class="column-trialexpire"> <?php _e('Trial until', 'membership2'); ?> </th> <?php } ?> <th class="column-payments"> <?php _e('Payments', 'membership2'); ?> </th> </tr> </thead> <tbody> <?php foreach ($all_subscriptions as $subscription) { $membership = $subscription->get_membership(); $payments = $subscription->get_payments(); $num_payments = count($payments); $amount_payments = 0; foreach ($payments as $payment) { $amount_payments += $payment['amount']; } $subscription_info = array('subscription_id' => $subscription->id); $update_info = array('subscription_id' => $subscription->id, 'statuscheck' => 'yes'); ?> <tr> <td class="column-membership"> <?php $membership->name_tag(); ?> </td> <td class="column-status"> <?php printf('<a href="#" data-ms-dialog="View_Member_Subscription" data-ms-data="%2$s">%1$s</a> <a href="#" data-ms-dialog="View_Member_Subscription" data-ms-data="%3$s" title="%5$s">%4$s</a>', $subscription->status, esc_attr(json_encode($subscription_info)), esc_attr(json_encode($update_info)), '<i class="dashicons dashicons-update"></i>', __('Check and update subscription status', 'membership2')); ?> </td> <td class="column-start"> <?php echo $subscription->start_date; ?> </td> <td class="column-expire"> <?php echo $subscription->expire_date; ?> </td> <?php if ($show_trial) { ?> <td class="column-trialexpire"> <?php if ($subscription->start_date == $subscription->trial_expire_date) { echo '-'; } else { echo $subscription->trial_expire_date; } ?> </td> <?php } ?> <td class="column-payments"> <?php $total = sprintf('<b>%1$s</b> (%3$s %2$s)', $num_payments, MS_Helper_Billing::format_price($amount_payments), $currency); printf('<a href="#" data-ms-dialog="View_Member_Payment" data-ms-data="%1$s">%2$s</a>', esc_attr(json_encode($subscription_info)), $total); ?> </td> </tr> <?php } ?> </tbody> </table> </div> <?php MS_Helper_Html::html_element($inp_id); MS_Helper_Html::html_element($inp_dialog); MS_Helper_Html::html_element($inp_nonce); MS_Helper_Html::html_element($inp_action); ?> </form> <div class="buttons"> <?php MS_Helper_Html::html_element($inp_cancel); // MS_Helper_Html::html_element( $inp_save ); ?> </div> </div> <?php $html = ob_get_clean(); return apply_filters('ms_view_member_dialog_to_html', $html); }
/** * Render membership payment information. * * Related Filter Hooks: * - the_content * * @since 1.0.0 * * @param string $content The page content to filter. * @return string The filtered content. */ public function payment_table($content) { $data = array(); $subscription = null; $member = MS_Model_Member::get_current_member(); $membership_id = 0; lib2()->array->equip_request('membership_id', 'move_from_id', 'ms_relationship_id'); if (!empty($_POST['ms_relationship_id'])) { // Error path, showing payment table again with error msg $subscription = MS_Factory::load('MS_Model_Relationship', absint(intval($_POST['ms_relationship_id']))); $membership = $subscription->get_membership(); $membership_id = $membership->id; if (!empty($_POST['error'])) { lib2()->array->strip_slashes($_POST, 'error'); $data['error'] = $_POST['error']; } } elseif (!empty($_REQUEST['membership_id'])) { // First time loading $membership_id = intval($_REQUEST['membership_id']); $membership = MS_Factory::load('MS_Model_Membership', $membership_id); $move_from_id = absint($_REQUEST['move_from_id']); $subscription = MS_Model_Relationship::create_ms_relationship($membership_id, $member->id, '', $move_from_id); } else { MS_Helper_Debug::log('Error: missing POST params'); MS_Helper_Debug::log($_POST); return $content; } $invoice = $subscription->get_current_invoice(); /** * Notify Add-ons that we are preparing payment details for a membership * subscription. * * E.g. Coupon discount is applied by this hook. * * @since 1.0.0 */ $invoice = apply_filters('ms_signup_payment_details', $invoice, $subscription, $membership); $invoice->save(); $data['invoice'] = $invoice; $data['membership'] = $membership; $data['member'] = $member; $data['ms_relationship'] = $subscription; $view = MS_Factory::load('MS_View_Frontend_Payment'); $view->data = apply_filters('ms_view_frontend_payment_data', $data, $membership_id, $subscription, $member, $this); return apply_filters('ms_controller_frontend_payment_table', $view->to_html(), $this); }
/** * Completely whipe all Membership data from Database. * * Note: This function is not used currently... * * @since 1.0.0 */ private static function cleanup_db() { global $wpdb; $sql = array(); $trash_ids = array(); // Delete membership meta-data from users. $users = MS_Model_Member::get_members(); foreach ($users as $user) { $user->delete_all_membership_usermeta(); $user->save(); } // Determine IDs of Membership Pages. $page_types = MS_Model_Pages::get_page_types(); foreach ($page_types as $type => $name) { $page_id = MS_Model_Pages::get_setting($type); $trash_ids[] = $page_id; } /** * Delete all plugin settings. * Settings are saved by classes that extend MS_Model_option */ foreach (MS_Model_Gateway::get_gateways() as $option) { $option->delete(); } MS_Factory::load('MS_Model_Addon')->delete(); MS_Factory::load('MS_Model_Pages')->delete(); MS_Factory::load('MS_Model_Settings')->delete(); /** * Delete transient data * Transient data is saved by classed that extend MS_Model_Transient */ MS_Factory::load('MS_Model_Simulate')->delete(); /** * Delete all plugin content. * Content is saved by classes that extend MS_Model_CustomPostType */ $ms_posttypes = array(MS_Model_Communication::get_post_type(), MS_Model_Event::get_post_type(), MS_Model_Invoice::get_post_type(), MS_Model_Transactionlog::get_post_type(), MS_Model_Membership::get_post_type(), MS_Model_Relationship::get_post_type(), MS_Addon_Coupon_Model::get_post_type(), MS_Addon_Invitation_Model::get_post_type()); foreach ($ms_posttypes as $type) { $sql[] = $wpdb->prepare("DELETE FROM {$wpdb->posts} WHERE post_type = %s;", $type); } // Remove orphaned post-metadata. $sql[] = "\n\t\tDELETE FROM {$wpdb->postmeta}\n\t\tWHERE NOT EXISTS (\n\t\t\tSELECT 1 FROM {$wpdb->posts} tmp WHERE tmp.ID = post_id\n\t\t);\n\t\t"; // Clear all WP transient cache. $sql[] = "\n\t\tDELETE FROM {$wpdb->options}\n\t\tWHERE option_name LIKE '_transient_%';\n\t\t"; foreach ($sql as $s) { $wpdb->query($s); } // Move Membership pages to trash. foreach ($trash_ids as $id) { wp_delete_post($id, true); } // Clear all data from WP Object cache. wp_cache_flush(); // Redirect to the main page. wp_safe_redirect(MS_Controller_Plugin::get_admin_url()); exit; }
/** * Get post types that are part of this plugin. * * @since 1.0.0 * * @return array The plugin core post types. */ public static function get_ms_post_types() { $cpts = array(MS_Model_Membership::get_post_type(), MS_Model_Invoice::get_post_type(), MS_Model_Communication::get_post_type(), MS_Model_Relationship::get_post_type(), MS_Model_Event::get_post_type()); return apply_filters('ms_rule_cptgroup_model_get_ms_post_types', $cpts); }
/** * Creates a new subscription for the specified user/membership and returns * the MS_Model_Relationship object. * * @since 1.0.0 * @return MS_Model_Relationship */ public static function subscribe($user_key, $membership_key, $gateway_id = '') { if (!is_numeric($user_key)) { $user_key = self::id('user', $user_key); } if (!is_numeric($membership_key)) { $membership_key = self::id('membership', $membership_key); } $subscription = MS_Model_Relationship::create_ms_relationship($membership_key, $user_key, $gateway_id, 0); return $subscription; }
/** * Called by MS_Model_Invoice before a new invoice is saved. We apply the * coupon discount to the total amount, if a coupon was used. * * @since 1.0.0 * @param MS_Model_Invoice $invoice * @param MS_Model_Relationship $subscription * @return MS_Model_Invoice */ public function apply_discount($invoice, $subscription) { $membership = $subscription->get_membership(); $member = MS_Factory::load('MS_Model_Member', $subscription->user_id); if (isset($_POST['apply_coupon_code'])) { $coupon = apply_filters('ms_addon_coupon_model', MS_Addon_Coupon_Model::load_by_code($_POST['coupon_code'])); $coupon->save_application($subscription); } else { $coupon = MS_Addon_Coupon_Model::get_application($member->id, $membership->id); if (!empty($_POST['remove_coupon_code'])) { $note = sprintf(__('Remove Coupon "%s"', MS_TEXT_DOMAIN), $coupon->code); $invoice->add_notes($note); $coupon->remove_application($member->id, $membership->id); $coupon = false; } } self::the_coupon($coupon); if ($coupon && $coupon->is_valid($membership->id)) { $discount = $coupon->get_discount_value($subscription); $invoice->coupon_id = $coupon->id; $invoice->discount = $discount; $note = sprintf(__('Apply Coupon "%s": Discount %s %s!', MS_TEXT_DOMAIN), $coupon->code, $invoice->currency, $discount); $invoice->add_notes($note); } else { $invoice->coupon_id = ''; $invoice->discount = 0; } return $invoice; }
/** * Replace comm_vars with corresponding values. * * @since 1.0.0 * * @param MS_Model_Relationship $subscription The membership relationship to send message to. * @param MS_Model_Member $member The member object to get info from. * @return array { * Returns array of ( $var_name => $var_replace ). * * @type string $var_name The variable name to replace. * @type string $var_replace The variable corresponding replace string. * } */ public function get_comm_vars($subscription, $member) { $currency = MS_Plugin::instance()->settings->currency . ' '; $invoice = null; $membership = null; if ($subscription && $subscription instanceof MS_Model_Relationship) { // First try to fetch the current invoice. $invoice = $subscription->get_current_invoice(false); $prev_invoice = $subscription->get_previous_invoice(); // If no current invoice exists then fetch the previous invoice. if (empty($invoice)) { $invoice = $prev_invoice; } $membership = $subscription->get_membership(); } $comm_vars = apply_filters('ms_model_communication_comm_vars', $this->comm_vars, $this->type, $member, $subscription); $wp_user = $member->get_user(); foreach ($comm_vars as $key => $description) { $var_value = ''; switch ($key) { case self::COMM_VAR_BLOG_NAME: $var_value = get_option('blogname'); break; case self::COMM_VAR_BLOG_URL: $var_value = get_option('home'); break; case self::COMM_VAR_USERNAME: $var_value = $member->username; break; case self::COMM_VAR_PASSWORD: /** * $member->password is ONLY available in the same request * when the new user account was created! After this we only * have the encrypted password in the DB, and the plain-text * version will never be available again in code... * * @since 1.0.1.1 */ if (self::COMM_TYPE_SIGNUP == $this->type) { $var_value = $member->password; } break; case self::COMM_VAR_USER_DISPLAY_NAME: $var_value = $wp_user->display_name; break; case self::COMM_VAR_USER_FIRST_NAME: $var_value = $member->first_name; break; case self::COMM_VAR_USER_LAST_NAME: $var_value = $member->last_name; break; case self::COMM_VAR_NET_NAME: $var_value = get_site_option('site_name'); break; case self::COMM_VAR_NET_URL: $var_value = get_site_option('siteurl'); break; case self::COMM_VAR_MS_ACCOUNT_PAGE_URL: $var_value = sprintf('<a href="%s">%s</a>', MS_Model_Pages::get_page_url(MS_Model_Pages::MS_PAGE_ACCOUNT), __('account page', MS_TEXT_DOMAIN)); break; // Needs: $membership // Needs: $membership case self::COMM_VAR_MS_NAME: if ($membership && $membership->name) { $var_value = $membership->name; } break; // Needs: $invoice // Needs: $invoice case self::COMM_VAR_MS_INVOICE: if ($invoice) { if ($invoice->total > 0 || $invoice->uses_trial) { $attr = array('post_id' => $invoice->id, 'pay_button' => 0); $scode = MS_Factory::load('MS_Controller_Shortcode'); $var_value = $scode->membership_invoice($attr); } } break; // Needs: $subscription // Needs: $subscription case self::COMM_VAR_MS_REMAINING_DAYS: if ($subscription) { $days = $subscription->get_remaining_period(); $var_value = sprintf(__('%s day%s', MS_TEXT_DOMAIN), $days, abs($days) > 1 ? 's' : ''); } break; // Needs: $subscription // Needs: $subscription case self::COMM_VAR_MS_REMAINING_TRIAL_DAYS: if ($subscription) { $days = $subscription->get_remaining_trial_period(); $var_value = sprintf(__('%s day%s', MS_TEXT_DOMAIN), $days, abs($days) > 1 ? 's' : ''); } break; // Needs: $subscription // Needs: $subscription case self::COMM_VAR_MS_EXPIRY_DATE: if ($subscription) { $var_value = $subscription->expire_date; } break; } $comm_vars[$key] = apply_filters('ms_model_communication_send_message_comm_var-' . $key, $var_value, $this->type, $member, $subscription, $invoice); } return apply_filters('ms_model_communication_get_comm_vars', $comm_vars, $member); }
/** * Make sure that we respect the Single-Membership rule. * This rule is active when the "Multiple-Memberships" Add-on is DISABLED. * * @since 1.0.0 * * @param MS_Model_Relationship $new_relationship */ protected function validate_membership_states($new_relationship) { if (MS_Model_Addon::is_enabled(MS_Model_Addon::ADDON_MULTI_MEMBERSHIPS)) { // Multiple memberships allowed. No need to check anything. return; } $cancel_these = array(MS_Model_Relationship::STATUS_TRIAL, MS_Model_Relationship::STATUS_ACTIVE, MS_Model_Relationship::STATUS_PENDING); $member = $new_relationship->get_member(); foreach ($member->subscriptions as $subscription) { if ($subscription->id === $new_relationship->id) { continue; } if (in_array($subscription->status, $cancel_these)) { $subscription->cancel_membership(); } } }
/** * A payment was received, award affiliate reward to the referrer. * * Whenever a Membership2 invoice is paid we try to find the referrer of * the member and award a reward to him according to the payment settings. * * This function uses the Membership2 hook `ms_invoice_paid` which is called * when a user does either * (1) sucessfully make a payment for a paid membership or * (2) successfully subscribe to a free membership. * * @since 1.0.0 * @param MS_Model_Invoice $invoice The invoice which was paid. * @param MS_Model_Relationship $subscription */ public function payment_processed($invoice, $subscription) { global $affiliate; // Used for communication with Affiliates plugin. global $blog_id, $site_id; // Used for logging. $user_id = $invoice->user_id; $membership = $subscription->get_membership(); $pay_once = defined('AFFILIATE_PAYONCE') && 'yes' == AFFILIATE_PAYONCE; $user_id_referrer = get_user_meta($user_id, 'affiliate_referred_by', true); if (empty($user_id_referrer)) { // We do not know who referred the user, don't pay a commission. return; } $affiliate_paid = get_user_meta($user_id, 'affiliate_paid', true); if ($pay_once && 'yes' == $affiliate_paid) { // The referrer already got a one-time commission, don't pay again. return; } $complete_records = $affiliate->get_complete_records($user_id_referrer, date('Ym'), array(self::AREA_KEY), $user_id); if (is_array($complete_records)) { // Make sure that this subscription was not commissioned before. foreach ($complete_records as $record) { $meta = maybe_unserialize($record->meta); if ($meta['subscription_id'] == $subscription->id) { // It seems this subscription was already commissioned. return; } } } // Okay, the referrer is entitiled to the commission! /* * Reward is the money that the user receives. * It is stored in cents/milli-percent. * I.e. '100' $ -> 1.00 $ and '100' % -> 1.00 % */ $reward = $this->get_value($membership); $type = $this->get_type($membership); switch ($type) { case 'inv': $base = $invoice->subtotal; // Invoice amount without taxes. $reward = $base * $reward / 100; break; case 'mem': $base = $membership->price; // Membership price setting. $reward = $base * $reward / 100; break; case 'abs': default: // Reward already has correct value. break; } $reward = round($reward, 2, PHP_ROUND_HALF_DOWN); // Note: lib2() used here is provided by the Membership2 plugin. $meta = array('subscription_id' => $subscription->id, 'invoice_id' => $invoice->id, 'gateway_id' => $invoice->gateway_id, 'transaction_id' => $invoice->external_id, 'blog_id' => $blog_id, 'site_id' => $site_id, 'current_user_id' => get_current_user_id(), 'REMOTE_URL' => $_SERVER['HTTP_REFERER'], 'LOCAL_URL' => lib2()->net->current_url(), 'IP' => lib2()->net->current_ip()->ip); do_action('affiliate_purchase', $user_id_referrer, $reward, self::AREA_KEY, $user_id, __('Membership2', 'affiliate'), $meta); if ($pay_once) { update_user_meta($user_id, 'affiliate_paid', 'yes'); } }
/** * Adds the Pro-Rating discount to an invoice. * * @since 1.0.1.0 * @param MS_Model_Invoice $invoice * @return MS_Model_Invoice Modified Invoice. */ public function add_discount($invoice) { // Only the first invoice can be pro-rated. if ($invoice->invoice_number > 1) { return $invoice; } $subscription = $invoice->get_subscription(); $membership = $invoice->get_membership(); if (!$subscription->move_from_id) { return $invoice; } $ids = explode(',', $subscription->move_from_id); if (empty($ids)) { return $invoice; } if ($membership->is_free()) { return $invoice; } // Calc pro rate discount if moving from another membership. $pro_rate = 0; foreach ($ids as $id) { if (!$id) { continue; } $move_from = MS_Model_Relationship::get_subscription($subscription->user_id, $id); if ($move_from->is_valid() && $move_from->id == $id) { $pro_rate += $this->get_discount($move_from); } } $pro_rate = floatval(apply_filters('ms_addon_prorate_apply_discount', abs($pro_rate), $invoice)); if ($pro_rate > $invoice->amount) { $pro_rate = $invoice->amount; } if ($pro_rate > 0) { $invoice->pro_rate = $pro_rate; $notes[] = sprintf(__('Pro-Rate Discount: %s.', MS_TEXT_DOMAIN) . ' ', $invoice->currency . ' ' . $pro_rate); } return $invoice; }
/** * Load MS_Model_Member Object. * * Load from user and user meta. * This data is always network-wide. * * @since 1.0.0 * * @param MS_Model_Member $model The empty member instance. * @param int $user_id The user/member ID. * * @return MS_Model_Member The retrieved object. */ protected static function load_from_wp_user($model, $user_id, $name = null) { $class = get_class($model); $cache = wp_cache_get($user_id, $class); if ($cache) { $model = $cache; } else { $wp_user = new WP_User($user_id, $name); if (!empty($wp_user->ID)) { $member_details = get_user_meta($user_id); $model->id = $wp_user->ID; $model->username = $wp_user->user_login; $model->email = $wp_user->user_email; $model->name = $wp_user->display_name; $model->first_name = $wp_user->first_name; $model->last_name = $wp_user->last_name; $model->wp_user = $wp_user; if (!$model->name) { if ($model->first_name) { $model->name = $model->first_name . ' ' . $model->last_name; } else { $model->name = $wp_user->user_login; } $model->name = ucwords(strtolower($model->name)); } $model->name = trim($model->name); /** * Manually customize the display name of the user via a filter. * * @since 1.0.1.2 * @param string $name The default display name used by M2. * @param WP_User $wp_user The user object used to populate the name. */ $model->name = apply_filters('ms_model_user_set_name', $model->name, $wp_user); // Remove automatic populated values from metadata, if present. unset($member_details['ms_username']); unset($member_details['ms_email']); unset($member_details['ms_name']); unset($member_details['ms_first_name']); unset($member_details['ms_last_name']); self::populate_model($model, $member_details, 'ms_'); // Load membership_relationships $model->subscriptions = MS_Model_Relationship::get_subscriptions(array('user_id' => $model->id)); } } return apply_filters('ms_factory_load_from_wp_user', $model, $class, $user_id); }
/** * Sort function used as second param by `uasort()` to sort a subscription * list by membership priority. * Memberships with equal priority are sorted alphabeically. * * @since 1.0.1.0 * @param MS_Model_Relationship $a * @param MS_Model_Relationship $b * @return int -1: a < b | 0: a = b | +1: a > b */ public static function sort_by_priority($a, $b) { $m1 = $a->get_membership(); $m2 = $b->get_membership(); if ($m1->priority == $m2->priority) { return $m1->name < $m2->name ? -1 : 1; } else { return $m1->priority - $m2->priority; } }
/** * Checks if a subscription has reached the maximum paycycle repetitions. * If the last paycycle was paid then the subscription is cancelled. * * @since 1.0.0 * @internal Called by process_purchase() and request_payment() * * @param MS_Model_Relationship $subscription * @param M2_Stripe_Subscription $stripe_sub */ protected function cancel_if_done($subscription, $stripe_sub) { $membership = $subscription->get_membership(); if ($membership->pay_cycle_repetitions < 1) { return; } $payments = $subscription->payments; if (count($payments) < $membership->pay_cycle_repetitions) { return; } $stripe_sub->cancel(array('at_period_end' => true)); }
/** * Echo a member-list. This function can be overwritten by other views * to customize the list. * * @since 1.0.0 * * @param array $members List of members to display. */ protected function members_panel_data($members, $membership_id) { $item = 0; $max_items = 10; $class = ''; $status_types = MS_Model_Relationship::get_status_types(); ?> <table class="ms-list-table widefat"> <thead> <th><?php _e('Member', 'membership2'); ?> </th> <th><?php _e('Since', 'membership2'); ?> </th> <th><?php _e('Status', 'membership2'); ?> </th> </thead> <tbody> <?php foreach ($this->data['members'] as $member) { $item += 1; if ($item > $max_items) { break; } $class = $class == 'alternate' ? '' : 'alternate'; $subscription = $member->get_subscription($membership_id); ?> <tr class="<?php echo esc_attr($class); ?> "> <td><?php echo esc_html($member->username); ?> </td> <td><?php echo esc_html(MS_Helper_Period::format_date($subscription->start_date)); ?> </td> <td><?php echo esc_html($status_types[$subscription->status]); ?> </td> </tr> <?php } ?> </tbody> </table> <?php }
/** * Check membership status. * * Execute actions when time/period condition are met. * E.g. change membership status, add communication to queue, create invoices. * * @since 1.0.0 */ public function check_membership_status() { do_action('ms_model_plugin_check_membership_status_before', $this); if ($this->member->is_simulated_user()) { return; } /* * For performance reasons we only process a small batch at once. * Here we find out, which subscriptions should be processed during * the current request. */ $offset = (int) MS_Factory::get_option('ms_batch_check_offset_flag'); // Find the next X subscriptions from DB. $args = apply_filters('ms_model_plugin_check_membership_status_get_subscription_args', array('status' => 'valid', 'orderby' => 'ID', 'posts_per_page' => $this->_process_per_batch, 'offset' => $offset, 'nopaging' => false)); $subscriptions = MS_Model_Relationship::get_subscriptions($args); if (count($subscriptions) < $this->_process_per_batch) { // We processed all subscriptions. Clean up. MS_Factory::delete_option('ms_batch_check_offset_flag'); } else { // We did not process all subscriptions. Remember where to continue. MS_Factory::update_option('ms_batch_check_offset_flag', $offset + $this->_process_per_batch); // Re-scheduling the cron job will run it again on next page load. $hook = 'ms_cron_check_membership_status'; wp_clear_scheduled_hook($hook); $this->setup_cron_services($hook); } // Perform the actual status checks! foreach ($subscriptions as $subscription) { error_log('This is ' . $subscription->id); $subscription->check_membership_status(); } do_action('ms_model_plugin_check_membership_status_after', $this); }
public function can_change_payment() { // Allow if Membership is new/unsaved. if (empty($this->id)) { return true; } // Allow if no payment detail was entered yet (incomplete setup). if (empty($this->payment_type)) { return true; } // Allow if no members signed up yet. $members = MS_Model_Relationship::get_subscription_count(array('membership_id' => $this->id)); if (empty($members)) { return true; } // Otherwise payment details cannot be changed anymore. return false; }
/** * Check membership status. * * Execute actions when time/period condition are met. * E.g. change membership status, add communication to queue, create invoices. * * @since 1.0.0 */ public function check_membership_status() { do_action('ms_model_plugin_check_membership_status_before', $this); if ($this->member->is_simulated_user()) { return; } $args = apply_filters('ms_model_plugin_check_membership_status_get_subscription_args', array('status' => 'valid')); $subscriptions = MS_Model_Relationship::get_subscriptions($args); foreach ($subscriptions as $subscription) { $subscription->check_membership_status(); } do_action('ms_model_plugin_check_membership_status_after', $this); }
/** * Move a membership. * * @since 1.0.0 * @api * * @param int $old_membership_id The membership id to move from. * @param int $mew_membership_id The membership id to move to. */ public function move_membership($old_membership_id, $mew_membership_id) { $old_subscription = $this->get_subscription($old_membership_id); if ($old_subscription) { $new_subscription = MS_Model_Relationship::create_ms_relationship($mew_membership_id, $this->id, $old_subscription->gateway_id, $old_membership_id); $this->cancel_membership($old_membership_id); $this->subscriptions[] = $new_subscription; MS_Model_Event::save_event(MS_Model_Event::TYPE_MS_MOVED, $new_subscription); } do_action('ms_model_membership_move_membership', $old_membership_id, $mew_membership_id, $this); }
/** * Load MS_Model_Member Object. * * Load from user and user meta. * This data is always network-wide. * * @since 1.0.0 * * @param MS_Model_Member $model The empty member instance. * @param int $user_id The user/member ID. * * @return MS_Model_Member The retrieved object. */ protected static function load_from_wp_user($model, $user_id, $name = null) { $class = get_class($model); $cache = wp_cache_get($user_id, $class); if ($cache) { $model = $cache; } else { $wp_user = new WP_User($user_id, $name); if (!empty($wp_user->ID)) { $member_details = get_user_meta($user_id); $model->id = $wp_user->ID; $model->username = $wp_user->user_login; $model->email = $wp_user->user_email; $model->name = $wp_user->user_nicename; $model->first_name = $wp_user->first_name; $model->last_name = $wp_user->last_name; $model->wp_user = $wp_user; self::populate_model($model, $member_details, 'ms_'); // Load membership_relationships $model->subscriptions = MS_Model_Relationship::get_subscriptions(array('user_id' => $model->id)); } } return apply_filters('ms_factory_load_from_wp_user', $model, $class, $user_id); }
/** * Register plugin custom post types. * * @since 1.0.0 */ public function register_custom_post_types() { do_action('ms_plugin_register_custom_post_types_before', $this); $cpts = apply_filters('ms_plugin_register_custom_post_types', array(MS_Model_Membership::get_post_type() => MS_Model_Membership::get_register_post_type_args(), MS_Model_Relationship::get_post_type() => MS_Model_Relationship::get_register_post_type_args(), MS_Model_Invoice::get_post_type() => MS_Model_Invoice::get_register_post_type_args(), MS_Model_Communication::get_post_type() => MS_Model_Communication::get_register_post_type_args(), MS_Model_Event::get_post_type() => MS_Model_Event::get_register_post_type_args())); foreach ($cpts as $cpt => $args) { MS_Helper_Utility::register_post_type($cpt, $args); } }
/** * Removes all subscriptions and memberships from the current site. * This is done before the import if the "Replace existing data" flag is set. * * @since 1.0.0 */ protected function clear_memberships() { // Delete all Relationships. $subscriptions = MS_Model_Relationship::get_subscriptions(array('status' => 'all')); foreach ($subscriptions as $subscription) { $subscription->delete(); } // Delete all Memberships. $memberships = MS_Model_Membership::get_memberships(); foreach ($memberships as $membership) { if ($membership->is_base()) { continue; } $membership->delete(true); } }
/** * Display Membership Overview page. * * @since 1.0.0 */ public function page_overview() { $membership = $this->load_membership(); $membership_id = $membership->id; $data = array(); $data['step'] = $this->get_step(); $data['action'] = self::ACTION_SAVE; $data['membership'] = $membership; $data['bread_crumbs'] = $this->get_bread_crumbs(); $data['members'] = array(); $subscriptions = MS_Model_Relationship::get_subscriptions(array('membership_id' => $membership->id)); foreach ($subscriptions as $subscription) { $data['members'][] = $subscription->get_member(); } switch ($membership->type) { case MS_Model_Membership::TYPE_DRIPPED: $view = MS_Factory::create('MS_View_Membership_Overview_Dripped'); break; default: case MS_Model_Membership::TYPE_STANDARD: $view = MS_Factory::create('MS_View_Membership_Overview_Simple'); break; } // Select Events args $args = array(); $args['meta_query']['membership_id'] = array('key' => 'membership_id', 'value' => array($membership_id, 0), 'compare' => 'IN'); $data['events'] = MS_Model_Event::get_events($args); $view = apply_filters('ms_view_membership_overview', $view); $view->data = apply_filters('ms_view_membership_overview_data', $data, $this); $view->render(); }
/** * Membership account page shortcode callback function. * * @since 1.0.0 * * @param mixed[] $atts Shortcode attributes. */ public function membership_account($atts) { MS_Helper_Shortcode::did_shortcode(MS_Helper_Shortcode::SCODE_MS_ACCOUNT); $data = apply_filters('ms_controller_shortcode_membership_account_atts', shortcode_atts(array('show_membership' => true, 'show_membership_change' => true, 'membership_title' => __('Your Membership', 'membership2'), 'membership_change_label' => __('Change', 'membership2'), 'show_profile' => true, 'show_profile_change' => true, 'profile_title' => __('Personal details', 'membership2'), 'profile_change_label' => __('Edit', 'membership2'), 'show_invoices' => true, 'limit_invoices' => 10, 'show_all_invoices' => true, 'invoices_title' => __('Invoices', 'membership2'), 'invoices_details_label' => __('View all', 'membership2'), 'show_activity' => true, 'limit_activities' => 10, 'show_all_activities' => true, 'activity_title' => __('Activities', 'membership2'), 'activity_details_label' => __('View all', 'membership2')), $atts)); $data['show_membership'] = lib3()->is_true($data['show_membership']); $data['show_membership_change'] = lib3()->is_true($data['show_membership_change']); $data['show_profile'] = lib3()->is_true($data['show_profile']); $data['show_profile_change'] = lib3()->is_true($data['show_profile_change']); $data['show_invoices'] = lib3()->is_true($data['show_invoices']); $data['show_all_invoices'] = lib3()->is_true($data['show_all_invoices']); $data['show_activity'] = lib3()->is_true($data['show_activity']); $data['show_all_activities'] = lib3()->is_true($data['show_all_activities']); $data['limit_invoices'] = absint($data['limit_invoices']); $data['limit_activities'] = absint($data['limit_activities']); $data['member'] = MS_Model_Member::get_current_member(); $data['membership'] = array(); $subscriptions = MS_Model_Relationship::get_subscriptions(array('user_id' => $data['member']->id, 'status' => 'all')); if (is_array($subscriptions)) { foreach ($subscriptions as $subscription) { // Do not display system-memberships in Account if ($subscription->is_system()) { continue; } // Do not display deactivated memberships in Account if ($subscription->get_status() == MS_Model_Relationship::STATUS_DEACTIVATED) { continue; } $data['subscription'][] = $subscription; } } $data['invoices'] = MS_Model_Invoice::get_public_invoices($data['member']->id, $data['limit_invoices']); $data['events'] = MS_Model_Event::get_events(array('author' => $data['member']->id, 'posts_per_page' => $data['limit_activities'])); $view = MS_Factory::create('MS_View_Shortcode_Account'); $view->data = apply_filters('ms_view_shortcode_account_data', $data, $this); return $view->to_html(); }
/** * Returns a query arg structure tailored to give the defined results * * @since 1.0.0 * @return array Query args */ protected function prepare_query_args($args) { lib2()->array->equip_request('s', 'membership_id', 'search_options', 'status'); // Prepare order by statement. if (!empty($_REQUEST['orderby']) && !empty($_REQUEST['order'])) { $args['orderby'] = $_REQUEST['orderby']; $args['order'] = $_REQUEST['order']; } // Filter by search-term $search_filter = $_REQUEST['s']; if (!empty($search_filter)) { $this->search_string = $search_filter; $search_option = $_REQUEST['search_options']; switch ($search_option) { case 'email': case 'username': $args['search'] = sprintf('*%s*', $search_filter); break; default: $args['meta_query'][$search_option] = array('key' => $search_option, 'value' => $search_filter, 'compare' => 'LIKE'); break; } $args['posts_per_page'] = -1; $args['number'] = false; $args['offset'] = 0; } // Filter by membership_id and membership status $membership_id = $_REQUEST['membership_id']; $members = array(); $filter = array(); if (!empty($membership_id)) { $filter['membership_id'] = $membership_id; } if (!empty($status)) { $filter['status'] = $status; } if (!empty($filter)) { $subscriptions = MS_Model_Relationship::get_subscriptions($filter); foreach ($subscriptions as $subscription) { $members[$subscription->user_id] = $subscription->user_id; } // Workaround to invalidate query if (empty($members)) { $members[0] = 0; } $args['include'] = $members; } return $args; }
/** * Update the subscription details after the invoice has changed. * * Process transaction status change related to this membership relationship. * Change status accordinly to transaction status. * * @since 1.0.0 * @param MS_Model_Invoice $invoice The invoice to process. * @return MS_Model_Invoice The processed invoice. */ public function changed() { do_action('ms_model_invoice_changed_before', $this); if (!$this->ms_relationship_id) { MS_Helper_Debug::log('Cannot process transaction: No relationship defined (inv #' . $this->id . ')'); } else { $subscription = $this->get_subscription(); $member = MS_Factory::load('MS_Model_Member', $this->user_id); $membership = $subscription->get_membership(); switch ($this->status) { case self::STATUS_NEW: case self::STATUS_BILLED: break; case self::STATUS_PAID: if ($this->total > 0) { MS_Model_Event::save_event(MS_Model_Event::TYPE_PAID, $subscription); } do_action('ms_model_invoice_changed-paid', $this, $member); // Check for moving memberships if ($subscription->move_from_id) { $ids = explode(',', $subscription->move_from_id); foreach ($ids as $id) { $move_from = MS_Model_Relationship::get_subscription($subscription->user_id, $id); if ($move_from->is_valid()) { $move_from->cancel_membership(); } } $subscription->cancelled_memberships = $subscription->move_from_id; $subscription->move_from_id = ''; } /* * Memberships with those payment types can have multiple * invoices for a single subscription. */ $multi_invoice = array(MS_Model_Membership::PAYMENT_TYPE_RECURRING, MS_Model_Membership::PAYMENT_TYPE_FINITE); if (in_array($membership->payment_type, $multi_invoice)) { // Update the current_invoice_number counter. $subscription->current_invoice_number = max($subscription->current_invoice_number, $this->invoice_number + 1); } if (MS_Gateway_Manual::ID == $this->gateway_id) { $this->pay_it($this->gateway_id); } break; case self::STATUS_DENIED: MS_Model_Event::save_event(MS_Model_Event::TYPE_PAYMENT_DENIED, $subscription); break; case self::STATUS_PENDING: MS_Model_Event::save_event(MS_Model_Event::TYPE_PAYMENT_PENDING, $subscription); break; default: do_action('ms_model_invoice_changed-unknown', $this); break; } $member->save(); $subscription->gateway_id = $this->gateway_id; $subscription->save(); $this->gateway_id = $this->gateway_id; $this->save(); } return apply_filters('ms_model_invoice_changed', $this, $this); }
/** * Apply coupon to get discount. * * If trial period is enabled, the discount will be applied in the trial price (even if it is free). * If the membership price is free, the discount will be zero. * If discount is bigger than the price, the discount will be equal to the price. * * @since 1.0.0 * * @param MS_Model_Relationship $subscription The membership relationship to apply coupon. * @return float The discount value. */ public function get_discount_value($subscription) { $membership = $subscription->get_membership(); $price = $membership->price; // Excluding Tax $original_price = $price; $discount = 0; if ($this->is_valid($membership->id)) { $discount = $this->discount; if (self::TYPE_PERCENT == $this->discount_type) { $discount = $price * $discount / 100; } $price -= $discount; if ($price < 0) { $price = 0; } $discount = $original_price - $price; $this->coupon_message = sprintf(__('Coupon applied: %1$s', 'membership2'), $this->code); } return apply_filters('ms_addon_coupon_model_apply_discount', $discount, $membership, $this); }
/** * Save invitation application. * * Saving the application to keep track of the application in gateway return. * Using INVITATION_REDEMPTION_TIME to expire invitation application. * * This is a non-static function, as it saves the current object! * * @since 1.0.0 * @param MS_Model_Relationship $subscription The subscription to apply the invitation. */ public function save_application($subscription) { // Don't save empty invitations. if (empty($this->code)) { return false; } $membership = $subscription->get_membership(); $time = apply_filters('ms_addon_invitation_model_save_application_redemption_time', self::INVITATION_REDEMPTION_TIME); // Grab the user account as we should be logged in by now. $user = MS_Model_Member::get_current_member(); $key = self::get_transient_name($user->id, $membership->id); $transient = apply_filters('ms_addon_invitation_model_transient_value', array('id' => $this->id, 'user_id' => $user->id, 'membership_id' => $membership->id, 'message' => $this->invitation_message)); MS_Factory::set_transient($key, $transient, $time); $this->save(); do_action('ms_addon_invitation_model_save_application', $subscription, $this); }