/app/model/class-ms-model-membership.php

  1. <?php 
  2. /** 
  3. * Membership model. 
  4. * 
  5. * A membership defines payment options, access rules and similar details. 
  6. * It is not related to a specific user! 
  7. * 
  8. * Note that all properties are declared protected but they can be access 
  9. * directly (e.g. `$membership->type` to get the type value). 
  10. * There are magic methods \_\_get() and \_\_set() that do some validation before 
  11. * accessing the properties. 
  12. * 
  13. * @since 1.0.0 
  14. * @package Membership2 
  15. * @subpackage Model 
  16. */ 
  17. class MS_Model_Membership extends MS_Model_CustomPostType { 
  18.  
  19. /** 
  20. * Model custom post type. 
  21. * 
  22. * @since 1.0.0 
  23. * @internal self::get_post_type() to get this value! 
  24. * 
  25. * @var string $POST_TYPE 
  26. */ 
  27. protected static $POST_TYPE = 'ms_membership'; 
  28.  
  29. /** 
  30. * Membership type constant. 
  31. * 
  32. * @since 1.0.0 
  33. * @see $type $type property. 
  34. */ 
  35. const TYPE_STANDARD = 'simple'; 
  36.  
  37. /** 
  38. * Membership type constant. 
  39. * 
  40. * @since 1.0.0 
  41. * @see $type $type property. 
  42. */ 
  43. const TYPE_DRIPPED = 'dripped'; 
  44.  
  45. /** 
  46. * Membership type constant. 
  47. * System membership, hidden, created automatically. 
  48. * 
  49. * @since 1.0.0 
  50. * @see $type $type property. 
  51. */ 
  52. const TYPE_BASE = 'base'; 
  53.  
  54. /** 
  55. * Membership type constant. 
  56. * Guest membership, only one membership possible. 
  57. * 
  58. * @since 1.0.0 
  59. * @see $type $type property. 
  60. */ 
  61. const TYPE_GUEST = 'guest'; 
  62.  
  63. /** 
  64. * Membership type constant. 
  65. * User membership, only one membership possible. 
  66. * 
  67. * @since 1.0.0 
  68. * @see $type $type property. 
  69. */ 
  70. const TYPE_USER = 'user'; 
  71.  
  72. /** 
  73. * Membership payment type constants. 
  74. * 
  75. * @since 1.0.0 
  76. * @see $payment_type $payment_type property. 
  77. */ 
  78. const PAYMENT_TYPE_PERMANENT = 'permanent'; 
  79.  
  80. /** 
  81. * Membership payment type constants. 
  82. * 
  83. * @since 1.0.0 
  84. * @see $payment_type $payment_type property. 
  85. */ 
  86. const PAYMENT_TYPE_FINITE = 'finite'; 
  87.  
  88. /** 
  89. * Membership payment type constants. 
  90. * 
  91. * @since 1.0.0 
  92. * @see $payment_type $payment_type property. 
  93. */ 
  94. const PAYMENT_TYPE_DATE_RANGE = 'date-range'; 
  95.  
  96. /** 
  97. * Membership payment type constants. 
  98. * The only type that auto-renews without asking the user! 
  99. * 
  100. * @since 1.0.0 
  101. * @see $payment_type $payment_type property. 
  102. */ 
  103. const PAYMENT_TYPE_RECURRING = 'recurring'; 
  104.  
  105. /** 
  106. * Membership type. 
  107. * Default is TYPE_STANDARD. 
  108. * 
  109. * @since 1.0.0 
  110. * @var string $type 
  111. */ 
  112. protected $type = self::TYPE_STANDARD; 
  113.  
  114. /** 
  115. * Membership payment type. 
  116. * Default is PAYMENT_TYPE_PERMANENT. 
  117. * 
  118. * @since 1.0.0 
  119. * @var string $payment_type 
  120. */ 
  121. protected $payment_type = self::PAYMENT_TYPE_PERMANENT; 
  122.  
  123. /** 
  124. * Membership active status. 
  125. * By default a new membership is active. 
  126. * 
  127. * @since 1.0.0 
  128. * @var bool $active 
  129. */ 
  130. protected $active = true; 
  131.  
  132. /** 
  133. * Membership private status. 
  134. * Private means that the membership will not be displayed on the default 
  135. * registration page and membership list. 
  136. * Users can still stubscribe to the membership via the shortcode 
  137. * [ms-membership-buy] or by otherwise reaching the subscription URL. 
  138. * 
  139. * @since 1.0.0 
  140. * @var bool $private 
  141. */ 
  142. protected $private = false; 
  143.  
  144. /** 
  145. * A priority value that is used to determine the effective override 
  146. * settings if a user has multiple memberships. 
  147. * 
  148. * @since 1.0.1.0 
  149. * @var int $priority 
  150. */ 
  151. protected $priority = 0; 
  152.  
  153. /** 
  154. * Membership free status. 
  155. * 
  156. * @since 1.0.0 
  157. * @var bool $free. 
  158. */ 
  159. protected $is_free = false; 
  160.  
  161. /** 
  162. * Membership price. 
  163. * 
  164. * @since 1.0.0 
  165. * @var float $price. 
  166. */ 
  167. protected $price = 0; 
  168.  
  169. /** 
  170. * A list of disabled gateways. 
  171. * 
  172. * @since 1.0.0 
  173. * @var array $disabled_gateways. 
  174. */ 
  175. protected $disabled_gateways = array(); 
  176.  
  177. /** 
  178. * Membership period for finite access. 
  179. * Used for payment_type PAYMENT_TYPE_FINITE. 
  180. * 
  181. * @since 1.0.0 
  182. * @see $payment_type $payment_type property. 
  183. * @var array $period { 
  184. * @type int $period_unit The period of time quantity. 
  185. * @type string $period_type The period type (days, weeks, months, years). 
  186. * } 
  187. */ 
  188. protected $period = array( 'period_unit' => 1, 'period_type' => 'days' ); 
  189.  
  190. /** 
  191. * Membership payment recurring period cycle. 
  192. * Used for the payment_type PAYMENT_TYPE_RECURRING. 
  193. * 
  194. * @since 1.0.0 
  195. * @see $payment_type $payment_type property. 
  196. * @var array $pay_cycle_period {@see $period $period property}. 
  197. */ 
  198. protected $pay_cycle_period = array( 'period_unit' => 1, 'period_type' => 'days' ); 
  199.  
  200. /** 
  201. * Defines how many payments are made before the membership ends. 
  202. * Used for the payment_type PAYMENT_TYPE_RECURRING. 
  203. * 
  204. * @since 1.0.0 
  205. * @see $payment_type $payment_type property. 
  206. * @var int 
  207. */ 
  208. protected $pay_cycle_repetitions = 0; 
  209.  
  210. /** 
  211. * Membership start date for date range payment type. 
  212. * Used for the payment_type PAYMENT_TYPE_DATE_RANGE. 
  213. * 
  214. * @since 1.0.0 
  215. * @see $payment_type $payment_type property. 
  216. * @var string The membership start date. 
  217. */ 
  218. protected $period_date_start = ''; 
  219.  
  220. /** 
  221. * Membership end date for date range payment type. 
  222. * Used for the payment_type PAYMENT_TYPE_DATE_RANGE. 
  223. * 
  224. * @since 1.0.0 
  225. * @see $payment_type $payment_type property. 
  226. * @var string The membership end date. 
  227. */ 
  228. protected $period_date_end = ''; 
  229.  
  230. /** 
  231. * Membership trial period enabled indicator. 
  232. * Requires the Trial Period Add-on to work. 
  233. * 
  234. * @since 1.0.0 
  235. * @var bool $trial_period_enabled. 
  236. */ 
  237. protected $trial_period_enabled = false; 
  238.  
  239. /** 
  240. * Membership trial price value. 
  241. * Requires the Trial Period Add-on to work. 
  242. * 
  243. * @since 1.0.0 
  244. * @internal This property has no effect yet. 
  245. * @var float $trial_price. 
  246. */ 
  247. protected $trial_price = 0; 
  248.  
  249. /** 
  250. * Membership trial period. 
  251. * 
  252. * @since 1.0.0 
  253. * @var array $trial_period {@see $period $period property}. 
  254. */ 
  255. protected $trial_period = array( 'period_unit' => 1, 'period_type' => 'days' ); 
  256.  
  257. /** 
  258. * Move to Membership when the current one expires. 
  259. * 
  260. * After current membership expire move to the indicated membership_id. 
  261. * This membership is assigned when the current membership expires. 
  262. * 
  263. * @see MS_Model_Relationship::check_membership_status() 
  264. * 
  265. * @since 1.0.0 
  266. * @var int $on_end_membership_id. 
  267. */ 
  268. protected $on_end_membership_id = 0; 
  269.  
  270. /** 
  271. * Membership setup completed flag. 
  272. * 
  273. * We need this to determine if payment options of the membership are edited 
  274. * the first time during the setup assistant, or later via the membership 
  275. * list. 
  276. * 
  277. * @since 1.0.0 
  278. * @internal 
  279. * @var bool $is_setup_completed. 
  280. */ 
  281. protected $is_setup_completed = false; 
  282.  
  283. /** 
  284. * Where the data came from. Can only be changed by data import tool. 
  285. * 
  286. * @since 1.0.0 
  287. * @internal 
  288. * @var string 
  289. */ 
  290. protected $source = ''; 
  291.  
  292. /** 
  293. * Relevant for imported items. This is the ID that was used by the import 
  294. * source. 
  295. * 
  296. * @since 1.0.1.0 
  297. * @internal 
  298. * @var string 
  299. */ 
  300. protected $source_id = ''; 
  301.  
  302. /** 
  303. * Membership composite Rules. 
  304. * 
  305. * These are protection rules for this membership only. 
  306. * 
  307. * Network-wide mode: The rules stored in here are the rules that apply 
  308. * to the currently selected site in the network! 
  309. * 
  310. * Example: 
  311. * When the network has 10 sites then $rule_values will have 10 "page" rules 
  312. * which are stored as "1:page", "2:page", ... 
  313. * However, the $_rules property will only have ONE "page" rule, and that's 
  314. * the one for the currently visible site! 
  315. * 
  316. * @since 1.0.0 
  317. * @internal 
  318. * @var array MS_Rule[]. 
  319. */ 
  320. protected $_rules = array(); 
  321.  
  322. /** 
  323. * Only used for serialization of the membership. 
  324. * @see __sleep() 
  325. * 
  326. * @since 1.0.0 
  327. * @internal 
  328. * @var array 
  329. */ 
  330. protected $rule_values = array(); 
  331.  
  332. /** 
  333. * Defines which members can NOT subscribe to the current membership. 
  334. * 
  335. * @since 1.0.1.0 
  336. * @internal 
  337. * @var array 
  338. */ 
  339. protected $update_denied = array(); 
  340.  
  341. /** 
  342. * Defines if the current membership replaces other memberships on 
  343. * subscription. 
  344. * 
  345. * @since 1.0.1.0 
  346. * @internal 
  347. * @var array 
  348. */ 
  349. protected $update_replace = array(); 
  350.  
  351. /** 
  352. * An internal counter that is increased every time membership details are 
  353. * changed. 
  354. * 
  355. * @since 1.0.3.0 
  356. * @internal 
  357. * @var int 
  358. */ 
  359. protected $revision = 0; 
  360.  
  361. /** 
  362. * Used in simulation mode explaining why a page is allowed or denied. 
  363. * 
  364. * @since 1.0.0 
  365. * @internal 
  366. * @var array 
  367. */ 
  368. public $_access_reason = array(); 
  369.  
  370. /** 
  371. * Similar to $_access_reason, but only contains the rules that denied page 
  372. * access. 
  373. * 
  374. * @since 1.0.0 
  375. * @internal 
  376. * @var array 
  377. */ 
  378. public $_deny_rule = array(); 
  379.  
  380. /** 
  381. * Similar to $_access_reason, but only contains the rules that allowed page 
  382. * access. 
  383. * 
  384. * @since 1.0.0 
  385. * @internal 
  386. * @var array 
  387. */ 
  388. public $_allow_rule = array(); 
  389.  
  390. /** 
  391. * Stores the subscription-ID of the parent object. 
  392. * This value will only have a value when the Membership is loaded within 
  393. * the context of a subscription. 
  394. * 
  395. * @since 1.0.0 
  396. * @var int 
  397. */ 
  398. protected $subscription_id = 0; 
  399.  
  400. /** 
  401. * This property is used to build the signup list (shortcode). 
  402. * 
  403. * It's a temporary value that is not saved to database. 
  404. * 
  405. * @since 1.0.1.0 
  406. * @internal 
  407. * @var array 
  408. */ 
  409. public $_move_from = array(); 
  410.  
  411.  
  412. // 
  413. // 
  414. // 
  415. // -------------------------------------------------------------- COLLECTION 
  416.  
  417.  
  418. /** 
  419. * Returns the post-type of the current object. 
  420. * 
  421. * @since 1.0.0 
  422. * @api 
  423. * 
  424. * @return string The post-type name. 
  425. */ 
  426. public static function get_post_type() { 
  427. return parent::_post_type( self::$POST_TYPE ); 
  428.  
  429. /** 
  430. * Get custom register post type args for this model. 
  431. * 
  432. * @since 1.0.0 
  433. * @internal 
  434. */ 
  435. public static function get_register_post_type_args() { 
  436. $args = array( 
  437. 'label' => __( 'Membership2 Memberships', 'membership2' ),  
  438. 'description' => __( 'Memberships user can join to.', 'membership2' ),  
  439. 'show_ui' => false,  
  440. 'show_in_menu' => false,  
  441. 'menu_position' => 70, // below Users 
  442. 'menu_icon' => 'dashicons-lock',  
  443. 'public' => true,  
  444. 'has_archive' => false,  
  445. 'publicly_queryable' => false,  
  446. 'supports' => false,  
  447. 'hierarchical' => false,  
  448. 'exclude_from_search' => true 
  449. ); 
  450.  
  451. return apply_filters( 
  452. 'ms_customposttype_register_args',  
  453. $args,  
  454. self::get_post_type() 
  455. ); 
  456.  
  457. /** 
  458. * Get membership types. 
  459. * 
  460. * @since 1.0.0 
  461. * @internal 
  462. * 
  463. * @return array { 
  464. * Returns array of $type => $title. 
  465. * 
  466. * @type string $type The membership type 
  467. * @type string $title The membership type title 
  468. * } 
  469. */ 
  470. static public function get_types() { 
  471. $types = array( 
  472. self::TYPE_STANDARD => __( 'Standard Membership', 'membership2' ),  
  473. self::TYPE_DRIPPED => __( 'Dripped Content Membership', 'membership2' ),  
  474. self::TYPE_GUEST => __( 'Guest Membership', 'membership2' ),  
  475. self::TYPE_USER => __( 'Default Membership', 'membership2' ),  
  476. self::TYPE_BASE => __( 'System Membership', 'membership2' ),  
  477. ); 
  478.  
  479. return apply_filters( 'ms_model_membership_get_types', $types ); 
  480.  
  481. /** 
  482. * Get membership payment types. 
  483. * 
  484. * @since 1.0.0 
  485. * @internal 
  486. * 
  487. * @return array { 
  488. * Returns array of $type => $title. 
  489. * 
  490. * @type string $type The membership payment type 
  491. * @type string $title The membership payment type title 
  492. * } 
  493. */ 
  494. public static function get_payment_types( $type = 'paid' ) { 
  495. if ( 'free' == $type ) { 
  496. $payment_types = array( 
  497. self::PAYMENT_TYPE_PERMANENT => __( 'Permanent access', 'membership2' ),  
  498. self::PAYMENT_TYPE_FINITE => __( 'Finite access', 'membership2' ),  
  499. self::PAYMENT_TYPE_DATE_RANGE => __( 'Date range access', 'membership2' ),  
  500. ); 
  501. } else { 
  502. $payment_types = array( 
  503. self::PAYMENT_TYPE_PERMANENT => __( 'One payment for permanent access', 'membership2' ),  
  504. self::PAYMENT_TYPE_FINITE => __( 'One payment for finite access', 'membership2' ),  
  505. self::PAYMENT_TYPE_DATE_RANGE => __( 'One payment for date range access', 'membership2' ),  
  506. self::PAYMENT_TYPE_RECURRING => __( 'Recurring payments', 'membership2' ),  
  507. ); 
  508.  
  509. return apply_filters( 
  510. 'ms_model_membership_get_payment_types',  
  511. $payment_types,  
  512. $type 
  513. ); 
  514.  
  515. /** 
  516. * Get available Memberships count. 
  517. * 
  518. * @since 1.0.0 
  519. * @internal 
  520. * 
  521. * @param $args The query post args 
  522. * @see @link http://codex.wordpress.org/Class_Reference/WP_Query 
  523. * @return int The membership count. 
  524. */ 
  525. public static function get_membership_count( $args = null ) { 
  526. $ids = self::get_membership_ids( $args ); 
  527. $count = count( $ids ); 
  528.  
  529. return apply_filters( 
  530. 'ms_model_membership_get_membership_count',  
  531. $count,  
  532. $args 
  533. ); 
  534.  
  535. /** 
  536. * Find out if the installation has at least one paid membership 
  537. * 
  538. * @since 1.0.0 
  539. * @internal 
  540. * 
  541. * @return bool 
  542. */ 
  543. public static function have_paid_membership() { 
  544. static $Have_Paid = null; 
  545.  
  546. if ( null === $Have_Paid ) { 
  547. global $wpdb; 
  548. // Using a custom WPDB query because building the meta-query is more 
  549. // complex than really required here... 
  550. $sql = " 
  551. SELECT COUNT( 1 ) 
  552. FROM {$wpdb->posts} p 
  553. INNER JOIN {$wpdb->postmeta} free ON free.post_id = p.ID AND free.meta_key = %s 
  554. INNER JOIN {$wpdb->postmeta} pric ON pric.post_id = p.ID AND pric.meta_key = %s 
  555. INNER JOIN {$wpdb->postmeta} acti ON acti.post_id = p.ID AND acti.meta_key = %s 
  556. WHERE 
  557. p.post_type = %s 
  558. AND acti.meta_value = '1' 
  559. AND NOT ( 
  560. free.meta_value = '1' 
  561. OR pric.meta_value = '0' 
  562. "; 
  563.  
  564. $sql = $wpdb->prepare( 
  565. $sql,  
  566. 'is_free', // INNER JOIN 
  567. 'price', // INNER JOIN 
  568. 'active', // INNER JOIN 
  569. self::get_post_type() // WHERE condition 
  570. ); 
  571.  
  572. $res = $wpdb->get_var( $sql ); 
  573.  
  574. $Have_Paid = apply_filters( 
  575. 'ms_model_membership_have_paid_membership',  
  576. intval( $res ) > 0 
  577. ); 
  578.  
  579. return $Have_Paid; 
  580.  
  581. /** 
  582. * Get WP_Query object arguments. 
  583. * 
  584. * Default search arguments for this custom post_type. 
  585. * 
  586. * @since 1.0.0 
  587. * @internal 
  588. * 
  589. * @param $args The query post args 
  590. * @see @link http://codex.wordpress.org/Class_Reference/WP_Query 
  591. * @return array $args The parsed args. 
  592. */ 
  593. public static function get_query_args( $args = null ) { 
  594. $defaults = apply_filters( 
  595. 'ms_model_membership_get_query_args_defaults',  
  596. array( 
  597. 'post_type' => self::get_post_type(),  
  598. 'order' => 'ASC',  
  599. 'orderby' => 'menu_order',  
  600. 'post_status' => 'any',  
  601. 'post_per_page' => -1,  
  602. 'nopaging' => true,  
  603. 'include_base' => false,  
  604. 'include_guest' => true,  
  605. ); 
  606.  
  607. $args = wp_parse_args( $args, $defaults ); 
  608.  
  609. if ( isset( $args['active'] ) ) { 
  610. $args['meta_query']['active'] = array( 
  611. 'key' => 'active',  
  612. 'value' => 1,  
  613. ); 
  614.  
  615. if ( lib3()->is_true( $args['active'] ) ) { 
  616. $args['meta_query']['active']['compare'] = '='; 
  617. } else { 
  618. $args['meta_query']['active']['compare'] = '!='; 
  619.  
  620. if ( ! lib3()->is_true( $args['include_base'] ) ) { 
  621. $args['meta_query']['base'] = array( 
  622. 'key' => 'type',  
  623. 'value' => self::TYPE_BASE,  
  624. 'compare' => '!=',  
  625. ); 
  626.  
  627. if ( ! lib3()->is_true( $args['include_guest'] ) ) { 
  628. $args['meta_query']['guest'] = array( 
  629. 'key' => 'type',  
  630. 'value' => self::TYPE_GUEST,  
  631. 'compare' => '!=',  
  632. ); 
  633. $args['meta_query']['user'] = array( 
  634. 'key' => 'type',  
  635. 'value' => self::TYPE_USER,  
  636. 'compare' => '!=',  
  637. ); 
  638.  
  639. return apply_filters( 
  640. 'ms_model_membership_get_query_args',  
  641. $args,  
  642. $defaults 
  643. ); 
  644.  
  645. /** 
  646. * Returns a list of Membership IDs that match the given WP_Query arguments. 
  647. * 
  648. * @since 1.0.1.0 
  649. * @internal 
  650. * 
  651. * @param $args The query post args. 
  652. * @see @link http://codex.wordpress.org/Class_Reference/WP_Query 
  653. * @return array A list of membership IDs. 
  654. */ 
  655. static public function get_membership_ids( $args = null ) { 
  656. static $Membership_IDs = array(); 
  657. $args = self::get_query_args( $args ); 
  658. $key = md5( json_encode( $args ) ); 
  659.  
  660. if ( ! isset( $Membership_IDs[ $key ] ) ) { 
  661. $Membership_IDs[ $key ] = array(); 
  662.  
  663. MS_Factory::select_blog(); 
  664. $query = new WP_Query( $args ); 
  665. $items = $query->posts; 
  666. MS_Factory::revert_blog(); 
  667.  
  668. /** 
  669. * We only cache the IDs to avoid re-querying the database. 
  670. * The positive side effect is, that the memory used by the 
  671. * membership list will be freed again after the calling function 
  672. * is done with it. 
  673. * 
  674. * If we cache the whole list here, it would not occupy memory for 
  675. * the whole request duration which can cause memory_limit errors. 
  676. * 
  677. * @see MS_Model_Relationship::get_subscriptions() 
  678. */ 
  679. foreach ( $items as $item ) { 
  680. $Membership_IDs[ $key ][] = $item->ID; 
  681.  
  682. return apply_filters( 
  683. 'ms_model_membership_get_membership_ids',  
  684. $Membership_IDs[ $key ],  
  685. $args 
  686. ); 
  687.  
  688. /** 
  689. * Get Memberships models. 
  690. * 
  691. * When no $args are specified then all memberships except the base 
  692. * membership will be returned. 
  693. * 
  694. * To include the base membership use: 
  695. * $args = array( 'include_base' => 1 ) 
  696. * 
  697. * To exclude the guest membership use: 
  698. * $args = array( 'include_guest' => 0 ) 
  699. * 
  700. * @since 1.0.0 
  701. * @internal 
  702. * 
  703. * @param $args The query post args 
  704. * @see @link http://codex.wordpress.org/Class_Reference/WP_Query 
  705. * @return MS_Model_Membership[] The selected memberships. 
  706. */ 
  707. static public function get_memberships( $args = null ) { 
  708. $ids = self::get_membership_ids( $args ); 
  709. $memberships = array(); 
  710.  
  711. foreach ( $ids as $id ) { 
  712. $memberships[] = MS_Factory::load( 
  713. 'MS_Model_Membership',  
  714. $id 
  715. ); 
  716.  
  717. return apply_filters( 
  718. 'ms_model_membership_get_memberships',  
  719. $memberships,  
  720. $args 
  721. ); 
  722.  
  723. /** 
  724. * Returns a list of the dripped memberships. 
  725. * 
  726. * @since 1.0.0 
  727. * @internal 
  728. * 
  729. * @param $args The query post args 
  730. * @see @link http://codex.wordpress.org/Class_Reference/WP_Query 
  731. * @return MS_Model_Membership[] The selected memberships. 
  732. */ 
  733. static public function get_dripped_memberships( $args = null ) { 
  734. $drip_args = array( 
  735. 'meta_query' => array( 
  736. array( 
  737. 'key' => 'type',  
  738. 'value' => self::TYPE_DRIPPED,  
  739. ),  
  740. ),  
  741. ); 
  742.  
  743. $drip_args = wp_parse_args( $drip_args, $args ); 
  744. $memberships = self::get_memberships( $drip_args ); 
  745.  
  746. return apply_filters( 
  747. 'ms_model_membership_get_dripped_memberships',  
  748. $memberships,  
  749. $args 
  750. ); 
  751.  
  752. /** 
  753. * Get membership names. 
  754. * 
  755. * Note that this function returns an array with membership_id as index,  
  756. * while the function get_memberships() returns an array with sort-order as 
  757. * index. 
  758. * 
  759. * @since 1.0.0 
  760. * @internal 
  761. * 
  762. * @param $args The query post args 
  763. * @see @link http://codex.wordpress.org/Class_Reference/WP_Query 
  764. * @param bool $include_base_membership Include base membership from the list. 
  765. * @return array { 
  766. * Returns array of $membership_id => $name 
  767. * @type int $membership_id The membership Id. 
  768. * @type string $name The membership name; 
  769. * } 
  770. */ 
  771. public static function get_membership_names( $args = null ) { 
  772. $items = self::get_memberships( $args ); 
  773.  
  774. $memberships = array(); 
  775. foreach ( $items as $item ) { 
  776. $memberships[ $item->id ] = $item->name; 
  777.  
  778. return apply_filters( 
  779. 'ms_model_membership_get_membership_names',  
  780. $memberships,  
  781. $args 
  782. ); 
  783.  
  784. /** 
  785. * Get membership eligible to signup. 
  786. * 
  787. * This function also checks for membership permissions and only display 
  788. * memberships that are available for the current member. 
  789. * 
  790. * @since 1.0.0 
  791. * @internal 
  792. * 
  793. * @param $args The query post args 
  794. * @see @link http://codex.wordpress.org/Class_Reference/WP_Query 
  795. * @param int[] $exclude_ids Optional. The membership ids to exclude. 
  796. * @param bool $only_names Optional. Return only array { 
  797. * @type int $membership_id The membership ID. 
  798. * @type string $membership_name The membership name. 
  799. * } 
  800. * @param bool $include_private If private memberships should be listed 
  801. * This param is only recognized in the admin section so admins can 
  802. * manually assign a private membership to a user. 
  803. * @return array Returns sorted array of memberships. Sorted by priority. 
  804. */ 
  805. public static function get_signup_membership_list( 
  806. $args = null,  
  807. $exclude_ids = null,  
  808. $only_names = false,  
  809. $include_private = false 
  810. ) { 
  811. $not_in = array(); 
  812. if ( is_array( $exclude_ids ) ) { 
  813. $not_in = $exclude_ids; 
  814. $args['post__not_in'] = array_unique( $not_in ); 
  815. $member = MS_Model_Member::get_current_member(); 
  816.  
  817. if ( ! is_admin() ) { 
  818. $include_private = false; 
  819. // List of private memberships (they are grouped in own array). 
  820. $private = array(); 
  821.  
  822. // Retrieve memberships user is not part of, using selected args 
  823. $memberships = self::get_memberships( $args ); 
  824.  
  825. // Check the upgrade-paths settings 
  826. foreach ( $memberships as $key => $ms ) { 
  827. if ( $ms->is_system() ) { 
  828. unset( $memberships[ $key ] ); 
  829. } elseif ( ! $member->can_subscribe_to( $ms->id ) ) { 
  830. unset( $memberships[ $key ] ); 
  831.  
  832. // Filter memberships based on status. 
  833. $order = array(); 
  834. foreach ( $memberships as $key => $membership ) { 
  835. // Remove if not active. 
  836. if ( ! $membership->active ) { 
  837. unset( $memberships[ $key ] ); 
  838. continue; 
  839.  
  840. if ( $membership->private ) { 
  841. if ( $include_private ) { 
  842. // Move the private memberships to a option-group. 
  843. $private[ $key ] = $memberships[ $key ]; 
  844. unset( $memberships[ $key ] ); 
  845. continue; 
  846.  
  847. if ( $only_names ) { 
  848. $ms_names = array(); 
  849. foreach ( $memberships as $ms ) { 
  850. $ms_names[ $ms->id ] = $ms->name; 
  851. if ( ! empty( $private ) ) { 
  852. $priv_key = __( 'Private Memberships', 'membership2' ); 
  853. $ms_names[ $priv_key ] = array(); 
  854. foreach ( $private as $ms ) { 
  855. $ms_names[ $priv_key ][ $ms->id ] = $ms->name; 
  856. $memberships = $ms_names; 
  857. } else { 
  858. $memberships = array_merge( $memberships, $private ); 
  859.  
  860. // Sort memberships by priority. 
  861. usort( 
  862. $memberships,  
  863. array( __CLASS__, 'sort_by_priority' ) 
  864. ); 
  865.  
  866. return apply_filters( 
  867. 'ms_model_membership_get_signup_membership_list',  
  868. $memberships,  
  869. $exclude_ids,  
  870. $only_names 
  871. ); 
  872.  
  873. /** 
  874. * Sort function used as second param by `uasort()` to sort a membership 
  875. * list by priority. 
  876. * Memberships with equal priority are sorted alphabeically. 
  877. * 
  878. * @since 1.0.1.0 
  879. * @param MS_Model_Membership $a 
  880. * @param MS_Model_Membership $b 
  881. * @return int -1: a < b | 0: a = b | +1: a > b 
  882. */ 
  883. static public function sort_by_priority( $a, $b ) { 
  884. if ( $a->priority == $b->priority ) { 
  885. return $a->name < $b->name ? -1 : 1; 
  886. } else { 
  887. return $a->priority - $b->priority; 
  888.  
  889. /** 
  890. * Verify if membership is valid. 
  891. * 
  892. * Verify if membership was not deleted, trying to load from DB. 
  893. * 
  894. * @since 1.0.0 
  895. * @api 
  896. * 
  897. * @param int $membership_id The membership id to verify. 
  898. * @return bool True if is valid. 
  899. */ 
  900. public static function is_valid_membership( $membership_id ) { 
  901. $membership = MS_Factory::load( 'MS_Model_Membership', $membership_id, '_is_valid_' ); 
  902. $valid = ( $membership->id > 0 ); 
  903.  
  904. return apply_filters( 
  905. 'ms_model_membership_is_valid_membership',  
  906. $valid,  
  907. $membership_id 
  908. ); 
  909.  
  910. /** 
  911. * Get Membership2 membership. 
  912. * 
  913. * Create a new membership if membership does not exist. 
  914. * 
  915. * @since 1.0.0 
  916. * @internal 
  917. * 
  918. * @param string $type The membership to load [protected_content|role] 
  919. * @param book $create_missing If set to false then missing special 
  920. * memberships are not created. 
  921. * @return MS_Model_Membership The Membership2. 
  922. */ 
  923. public static function _get_system_membership( $type, $create_missing = true ) { 
  924. static $Special_Membership = array(); 
  925. $comp_key = $type; 
  926. $membership = false; 
  927.  
  928. if ( ! isset( $Special_Membership[ $comp_key ] ) ) { 
  929. $membership = false; 
  930. global $wpdb; 
  931.  
  932. MS_Factory::select_blog(); 
  933. /** 
  934. * We are using a normal SQL query instead of using the WP_Query object 
  935. * here, because the WP_Query object does some strange things sometimes: 
  936. * In some cases new Membership2 memberships were created when a 
  937. * guest accessed the page. 
  938. * 
  939. * By using a manual query we are very certain that only one 
  940. * base-membership exists on the database. 
  941. */ 
  942. $sql = " 
  943. SELECT ID 
  944. FROM {$wpdb->posts} p 
  945. INNER JOIN {$wpdb->postmeta} m_type ON m_type.post_id = p.ID 
  946. WHERE 
  947. p.post_type = %s 
  948. AND m_type.meta_key = %s 
  949. AND m_type.meta_value = %s 
  950. "; 
  951. $values = array( 
  952. self::get_post_type(),  
  953. 'type',  
  954. $type,  
  955. ); 
  956.  
  957. $sql = $wpdb->prepare( $sql, $values ); 
  958. $item = $wpdb->get_results( $sql ); 
  959. $base = array_shift( $item ); // Remove the base membership from the results 
  960. MS_Factory::revert_blog(); 
  961.  
  962. if ( ! empty( $base ) ) { 
  963. $membership = MS_Factory::load( 'MS_Model_Membership', $base->ID ); 
  964. } elseif ( $create_missing ) { 
  965. $names = self::get_types(); 
  966.  
  967. $description = __( 'Membership2 Core Membership', 'membership2' ); 
  968. $membership = MS_Factory::create( 'MS_Model_Membership' ); 
  969. $membership->name = $names[ $type ]; 
  970. $membership->title = $names[ $type ]; 
  971. $membership->description = $description; 
  972. $membership->type = $type; 
  973. $membership->save(); 
  974.  
  975. $Special_Membership[ $comp_key ] = $membership; 
  976.  
  977. return apply_filters( 
  978. 'ms_model_membership_get_system_membership',  
  979. $Special_Membership[ $comp_key ],  
  980. $type 
  981. ); 
  982.  
  983. /** 
  984. * Get Membership2 base membership. 
  985. * 
  986. * Create a new membership if membership does not exist. 
  987. * This is an internal membership which is never displayed anywhere. 
  988. * 
  989. * @since 1.0.0 
  990. * @api 
  991. * 
  992. * @return MS_Model_Membership The base membership. 
  993. */ 
  994. public static function get_base() { 
  995. static $Base_Membership = null; 
  996.  
  997. if ( null === $Base_Membership ) { 
  998. $Base_Membership = self::_get_system_membership( 
  999. self::TYPE_BASE 
  1000. ); 
  1001.  
  1002. foreach ( $Base_Membership->_rules as $key => $rule ) { 
  1003. $Base_Membership->_rules[ $key ]->is_base_rule = true; 
  1004.  
  1005. return apply_filters( 
  1006. 'ms_model_membership_get_base',  
  1007. $Base_Membership 
  1008. ); 
  1009.  
  1010. /** 
  1011. * Get special membership that is assigned to all guests. 
  1012. * 
  1013. * Create a new membership if membership does not exist. 
  1014. * 
  1015. * @since 1.0.0 
  1016. * @api 
  1017. * 
  1018. * @param string $role A WordPress user-role. 
  1019. * @return MS_Model_Membership The guest membership. 
  1020. */ 
  1021. public static function get_guest() { 
  1022. static $Guest_Membership = null; 
  1023.  
  1024. if ( null === $Guest_Membership ) { 
  1025. $Guest_Membership = self::_get_system_membership( 
  1026. self::TYPE_GUEST,  
  1027. false // Don't create this membership automatically 
  1028. ); 
  1029.  
  1030. if ( ! $Guest_Membership ) { 
  1031. $Guest_Membership = MS_Factory::create( 'MS_Model_Membership' ); 
  1032.  
  1033. return apply_filters( 
  1034. 'ms_model_membership_get_guest',  
  1035. $Guest_Membership 
  1036. ); 
  1037.  
  1038. /** 
  1039. * Get default membership for all logged-in users that did not yet subscribe 
  1040. * to any membership. 
  1041. * 
  1042. * Create a new membership if membership does not exist. 
  1043. * 
  1044. * @since 1.0.0 
  1045. * @api 
  1046. * 
  1047. * @param string $role A WordPress user-role. 
  1048. * @return MS_Model_Membership The guest membership. 
  1049. */ 
  1050. public static function get_user() { 
  1051. static $User_Membership = null; 
  1052.  
  1053. if ( null === $User_Membership ) { 
  1054. $User_Membership = self::_get_system_membership( 
  1055. self::TYPE_USER,  
  1056. false // Don't create this membership automatically 
  1057. ); 
  1058.  
  1059. if ( ! $User_Membership ) { 
  1060. $User_Membership = MS_Factory::create( 'MS_Model_Membership' ); 
  1061.  
  1062. return apply_filters( 
  1063. 'ms_model_membership_get_user',  
  1064. $User_Membership 
  1065. ); 
  1066.  
  1067. /** 
  1068. * Checks if the specified string is a valid Membership-Type identifier. 
  1069. * 
  1070. * @since 1.0.1.0 
  1071. * @param string $type A string to check against all known membership types. 
  1072. * @return bool True if the string is a valid type. 
  1073. */ 
  1074. static public function is_valid_type( $type ) { 
  1075. switch ( $type ) { 
  1076. case self::TYPE_BASE: 
  1077. case self::TYPE_GUEST: 
  1078. case self::TYPE_USER: 
  1079. case self::TYPE_DRIPPED: 
  1080. $result = true; 
  1081. break; 
  1082.  
  1083. default: 
  1084. $result = false; 
  1085. break; 
  1086.  
  1087. return apply_filters( 
  1088. 'ms_model_membership_is_valid_type',  
  1089. $result,  
  1090. $type 
  1091. ); 
  1092.  
  1093.  
  1094. // 
  1095. // 
  1096. // 
  1097. // ------------------------------------------------------------- SINGLE ITEM 
  1098.  
  1099.  
  1100. /** 
  1101. * Returns a list of variables that should be included in serialization,  
  1102. * i.e. these values are the only ones that are stored in DB 
  1103. * 
  1104. * @since 1.0.0 
  1105. * @internal 
  1106. * @return array 
  1107. */ 
  1108. public function __sleep() { 
  1109. /** 
  1110. * Rule values are pre-processd before saving... 
  1111. * Note: $this->_rules only contains rules for the *current* site, so 
  1112. * all rules that are serialized here get the current-site prefix. 
  1113. * Rules for the other sites are already in the $this->rule_values 
  1114. * array and were not de-serialized on page load. 
  1115. */ 
  1116. $this->rule_values = lib3()->array->get( $this->rule_values ); 
  1117. foreach ( $this->_rules as $rule_type => $rule ) { 
  1118. $key = MS_Rule::rule_key( $rule_type ); 
  1119.  
  1120. $this->rule_values[ $key ] = $rule->serialize(); 
  1121. if ( empty( $this->rule_values[ $key ] ) ) { 
  1122. unset( $this->rule_values[ $key ] ); 
  1123.  
  1124. return array( 
  1125. 'id',  
  1126. 'name',  
  1127. 'title',  
  1128. 'description',  
  1129. 'rule_values',  
  1130. 'type',  
  1131. 'payment_type',  
  1132. 'active',  
  1133. 'private',  
  1134. 'is_free',  
  1135. 'disabled_gateways',  
  1136. 'price',  
  1137. 'period',  
  1138. 'pay_cycle_period',  
  1139. 'pay_cycle_repetitions',  
  1140. 'period_date_start',  
  1141. 'period_date_end',  
  1142. 'trial_period_enabled',  
  1143. 'trial_price',  
  1144. 'trial_period',  
  1145. 'on_end_membership_id',  
  1146. 'is_setup_completed',  
  1147. 'source',  
  1148. 'source_id',  
  1149. 'custom_data',  
  1150. 'update_denied',  
  1151. 'update_replace',  
  1152. 'revision',  
  1153. ); 
  1154.  
  1155. /** 
  1156. * Set rules membership_id before saving. 
  1157. * 
  1158. * @since 1.0.0 
  1159. * @internal 
  1160. */ 
  1161. public function before_save() { 
  1162. parent::before_save(); 
  1163.  
  1164. foreach ( $this->_rules as $rule ) { 
  1165. $rule->membership_id = $this->id; 
  1166.  
  1167. if ( $this->is_valid() ) { 
  1168. $this->check_revision(); 
  1169.  
  1170. /** 
  1171. * Save model and move the object to the singleton cache if required. 
  1172. * 
  1173. * @since 1.0.0 
  1174. */ 
  1175. public function save() { 
  1176. parent::save(); 
  1177. parent::store_singleton(); 
  1178.  
  1179. /** 
  1180. * After the membership was saved to DB we make sure that it is published. 
  1181. * 
  1182. * Network-wide mode: We are still in the switched blog (main site) so 
  1183. * there is no need to call MS_Factory::select_blog() in this function. 
  1184. * 
  1185. * @since 1.0.0 
  1186. * @internal 
  1187. */ 
  1188. public function after_save() { 
  1189. // It is important! The Membership2 membership must be public 
  1190. // so that the membership options are available for guest users. 
  1191. wp_publish_post( $this->id ); 
  1192.  
  1193. /** 
  1194. * Save custom values in the wp_posts table. 
  1195. * 
  1196. * @since 1.0.1.0 
  1197. * @internal 
  1198. */ 
  1199. public function save_post_data( $post ) { 
  1200. if ( $this->is_system() ) { 
  1201. $this->priority = 0; 
  1202. } elseif ( $this->priority < 1 ) { 
  1203. $this->priority = 1; 
  1204.  
  1205. $post['menu_order'] = $this->priority; 
  1206. return $post; 
  1207.  
  1208. /** 
  1209. * Called by the before_save() function to detect what kind of revision 
  1210. * was made (i.e. which values were changed). 
  1211. * 
  1212. * This is used by the stripe gateway to sync membership infos with Stripe. 
  1213. * 
  1214. * @since 1.0.3.0 
  1215. */ 
  1216. public function check_revision() { 
  1217. // Changes of these values are not counted as "revision". 
  1218. $ignore = array( 
  1219. 'revision',  
  1220. 'id',  
  1221. 'is_setup_completed',  
  1222. 'post_modified',  
  1223. 'source',  
  1224. 'source_id',  
  1225. 'subscription_id',  
  1226. 'title',  
  1227. 'user_id',  
  1228. ); 
  1229.  
  1230. $new_revision = false; 
  1231. $changes = array(); 
  1232. if ( empty( $this->revision ) ) { $this->revision = 0; } 
  1233.  
  1234. foreach ( $this->_saved_data as $field => $old_value ) { 
  1235. if ( in_array( $field, $ignore ) ) { continue; } 
  1236.  
  1237. $new_value = $this->$field; 
  1238. if ( ! is_scalar( $old_value ) ) { 
  1239. $old_value = json_encode( $old_value ); 
  1240. if ( ! is_scalar( $new_value ) ) { 
  1241. $new_value = json_encode( $new_value ); 
  1242.  
  1243. if ( $old_value == $new_value ) { continue; } 
  1244.  
  1245. $new_revision = true; 
  1246. $changes[] = $field; 
  1247.  
  1248. /** 
  1249. * Notification that a specific field of the membership changed. 
  1250. * 
  1251. * @since 1.0.3.0 
  1252. * @param MS_Model_Membership The membership object (this). 
  1253. * @param mixed The old value, might be serialized. 
  1254. * @param mixed The new value. 
  1255. */ 
  1256. do_action( 
  1257. 'ms_model_membership_revision_change-' . $field,  
  1258. $this,  
  1259. $old_value,  
  1260. $this->$field 
  1261. ); 
  1262.  
  1263. if ( $new_revision ) { 
  1264. $this->revision += 1; 
  1265.  
  1266. /** 
  1267. * Notification that any field of the membership has changed. 
  1268. * 
  1269. * @since 1.0.3.0 
  1270. * @param MS_Model_Membership The membership object (this). 
  1271. * @param array List of changed fields (only field-names). 
  1272. */ 
  1273. do_action( 
  1274. 'ms_model_membership_revision_change',  
  1275. $this,  
  1276. $changes 
  1277. ); 
  1278.  
  1279. /** 
  1280. * Load custom values from the wp_posts table. 
  1281. * 
  1282. * @since 1.0.1.0 
  1283. * @internal 
  1284. */ 
  1285. public function load_post_data( $post ) { 
  1286. $this->priority = $post->menu_order; 
  1287.  
  1288. /** 
  1289. * Permanently delete the membership. 
  1290. * 
  1291. * @since 1.0.0 
  1292. * @api 
  1293. * 
  1294. * @return bool 
  1295. */ 
  1296. public function delete() { 
  1297. do_action( 'ms_model_membership_before_delete', $this ); 
  1298. $res = false; 
  1299.  
  1300. if ( $this->is_base() ) { 
  1301. throw new Exception( 
  1302. 'Can not delete the system membership.' 
  1303. ); 
  1304.  
  1305. if ( ! empty( $this->id ) ) { 
  1306. if ( $this->get_members_count() > 0 ) { 
  1307. $subscriptions = MS_Model_Relationship::get_subscriptions( 
  1308. array( 'membership_id' => $this->id ),  
  1309. true 
  1310. ); 
  1311.  
  1312. foreach ( $subscriptions as $subscription ) { 
  1313. $subscription->delete(); 
  1314.  
  1315. $res = ( false !== wp_delete_post( $this->id, true ) ); 
  1316.  
  1317. do_action( 'ms_model_membership_after_delete', $this, $res ); 
  1318. return $res; 
  1319.  
  1320. /** 
  1321. * Merge current rules to Membership2. 
  1322. * 
  1323. * Assure the membership rules get updated whenever Membership2 is changed. 
  1324. * 
  1325. * @since 1.0.0 
  1326. * @internal 
  1327. */ 
  1328. public function prepare_obj() { 
  1329. parent::prepare_obj(); 
  1330.  
  1331. if ( false !== strpos( $this->_factory_id, '_is_valid_' ) ) { 
  1332. // This object only checks if the item ID is valid. 
  1333. // No need to load any rules yet... 
  1334. return; 
  1335.  
  1336. foreach ( $this->rule_values as $key => $values ) { 
  1337. // Skip rules without any values. 
  1338. if ( empty( $values ) ) { continue; } 
  1339.  
  1340. // Network-wide: Only instanciate rules for the *current* site! 
  1341. if ( ! MS_Rule::is_current_site( $key ) ) { continue; } 
  1342.  
  1343. // Key could be "type" of "site:type" format. 
  1344. $rule_type = MS_Rule::rule_type( $key ); 
  1345.  
  1346. // At this point we have an empty rule-instance 
  1347. $rule = $this->get_rule( $rule_type ); 
  1348.  
  1349. // Now we populate that rule-instance with site-specific settings. 
  1350. $rule->populate( $values ); 
  1351.  
  1352. // validate rules using Membership2 rules 
  1353. if ( ! $this->is_base() && $this->is_valid() ) { 
  1354. $this->merge_protection_rules(); 
  1355.  
  1356. /** 
  1357. * Get current payment type description. 
  1358. * 
  1359. * Description to show in the admin list table. 
  1360. * 
  1361. * @since 1.0.0 
  1362. * @api 
  1363. * 
  1364. * @return string The current payment type description. 
  1365. */ 
  1366. public function get_payment_type_desc() { 
  1367. $desc = __( 'N/A', 'membership2' ); 
  1368. $has_payment = ! $this->is_free(); 
  1369.  
  1370. switch ( $this->payment_type ) { 
  1371. case self::PAYMENT_TYPE_FINITE: 
  1372. if ( $has_payment ) { 
  1373. $desc = sprintf( 
  1374. __( 'Pay for %1$s', 'membership2' ),  
  1375. MS_Helper_Period::get_period_desc( $this->period, true ) 
  1376. ); 
  1377. } else { 
  1378. $desc = sprintf( 
  1379. __( 'Free for %1$s', 'membership2' ),  
  1380. MS_Helper_Period::get_period_desc( $this->period, true ) 
  1381. ); 
  1382. break; 
  1383.  
  1384. case self::PAYMENT_TYPE_DATE_RANGE: 
  1385. if ( $has_payment ) { 
  1386. $desc = sprintf( 
  1387. __( 'Pay from %1$s to %2$s', 'membership2' ),  
  1388. $this->period_date_start,  
  1389. $this->period_date_end 
  1390. ); 
  1391. } else { 
  1392. $desc = sprintf( 
  1393. __( 'Free from %1$s to %2$s', 'membership2' ),  
  1394. $this->period_date_start,  
  1395. $this->period_date_end 
  1396. ); 
  1397. break; 
  1398.  
  1399. case self::PAYMENT_TYPE_RECURRING: 
  1400. if ( $has_payment ) { 
  1401. $desc = __( 'Pay each %1$s', 'membership2' ); 
  1402. if ( 1 == $this->pay_cycle_repetitions ) { 
  1403. $desc = __( 'Single payment', 'membership2' ); 
  1404. } elseif ( $this->pay_cycle_repetitions > 1 ) { 
  1405. $desc .= ', ' . __( '%2$s payments', 'membership2' ); 
  1406. } else { 
  1407. $desc = __( 'Free access', 'membership2' ); 
  1408.  
  1409. $desc = sprintf( 
  1410. $desc,  
  1411. MS_Helper_Period::get_period_desc( $this->pay_cycle_period ),  
  1412. $this->pay_cycle_repetitions 
  1413. ); 
  1414. break; 
  1415.  
  1416. case self::PAYMENT_TYPE_PERMANENT: 
  1417. default: 
  1418. if ( $has_payment ) { 
  1419. $desc = __( 'Single payment', 'membership2' ); 
  1420. } else { 
  1421. $desc = __( 'Free access', 'membership2' ); 
  1422. break; 
  1423.  
  1424. return apply_filters( 
  1425. 'ms_model_membership_get_payment_type_desc',  
  1426. $desc,  
  1427. $this 
  1428. ); 
  1429.  
  1430. /** 
  1431. * Returns true if the current membership is free. 
  1432. * 
  1433. * A membership is free when... 
  1434. * ... it is explicitely marked as "free" 
  1435. * ... the price is 0.00 
  1436. * ... it is a parent membership that cannot be signed up for 
  1437. * 
  1438. * @since 1.0.0 
  1439. * @api 
  1440. * 
  1441. * @return bool 
  1442. */ 
  1443. public function is_free() { 
  1444. $result = false; 
  1445.  
  1446. if ( $this->is_free ) { 
  1447. $result = true; 
  1448. } elseif ( 0 == (int) ($this->price * 100) ) { 
  1449. $result = true; 
  1450.  
  1451. $result = apply_filters( 
  1452. 'ms_model_membership_is_free',  
  1453. $result,  
  1454. $this 
  1455. ); 
  1456.  
  1457. if ( $result && $this->is_free ) { 
  1458. $this->is_free = $result; 
  1459.  
  1460. return $result; 
  1461.  
  1462. /** 
  1463. * Returns true if this membership is eligable for trial period. 
  1464. * 
  1465. * @since 1.0.1.0 
  1466. * @return bool 
  1467. */ 
  1468. public function has_trial() { 
  1469. $result = $this->trial_period_enabled; 
  1470.  
  1471. if ( $result ) { 
  1472. if ( ! MS_Model_Addon::is_enabled( MS_Model_Addon::ADDON_TRIAL ) ) { 
  1473. $result = false; 
  1474.  
  1475. return $result; 
  1476.  
  1477. /** 
  1478. * Returns the access flag, if a specific membership can subscribe to the 
  1479. * current membership. 
  1480. * 
  1481. * A special value for $id is 'guest', which is used for all users without 
  1482. * a normal membership (is_system() type memberships are not normal) 
  1483. * 
  1484. * @since 1.0.1.0 
  1485. * @param int|string $id A membership ID or the value 'guest'. 
  1486. * @return bool True if the specified membership can subscribe. 
  1487. */ 
  1488. public function update_allowed( $id ) { 
  1489. $denied = false; 
  1490.  
  1491. if ( isset( $this->update_denied[ $id ] ) ) { 
  1492. $denied = $this->update_denied[ $id ]; 
  1493.  
  1494. return ! $denied; 
  1495.  
  1496. /** 
  1497. * Returns the update-replacement flag, which defines if the OLD membership 
  1498. * should be cancelled during subscription. 
  1499. * 
  1500. * This is used in cases where the new membership is an upgraded version of 
  1501. * the old membership and the user can only have one of both memberships. 
  1502. * 
  1503. * @since 1.0.1.0 
  1504. * @param int|string $id A membership ID. 
  1505. * @return bool True if the specified membership should be cancelled. 
  1506. */ 
  1507. public function update_replaces( $id ) { 
  1508. $deny = false; 
  1509.  
  1510. if ( isset( $this->update_replace[ $id ] ) ) { 
  1511. $deny = $this->update_replace[ $id ]; 
  1512.  
  1513. return ! ! $deny; 
  1514.  
  1515. /** 
  1516. * Checks if a specific payment gateway is allowed for the current 
  1517. * membership. 
  1518. * 
  1519. * @since 1.0.0 
  1520. * @param string $gateway_id The payment gateway ID. 
  1521. * @return bool 
  1522. */ 
  1523. public function can_use_gateway( $gateway_id ) { 
  1524. $result = true; 
  1525.  
  1526. $this->disabled_gateways = lib3()->array->get( $this->disabled_gateways ); 
  1527. if ( isset( $this->disabled_gateways[ $gateway_id ] ) ) { 
  1528. $state = $this->disabled_gateways[ $gateway_id ]; 
  1529. $result = ! lib3()->is_true( $state ); 
  1530.  
  1531. if ( $result ) { 
  1532. $gateway = MS_Model_Gateway::factory( $gateway_id ); 
  1533. $result = $gateway->payment_type_supported( $this ); 
  1534.  
  1535. $result = apply_filters( 
  1536. 'ms_model_membership_can_use_gateway',  
  1537. $result,  
  1538. $gateway_id,  
  1539. $this 
  1540. ); 
  1541. return $result; 
  1542.  
  1543. /** 
  1544. * Get protection Rule Model. 
  1545. * 
  1546. * Note for network-wide mode: 
  1547. * In DB the rules for each site are stored in different objects. 
  1548. * When loading a membership we will always load 1 instance of each 
  1549. * rule_type, and this is the instance that belongs to the current site! 
  1550. * Instances for other sites are not accessible. 
  1551. * -> This is why we do not use/need a site_id or similar in this function. 
  1552. * 
  1553. * @since 1.0.0 
  1554. * @api 
  1555. * 
  1556. * @param string $rule_type The rule model type @see MS_Rule 
  1557. * @return MS_Rule The requested rule model. 
  1558. */ 
  1559. public function get_rule( $rule_type ) { 
  1560. if ( 'attachment' === $rule_type ) { 
  1561. $rule_type = MS_Rule_Media::RULE_ID; 
  1562.  
  1563. if ( ! isset( $this->_rules[ $rule_type ] ) 
  1564. || ! is_object( $this->_rules[ $rule_type ] ) // During plugin update. 
  1565. ) { 
  1566. // Create a new rule model object. 
  1567. $rule = MS_Rule::rule_factory( 
  1568. $rule_type,  
  1569. $this->id,  
  1570. $this->subscription_id 
  1571. ); 
  1572.  
  1573. $rule = apply_filters( 
  1574. 'ms_model_membership_get_rule',  
  1575. $rule,  
  1576. $rule_type,  
  1577. $this 
  1578. ); 
  1579.  
  1580. $this->_rules[ $rule_type ] = $rule; 
  1581. if ( ! is_array( $rule->rule_value ) ) { 
  1582. $rule->rule_value = array(); 
  1583.  
  1584. return $this->_rules[ $rule_type ]; 
  1585.  
  1586. /** 
  1587. * Set protection Rule Model. 
  1588. * 
  1589. * Note for network-wide mode: 
  1590. * In DB the rules for each site are stored in different objects. 
  1591. * When loading a membership we will always load 1 instance of each 
  1592. * rule_type, and this is the instance that belongs to the current site! 
  1593. * Instances for other sites are not accessible. 
  1594. * -> This is why we do not use/need a site_id or similar in this function. 
  1595. * 
  1596. * @since 1.0.0 
  1597. * @api 
  1598. * 
  1599. * @param string The rule model type @see MS_Rule 
  1600. * @param MS_Rule $rule The protection rule to set. 
  1601. */ 
  1602. public function set_rule( $rule_type, $rule ) { 
  1603. $this->_rules[ $rule_type ] = apply_filters( 
  1604. 'ms_model_membership_set_rule',  
  1605. $rule,  
  1606. $rule_type,  
  1607. $this 
  1608. ); 
  1609.  
  1610. /** 
  1611. * Returns the unique HEX color for this membership. 
  1612. * The color is calculated from the membership-ID and therefore will never 
  1613. * change. 
  1614. * 
  1615. * @since 1.0.0 
  1616. * @api 
  1617. * 
  1618. * @return string Hex color, e.g. '#FFFFFF' 
  1619. */ 
  1620. public function get_color() { 
  1621. return apply_filters( 
  1622. 'ms_model_membership_get_color',  
  1623. MS_Helper_Utility::color_index( $this->type . $this->id ),  
  1624. $this->type,  
  1625. $this->id 
  1626. ); 
  1627.  
  1628. /** 
  1629. * Returns a HTML tag that shows the membership name with the internal 
  1630. * membership color. 
  1631. * 
  1632. * @since 1.0.0 
  1633. * @api 
  1634. * @param bool $with_tooltip Whether to add tooltip with Membership infos. 
  1635. * @return string The title HTML code. 
  1636. */ 
  1637. public function get_name_tag( $with_tooltip = false ) { 
  1638. if ( $with_tooltip ) { 
  1639. $tooltip = sprintf( 
  1640. __( 'Revision: %s | Type: %s', 'membership2' ),  
  1641. $this->revision,  
  1642. $this->type 
  1643. ); 
  1644. $tag = sprintf( 
  1645. '<span class="ms-membership" style="background:%%2$s" title="%s">%%1$s</span>',  
  1646. $tooltip 
  1647. ); 
  1648. } else { 
  1649. $tag = '<span class="ms-membership" style="background:%2$s">%1$s</span>'; 
  1650.  
  1651. $code = sprintf( 
  1652. $tag,  
  1653. esc_html( $this->name ),  
  1654. $this->get_color() 
  1655. ); 
  1656.  
  1657. return $code; 
  1658.  
  1659. /** 
  1660. * Echo a HTML tag that shows the membership name with the internal 
  1661. * membership color. 
  1662. * 
  1663. * @since 1.0.0 
  1664. * @api 
  1665. */ 
  1666. public function name_tag() { 
  1667. echo $this->get_name_tag(); 
  1668.  
  1669. /** 
  1670. * Returns the parsed membership description for display. Shortcodes are 
  1671. * replaced and the content is filtered. 
  1672. * 
  1673. * @since 1.0.1.2 
  1674. * @return string The parsed membership description. 
  1675. */ 
  1676. public function get_description() { 
  1677. $desc = apply_filters( 
  1678. 'ms_model_membership_get_description',  
  1679. $this->description,  
  1680. $this 
  1681. ); 
  1682.  
  1683. $desc = do_shortcode( wpautop( $desc ) ); 
  1684.  
  1685. return $desc; 
  1686.  
  1687. /** 
  1688. * Get current membership type description. 
  1689. * 
  1690. * @since 1.0.0 
  1691. * @api 
  1692. * 
  1693. * @return string The membership type description. 
  1694. */ 
  1695. public function get_type_description() { 
  1696. $types = self::get_types(); 
  1697. $desc = $types[ $this->type ]; 
  1698.  
  1699. return apply_filters( 
  1700. 'ms_model_membership_get_type_description',  
  1701. $desc,  
  1702. $this 
  1703. ); 
  1704.  
  1705. /** 
  1706. * Either creates or updates the value of a custom data field. 
  1707. * 
  1708. * Note: Remember to prefix the $key with a unique string to prevent 
  1709. * conflicts with other plugins that also use this function. 
  1710. * 
  1711. * @since 1.0.0 
  1712. * @api 
  1713. * 
  1714. * @param string $key The field-key. 
  1715. * @param mixed $value The new value to assign to the field. 
  1716. */ 
  1717. public function set_custom_data( $key, $value ) { 
  1718. // Wrapper function, so this function shows up in API docs. 
  1719. parent::set_custom_data( $key, $value ); 
  1720.  
  1721. /** 
  1722. * Removes a custom data field from this object. 
  1723. * 
  1724. * @since 1.0.0 
  1725. * @api 
  1726. * 
  1727. * @param string $key The field-key. 
  1728. */ 
  1729. public function delete_custom_data( $key ) { 
  1730. // Wrapper function, so this function shows up in API docs. 
  1731. parent::delete_custom_data( $key ); 
  1732.  
  1733. /** 
  1734. * Returns the value of a custom data field. 
  1735. * 
  1736. * @since 1.0.0 
  1737. * @api 
  1738. * 
  1739. * @param string $key The field-key. 
  1740. * @return mixed The value that was previously assigned to the custom field 
  1741. * or false if no value was set for the field. 
  1742. */ 
  1743. public function get_custom_data( $key ) { 
  1744. // Wrapper function, so this function shows up in API docs. 
  1745. return parent::get_custom_data( $key ); 
  1746.  
  1747. /** 
  1748. * Merge Membership2 rules. 
  1749. * 
  1750. * Merge every rule model with Membership2/visitor membership rules. 
  1751. * This ensure rules are consistent with Membership2 rules. 
  1752. * 
  1753. * @since 1.0.0 
  1754. * @internal 
  1755. */ 
  1756. public function merge_protection_rules() { 
  1757. if ( $this->is_base() ) { 
  1758. // This is the visitor membership, no need to merge anything. 
  1759. return; 
  1760.  
  1761. $base_rules = self::get_base()->_rules; 
  1762.  
  1763. foreach ( $base_rules as $key => $base_rule ) { 
  1764. try { 
  1765. // Key could be "type" of "site:type" format. 
  1766. $rule_type = MS_Rule::rule_type( $key ); 
  1767.  
  1768. $rule = $this->get_rule( $rule_type ); 
  1769. $rule->protect_undefined_items( $base_rule, true ); 
  1770. $this->set_rule( $rule_type, $rule ); 
  1771. } catch ( Exception $e ) { 
  1772. MS_Helper_Debug::log( $e ); 
  1773.  
  1774. $this->_rules = apply_filters( 
  1775. 'ms_model_membership_merge_protection_rules',  
  1776. $this->_rules,  
  1777. $this 
  1778. ); 
  1779.  
  1780. /** 
  1781. * Get after membership expired options. 
  1782. * 
  1783. * Memberships can be downgraded to the guest level protection. 
  1784. * 
  1785. * @since 1.0.0 
  1786. * @api 
  1787. * 
  1788. * @return array { 
  1789. * Returns array of $membership_id => $description. 
  1790. * @type int $membership_id The membership Id. 
  1791. * @type string $description The expired option description. 
  1792. * } 
  1793. */ 
  1794. public function get_after_ms_ends_options() { 
  1795. $options = array( 
  1796. 0 => __( 'Restrict access to Visitor-Level', 'membership2' ),  
  1797. ); 
  1798.  
  1799. $args = array( 
  1800. 'include_guest' => false,  
  1801. ); 
  1802. $options += $this->get_membership_names( $args ); 
  1803. unset( $options[ $this->id ] ); 
  1804.  
  1805. $label = __( 'Change to: %s', 'membership2' ); 
  1806. foreach ( $options as $id => $option ) { 
  1807. if ( $id > 0 ) { 
  1808. $options[ $id ] = sprintf( $label, $option ); 
  1809.  
  1810. return apply_filters( 
  1811. 'ms_model_membership_get_membership_names',  
  1812. $options,  
  1813. $this 
  1814. ); 
  1815.  
  1816. /** 
  1817. * Get a list of all subscriptions to this membership. 
  1818. * 
  1819. * Note that this function will also return expired/cancelled subscriptions. 
  1820. * 
  1821. * @since 1.0.1.0 
  1822. * @api 
  1823. * 
  1824. * @return array All subscriptions. 
  1825. */ 
  1826. public function get_subscriptions() { 
  1827. $subscriptions = MS_Model_Relationship::get_subscriptions( 
  1828. array( 'membership_id' => $this->id ) 
  1829. ); 
  1830.  
  1831. return apply_filters( 
  1832. 'ms_model_membership_get_subscriptions',  
  1833. $subscriptions 
  1834. ); 
  1835.  
  1836. /** 
  1837. * Get members count of this membership. 
  1838. * 
  1839. * This will also count members that have "cancelled" or "expired" 
  1840. * subscriptions but not "pending" or "deactivated". 
  1841. * 
  1842. * To change this use the filter parameter: 
  1843. * $args = array( 'status' => 'all' ) 
  1844. * 
  1845. * @since 1.0.0 
  1846. * @api 
  1847. * 
  1848. * @param array $args The query post args 
  1849. * @return int The members count. 
  1850. */ 
  1851. public function get_members_count( $args = null ) { 
  1852. $args = wp_parse_args( 
  1853. array( 'membership_id' => $this->id ),  
  1854. $args 
  1855. ); 
  1856.  
  1857. $count = MS_Model_Relationship::get_subscription_count( $args ); 
  1858.  
  1859. return apply_filters( 
  1860. 'ms_model_membership_get_members_count',  
  1861. $count,  
  1862. $args,  
  1863. $this 
  1864. ); 
  1865.  
  1866. /** 
  1867. * Get members list of this membership. 
  1868. * 
  1869. * This will also count members that have "cancelled" or "expired" 
  1870. * subscriptions but not "pending" or "deactivated". 
  1871. * 
  1872. * To change this use the filter parameter: 
  1873. * $args = array( 'status' => 'all' ) 
  1874. * 
  1875. * @since 1.0.1.0 
  1876. * @api 
  1877. * 
  1878. * @param array $args The query post args 
  1879. * @return array List of members. 
  1880. */ 
  1881. public function get_members( $args = null ) { 
  1882. $args = wp_parse_args( 
  1883. array( 'membership_id' => $this->id ),  
  1884. $args 
  1885. ); 
  1886.  
  1887. // Get a list of subscriptions. 
  1888. $items = MS_Model_Relationship::get_subscriptions( $args ); 
  1889.  
  1890. // Get a list of members. 
  1891. $result = array(); 
  1892. foreach ( $items as $item ) { 
  1893. $result[ $item->user_id ] = $item->get_member(); 
  1894.  
  1895. return apply_filters( 
  1896. 'ms_model_membership_get_members',  
  1897. $result,  
  1898. $args,  
  1899. $this 
  1900. ); 
  1901.  
  1902. /** 
  1903. * Return membership has dripped content. 
  1904. * 
  1905. * Verify post and page rules if there is a dripped content. 
  1906. * 
  1907. * @since 1.0.0 
  1908. * @api 
  1909. * 
  1910. * @return boolean 
  1911. */ 
  1912. public function has_dripped_content() { 
  1913. $has_dripped = false; 
  1914. $dripped = array( 'post', 'page' ); 
  1915.  
  1916. foreach ( $dripped as $rule_type ) { 
  1917. // using count() as !empty() never returned true 
  1918. if ( 0 < count( $this->get_rule( $rule_type )->dripped ) ) { 
  1919. $has_dripped = true; 
  1920.  
  1921. return apply_filters( 
  1922. 'ms_model_membership_has_dripped_content',  
  1923. $has_dripped,  
  1924. $this 
  1925. ); 
  1926.  
  1927. /** 
  1928. * Get protection rules sorted. 
  1929. * 
  1930. * First one has priority over the last one. 
  1931. * These rules are used to determine access. 
  1932. * 
  1933. * @since 1.0.0 
  1934. * @internal 
  1935. */ 
  1936. private function get_rules_hierarchy() { 
  1937. $rule_types = MS_Model_Rule::get_rule_types(); 
  1938. $rules = array(); 
  1939. $subscription = MS_Factory::load( 'MS_Model_Relationship', $this->subscription_id ); 
  1940.  
  1941. foreach ( $rule_types as $rule_type ) { 
  1942. $rule = $this->get_rule( $rule_type ); 
  1943.  
  1944. if ( $rule->rule_type != $rule_type ) { 
  1945. // This means that the $rule_type was not found... 
  1946. continue; 
  1947.  
  1948. // Sometimes the $subscription->id can be 0, which is intentional: 
  1949. // This is the case when the membership was auto-assigned to guest 
  1950. // or default membership. 
  1951. $rule->_subscription_id = $subscription->id; 
  1952.  
  1953. $rule->membership_id = $this->id; 
  1954. $rules[ $rule_type ] = $rule; 
  1955.  
  1956. return apply_filters( 
  1957. 'ms_model_membership_get_rules_hierarchy',  
  1958. $rules,  
  1959. $this 
  1960. ); 
  1961.  
  1962. /** 
  1963. * Mark membership setup as completed. 
  1964. * 
  1965. * Only purpose of this flag is to display the correct update message to the 
  1966. * user: If setup_completed() returns true, then "Membership added" is 
  1967. * displayed, otherwise "Membership updated" 
  1968. * 
  1969. * @since 1.0.0 
  1970. * @internal 
  1971. * 
  1972. * @return bool $marked True in the first time setup is finished. 
  1973. */ 
  1974. public function setup_completed() { 
  1975. $marked = false; 
  1976.  
  1977. if ( ! $this->is_setup_completed ) { 
  1978. $this->is_setup_completed = true; 
  1979. $marked = true; 
  1980.  
  1981. return apply_filters( 
  1982. 'ms_model_membership_setup_completed',  
  1983. $marked,  
  1984. $this 
  1985. ); 
  1986.  
  1987. /** 
  1988. * Returns true if the membership the base membership. 
  1989. * 
  1990. * @since 1.0.0 
  1991. * @see description of MS_Model_Membership::get_base() 
  1992. * @api 
  1993. * 
  1994. * @return bool 
  1995. */ 
  1996. public function is_base( $type = null ) { 
  1997. if ( ! $type ) { $type = $this->type; } 
  1998. $res = (self::TYPE_BASE == $type); 
  1999.  
  2000. return apply_filters( 
  2001. 'ms_model_membership_is_base',  
  2002. $res,  
  2003. $type 
  2004. ); 
  2005.  
  2006. /** 
  2007. * Returns true if the membership the guest membership. 
  2008. * 
  2009. * @since 1.0.0 
  2010. * @see description of MS_Model_Membership::get_guest() 
  2011. * @api 
  2012. * 
  2013. * @return bool 
  2014. */ 
  2015. public function is_guest( $type = null ) { 
  2016. if ( ! $type ) { $type = $this->type; } 
  2017. $res = (self::TYPE_GUEST == $type); 
  2018.  
  2019. return apply_filters( 
  2020. 'ms_model_membership_is_guest',  
  2021. $res,  
  2022. $type 
  2023. ); 
  2024.  
  2025. /** 
  2026. * Returns true if the membership the user membership. 
  2027. * 
  2028. * @since 1.0.0 
  2029. * @see description of MS_Model_Membership::get_user() 
  2030. * @api 
  2031. * 
  2032. * @return bool 
  2033. */ 
  2034. public function is_user( $type = null ) { 
  2035. if ( ! $type ) { $type = $this->type; } 
  2036. $res = (self::TYPE_USER == $type); 
  2037.  
  2038. return apply_filters( 
  2039. 'ms_model_membership_is_user',  
  2040. $res,  
  2041. $type 
  2042. ); 
  2043.  
  2044. /** 
  2045. * Returns true if the membership a dripped membership. 
  2046. * 
  2047. * @since 1.0.0 
  2048. * @api 
  2049. * 
  2050. * @return bool 
  2051. */ 
  2052. public function is_dripped( $type = null ) { 
  2053. if ( ! $type ) { $type = $this->type; } 
  2054. $res = (self::TYPE_DRIPPED == $type); 
  2055.  
  2056. return apply_filters( 
  2057. 'ms_model_membership_is_dripped',  
  2058. $res,  
  2059. $type 
  2060. ); 
  2061.  
  2062. /** 
  2063. * Returns true if the membership the base or guest/user membership. 
  2064. * 
  2065. * @since 1.0.0 
  2066. * @api 
  2067. * 
  2068. * @return bool 
  2069. */ 
  2070. public function is_system( $type = null ) { 
  2071. if ( ! $type ) { $type = $this->type; } 
  2072.  
  2073. $res = false; 
  2074. if ( $this->is_base( $type ) ) { 
  2075. $res = true; 
  2076. } elseif ( $this->is_guest( $type ) ) { 
  2077. $res = true; 
  2078. } elseif ( $this->is_user( $type ) ) { 
  2079. $res = true; 
  2080.  
  2081. return apply_filters( 
  2082. 'ms_model_membership_is_system',  
  2083. $res,  
  2084. $type 
  2085. ); 
  2086.  
  2087. /** 
  2088. * Can be used to validate if the current membership is actually loaded 
  2089. * from database. If this function returns false, then the specified 
  2090. * membership-ID does not exist in DB. 
  2091. * 
  2092. * @since 1.0.0 
  2093. * @api 
  2094. * 
  2095. * @return bool 
  2096. */ 
  2097. public function is_valid() { 
  2098. $res = ! empty( $this->id ); 
  2099.  
  2100. return apply_filters( 
  2101. 'ms_model_membership_is_valid',  
  2102. $res,  
  2103. $this 
  2104. ); 
  2105.  
  2106. /** 
  2107. * Verify access to current page. 
  2108. * 
  2109. * Verify membership rules hierarchy for content accessed directly. 
  2110. * If 'has access' is found, it does have access. 
  2111. * Only for active memberships. 
  2112. * 
  2113. * @since 1.0.0 
  2114. * @api 
  2115. * 
  2116. * @param int $post_id 
  2117. * @return bool|null True if has access to current page. Default is false. 
  2118. * Null means: Rule not relevant for current page. 
  2119. */ 
  2120. public function has_access_to_current_page( $post_id = null ) { 
  2121. $has_access = null; 
  2122. $this->_access_reason = array(); 
  2123. $this->_deny_rule = array(); 
  2124. $this->_allow_rule = array(); 
  2125.  
  2126. // Only verify access if membership is Active. 
  2127. if ( $this->active ) { 
  2128.  
  2129. // If 'has access' is found in the hierarchy, it does have access. 
  2130. $rules = $this->get_rules_hierarchy(); 
  2131. foreach ( $rules as $rule ) { 
  2132. $rule_access = $rule->has_access( $post_id ); 
  2133.  
  2134. if ( null === $rule_access ) { 
  2135. $this->_access_reason[] = sprintf( 
  2136. __( 'Ignored: Rule "%s"', 'membership2' ),  
  2137. $rule->rule_type 
  2138. ); 
  2139. continue; 
  2140.  
  2141. $this->_access_reason[] = sprintf( 
  2142. __( '%s: Rule "%s"', 'membership2' ),  
  2143. $rule_access ? __( 'Allow', 'membership2' ) : __( 'Deny', 'membership2' ),  
  2144. $rule->rule_type 
  2145. ); 
  2146.  
  2147. if ( ! $rule_access ) { 
  2148. $this->_deny_rule[] = $rule->rule_type; 
  2149. } else { 
  2150. $this->_allow_rule[] = $rule->rule_type; 
  2151.  
  2152. // URL groups have final decission. 
  2153. if ( MS_Rule_Url::RULE_ID === $rule->rule_type ) { 
  2154. $has_access = $rule_access; 
  2155. break; 
  2156.  
  2157. // Special pages have final decission after URL groups. 
  2158. if ( MS_Rule_Special::RULE_ID === $rule->rule_type ) { 
  2159. $has_access = $rule_access; 
  2160. $this->_access_reason[] = $rule->matched_type; 
  2161. break; 
  2162.  
  2163. $has_access = ( $has_access || $rule_access ); 
  2164.  
  2165. if ( true === $has_access ) { 
  2166. break; 
  2167.  
  2168. return apply_filters( 
  2169. 'ms_model_membership_has_access_to_current_page',  
  2170. $has_access,  
  2171. $post_id,  
  2172. $this 
  2173. ); 
  2174.  
  2175. /** 
  2176. * Verify access to post. 
  2177. * 
  2178. * Verify membership rules hierarchy for specific post or CPT. 
  2179. * 
  2180. * @since 1.0.0 
  2181. * @api 
  2182. * 
  2183. * @param int $post_id ID of specific post 
  2184. * @return boolean True if has access to current page. Default is false. 
  2185. */ 
  2186. public function has_access_to_post( $post_id ) { 
  2187. $has_access = null; 
  2188.  
  2189. if ( MS_Model_Member::is_normal_admin() ) { 
  2190. return true; 
  2191.  
  2192. if ( ! empty( $post_id ) ) { 
  2193. $post = get_post( $post_id ); 
  2194. if ( 'attachment' === $post->post_type ) { 
  2195. $post_id = get_post_field( 'post_parent', $post_id ); 
  2196.  
  2197. // If 'has access' is found in the hierarchy, it does have access. 
  2198. $rules = $this->get_rules_hierarchy(); 
  2199. foreach ( $rules as $rule ) { 
  2200. $rule->prepare_rule( $subscription ); 
  2201.  
  2202. // url groups have final decision 
  2203. if ( MS_Rule_Url::RULE_ID == $rule->rule_type 
  2204. && $rule->has_rule_for_post( $post_id ) 
  2205. ) { 
  2206. $has_access = $rule->has_access( $post_id ); 
  2207. break; 
  2208. } else { 
  2209. $rule_access = $rule->has_access( $post_id ); 
  2210. if ( null !== $rule_access ) { 
  2211. $has_access = $rule_access; 
  2212.  
  2213. if ( $has_access ) { 
  2214. break; 
  2215.  
  2216. if ( null === $has_access ) { 
  2217. // The post is not denied by any rule, so allow access. 
  2218. $has_access = true; 
  2219.  
  2220. return apply_filters( 
  2221. 'ms_model_membership_has_access_to_post',  
  2222. $has_access,  
  2223. $this 
  2224. ); 
  2225.  
  2226. /** 
  2227. * Set up the membership. This is always done, regardless if the user is 
  2228. * a normal user or an Admin user. 
  2229. * 
  2230. * @since 1.0.0 
  2231. * @internal 
  2232. * 
  2233. * @param MS_Model_Relationship $subscription The membership relationship. 
  2234. */ 
  2235. public function initialize( $subscription ) { 
  2236. do_action( 
  2237. 'ms_model_membership_initialize_before',  
  2238. $subscription,  
  2239. $this 
  2240. ); 
  2241.  
  2242. $this->subscription_id = $subscription->id; 
  2243. $rules = $this->get_rules_hierarchy(); 
  2244.  
  2245. // Apply protection settings of all rules (replace/hide contents, ...) 
  2246. foreach ( $rules as $rule ) { 
  2247. $rule->prepare_rule( $subscription ); 
  2248.  
  2249. do_action( 
  2250. 'ms_model_membership_initialize_after',  
  2251. $subscription,  
  2252. $this 
  2253. ); 
  2254.  
  2255. /** 
  2256. * Set initial protection for front-end. 
  2257. * This function is only executed when the current user is no Admin user. 
  2258. * 
  2259. * Hide restricted content for this membership. 
  2260. * 
  2261. * @since 1.0.0 
  2262. * @internal 
  2263. */ 
  2264. public function protect_content() { 
  2265. do_action( 
  2266. 'ms_model_membership_protect_content_before',  
  2267. $this 
  2268. ); 
  2269.  
  2270. $rules = $this->get_rules_hierarchy(); 
  2271.  
  2272. // Apply protection settings of all rules (replace/hide contents, ...) 
  2273. foreach ( $rules as $rule ) { 
  2274. $rule->protect_content(); 
  2275.  
  2276. do_action( 
  2277. 'ms_model_membership_protect_content_after',  
  2278. $this 
  2279. ); 
  2280.  
  2281. /** 
  2282. * Check protection for front-end. 
  2283. * 
  2284. * Return if content is restricted for this membership. 
  2285. * 
  2286. * @since 1.0.0 
  2287. * @internal 
  2288. */ 
  2289. public function has_access_to_content( $id ) { 
  2290. $rules = $this->get_rules_hierarchy(); 
  2291. return $rules['content']->get_rule_value($id); 
  2292. }  
  2293.  
  2294. /** 
  2295. * Set initial protection for admin side. 
  2296. * 
  2297. * Hide restricted content for this membership. 
  2298. * 
  2299. * @since 1.0.0 
  2300. * @internal 
  2301. */ 
  2302. public function protect_admin_content() { 
  2303. do_action( 
  2304. 'ms_model_membership_protect_content_before',  
  2305. $this 
  2306. ); 
  2307.  
  2308. $rules = $this->get_rules_hierarchy(); 
  2309.  
  2310. foreach ( $rules as $rule ) { 
  2311. $rule->protect_admin_content(); 
  2312.  
  2313. do_action( 
  2314. 'ms_model_membership_protect_content_after',  
  2315. $this 
  2316. ); 
  2317.  
  2318. /** 
  2319. * Checks if the user is allowed to change the payment details for the 
  2320. * current membership. 
  2321. * 
  2322. * Payment details can only be changed when 
  2323. * (A) no payment details were saved yet - OR - 
  2324. * (B) no members signed up for the memberships 
  2325. * 
  2326. * @since 1.0.0 
  2327. * @api 
  2328. * 
  2329. * @return bool 
  2330. */ 
  2331. public function can_change_payment() { 
  2332. // Allow if Membership is new/unsaved. 
  2333. if ( empty( $this->id ) ) { return true; } 
  2334.  
  2335. // Allow if no payment detail was entered yet (incomplete setup). 
  2336. if ( empty( $this->payment_type ) ) { return true; } 
  2337.  
  2338. // Allow if no members signed up yet. 
  2339. $members = MS_Model_Relationship::get_subscription_count( 
  2340. array( 'membership_id' => $this->id ) 
  2341. ); 
  2342. if ( empty( $members ) ) { return true; } 
  2343.  
  2344. // Otherwise payment details cannot be changed anymore. 
  2345. return false; 
  2346.  
  2347. /** 
  2348. * Returns property associated with the render. 
  2349. * 
  2350. * @since 1.0.0 
  2351. * @internal 
  2352. * 
  2353. * @param string $property The name of a property. 
  2354. * @return mixed Returns mixed value of a property or NULL if a property doesn't exist. 
  2355. */ 
  2356. public function __get( $property ) { 
  2357. $value = null; 
  2358.  
  2359. switch ( $property ) { 
  2360. case 'type': 
  2361. if ( ! self::is_valid_type( $this->type ) ) { 
  2362. $this->type = self::TYPE_STANDARD; 
  2363.  
  2364. $value = $this->type; 
  2365. break; 
  2366.  
  2367. case 'payment_type': 
  2368. $types = self::get_payment_types(); 
  2369. if ( ! array_key_exists( $this->payment_type, $types ) ) { 
  2370. $this->payment_type = self::PAYMENT_TYPE_PERMANENT; 
  2371. $value = $this->payment_type; 
  2372. break; 
  2373.  
  2374. case 'trial_period_enabled': 
  2375. case 'active': 
  2376. case 'private': 
  2377. case 'is_free': 
  2378. $value = lib3()->is_true( $this->$property ); 
  2379. break; 
  2380.  
  2381. case 'type_description': 
  2382. $value = $this->get_type_description(); 
  2383. break; 
  2384.  
  2385. case 'period_unit': 
  2386. $value = MS_Helper_Period::get_period_value( $this->period, 'period_unit' ); 
  2387. break; 
  2388.  
  2389. case 'period_type': 
  2390. $value = MS_Helper_Period::get_period_value( $this->period, 'period_type' ); 
  2391. break; 
  2392.  
  2393. case 'pay_cycle_period_unit': 
  2394. $value = MS_Helper_Period::get_period_value( $this->pay_cycle_period, 'period_unit' ); 
  2395. break; 
  2396.  
  2397. case 'pay_cycle_period_type': 
  2398. $value = MS_Helper_Period::get_period_value( $this->pay_cycle_period, 'period_type' ); 
  2399. break; 
  2400.  
  2401. case 'trial_period_unit': 
  2402. $value = MS_Helper_Period::get_period_value( $this->trial_period, 'period_unit' ); 
  2403. break; 
  2404.  
  2405. case 'trial_period_type': 
  2406. $value = MS_Helper_Period::get_period_value( $this->trial_period, 'period_type' ); 
  2407. break; 
  2408.  
  2409. case 'price': 
  2410. if ( $this->is_free() ) { 
  2411. $value = 0; 
  2412. } else { 
  2413. $value = $this->price; 
  2414. break; 
  2415.  
  2416. case 'total_price': 
  2417. if ( $this->is_free() ) { 
  2418. $value = 0; 
  2419. } else { 
  2420. $value = $this->price; 
  2421.  
  2422. $value = apply_filters( 
  2423. 'ms_apply_taxes',  
  2424. $value,  
  2425. $this 
  2426. ); 
  2427. break; 
  2428.  
  2429. case 'pay_cycle_repetitions': 
  2430. $value = absint( $this->pay_cycle_repetitions ); 
  2431. break; 
  2432.  
  2433. case 'disabled_gateways': 
  2434. $value = lib3()->array->get( $this->disabled_gateways ); 
  2435. break; 
  2436.  
  2437. case 'is_paid': 
  2438. $value = ! $this->is_free; 
  2439. break; 
  2440.  
  2441. case 'public': 
  2442. $value = ! $this->private; 
  2443. break; 
  2444.  
  2445. default: 
  2446. if ( property_exists( $this, $property ) ) { 
  2447. $value = $this->$property; 
  2448. break; 
  2449.  
  2450. return apply_filters( 
  2451. 'ms_model_membership__get',  
  2452. $value,  
  2453. $property,  
  2454. $this 
  2455. ); 
  2456.  
  2457. /** 
  2458. * Validate specific property before set. 
  2459. * 
  2460. * @since 1.0.0 
  2461. * @internal 
  2462. * 
  2463. * @param string $property The name of a property to associate. 
  2464. * @param mixed $value The value of a property. 
  2465. */ 
  2466. public function __set( $property, $value ) { 
  2467. if ( property_exists( $this, $property ) ) { 
  2468. switch ( $property ) { 
  2469. case 'name': 
  2470. case 'title': 
  2471. $this->$property = sanitize_text_field( $value ); 
  2472. break; 
  2473.  
  2474. case 'description': 
  2475. $this->$property = wp_kses( $value, 'post' ); 
  2476. break; 
  2477.  
  2478. case 'type': 
  2479. if ( ! self::is_valid_type( $value ) ) { 
  2480. $value = self::TYPE_STANDARD; 
  2481.  
  2482. if ( $this->is_system( $value ) ) { 
  2483. // Only one instance of these types can exist. 
  2484. $existing = $this->_get_system_membership( $value, false ); 
  2485.  
  2486. if ( $existing && $existing->id != $this->id ) { 
  2487. $value = self::TYPE_STANDARD; 
  2488. } else { 
  2489. $this->active = true; 
  2490. $this->private = true; 
  2491. $this->is_free = true; 
  2492. $this->price = 0; 
  2493. $this->post_name = sanitize_html_class( $this->title ); 
  2494. $this->payment_type = self::PAYMENT_TYPE_PERMANENT; 
  2495. $this->post_author = get_current_user_id(); 
  2496.  
  2497. $this->type = $value; 
  2498. break; 
  2499.  
  2500. case 'payment_type': 
  2501. $types = self::get_payment_types(); 
  2502. if ( array_key_exists( $value, $types ) ) { 
  2503. $this->payment_type = $value; 
  2504. } else { 
  2505. throw new Exception( 'Invalid membership type.' ); 
  2506. break; 
  2507.  
  2508. case 'trial_period_enabled': 
  2509. case 'active': 
  2510. case 'private': 
  2511. case 'is_free': 
  2512. $this->$property = lib3()->is_true( $value ); 
  2513. break; 
  2514.  
  2515. case 'price': 
  2516. case 'trial_price': 
  2517. $this->$property = floatval( $value ); 
  2518. break; 
  2519.  
  2520. case 'pay_cycle_repetitions': 
  2521. $this->$property = absint( $value ); 
  2522. break; 
  2523.  
  2524. case 'period': 
  2525. case 'pay_cycle_period': 
  2526. case 'trial_period': 
  2527. $this->$property = $this->validate_period( $value ); 
  2528. break; 
  2529.  
  2530. case 'period_date_start': 
  2531. case 'period_date_end': 
  2532. $this->$property = $this->validate_date( $value ); 
  2533. break; 
  2534.  
  2535. case 'on_end_membership_id': 
  2536. if ( 0 == $value ) { 
  2537. $this->$property = 0; 
  2538. } else if ( 0 < MS_Factory::load( 'MS_Model_Membership', $value )->id ) { 
  2539. $this->$property = $value; 
  2540. break; 
  2541.  
  2542. default: 
  2543. $this->$property = $value; 
  2544. break; 
  2545. } else { 
  2546. switch ( $property ) { 
  2547. case 'period_unit': 
  2548. $this->period['period_unit'] = $this->validate_period_unit( $value ); 
  2549. break; 
  2550.  
  2551. case 'period_type': 
  2552. $this->period['period_type'] = $this->validate_period_type( $value ); 
  2553. break; 
  2554.  
  2555. case 'pay_cycle_period_unit': 
  2556. $this->pay_cycle_period['period_unit'] = $this->validate_period_unit( $value ); 
  2557. break; 
  2558.  
  2559. case 'pay_cycle_period_type': 
  2560. $this->pay_cycle_period['period_type'] = $this->validate_period_type( $value ); 
  2561. break; 
  2562.  
  2563. case 'trial_period_unit': 
  2564. $this->trial_period['period_unit'] = $this->validate_period_unit( $value ); 
  2565. break; 
  2566.  
  2567. case 'trial_period_type': 
  2568. $this->trial_period['period_type'] = $this->validate_period_type( $value ); 
  2569. break; 
  2570.  
  2571. case 'public': 
  2572. $this->private = ! lib3()->is_true( $value ); 
  2573. break; 
  2574.  
  2575. case 'is_paid': 
  2576. $this->is_free = ! lib3()->is_true( $value ); 
  2577. break; 
  2578.  
  2579. case 'deny_update': 
  2580. foreach ( $value as $key => $state ) { 
  2581. $this->update_denied[ $key ] = lib3()->is_true( $state ); 
  2582. break; 
  2583.  
  2584. case 'replace_update': 
  2585. foreach ( $value as $key => $state ) { 
  2586. $this->update_replace[ $key ] = lib3()->is_true( $state ); 
  2587. break; 
  2588.  
  2589.  
  2590. do_action( 
  2591. 'ms_model_membership__set_after',  
  2592. $property,  
  2593. $value,  
  2594. $this 
  2595. ); 
  2596.  
  2597. /** 
  2598. * Check if property isset. 
  2599. * 
  2600. * @since 1.0.0 
  2601. * @internal 
  2602. * 
  2603. * @param string $property The name of a property. 
  2604. * @return mixed Returns true/false. 
  2605. */ 
  2606. public function __isset( $property ) { 
  2607. return isset($this->$property); 
  2608. }  
.