/**
  * Import user membership from group membership
  *
  * @since 1.0.0
  * @param int $user_id
  * @param int $group_id
  * @param int $plan_id
  * @return int|bool Imported membership ID or false on skip/failure
  */
 private function import_user_membership($user_id, $group_id, $plan_id)
 {
     // User is already a member, skip
     if (wc_memberships_is_user_member($user_id, $plan_id)) {
         return false;
     }
     /**
      * Filter new membership data, used when importing membership from Groups
      *
      * @param array $data
      * @param array $args
      */
     $data = apply_filters('wc_memberships_groups_import_membership_data', array('post_parent' => $plan_id, 'post_author' => $user_id, 'post_type' => 'wc_user_membership', 'post_status' => 'wcm-active', 'comment_status' => 'open'), array('user_id' => $user_id, 'group_id' => $group_id));
     // Create a new membership
     $user_membership_id = wp_insert_post($data);
     // Bail out on failure
     if (is_wp_error($user_membership_id)) {
         return false;
     }
     // Save group ID that granted access
     update_post_meta($user_membership_id, '_group_id', $group_id);
     // Save the membership start date
     update_post_meta($user_membership_id, '_start_date', current_time('mysql', true));
     // Calculate membership end date based on membership length
     $plan = wc_memberships_get_membership_plan($plan_id);
     $end_date = '';
     if ($plan->get_access_length_amount()) {
         $now = current_time('timestamp');
         if (strpos($plan->get_access_length_period(), 'month') !== false) {
             $end = wc_memberships()->add_months($now, $plan->get_access_length_amount());
         } else {
             $end = strtotime('+ ' . $plan->get_access_length(), $now);
         }
         $end_date = date('Y-m-d H:i:s', $end);
     }
     // Save/update end date
     $user_membership = wc_memberships_get_user_membership($user_membership_id);
     $user_membership->set_end_date($end_date);
     // Add membership note
     $group = Groups_Group::read($group_id);
     $user_membership->add_note(sprintf(__('Membership imported from Group "%s" (ID #%d)'), $group->name, $group_id));
     return $user_membership_id;
 }
/**
 * Removes membership access for processing orders
 * so access is only granted at when orders are complete
 */
function sv_wc_memberships_remove_processing_access()
{
    // make sure Memberships is active first
    if (function_exists('wc_memberships')) {
        remove_action('woocommerce_order_status_processing', array(wc_memberships(), 'grant_membership_access'), 11);
    }
}
 /**
  * Adjust booking cost
  *
  * @since 1.3.0
  * @param int $cost
  * @param WC_Booking_Form $form
  * @param array $posted
  * @return int
  */
 function adjust_booking_cost($cost, WC_Booking_Form $form, $posted)
 {
     if (wc_memberships()->member_discounts->has_discounted_price($cost, $form->product)) {
         $cost = wc_memberships()->member_discounts->get_discounted_price($cost, $form->product);
     }
     return $cost;
 }
/**
 * Membership access is already granted when orders are processing or completed
 * Add membership access for custom order statuses as well
 *
 * Example: grant membership access for "Shipped" and "Invoice Paid" orders during purchase cycle as well
 * Use the appropriate action for your custom order status
 */
function sv_wc_memberships_grant_access_for_custom_order_statuses()
{
    // bail if Memberships is not active
    if (function_exists('wc_memberships')) {
        add_action('woocommerce_order_status_shipped', array(wc_memberships(), 'grant_membership_access'), 11);
        add_action('woocommerce_order_status_invoice-paid', array(wc_memberships(), 'grant_membership_access'), 11);
    }
}
 /**
  * Constructor
  *
  * @since 1.0.0
  */
 public function __construct()
 {
     require_once wc_memberships()->get_plugin_path() . '/includes/class-wc-memberships-user-membership.php';
     add_filter('wp_insert_post_data', array($this, 'user_membership_post_date'));
     add_action('transition_post_status', array($this, 'transition_post_status'), 10, 3);
     add_action('wc_memberships_user_membership_expiry', array($this, 'expire_user_membership'));
     add_action('delete_user', array($this, 'delete_user_memberships'));
     add_action('trashed_post', array($this, 'handle_order_trashed'));
     add_action('woocommerce_order_status_refunded', array($this, 'handle_order_refunded'));
     add_filter('comments_clauses', array($this, 'exclude_membership_notes'), 10, 1);
     add_action('comment_feed_join', array($this, 'exclude_membership_notes_from_feed_join'));
     add_action('comment_feed_where', array($this, 'exclude_membership_notes_from_feed_where'));
 }
/**
 * Creates a shortcode to output the "My Memberships" table anywhere on the site
 * Outputs this section only if the current user has 1 or more memberships
 *
 * Use the shortcode: [wcm_my_memberships]
 */
