/**
  * Get membership plan rules
  *
  * General rules builder & getter.
  *
  * @since 1.0.0
  *
  * @param string $rule_type Rule type. One of 'content_restriction', 'product_restriction' or 'purchasing_discount'.
  * @return array|bool $rules Array of rules or false on error
  */
 private function get_rules($rule_type)
 {
     if (!isset($this->rules[$rule_type])) {
         $all_rules = get_option('wc_memberships_rules');
         $this->rules[$rule_type] = array();
         if (!empty($all_rules)) {
             foreach ($all_rules as $rule) {
                 // Skip empty items
                 if (empty($rule) || !is_array($rule)) {
                     continue;
                 }
                 $rule = new WC_Memberships_Membership_Plan_Rule($rule);
                 if ($rule_type == $rule->get_rule_type() && $rule->get_membership_plan_id() == $this->get_id()) {
                     $this->rules[$rule_type][] = $rule;
                 }
             }
         }
     }
     return $this->rules[$rule_type];
 }
 /**
  * Adjust user membership post scheduled content 'access from' time for subscription-based memberships
  *
  * @since 1.0.0
  * @param string $from_time Access from time, as a timestamp
  * @param WC_Memberships_Membership_Plan_rule $rule Related rule
  * @param WC_Memberships_User_Membership $user_membership
  * @return string Modified from_time, as timestamp
  */
 public function adjust_post_access_from_time($from_time, WC_Memberships_Membership_Plan_Rule $rule, WC_Memberships_User_Membership $user_membership)
 {
     if ('yes' == $rule->get_access_schedule_exclude_trial()) {
         $has_subscription = $this->has_user_membership_subscription($user_membership->get_id());
         $trial_end_date = $this->get_user_membership_trial_end_date($user_membership->get_id(), 'timestamp');
         if ($has_subscription && $trial_end_date) {
             $from_time = $trial_end_date;
         }
     }
     return $from_time;
 }
 /**
  * Update rules for each provided rule type
  *
  * This method should be used by individual meta boxes that are updating rules
  *
  * @since 1.0.0
  * @param int $post_id
  * @param array $rule_types Array of rule types to update
  * @param string $target Optional. Indicates the context we are updating rules in. One of 'plan' or 'post'
  */
 public function update_rules($post_id, $rule_types, $target = 'plan')
 {
     $rules = get_option('wc_memberships_rules');
     foreach ($rule_types as $rule_type) {
         $rule_type_post_key = '_' . $rule_type . '_rules';
         if (!isset($_POST[$rule_type_post_key])) {
             continue;
         }
         // Save rule type
         $posted_rules = $_POST[$rule_type_post_key];
         // Remove template rule
         if (isset($posted_rules['__INDEX__'])) {
             unset($posted_rules['__INDEX__']);
         }
         // Stop processing rule type if no rules left
         if (empty($posted_rules)) {
             continue;
         }
         // Pre-process rules before saving
         foreach ($posted_rules as $key => $rule) {
             // If not updating rules for a plan, but rather a single post,
             // do not process or update inherited rules or rules that apply to multiple objects
             if ('post' === $target && isset($rule['object_ids']) && is_array($rule['object_ids']) && isset($rule['object_ids'][0]) && $rule['object_ids'][0] != $post_id) {
                 unset($posted_rules[$key]);
                 continue;
             }
             // Make sure each rule has an ID
             if (!isset($rule['id']) || !$rule['id']) {
                 $rule['id'] = uniqid('rule_');
             }
             // Make sure each rule has the rule type set
             $rule['rule_type'] = $rule_type;
             // If updating rules for a single plan, set the plan ID
             // and content type fields on the rule
             if ('plan' == $target) {
                 // Make sure each rule has correct membership plan ID
                 $rule['membership_plan_id'] = $post_id;
                 // Normalize content type: break content_type_key into parts
                 $content_type_parts = explode('|', $rule['content_type_key']);
                 $rule['content_type'] = $content_type_parts[0];
                 $rule['content_type_name'] = $content_type_parts[1];
                 unset($rule['content_type_key']);
                 // Normalize object IDs
                 if (isset($rule['object_ids']) && $rule['object_ids'] && !is_array($rule['object_ids'])) {
                     $rule['object_ids'] = explode(',', $rule['object_ids']);
                 }
             } else {
                 // Ensure that the correct object ID is set
                 if (!isset($rule['object_ids']) || empty($rule['object_ids'])) {
                     $rule['object_ids'] = array($post_id);
                 }
                 // Ensure correct content type & name is set
                 $rule['content_type'] = 'post_type';
                 $rule['content_type_name'] = get_post_type($post_id);
             }
             // Content restriction & product restricion:
             if (in_array($rule_type, array('content_restriction', 'product_restriction'))) {
                 // Make sure access_schedule_exclude_trial is set, even if it's a no
                 if (!isset($rule['access_schedule_exclude_trial'])) {
                     $rule['access_schedule_exclude_trial'] = 'no';
                 }
                 // If no access schedule is set, set it to immediate by default
                 if (!isset($rule['access_schedule'])) {
                     $rule['access_schedule'] = 'immediate';
                 }
                 // Normalize access schedule
                 if ('specific' == $rule['access_schedule']) {
                     if (!$rule['access_schedule_amount']) {
                         $rule['access_schedule'] = 'immediate';
                     } else {
                         // Create textual (human-readable) representation of the access schedule
                         $rule['access_schedule'] = sprintf('%d %s', $rule['access_schedule_amount'], $rule['access_schedule_period']);
                     }
                 }
                 unset($rule['access_schedule_amount']);
                 unset($rule['access_schedule_period']);
             } else {
                 if ('purchasing_discount' == $rule_type) {
                     // Make sure active is set, even if it's a no
                     $rule['active'] = isset($rule['active']) && $rule['active'] ? 'yes' : 'no';
                 }
             }
             // Update rule properties
             $posted_rules[$key] = $rule;
         }
         // end pre-processing rules
         // Process posted rules
         foreach ($posted_rules as $key => $posted) {
             $existing_rule_key = wc_memberships()->array_search_key_value($rules, 'id', $posted['id']);
             // This is an existing rule
             if (is_numeric($existing_rule_key)) {
                 $rule = new WC_Memberships_Membership_Plan_Rule($rules[$existing_rule_key]);
                 // Check capabilities
                 if ($rule->content_type_exists() && !$rule->current_user_can_edit()) {
                     continue;
                 }
                 // Check if current context allows editing
                 if (!$rule->current_context_allows_editing()) {
                     continue;
                 }
                 if (isset($posted['remove']) && $posted['remove']) {
                     unset($rules[$existing_rule_key]);
                     continue;
                 }
                 // Remove unnecessary keys
                 unset($posted['remove']);
                 // Update existing rule
                 $rules[$existing_rule_key] = $posted;
             } else {
                 // Remove unnecessary keys
                 unset($posted['remove']);
                 // Check capabilities
                 switch ($posted['content_type']) {
                     case 'post_type':
                         $post_type = get_post_type_object($posted['content_type_name']);
                         // Skip if user has no capabilities to edit the associated post type
                         if (!(current_user_can($post_type->cap->edit_posts) && current_user_can($post_type->cap->edit_others_posts))) {
                             continue;
                         }
                         break;
                     case 'taxonomy':
                         $taxonomy = get_taxonomy($posted['content_type_name']);
                         // Skip if user has no capabilities to edit the associated taxonomy
                         if (!(current_user_can($taxonomy->cap->manage_terms) && current_user_can($taxonomy->cap->edit_terms))) {
                             continue;
                         }
                         break;
                 }
                 $rules[] = $posted;
             }
         }
     }
     update_option('wc_memberships_rules', !empty($rules) ? array_values($rules) : array());
 }