function sv_wc_memberships_my_memberships_shortcode()
{
    // bail if Memberships isn't active or we're in the admin
    if (!function_exists('wc_memberships') || is_admin()) {
        return;
    }
    // buffer contents
    ob_start();
    ?>
<div class="woocommerce"><?php 
    wc_memberships()->get_frontend_instance()->get_member_area_instance()->my_account_memberships();
    ?>
</div><?php 
    // output buffered content
    echo ob_get_clean();
}
 /**
  * Get the delayed content message
  *
  * @since 1.0.0
  * @param int $user_id Optional. Defaults to current user ID.
  * @param int $post_id Optional. Defaults to current post ID.
  * @param string $access_type Optional. Defaults to "view". Applies to products only.
  * @return string
  */
 public function get_content_delayed_message($user_id = null, $post_id = null, $access_type = 'view')
 {
     if (!$user_id) {
         $user_id = get_current_user_id();
     }
     if (!$post_id) {
         global $post;
         $post_id = $post->ID;
     }
     $access_time = wc_memberships()->capabilities->get_user_access_start_time_for_post($user_id, $post_id, $access_type);
     switch (get_post_type($post_id)) {
         case 'product':
         case 'product_variation':
             if ('view' == $access_type) {
                 $message = __('This product is part of your membership, but not yet! You will gain access on {date}', WC_Memberships::TEXT_DOMAIN);
             } else {
                 $message = __('This product is part of your membership, but not yet! You can purchase it on {date}', WC_Memberships::TEXT_DOMAIN);
             }
             break;
         case 'page':
             $message = __('This page is part of your membership, but not yet! You will gain access on {date}', WC_Memberships::TEXT_DOMAIN);
             break;
         case 'post':
             $message = __('This post is part of your membership, but not yet! You will gain access on {date}', WC_Memberships::TEXT_DOMAIN);
             break;
         default:
             $message = __('This content is part of your membership, but not yet! You will gain access on {date}', WC_Memberships::TEXT_DOMAIN);
             break;
     }
     /**
      * Filter the delayed content message
      *
      * @since 1.0.0
      * @param string $message Delayed content message
      * @param int $post_id Post ID that the message applies to
      * @param string $access_time Access time timestamp
      */
     $message = apply_filters('get_content_delayed_message', $message, $post_id, $access_time);
     $message = str_replace('{date}', date_i18n(get_option('date_format'), $access_time), $message);
     return $message;
 }
 /**
  * Remove third party meta boxes from our CPT screens unless they're on the
  * whitelist
  *
  * @since 1.0.0
  * @param string $post_type
  */
 public static function maybe_remove_meta_boxes($post_type)
 {
     if (!in_array($post_type, array('wc_membership_plan', 'wc_user_membership'))) {
         return;
     }
     $allowed_meta_box_ids = apply_filters('wc_memberships_allowed_meta_box_ids', array_merge(array('submitdiv'), wc_memberships()->admin->get_meta_box_ids()));
     $screen = get_current_screen();
     foreach ($GLOBALS['wp_meta_boxes'][$screen->id] as $context => $meta_boxes_by_context) {
         foreach ($meta_boxes_by_context as $subcontext => $meta_boxes_by_subcontext) {
             foreach ($meta_boxes_by_subcontext as $meta_box_id => $meta_box) {
                 if (!in_array($meta_box_id, $allowed_meta_box_ids)) {
                     remove_meta_box($meta_box_id, $post_type, $context);
                 }
             }
         }
     }
 }
 /**
  * Duplicate memberships data for a product
  *
  * @since 1.3.0
  * @param int $new_id
  * @param object $post
  */
 public function duplicate_product_memberships_data($new_id, $post)
 {
     // Get product restriction rules
     $product_restriction_rules = wc_memberships()->rules->get_rules(array('rule_type' => 'product_restriction', 'object_id' => $post->ID, 'content_type' => 'post_type', 'content_type_name' => $post->post_type, 'exclude_inherited' => true, 'plan_status' => 'any'));
     // Get purchasing discount rules
     $purchasing_discount_rules = wc_memberships()->rules->get_rules(array('rule_type' => 'purchasing_discount', 'object_id' => $post->ID, 'content_type' => 'post_type', 'content_type_name' => $post->post_type, 'exclude_inherited' => true, 'plan_status' => 'any'));
     $product_rules = array_merge($product_restriction_rules, $purchasing_discount_rules);
     // Duplicate rules
     if (!empty($product_rules)) {
         $all_rules = get_option('wc_memberships_rules');
         foreach ($product_rules as $rule) {
             $new_rule = $rule->get_raw_data();
             $new_rule['object_ids'] = array($new_id);
             $all_rules[] = $new_rule;
         }
         update_option('wc_memberships_rules', $all_rules);
     }
     // Duplicate custom messages
     foreach (array('product_viewing_restricted', 'product_purchasing_restricted') as $message_type) {
         $message = get_post_meta($post->ID, "_wc_memberships_{$message_type}_message", true);
         $use_custom = get_post_meta($post->ID, "_wc_memberships_use_custom_{$message_type}_message", true);
         if ($message) {
             update_post_meta($new_id, "_wc_memberships_{$message_type}_message", $message);
         }
         if ($use_custom) {
             update_post_meta($new_id, "_wc_memberships_use_custom_{$message_type}_message", $use_custom);
         }
     }
     // Duplicate 'grants access to'
     foreach (wc_memberships_get_membership_plans() as $plan) {
         if ($plan->has_product($post->ID)) {
             $product_ids = get_post_meta($plan->get_id(), '_product_ids', true);
             $product_ids[] = $new_id;
             update_post_meta($plan->get_id(), '_product_ids', $product_ids);
         }
     }
     // Duplicate other settings
     update_post_meta($new_id, '_wc_memberships_force_public', get_post_meta($post->ID, '_wc_memberships_force_public', true));
 }
 /**
  * Process and save restriction rules
  *
  * @since 1.0.0
  * @param int $post_id
  * @param WP_Post $post
  */
 public function update_data($post_id, WP_Post $post)
 {
     // Update restriction rules
     wc_memberships()->admin->update_rules($post_id, array('content_restriction'), 'post');
     wc_memberships()->admin->update_custom_message($post_id, array('content_restricted'));
     update_post_meta($post_id, '_wc_memberships_force_public', isset($_POST['_wc_memberships_force_public']) ? 'yes' : 'no');
 }
 /**
  * Process and save restriction rules
  *
  * @since 1.0.0
  * @param int $post_id
  * @param WP_Post $post
  */
 public function update_data($post_id, WP_Post $post)
 {
     // Update restriction & discount rules
     wc_memberships()->admin->update_rules($post_id, array('product_restriction', 'purchasing_discount'), 'post');
     wc_memberships()->admin->update_custom_message($post_id, array('product_viewing_restricted', 'product_purchasing_restricted'));
     update_post_meta($post_id, '_wc_memberships_force_public', isset($_POST['_wc_memberships_force_public']) ? 'yes' : 'no');
     // Update membership plans that this product grants access to
     $plan_ids = $this->get_product_membership_plans($post->ID, 'id');
     $posted_plan_ids = isset($_POST['_wc_memberships_membership_plan_ids']) ? $_POST['_wc_memberships_membership_plan_ids'] : array();
     if (!is_array($posted_plan_ids)) {
         $posted_plan_ids = explode(',', $posted_plan_ids);
     }
     sort($plan_ids);
     sort($posted_plan_ids);
     // Only continue processing if there are changes
     if ($plan_ids != $posted_plan_ids) {
         $removed = array_diff($plan_ids, $posted_plan_ids);
         $new = array_diff($posted_plan_ids, $plan_ids);
         // Handle removed plans
         if (!empty($removed)) {
             foreach ($removed as $plan_id) {
                 $product_ids = get_post_meta($plan_id, '_product_ids', true);
                 if (($key = array_search($post_id, $product_ids)) !== false) {
                     unset($product_ids[$key]);
                 }
                 update_post_meta($plan_id, '_product_ids', $product_ids);
             }
         }
         // Handle new plans
         if (!empty($new)) {
             foreach ($new as $plan_id) {
                 $product_ids = get_post_meta($plan_id, '_product_ids', true);
                 $product_ids[] = $post_id;
                 update_post_meta($plan_id, '_product_ids', $product_ids);
             }
         }
     }
 }
 /**
  * Get rule access start time
  *
  * Returns the access start time this rule grants
  * for a piece of content, based on the input time.
  *
  * @since 1.0.0
  * @param string $from_time Timestamp for the time the access start
  *                               time should be calculated from
  * @return string Access start time as a timestamp
  */
 public function get_access_start_time($from_time)
 {
     $access_time = $from_time;
     if (!$this->grants_immediate_access()) {
         if (strpos($this->get_access_schedule(), 'month') !== false) {
             $access_time = wc_memberships()->add_months($from_time, $this->get_access_schedule_amount());
         } else {
             $access_time = strtotime($this->get_access_schedule(), $from_time);
         }
     }
     /**
      * Filter rule access start time
      *
      * @since 1.0.0
      * @param string $access_time Access time, as a timestamp
      * @param string $from_time From time, as a timestamp
      * @param WC_Memberships_Membership_Plan_Rule $rule
      */
     $access_time = apply_filters('wc_memberships_rule_access_start_time', $access_time, $from_time, $this);
     // Access always starts at midnight
     return strtotime('midnight', $access_time);
 }
/**
* Check if current page is restricted (works with BuddyPress who doesn't have any page/post id)
*
* @param string|int|array $plans Optional. The membership plan or plans to check against.
*                                Accepts a plan slug, ID, or an array of slugs or IDs. Default: all plans.
* @param string $delay
* @param bool $exclude_trial
*/
function kmpl_wc_memberships_has_access_to_restricted_page($plans = null, $delay = null, $exclude_trial = false)
{
    $has_access = false;
    $member_since = null;
    $access_time = null;
    // grant access to super users
    if (is_user_logged_in() && current_user_can('wc_memberships_access_all_restricted_content')) {
        $has_access = true;
    }
    // Convert to an array in all cases
    $plans = (array) $plans;
    // default to use all plans if no plan is specified
    if (empty($plans)) {
        $plans = wc_memberships_get_membership_plans();
    }
    foreach ($plans as $plan_id_or_slug) {
        $membership_plan = wc_memberships_get_membership_plan($plan_id_or_slug);
        if ($membership_plan && wc_memberships_is_user_active_member(get_current_user_id(), $membership_plan->get_id())) {
            $has_access = true;
            if (!$delay && !$exclude_trial) {
                break;
            }
            // Determine the earliest membership for the user
            $user_membership = wc_memberships()->user_memberships->get_user_membership(get_current_user_id(), $membership_plan->get_id());
            // Create a pseudo-rule to help applying filters
            $rule = new WC_Memberships_Membership_Plan_Rule(array('access_schedule_exclude_trial' => $exclude_trial ? 'yes' : 'no'));
            /** This filter is documented in includes/class-wc-memberships-capabilities.php **/
            $from_time = apply_filters('wc_memberships_access_from_time', $user_membership->get_start_date('timestamp'), $rule, $user_membership);
            // If there is no time to calculate the access time from, simply
            // use the current time as access start time
            if (!$from_time) {
                $from_time = current_time('timestamp', true);
            }
            if (is_null($member_since) || $from_time < $member_since) {
                $member_since = $from_time;
            }
        }
    }
    // Add delay
    if ($has_access && ($delay || $exclude_trial) && $member_since) {
        $access_time = $member_since;
        // Determine access time
        if (strpos($delay, 'month') !== false) {
            $parts = explode(' ', $delay);
            $amount = isset($parts[1]) ? (int) $parts[0] : '';
            $access_time = wc_memberships()->add_months($member_since, $amount);
        } else {
            if ($delay) {
                $access_time = strtotime($delay, $member_since);
            }
        }
        // Output or show delayed access message
        if ($access_time <= current_time('timestamp', true)) {
            $has_access = true;
        } else {
            $has_access = false;
            $message = __('This content is part of your membership, but not yet! You will gain access on {date}', 'woocommerce-memberships');
            // Apply the deprecated filter
            if (has_filter('get_content_delayed_message')) {
                /** This filter is documented in includes/frontend/class-wc-memberships-frontend.php **/
                $message = apply_filters('get_content_delayed_message', $message, null, $access_time);
                // Notify developers that this filter is deprecated
                _deprecated_function('The get_content_delayed_message filter', '1.3.1', 'wc_memberships_get_content_delayed_message');
            }
            /** This filter is documented in includes/frontend/class-wc-memberships-frontend.php **/
            $message = apply_filters('wc_memberships_get_content_delayed_message', $message, null, $access_time);
            $message = str_replace('{date}', date_i18n(wc_date_format(), $access_time), $message);
            $output = '<div class="wc-memberships-content-delayed-message">' . $message . '</div>';
            echo $output;
        }
    }
    return $has_access;
}
function init_woocommerce_memberships()
{
    /**
     * WooCommerce Memberships Main Plugin Class
     *
     * @since 1.0.0
     */
    class WC_Memberships extends SV_WC_Plugin
    {
        /** plugin version number */
        const VERSION = '1.3.0';
        /** @var WC_Memberships single instance of this plugin */
        protected static $instance;
        /** plugin id */
        const PLUGIN_ID = 'memberships';
        /** plugin text domain */
        const TEXT_DOMAIN = 'woocommerce-memberships';
        /** @var \WC_Memberships_Admin instance */
        public $admin;
        /** @var \WC_Memberships_Frontend instance */
        public $frontend;
        /** @var \WC_Memberships_Checkout instance */
        public $checkout;
        /** @var \WC_Memberships_Emails instance */
        public $emails;
        /** @var \WC_Memberships_Capabilities instance */
        public $capabilities;
        /** @var \WC_Memberships_Member_Discounts instance */
        public $member_discounts;
        /** @var \WC_Memberships_AJAX instance */
        public $ajax;
        /** @var \WC_Memberships_Rules instance */
        public $rules;
        /** @var \WC_Memberships_Membership_Plans instance */
        public $plans;
        /** @var \WC_Memberships_User_Memberships instance */
        public $user_memberships;
        /** @var bool helper for lazy subscriptions active check */
        private $subscriptions_active;
        /** @var bool helper for lazy user switching active check */
        private $user_switching_active;
        /** @var bool helper for lazy groups active check */
        private $groups_active;
        /** @var bool helper for lazy bookings active check */
        private $bookings_active;
        /**
         * Initializes the plugin
         *
         * @since 1.0.0
         * @return \WC_Memberships
         */
        public function __construct()
        {
            parent::__construct(self::PLUGIN_ID, self::VERSION, self::TEXT_DOMAIN);
            // Include required files
            add_action('sv_wc_framework_plugins_loaded', array($this, 'includes'));
            add_action('init', array($this, 'init'));
            // Make sure template files are searched for in our plugin
            add_filter('woocommerce_locate_template', array($this, 'locate_template'), 20, 3);
            add_filter('woocommerce_locate_core_template', array($this, 'locate_template'), 20, 3);
            add_action('woocommerce_order_status_completed', array($this, 'grant_membership_access'), 11);
            add_action('woocommerce_order_status_processing', array($this, 'grant_membership_access'), 11);
            // Lifecycle
            add_action('admin_init', array($this, 'maybe_activate'));
            register_deactivation_hook(__FILE__, array($this, 'deactivate'));
        }
        /**
         * Include required files
         *
         * @since 1.0.0
         */
        public function includes()
        {
            require_once $this->get_plugin_path() . '/includes/class-wc-memberships-post-types.php';
            require_once $this->get_plugin_path() . '/includes/class-wc-memberships-emails.php';
            require_once $this->get_plugin_path() . '/includes/class-wc-memberships-rules.php';
            require_once $this->get_plugin_path() . '/includes/class-wc-memberships-membership-plans.php';
            require_once $this->get_plugin_path() . '/includes/class-wc-memberships-user-memberships.php';
            require_once $this->get_plugin_path() . '/includes/class-wc-memberships-capabilities.php';
            require_once $this->get_plugin_path() . '/includes/class-wc-memberships-member-discounts.php';
            // Global functions
            require_once $this->get_plugin_path() . '/includes/wc-memberships-membership-plan-functions.php';
            require_once $this->get_plugin_path() . '/includes/wc-memberships-user-membership-functions.php';
            $this->emails = new WC_Memberships_Emails();
            $this->rules = new WC_Memberships_Rules();
            $this->plans = new WC_Memberships_Membership_Plans();
            $this->user_memberships = new WC_Memberships_User_Memberships();
            $this->capabilities = new WC_Memberships_Capabilities();
            $this->member_discounts = new WC_Memberships_Member_Discounts();
            // Frontend includes
            if (!is_admin()) {
                $this->frontend_includes();
            }
            // Admin includes
            if (is_admin() && !defined('DOING_AJAX')) {
                $this->admin_includes();
            }
            // AJAX includes
            if (defined('DOING_AJAX') && DOING_AJAX) {
                $this->ajax_includes();
            }
            // Integrations
            $this->integration_includes();
        }
        /**
         * Include required frontend files
         *
         * @since 1.0.0
         */
        private function frontend_includes()
        {
            require_once $this->get_plugin_path() . '/includes/wc-memberships-template-functions.php';
            require_once $this->get_plugin_path() . '/includes/class-wc-memberships-shortcodes.php';
            WC_Memberships_Shortcodes::initialize();
            require_once $this->get_plugin_path() . '/includes/frontend/class-wc-memberships-frontend.php';
            require_once $this->get_plugin_path() . '/includes/frontend/class-wc-memberships-checkout.php';
            require_once $this->get_plugin_path() . '/includes/frontend/class-wc-memberships-restrictions.php';
            $this->frontend = new WC_Memberships_Frontend();
            $this->checkout = new WC_Memberships_Checkout();
            $this->restrictions = new WC_Memberships_Restrictions();
        }
        /**
         * Include required admin files
         *
         * @since 1.0.0
         */
        private function admin_includes()
        {
            require_once $this->get_plugin_path() . '/includes/admin/class-wc-memberships-admin.php';
            $this->admin = new WC_Memberships_Admin();
            // message handler
            $this->admin->message_handler = $this->get_message_handler();
        }
        /**
         * Include required AJAX files
         *
         * @since 1.0.0
         */
        private function ajax_includes()
        {
            require_once $this->get_plugin_path() . '/includes/class-wc-memberships-ajax.php';
            $this->ajax = new WC_Memberships_AJAX();
            // checkout processes during Ajax request
            if (empty($this->checkout)) {
                require_once $this->get_plugin_path() . '/includes/frontend/class-wc-memberships-checkout.php';
                $this->checkout = new WC_Memberships_Checkout();
            }
        }
        /**
         * Include required integration files
         *
         * @since 1.0.0
         */
        private function integration_includes()
        {
            if ($this->is_subscriptions_active()) {
                require_once $this->get_plugin_path() . '/includes/integrations/class-wc-memberships-integration-subscriptions.php';
            }
            if ($this->is_user_switching_active()) {
                require_once $this->get_plugin_path() . '/includes/integrations/class-wc-memberships-integration-user-switching.php';
            }
            if ($this->is_groups_active()) {
                require_once $this->get_plugin_path() . '/includes/integrations/class-wc-memberships-integration-groups.php';
            }
            if ($this->is_bookings_active()) {
                require_once $this->get_plugin_path() . '/includes/integrations/class-wc-memberships-integration-bookings.php';
            }
        }
        /**
         * Initialize post types
         *
         * @since 1.0.0
         */
        public function init()
        {
            WC_Memberships_Post_Types::initialize();
        }
        /**
         * Load plugin text domain.
         *
         * @since 1.0.0
         * @see SV_WC_Plugin::load_translation()
         */
        public function load_translation()
        {
            load_plugin_textdomain('woocommerce-memberships', false, dirname(plugin_basename($this->get_file())) . '/i18n/languages');
        }
        /**
         * Locates the WooCommerce template files from our templates directory
         *
         * @since 1.0.0
         * @param string $template Already found template
         * @param string $template_name Searchable template name
         * @param string $template_path Template path
         * @return string Search result for the template
         */
        public function locate_template($template, $template_name, $template_path)
        {
            // Tmp holder
            $_template = $template;
            if (!$template_path) {
                $template_path = WC()->template_path();
            }
            // Set our base path
            $plugin_path = $this->get_plugin_path() . '/templates/';
            // Look within passed path within the theme - this is priority
            $template = locate_template(array(trailingslashit($template_path) . $template_name, $template_name));
            // Get the template from this plugin, if it exists
            if (!$template && file_exists($plugin_path . $template_name)) {
                $template = $plugin_path . $template_name;
            }
            // Use default template
            if (!$template) {
                $template = $_template;
            }
            // Return what we found
            return $template;
        }
        /** Plugin functionality methods ***************************************/
        /**
         * Grant customer access to membership when making a purchase
         *
         * This method is run also when an order is made manually in WC admin
         *
         * TODO: this should be refactored to separate the code that checks
         * if a given order contains a product that grants access to a membership @MR June 2015
         *
         * @since 1.0.0
         * @param int $order_id Order ID
         */
        public function grant_membership_access($order_id)
        {
            // Get order items
            $order = wc_get_order($order_id);
            $user_id = $order->get_user_id();
            $items = $order->get_items();
            // Skip if there is no user associated with this order or there are no items
            if (!$user_id || empty($items)) {
                return;
            }
            // Get membership plans
            $membership_plans = $this->plans->get_membership_plans();
            // Bail out if there are no membership plans
            if (empty($membership_plans)) {
                return;
            }
            // Loop over all available membership plans
            foreach ($membership_plans as $plan) {
                // Skip if no products grant access to this plan
                if (!$plan->has_products()) {
                    continue;
                }
                // Array to store products that grant access to this plan
                $access_granting_product_ids = array();
                // Loop over items to see if any of them grant access to any memberships
                foreach ($items as $key => $item) {
                    // Product grants access to this membership
                    if ($plan->has_product($item['product_id'])) {
                        $access_granting_product_ids[] = $item['product_id'];
                    }
                    // Variation access
                    if (isset($item['variation_id']) && $item['variation_id'] && $plan->has_product($item['variation_id'])) {
                        $access_granting_product_ids[] = $item['variation_id'];
                    }
                }
                // No products grant access, skip further processing
                if (empty($access_granting_product_ids)) {
                    continue;
                }
                /**
                 * Filter the product ID that grants access to the membership plan via purchase
                 *
                 * Multiple products from a single order can grant access to a membership plan.
                 * Default behavior is to use the first product that grants access, but this can
                 * be overriden using this filter.
                 *
                 * @since 1.0.0
                 * @param int $product_id
                 * @param array $access_granting_product_ids Array of product IDs that can grant access to this plan
                 * @param WC_Memberships_Membership_Plan $plan Membership plan access will be granted to
                 */
                $product_id = apply_filters('wc_memberships_access_granting_purchased_product_id', $access_granting_product_ids[0], $access_granting_product_ids, $plan);
                // Sanity check: make sure the selected product ID in fact does grant access
                if (!$plan->has_product($product_id)) {
                    continue;
                }
                // Delegate granting access to the membership plan instance
                $plan->grant_access_from_purchase($user_id, $product_id, $order_id);
            }
        }
        /** Admin methods ******************************************************/
        /**
         * Render a notice for the user to read the docs before adding add-ons
         *
         * @since 1.0.0
         * @see SV_WC_Plugin::add_admin_notices()
         */
        public function add_admin_notices()
        {
            // show any dependency notices
            parent::add_admin_notices();
            $screen = get_current_screen();
            // only render on plugins or settings screen
            if ('plugins' === $screen->id || $this->is_plugin_settings()) {
                $this->get_admin_notice_handler()->add_admin_notice(sprintf(__('Thanks for installing Memberships! To get started, take a minute to %sread the documentation%s and then %ssetup a membership plan%s :)', self::TEXT_DOMAIN), '<a href="http://docs.woothemes.com/document/woocommerce-memberships/" target="_blank">', '</a>', '<a href="' . admin_url('edit.php?post_type=wc_membership_plan') . '">', '</a>'), 'get-started-notice', array('always_show_on_settings' => false, 'notice_class' => 'updated'));
            }
        }
        /** Helper methods ******************************************************/
        /**
         * Main Memberships Instance, ensures only one instance is/can be loaded
         *
         * @since 1.0.0
         * @see wc_memberships()
         * @return WC_Memberships
         */
        public static function instance()
        {
            if (is_null(self::$instance)) {
                self::$instance = new self();
            }
            return self::$instance;
        }
        /**
         * Gets the plugin documentation URL
         *
         * @since 1.2.0
         * @see SV_WC_Plugin::get_documentation_url()
         * @return string
         */
        public function get_documentation_url()
        {
            return 'http://docs.woothemes.com/document/woocommerce-memberships/';
        }
        /**
         * Gets the plugin support URL
         *
         * @since 1.2.0
         * @see SV_WC_Plugin::get_support_url()
         * @return string
         */
        public function get_support_url()
        {
            return 'http://support.woothemes.com/';
        }
        /**
         * Search an array of arrays by key-value
         *
         * If a match is found in the array more than once,
         * only the first matching key is returned.
         *
         * @since 1.0.0
         * @param array $array Array of arrays
         * @param string $key The key to search for
         * @param string $value The value to search for
         * @return array|boolean Found results, or false if none found
         */
        public function array_search_key_value($array, $key, $value)
        {
            if (!is_array($array)) {
                return null;
            }
            if (empty($array)) {
                return false;
            }
            $found_key = false;
            foreach ($array as $element_key => $element) {
                if (isset($element[$key]) && $value == $element[$key]) {
                    $found_key = $element_key;
                    break;
                }
            }
            return $found_key;
        }
        /**
         * Workaround the last day of month quirk in PHP's strtotime function.
         *
         * Adding +1 month to the last day of the month can yield unexpected results with strtotime()
         * For example,
         * - 30 Jan 2013 + 1 month = 3rd March 2013
         * - 28 Feb 2013 + 1 month = 28th March 2013
         *
         * What humans usually want is for the charge to continue on the last day of the month.
         *
         * Copied from WooCommerce Subscriptions
         *
         * @since 1.0.0
         * @param string $from_timestamp Original timestamp to add months to
         * @param int $months_to_add Number of months to add to the timestamp
         * @return int corrected timestamp
         */
        public function add_months($from_timestamp, $months_to_add)
        {
            $first_day_of_month = date('Y-m', $from_timestamp) . '-1';
            $days_in_next_month = date('t', strtotime("+ {$months_to_add} month", strtotime($first_day_of_month)));
            // It's the last day of the month OR number of days in next month is less than the the day of this month (i.e. current date is 30th January, next date can't be 30th February)
            if (date('d m Y', $from_timestamp) === date('t m Y', $from_timestamp) || date('d', $from_timestamp) > $days_in_next_month) {
                for ($i = 1; $i <= $months_to_add; $i++) {
                    $next_month = strtotime('+ 3 days', $from_timestamp);
                    // Add 3 days to make sure we get to the next month, even when it's the 29th day of a month with 31 days
                    $next_timestamp = $from_timestamp = strtotime(date('Y-m-t H:i:s', $next_month));
                    // NB the "t" to get last day of next month
                }
            } else {
                $next_timestamp = strtotime("+ {$months_to_add} month", $from_timestamp);
            }
            return $next_timestamp;
        }
        /**
         * Creates a human readable list of an array
         *
         * @since 1.0.0
         * @param string[] $ranges array to list items of
         * @param string $conjunction optional. The word to join together the penultimate and last item. Defaults to 'or'
         * @return string 'item1, item2, item3 or item4'
         */
        public function list_items($items, $conjunction = null)
        {
            if (!$conjunction) {
                $conjunction = __('or', WC_Memberships::TEXT_DOMAIN);
            }
            array_splice($items, -2, 2, implode(' ' . $conjunction . ' ', array_slice($items, -2, 2)));
            return implode(', ', $items);
        }
        /**
         * Return a list of edit post links for the provided posts
         *
         * @since 1.1.0
         * @param array Array of post objects
         * @return string
         */
        public function admin_list_post_links($posts)
        {
            if (empty($posts)) {
                return '';
            }
            $items = array();
            foreach ($posts as $post) {
                $items[] = '<a href="' . get_edit_post_link($post->ID) . '">' . get_the_title($post->ID) . '</a>';
            }
            return $this->list_items($items, __('and', WC_Memberships::TEXT_DOMAIN));
        }
        /**
         * Returns the plugin name, localized
         *
         * @since 1.0.0
         * @see SV_WC_Plugin::get_plugin_name()
         * @return string the plugin name
         */
        public function get_plugin_name()
        {
            return __('WooCommerce Memberships', $this->text_domain);
        }
        /**
         * Returns __FILE__
         *
         * @since 1.0.0
         * @see SV_WC_Plugin::get_file()
         * @return string the full path and filename of the plugin file
         */
        protected function get_file()
        {
            return __FILE__;
        }
        /**
         * Returns true if on the memberships settings page
         *
         * @since 1.0.0
         * @see SV_WC_Plugin::is_plugin_settings()
         * @return boolean true if on the settings page
         */
        public function is_plugin_settings()
        {
            return isset($_GET['page']) && 'wc-settings' == $_GET['page'] && isset($_GET['tab']) && 'memberships' == $_GET['tab'];
        }
        /**
         * Gets the plugin configuration URL
         *
         * @since 1.0.0
         * @see SV_WC_Plugin::get_settings_link()
         * @param string $plugin_id optional plugin identifier.  Note that this can be a
         *        sub-identifier for plugins with multiple parallel settings pages
         *        (ie a gateway that supports both credit cards and echecks)
         * @return string plugin settings URL
         */
        public function get_settings_url($plugin_id = null)
        {
            return admin_url('admin.php?page=wc-settings&tab=memberships');
        }
        /**
         * Checks is WooCommerce Subscriptions is active
         *
         * @since 1.0.0
         * @return bool true if the WooCommerce Subscriptions plugin is active, false if not active
         */
        public function is_subscriptions_active()
        {
            if (is_bool($this->subscriptions_active)) {
                return $this->subscriptions_active;
            }
            return $this->subscriptions_active = $this->is_plugin_active('woocommerce-subscriptions.php');
        }
        /**
         * Checks is User Switching is active
         *
         * @since 1.0.0
         * @return bool true if the User Switching plugin is active, false if not active
         */
        public function is_user_switching_active()
        {
            if (is_bool($this->user_switching_active)) {
                return $this->user_switching_active;
            }
            return $this->user_switching_active = $this->is_plugin_active('user-switching.php');
        }
        /**
         * Checks is Groups is active
         *
         * @since 1.0.0
         * @return bool true if the Groups plugin is active, false if not active
         */
        public function is_groups_active()
        {
            if (is_bool($this->groups_active)) {
                return $this->groups_active;
            }
            return $this->groups_active = $this->is_plugin_active('groups.php');
        }
        /**
         * Checks is Bookings is active
         *
         * @since 1.3.0
         * @return bool true if the WooCommerce Bookings plugin is active, false if not active
         */
        public function is_bookings_active()
        {
            if (is_bool($this->bookings_active)) {
                return $this->bookings_active;
            }
            return $this->bookings_active = $this->is_plugin_active('woocommmerce-bookings.php');
        }
        /**
         * Encode a variable into JSON via wp_json_encode() if available, fall back
         * to json_encode otherwise.
         *
         * json_encode() may fail and return `null` in some envrionments (esp. with
         * character encoding issues)
         *
         * @since 1.2.0
         * @param mixed $data    Variable (usually an array or object) to encode as JSON.
         * @param int   $options Optional. Options to be passed to json_encode(). Default 0.
         * @param int   $depth   Optional. Maximum depth to walk through $data. Must be greater than 0. Default 512.
         * @return bool|string   The JSON encoded string, or false if it cannot be encoded.
         */
        public function wp_json_encode($data, $options = 0, $depth = 512)
        {
            return function_exists('wp_json_encode') ? wp_json_encode($data, $options, $depth) : json_encode($data, $options, $depth);
        }
        /** Lifecycle methods ******************************************************/
        /**
         * Install default settings & pages
         *
         * @since 1.0.0
         * @see SV_WC_Plugin::install()
         */
        protected function install()
        {
            // install default "content restricted" page
            $title = _x('Content restricted', 'Page title', WC_Memberships::TEXT_DOMAIN);
            $slug = _x('content-restricted', 'Page slug', WC_Memberships::TEXT_DOMAIN);
            $content = '[wcm_content_restricted]';
            wc_create_page(esc_sql($slug), 'wc_memberships_redirect_page_id', $title, $content);
            // include settings so we can install defaults
            include_once WC()->plugin_path() . '/includes/admin/settings/class-wc-settings-page.php';
            $settings = (require_once $this->get_plugin_path() . '/includes/admin/class-wc-memberships-settings.php');
            // install default settings for each section
            foreach ($settings->get_sections() as $section => $label) {
                foreach ($settings->get_settings($section) as $setting) {
                    if (isset($setting['default'])) {
                        update_option($setting['id'], $setting['default']);
                    }
                }
            }
        }
        /**
         * Upgrade
         *
         * @since 1.1.0
         * @see SV_WC_Plugin::install()
         */
        protected function upgrade($installed_version)
        {
            // upgrade to version 1.1.0
            if (version_compare($installed_version, '1.1.0', '<')) {
                $all_rules = array();
                // Merge rules from different options into a single option
                foreach (array('content_restriction', 'product_restriction', 'purchasing_discount') as $rule_type) {
                    $rules = get_option("wc_memberships_{$rule_type}_rules");
                    if (is_array($rules) && !empty($rules)) {
                        foreach ($rules as $rule) {
                            // Skip empty/corrupt rules
                            if (empty($rule) || isset($rule[0]) && !$rule[0]) {
                                continue;
                            }
                            $rule['rule_type'] = $rule_type;
                            $all_rules[] = $rule;
                        }
                    }
                    delete_option("wc_memberships_{$rule_type}_rules");
                }
                update_option('wc_memberships_rules', $all_rules);
            }
        }
        /**
         * Handle plugin activation
         *
         * @since 1.0.0
         */
        public function maybe_activate()
        {
            $is_active = get_option('wc_memberships_is_active', false);
            if (!$is_active) {
                update_option('wc_memberships_is_active', true);
                /**
                 * Run when Memberships is activated
                 *
                 * @since 1.0.0
                 */
                do_action('wc_memberships_activated');
            }
        }
        /**
         * Handle plugin deactivation
         *
         * @since 1.0.0
         */
        public function deactivate()
        {
            delete_option('wc_memberships_is_active');
            /**
             * Run when Memberships is deactivated
             *
             * @since 1.0.0
             */
            do_action('wc_memberships_deactivated');
        }
    }
    // end WC_Memberships class
    /**
     * Returns the One True Instance of Memberships
     *
     * @since 1.0.0
     * @return WC_Memberships
     */
    function wc_memberships()
    {
        return WC_Memberships::instance();
    }
    // fire it up!
    wc_memberships();
}
 /**
  * Get user access date for a piece of content
  *
  * @since 1.0.0
  *
  * @param array $args {
  *   Optional. An array of arguments.
  *
  *   @type string|array $rule_type Optional. Content type. One or more of 'content_restriction' or 'product_restriction'
  *   @type string $content_type Optional. Content type. One of 'post_type' or 'taxonomy'
  *   @type string $content_type_name Optional. Content type name. A valid post type or taxonomy name.
  *   @type string|int $object_id Optional. Post or taxonomy term ID/slug
  *   @type string $access_type Optional. Defaults to "view". Applies only to products and product taxonomies/terms.
  * }
  * @return string|null Timestamp of start time or null if no access
  */
 public function get_user_access_start_time($args = array())
 {
     $defaults = array('rule_type' => array('content_restriction', 'product_restriction'), 'user_id' => get_current_user_id(), 'content_type' => null, 'content_type_name' => null, 'object_id' => null, 'access_type' => 'view');
     $args = wp_parse_args($args, $defaults);
     if (is_string($args['rule_type'])) {
         $args['rule_type'] = (array) $args['rule_type'];
     }
     $cache_key = http_build_query($args);
     $user_id = $args['user_id'];
     // Only calculate access time if not cached before, speeds up subsequent checks
     if (!isset($this->_user_access_start_time[$cache_key])) {
         $access_time = current_time('timestamp', true);
         // by default, access is immediate
         $access_type = $args['access_type'];
         $rules_args = $args;
         unset($rules_args['access_type']);
         unset($rules_args['user_id']);
         $rules = wc_memberships()->rules->get_rules($rules_args);
         // If rules apply, process them
         if (!empty($rules)) {
             // If there are no product restriction rules, then we can safely say that
             // access is restricted
             if (!in_array('product_restriction', $rules_args['rule_type'])) {
                 $access_time = null;
             } else {
                 foreach ($rules as $rule) {
                     // Check if the product restriction rule applies to the correct access type
                     if ('product_restriction' == $rule->get_rule_type()) {
                         if ($access_type === $rule->get_access_type()) {
                             $access_time = null;
                             break;
                         }
                     } else {
                         $access_time = null;
                         break;
                     }
                 }
             }
             // If access is restricted, determine if user has access and if, then when
             if (!$access_time) {
                 $last_priority = 0;
                 foreach ($rules as $rule) {
                     $rule_applies = true;
                     // Check if rule applies to this piece of content, based on the access type
                     // This affects products only.
                     if ('product_restriction' == $rule->get_rule_type()) {
                         $rule_applies = 'view' == $access_type ? in_array($rule->get_access_type(), array('view', 'purchase')) : $access_type == $rule->get_access_type();
                     }
                     if ($rule_applies && wc_memberships()->user_memberships->is_user_active_member($user_id, $rule->get_membership_plan_id())) {
                         $user_membership = wc_memberships()->user_memberships->get_user_membership($user_id, $rule->get_membership_plan_id());
                         /**
                          * Filter the rule's content 'access from' time for a user membership
                          *
                          * The 'access from' time is used as the base time for calculating the access
                          * start time for scheduled content.
                          *
                          * @since 1.0.0
                          * @param string $from_time Access from time, as a timestamp
                          * @param WC_Memberships_Membership_Plan_Rule $rule
                          * @param WC_Memberships_User_Membership $user_membership
                          */
                         $from_time = apply_filters('wc_memberships_access_from_time', $user_membership->get_start_date('timestamp'), $rule, $user_membership);
                         // If there is no time to calculate the access time from, simply
                         // use the current time as access start time
                         if (!$from_time) {
                             $access_time = current_time('timestamp', true);
                             break;
                             // Can't get any earlier, break the loop
                         }
                         $rule_access_time = $rule->get_access_start_time($from_time);
                         $rule_priority = $rule->get_priority();
                         // If this rule has higher priority than last rule,
                         // override the previous access time.
                         // If this has the same priority as the last rule,
                         // and grants earlier access, override previous access time.
                         if ($rule_priority > $last_priority || $rule_priority == $last_priority && (!$access_time || $rule_access_time < $access_time)) {
                             $access_time = $rule_access_time;
                             $last_priority = $rule_priority;
                         }
                     }
                 }
             }
         }
         /**
          * Filter user's access start time to a piece of content
          *
          * @since 1.0.0
          * @param string $access_time Access start timestamp
          * @param array $args {
          *   An array of arguments.
          *
          *   @type string $content_type Content type. One of 'post_type' or 'taxonomy'
          *   @type string $content_type_name Content type name. A valid post type or taxonomy name.
          *   @type string|int $object_id Optional. Post or taxonomy term ID/slug
          *   @type string $access_type
          * }
          */
         $access_time = apply_filters('wc_memberships_user_object_access_start_time', $access_time, $args);
         $this->_user_access_start_time[$cache_key] = $access_time;
     }
     return $this->_user_access_start_time[$cache_key];
 }
 /**
  * Grant access to a membership plan
  *
  * @since 1.0.0
  */
 public function grant_access_to_membership()
 {
     global $wpdb;
     if (empty($_REQUEST['post'])) {
         return;
     }
     // Get the ID
     $plan_id = isset($_REQUEST['post']) ? absint($_REQUEST['post']) : '';
     check_admin_referer('wc-memberships-grant-access-plan_' . $plan_id);
     $plan = wc_memberships_get_membership_plan($plan_id);
     $redirect_to = get_edit_post_link($plan_id, 'redirect');
     $product_ids = $plan->get_product_ids();
     $grant_count = 0;
     if (!empty($product_ids)) {
         foreach ($product_ids as $product_id) {
             $sql = "SELECT order_id FROM {$wpdb->prefix}woocommerce_order_items WHERE order_item_id IN ( SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE meta_key = '_product_id' AND meta_value = {$product_id} ) AND order_item_type = 'line_item'";
             $order_ids = $wpdb->get_col($sql);
             if (empty($order_ids)) {
                 continue;
             }
             foreach ($order_ids as $order_id) {
                 $order = wc_get_order($order_id);
                 if (!$order) {
                     continue;
                 }
                 /**
                  * Filter the array of valid order statuses that grant access
                  *
                  * Allows actors to include additional custom order statuses that
                  * should grant access when the admin uses the "grant previous purchases access"
                  * action
                  *
                  * @since 1.0.0
                  * @param array $valid_order_statuses_for_grant array of order statuses
                  * @param object $plan the associated membership plan object
                  */
                 $valid_order_statuses_for_grant = apply_filters('wc_memberships_grant_access_from_existing_purchase_order_statuses', array('processing', 'completed'), $plan);
                 // skip if purchase doesn't have a valid status
                 if (!$order->has_status($valid_order_statuses_for_grant)) {
                     continue;
                 }
                 $user_id = $order->get_user_id();
                 // Skip if guest purchase or user is already a member
                 if (!$user_id || wc_memberships_is_user_member($user_id, $plan_id)) {
                     continue;
                 }
                 /**
                  * Filter whether an existing purchase of the product should grant access
                  * to the membership plan or not.
                  *
                  * Allows plugins to override if a previously purchased product should
                  * retroactively grant access to a membership plan or not.
                  *
                  * @since 1.0.0
                  * @param array $args
                  */
                 $grant_access = apply_filters('wc_memberships_grant_access_from_existing_purchase', true, array('user_id' => $user_id, 'product_id' => $product_id, 'order_id' => $order_id));
                 if (!$grant_access) {
                     continue;
                 }
                 // Grant access
                 $result = $plan->grant_access_from_purchase($user_id, $product_id, $order_id);
                 if ($result) {
                     $grant_count++;
                 }
             }
         }
     }
     // Add admin message
     if ($grant_count) {
         $message = sprintf(_n('%d customer was granted access from existing purchases.', '%d customers were granted access from existing purchases', $grant_count, WC_Memberships::TEXT_DOMAIN), $grant_count);
     } else {
         $message = __('No customers were granted access from existing purchases.', WC_Memberships::TEXT_DOMAIN);
     }
     wc_memberships()->admin->message_handler->add_message($message);
     // Redirect back to the edit screen
     wp_safe_redirect($redirect_to);
     exit;
 }
 /**
  * Prevent adding a user membership if user is already a member of all plans
  *
  * @since 1.0.0
  */
 public function maybe_prevent_adding_user_membership()
 {
     global $pagenow;
     if ('post-new.php' === $pagenow) {
         // Get user details
         $user_id = isset($_GET['user']) ? $_GET['user'] : null;
         $user = $user_id ? get_userdata($user_id) : null;
         if (!$user_id || !$user) {
             wc_memberships()->admin->message_handler->add_error(__('Please select a user to add as a member.', WC_Memberships::TEXT_DOMAIN));
             wp_redirect(wp_get_referer());
             exit;
         }
         // All the user memberships
         $user_memberships = wc_memberships_get_user_memberships($user->ID);
         $membership_plans = wc_memberships_get_membership_plans(array('post_status' => array('publish', 'private', 'future', 'draft', 'pending', 'trash')));
         if (count($user_memberships) == count($membership_plans)) {
             wc_memberships()->admin->message_handler->add_message(__('This user is already a member of every plan.', WC_Memberships::TEXT_DOMAIN));
             wp_redirect(wp_get_referer());
             exit;
         }
     }
 }
/**
 * Main function for returning all available membership plans
 *
 * @since 1.0.0
 * @param array $args Optional array of arguments. Same as for get_posts
 * @return array of WC_Memberships_Membership_Plan objects
 */
function wc_memberships_get_membership_plans($args = null)
{
    return wc_memberships()->plans->get_membership_plans($args);
}
 /**
  * Get public posts
  *
  * @since 1.1.0
  * @return array
  */
 public function get_public_posts()
 {
     return get_posts(array('post_type' => array_keys(wc_memberships()->admin->get_valid_post_types_for_content_restriction()), 'post_status' => 'any', 'meta_key' => '_wc_memberships_force_public', 'meta_value' => 'yes'));
 }
 /**
  * Get product discounted price for member
  *
  * @since 1.3.0
  * @param int $base_price Original price
  * @param int|WC_product $product Product ID or product object
  * @param int $user_id Optional. Defaults to current user id.
  * @return int|null discounted price, or null if no discount applies
  */
 public function get_discounted_price($base_price, $product, $user_id = null)
 {
     if (!$user_id) {
         $user_id = get_current_user_id();
     }
     if (!is_object($product)) {
         $product = wc_get_product($product);
     }
     $product_id = $product->is_type('variation') ? $product->variation_id : $product->id;
     if (!isset($this->member_discounted_products[$user_id . '_' . $product_id])) {
         $discount_rules = wc_memberships()->rules->get_user_product_purchasing_discount_rules($user_id, $product_id);
         if (empty($discount_rules)) {
             $price = null;
         } else {
             $price = $base_price;
             // Find out the discounted price for the current user
             foreach ($discount_rules as $rule) {
                 switch ($rule->get_discount_type()) {
                     case 'percentage':
                         $discounted_price = $price * (100 - $rule->get_discount_amount()) / 100;
                         break;
                     case 'amount':
                         $discounted_price = max($price - $rule->get_discount_amount(), 0);
                         break;
                 }
                 // Make sure that the lowest price gets applied
                 if ($discounted_price < $price) {
                     $price = $discounted_price;
                 }
             }
             if ($price >= $base_price) {
                 $price = null;
             }
         }
         // If the price is lower than product's base price, return the discounted price, null otherwise
         $this->member_discounted_products[$user_id . '_' . $product_id] = $price;
     }
     return $this->member_discounted_products[$user_id . '_' . $product_id];
 }
 /**
  * Check if registration should be forced if all of the following are true:
  *
  * 1) user is not logged in
  * 2) an item in the cart contains a product that grants access to a membership
  *
  * @since 1.0.0
  * @return bool
  */
 protected function force_registration()
 {
     if (is_user_logged_in()) {
         return false;
     }
     // Get membership plans
     $membership_plans = wc_memberships()->plans->get_membership_plans();
     // Bail out if there are no membership plans
     if (empty($membership_plans)) {
         return false;
     }
     $force = false;
     // Loop over all available membership plans
     foreach ($membership_plans as $plan) {
         // Skip if no products grant access to this plan
         if (!$plan->has_products()) {
             continue;
         }
         // Array to store products that grant access to this plan
         $access_granting_product_ids = array();
         // Loop over items to see if any of them grant access to any memberships
         foreach (WC()->cart->get_cart() as $key => $item) {
             // Product grants access to this membership
             if ($plan->has_product($item['product_id'])) {
                 $access_granting_product_ids[] = $item['product_id'];
             }
             // Variation access
             if (isset($item['variation_id']) && $item['variation_id'] && $plan->has_product($item['variation_id'])) {
                 $access_granting_product_ids[] = $item['variation_id'];
             }
         }
         // No products grant access, skip further processing
         if (empty($access_granting_product_ids)) {
             continue;
         }
         /**
          * Filter the product ID that grants access to the membership plan via purchase
          *
          * Multiple products from a single order can grant access to a membership plan.
          * Default behavior is to use the first product that grants access, but this can
          * be overriden using this filter.
          *
          * @since 1.0.0
          *
          * @param int $product_id
          * @param array $access_granting_product_ids Array of product IDs that can grant access to this plan
          * @param WC_Memberships_Membership_Plan $plan Membership plan access will be granted to
          */
         $product_id = apply_filters('wc_memberships_access_granting_purchased_product_id', $access_granting_product_ids[0], $access_granting_product_ids, $plan);
         // Sanity check: make sure the selected product ID in fact does grant access
         if (!$plan->has_product($product_id)) {
             continue;
         }
         $force = true;
     }
     return $force;
 }
/**
 * Removes the "My Memberships" table from my account area
 */
function sv_remove_my_memberships_table()
{
    if (function_exists('wc_memberships') && !is_admin()) {
        remove_action('woocommerce_before_my_account', array(wc_memberships()->get_frontend_instance()->get_member_area_instance(), 'my_account_memberships'));
    }
}
/**
 * Create a new user membership programmatically
 *
 * Returns a new user membership object on success which can then be used to add additional data.
 *
 * @since 1.3.0
 * @param array $args Array of arguments
 * @param string $action Action - either 'create' or 'renew'. When in doubt, use 'create'
 * @return WC_Memberships_User_Membership on success, WP_Error on failure
 */
function wc_memberships_create_user_membership($args = array(), $action = 'create')
{
    $defaults = array('user_membership_id' => 0, 'plan_id' => 0, 'user_id' => 0, 'product_id' => 0, 'order_id' => 0);
    $args = wp_parse_args($args, $defaults);
    $data = array('post_parent' => $args['plan_id'], 'post_author' => $args['user_id'], 'post_type' => 'wc_user_membership', 'post_status' => 'wcm-active', 'comment_status' => 'open');
    if ($args['user_membership_id'] > 0) {
        $updating = true;
        $data['ID'] = $args['user_membership_id'];
    } else {
        $updating = false;
    }
    /**
     * Filter new membership data, used when a product purchase grants access
     *
     * @param array $data
     * @param array $args
     */
    $data = apply_filters('wc_memberships_new_membership_data', $data, array('user_id' => $args['user_id'], 'product_id' => $args['product_id'], 'order_id' => $args['order_id']));
    if ($updating) {
        $user_membership_id = wp_update_post($data);
    } else {
        $user_membership_id = wp_insert_post($data);
    }
    // Bail out on error
    if (is_wp_error($user_membership_id)) {
        return $user_membership_id;
    }
    // Save/update product and order id that granted access
    if ($args['product_id'] > 0) {
        update_post_meta($user_membership_id, '_product_id', $args['product_id']);
    }
    if ($args['order_id'] > 0) {
        update_post_meta($user_membership_id, '_order_id', $args['order_id']);
    }
    // Save/update the membership start date, but only if the membership
    // is not active, ie is not being renewed early.
    if ('renew' != $action) {
        update_post_meta($user_membership_id, '_start_date', current_time('mysql', true));
    }
    // Get the membership plan object so we can calculate end time
    $plan = wc_memberships_get_membership_plan($args['plan_id']);
    // Calculate membership end date based on membership length, optionally
    // from the existing end date, if renewing early
    $end_date = '';
    if ($plan->get_access_length_amount()) {
        // Early renewals add to the existing membership length, normal
        // cases calculate membership length from now
        $now = 'renew' == $action ? strtotime(get_post_meta($user_membership_id, '_end_date', true)) : current_time('timestamp');
        if (strpos($plan->get_access_length_period(), 'month') !== false) {
            $end = wc_memberships()->add_months($now, $plan->get_access_length_amount());
        } else {
            $end = strtotime('+ ' . $plan->get_access_length(), $now);
        }
        $end_date = date('Y-m-d H:i:s', $end);
    }
    // Save/update end date
    $user_membership = wc_memberships_get_user_membership($user_membership_id);
    $user_membership->set_end_date($end_date);
    /**
     * Fires after a user has been granted membership access
     *
     * @since 1.3.0
     * @param WC_Memberships_Membership_Plan $membership_plan The plan that user was granted access to
     * @param array $args
     */
    do_action('wc_memberships_user_membership_created', $plan, array('user_id' => $args['user_id'], 'user_membership_id' => $user_membership->get_id()));
    return $user_membership;
}
 /**
  * Restricted content messages
  *
  * @since 1.0.0
  * @param mixed $atts Shortcode attributes
  * @return string Shortcode result
  */
 public static function content_restricted($atts, $content = null)
 {
     // Get the restricted post
     $post_id = isset($_GET['r']) ? absint($_GET['r']) : null;
     // Skip if post ID not provided
     if (!$post_id) {
         return '';
     }
     $post = get_post($post_id);
     // Skip if post was not found
     if (!$post) {
         return '';
     }
     $output = '';
     // Special handling for products
     if (in_array(get_post_type($post_id), array('product', 'product_variation'))) {
         if ('yes' == get_option('wc_memberships_show_excerpts')) {
             $output = apply_filters('woocommerce_short_description', $post->post_excerpt);
         }
         // Check if user has access to viewing restricted content
         if (!current_user_can('wc_memberships_view_restricted_product', $post->ID)) {
             $output .= '<div class="wc-memberships-content-restricted-message">' . wc_memberships()->frontend->get_product_viewing_restricted_message($post->ID) . '</div>';
         } else {
             if (!current_user_can('wc_memberships_view_delayed_post_content', $post->ID)) {
                 $output .= '<div class="wc-memberships-content-delayed-message">' . wc_memberships()->frontend->get_content_delayed_message(get_current_user_id(), $post->ID, 'view') . '</div>';
             }
         }
     } else {
         if ('yes' == get_option('wc_memberships_show_excerpts')) {
             $output = apply_filters('get_the_excerpt', $post->post_excerpt);
         }
         // Check if user has access to restricted content
         if (!current_user_can('wc_memberships_view_restricted_post_content', $post->ID)) {
             $output .= '<div class="wc-memberships-content-restricted-message">' . wc_memberships()->frontend->get_content_restricted_message($post->ID) . '</div>';
         } else {
             if (!current_user_can('wc_memberships_view_delayed_post_content', $post->ID)) {
                 $output .= '<div class="wc-memberships-content-delayed-message">' . wc_memberships()->frontend->get_content_delayed_message(get_current_user_id(), $post->ID) . '</div>';
             }
         }
     }
     return $output;
 }
][object_ids]" data-placeholder="<?php 
        esc_attr_e('Search&hellip; or leave blank to apply to all', WC_Memberships::TEXT_DOMAIN);
        ?>
" data-action="<?php 
        echo esc_attr($rule->get_object_search_action_name());
        ?>
" data-multiple="true" data-selected="<?php 
        $json_ids = array();
        if ($rule->has_objects()) {
            foreach ($rule->get_object_ids() as $object_id) {
                if ($rule->get_object_label($object_id)) {
                    $json_ids[$object_id] = wp_kses_post(html_entity_decode($rule->get_object_label($object_id)));
                }
            }
        }
        echo esc_attr(wc_memberships()->wp_json_encode($json_ids));
        ?>
" value="<?php 
        echo esc_attr(implode(',', array_keys($json_ids)));
        ?>
" <?php 
        if (!$rule->current_user_can_edit()) {
            ?>
disabled<?php 
        }
        ?>
 />
			<?php 
    } else {
        ?>
				<select name="_content_restriction_rules[<?php 
 /**
  * Constructor
  *
  * @since 1.0.0
  */
 public function __construct()
 {
     require_once wc_memberships()->get_plugin_path() . '/includes/class-wc-memberships-membership-plan.php';
     add_action('delete_post', array($this, 'delete_related_data'));
 }
 /**
  * Updates status of membership
  *
  * @param string $new_status Status to change the order to. No internal wcm- prefix is required.
  * @param string $note (default: '') Optional note to add
  */
 public function update_status($new_status, $note = '')
 {
     if (!$this->get_id()) {
         return;
     }
     // Standardise status names.
     $new_status = 'wcm-' === substr($new_status, 0, 4) ? substr($new_status, 4) : $new_status;
     $old_status = $this->get_status();
     // Get valid statuses
     $valid_statuses = wc_memberships_get_user_membership_statuses();
     // Only update if they differ - and ensure post_status is a 'wcm' status.
     if ($new_status !== $old_status && in_array('wcm-' . $new_status, array_keys($valid_statuses))) {
         // Note will be added to the membership by the general User_Memberships utility class,
         // so that we add only 1 note instead of 2 when updating the status.
         wc_memberships()->user_memberships->set_membership_status_transition_note($note);
         // Update the order
         wp_update_post(array('ID' => $this->get_id(), 'post_status' => 'wcm-' . $new_status));
         $this->status = 'wcm-' . $new_status;
     }
 }
 /**
  * Save membership plan data
  *
  * @since 1.0.0
  * @param int $post_id
  * @param WP_Post $post
  */
 public function update_data($post_id, WP_Post $post)
 {
     // Save product IDs that grant access to this membership
     if (isset($_POST['_product_ids'])) {
         $product_ids = is_array($_POST['_product_ids']) ? $_POST['_product_ids'] : ($_POST['_product_ids'] ? explode(',', $_POST['_product_ids']) : array());
         // sanitize
         $product_ids = array_map('absint', $product_ids);
         update_post_meta($post_id, '_product_ids', $product_ids);
     }
     // Save membership length
     if (isset($_POST['_access_length'])) {
         if ('specific' == $_POST['_access_length']) {
             update_post_meta($post_id, '_access_length', sprintf('%d %s', $_POST['_access_length_amount'], $_POST['_access_length_period']));
         } else {
             delete_post_meta($post_id, '_access_length');
         }
     }
     // Save access from subscription
     update_post_meta($post_id, '_access_while_subscription_active', isset($_POST['_access_while_subscription_active']) ? 'yes' : 'no');
     // Update restriction & discount rules
     wc_memberships()->admin->update_rules($post_id, array('content_restriction', 'product_restriction', 'purchasing_discount'), 'plan');
 }
esc_html_e('Amount', WC_Memberships::TEXT_DOMAIN);
?>
			</th>

			<th class="purchasing-discount-active active-column">
				<?php 
esc_html_e('Active', WC_Memberships::TEXT_DOMAIN);
?>
			</th>

		</tr>
	</thead>

	<?php 
foreach ($purchasing_discount_rules as $index => $rule) {
    require wc_memberships()->get_plugin_path() . '/includes/admin/meta-boxes/views/html-purchasing-discount-rule.php';
}
?>

	<tbody class="norules <?php 
if (count($purchasing_discount_rules) > 1) {
    ?>
hide<?php 
}
?>
">
		<tr>
			<td colspan="<?php 
echo 'wc_membership_plan' == $post->post_type ? 6 : 5;
?>
">
 /**
  * Get content access conditions for the current user
  *
  * Returns an array of restricted and granted content based on
  * the content and product restriction rules.
  *
  * @since 1.1.0
  * @return array
  */
 private function get_user_content_access_conditions()
 {
     if (!isset($this->user_content_access_conditions)) {
         // Avoid filter loops
         remove_filter('pre_get_posts', array($this, 'exclude_restricted_posts'));
         // Find restricted posts from restriction rules
         $rules = wc_memberships()->rules->get_rules(array('rule_type' => array('content_restriction', 'product_restriction')));
         $restricted = $granted = array('posts' => array(), 'post_types' => array(), 'terms' => array(), 'taxonomies' => array());
         $conditions = array('restricted' => $restricted, 'granted' => $granted);
         // shop managers/admins can access everything
         if (is_user_logged_in() && current_user_can('wc_memberships_access_all_restricted_content')) {
             return $this->user_content_access_conditions = $conditions;
         }
         // Get all the content that is either restricted or granted for the user
         if (!empty($rules)) {
             foreach ($rules as $rule) {
                 // Skip rule if the plan is not published
                 if ('publish' != get_post_status($rule->get_membership_plan_id())) {
                     continue;
                 }
                 // Skip non-view product restriction rules
                 if ('product_restriction' == $rule->get_rule_type() && 'view' != $rule->get_access_type()) {
                     continue;
                 }
                 // Check if user is an active member of the plan
                 $is_member = is_user_logged_in() && wc_memberships()->user_memberships->is_user_active_member(get_current_user_id(), $rule->get_membership_plan_id());
                 $has_access = false;
                 // Check if user has scheduled access to the content
                 if ($is_member) {
                     $user_membership = wc_memberships()->user_memberships->get_user_membership(get_current_user_id(), $rule->get_membership_plan_id());
                     /** This filter is documented in includes/class-wc-memberships-capabilities.php **/
                     $from_time = apply_filters('wc_memberships_access_from_time', $user_membership->get_start_date('timestamp'), $rule, $user_membership);
                     // If there is no time to calculate the access time from, simply
                     // use the current time as access start time
                     if (!$from_time) {
                         $access_time = current_time('timestamp', true);
                         break;
                         // Can't get any earlier, break the loop
                     }
                     $rule_access_time = $rule->get_access_start_time($from_time);
                     $has_access = $rule_access_time <= current_time('timestamp', true);
                 }
                 $condition = $has_access ? 'granted' : 'restricted';
                 // Find posts that are either restricted or granted access to
                 if ('post_type' == $rule->get_content_type() && $rule->has_objects()) {
                     $post_type = $rule->get_content_type_name();
                     $post_ids = array();
                     // Leave out posts that have restrictions disabled
                     foreach ($rule->get_object_ids() as $post_id) {
                         if ('yes' != get_post_meta($post_id, '_wc_memberships_force_public', true)) {
                             $post_ids[] = $post_id;
                         }
                     }
                     // If there are no posts left, continue to next rule
                     if (empty($post_ids)) {
                         continue;
                     }
                     if (!isset($conditions[$condition]['posts'][$post_type])) {
                         $conditions[$condition]['posts'][$post_type] = array();
                     }
                     $conditions[$condition]['posts'][$post_type] = array_unique(array_merge($conditions[$condition]['posts'][$post_type], $post_ids));
                 } elseif ('post_type' == $rule->get_content_type()) {
                     $conditions[$condition]['post_types'] = array_unique(array_merge($conditions[$condition]['post_types'], (array) $rule->get_content_type_name()));
                 } elseif ('taxonomy' == $rule->get_content_type() && $rule->has_objects()) {
                     $taxonomy = $rule->get_content_type_name();
                     if (!isset($conditions[$condition]['terms'][$taxonomy])) {
                         $conditions[$condition]['terms'][$taxonomy] = array();
                     }
                     $conditions[$condition]['terms'][$taxonomy] = array_unique(array_merge($conditions[$condition]['terms'][$taxonomy], $rule->get_object_ids()));
                 } elseif ('taxonomy' == $rule->get_content_type()) {
                     $conditions[$condition]['taxonomies'] = array_unique(array_merge($conditions[$condition]['taxonomies'], (array) $rule->get_content_type_name()));
                 }
             }
         }
         // Loop over granted content and check if the user has access to delayed content
         foreach ($conditions['granted'] as $content_type => $values) {
             if (empty($values)) {
                 continue;
             }
             foreach ($values as $key => $value) {
                 switch ($content_type) {
                     case 'posts':
                         foreach ($value as $post_key => $post_id) {
                             if (!current_user_can('wc_memberships_view_delayed_post_content', $post_id)) {
                                 unset($conditions['granted'][$content_type][$key][$post_key]);
                             }
                         }
                         break;
                     case 'post_types':
                         if (!current_user_can('wc_memberships_view_delayed_post_type', $value)) {
                             unset($conditions['granted'][$content_type][$key]);
                         }
                         break;
                     case 'taxonomies':
                         if (!current_user_can('wc_memberships_view_delayed_taxonomy', $value)) {
                             unset($conditions['granted'][$content_type][$key]);
                         }
                         break;
                     case 'terms':
                         foreach ($value as $term_key => $term) {
                             if (!current_user_can('wc_memberships_view_delayed_taxonomy_term', $key, $term)) {
                                 unset($conditions['granted'][$content_type][$key][$term_key]);
                             }
                         }
                         break;
                 }
             }
         }
         // Remove restricted items that should be granted for the current user
         // content types are high-level restriction items - posts, post_types, terms, and taxonomies
         foreach ($conditions['restricted'] as $content_type => $object_types) {
             if (empty($conditions['granted'][$content_type]) || empty($object_types)) {
                 continue;
             }
             // object types are child elements of a content type, e.g. for the posts content type, object types are post_types( post and product)
             // for a term content type, object types are taxonomy names (e.g. category)
             foreach ($object_types as $object_type_name => $object_ids) {
                 if (empty($conditions['granted'][$content_type][$object_type_name]) || empty($object_ids)) {
                     continue;
                 }
                 if (is_array($object_ids)) {
                     // if the restricted object ID is also granted, remove it from restrictions
                     foreach ($object_ids as $object_id_index => $object_id) {
                         if (in_array($object_id, $conditions['granted'][$content_type][$object_type_name])) {
                             unset($conditions['restricted'][$content_type][$object_type_name][$object_id_index]);
                         }
                     }
                 } else {
                     // post type handling
                     if (in_array($object_ids, $conditions['granted'][$content_type])) {
                         unset($conditions['restricted'][$content_type][array_search($object_ids, $conditions['restricted'][$content_type])]);
                     }
                 }
             }
         }
         // Grant access to posts that have restrictions disabled
         global $wpdb;
         $public_posts = $wpdb->get_results("\n\t\t\t\tSELECT p.ID, p.post_type FROM {$wpdb->posts} p\n\t\t\t\tLEFT JOIN {$wpdb->postmeta} pm\n\t\t\t\tON p.ID = pm.post_id\n\t\t\t\tWHERE pm.meta_key = '_wc_memberships_force_public'\n\t\t\t\tAND pm.meta_value = 'yes'\n\t\t\t");
         if (!empty($public_posts)) {
             foreach ($public_posts as $post) {
                 if (!isset($conditions['granted']['posts'][$post->post_type])) {
                     $conditions['granted']['posts'][$post->post_type] = array();
                 }
                 $conditions['granted']['posts'][$post->post_type][] = $post->ID;
             }
         }
         // gather da results
         $this->user_content_access_conditions = $conditions;
         // Add the filter back
         add_filter('pre_get_posts', array($this, 'exclude_restricted_posts'));
     }
     return $this->user_content_access_conditions;
 }