MS_Model_Membership

Membership model.

Defined (1)

The class is defined in the following location(s).

/app/model/class-ms-model-membership.php  
  1. class MS_Model_Membership extends MS_Model_CustomPostType { 
  2.  
  3. /** 
  4. * Model custom post type. 
  5. * @since 1.0.0 
  6. * @internal self::get_post_type() to get this value! 
  7. * @var string $POST_TYPE 
  8. */ 
  9. protected static $POST_TYPE = 'ms_membership'; 
  10.  
  11. /** 
  12. * Membership type constant. 
  13. * @since 1.0.0 
  14. * @see $type $type property. 
  15. */ 
  16. const TYPE_STANDARD = 'simple'; 
  17.  
  18. /** 
  19. * Membership type constant. 
  20. * @since 1.0.0 
  21. * @see $type $type property. 
  22. */ 
  23. const TYPE_DRIPPED = 'dripped'; 
  24.  
  25. /** 
  26. * Membership type constant. 
  27. * System membership, hidden, created automatically. 
  28. * @since 1.1.0 
  29. * @see $type $type property. 
  30. */ 
  31. const TYPE_BASE = 'base'; 
  32.  
  33. /** 
  34. * Membership type constant. 
  35. * Guest membership, only one membership possible. 
  36. * @since 1.1.0 
  37. * @see $type $type property. 
  38. */ 
  39. const TYPE_GUEST = 'guest'; 
  40.  
  41. /** 
  42. * Membership type constant. 
  43. * User membership, only one membership possible. 
  44. * @since 1.1.0 
  45. * @see $type $type property. 
  46. */ 
  47. const TYPE_USER = 'user'; 
  48.  
  49. /** 
  50. * Membership payment type constants. 
  51. * @since 1.0.0 
  52. * @see $payment_type $payment_type property. 
  53. */ 
  54. const PAYMENT_TYPE_PERMANENT = 'permanent'; 
  55.  
  56. /** 
  57. * Membership payment type constants. 
  58. * @since 1.0.0 
  59. * @see $payment_type $payment_type property. 
  60. */ 
  61. const PAYMENT_TYPE_FINITE = 'finite'; 
  62.  
  63. /** 
  64. * Membership payment type constants. 
  65. * @since 1.0.0 
  66. * @see $payment_type $payment_type property. 
  67. */ 
  68. const PAYMENT_TYPE_DATE_RANGE = 'date-range'; 
  69.  
  70. /** 
  71. * Membership payment type constants. 
  72. * The only type that auto-renews without asking the user! 
  73. * @since 1.0.0 
  74. * @see $payment_type $payment_type property. 
  75. */ 
  76. const PAYMENT_TYPE_RECURRING = 'recurring'; 
  77.  
  78. /** 
  79. * Membership type. 
  80. * Default is TYPE_STANDARD. 
  81. * @since 1.0.0 
  82. * @var string $type 
  83. */ 
  84. protected $type = self::TYPE_STANDARD; 
  85.  
  86. /** 
  87. * Membership payment type. 
  88. * Default is PAYMENT_TYPE_PERMANENT. 
  89. * @since 1.0.0 
  90. * @var string $payment_type 
  91. */ 
  92. protected $payment_type = self::PAYMENT_TYPE_PERMANENT; 
  93.  
  94. /** 
  95. * Membership active status. 
  96. * By default a new membership is active. 
  97. * @since 1.0.0 
  98. * @var bool $active 
  99. */ 
  100. protected $active = true; 
  101.  
  102. /** 
  103. * Membership private status. 
  104. * Private means that the membership will not be displayed on the default 
  105. * registration page and membership list. 
  106. * Users can still stubscribe to the membership via the shortcode 
  107. * [ms-membership-buy] or by otherwise reaching the subscription URL. 
  108. * @since 1.0.0 
  109. * @var bool $private 
  110. */ 
  111. protected $private = false; 
  112.  
  113. /** 
  114. * Membership free status. 
  115. * @since 1.0.0 
  116. * @var bool $free. 
  117. */ 
  118. protected $is_free = false; 
  119.  
  120. /** 
  121. * Membership price. 
  122. * @since 1.0.0 
  123. * @var float $price. 
  124. */ 
  125. protected $price = 0; 
  126.  
  127. /** 
  128. * A list of disabled gateways. 
  129. * @since 2.0.0 
  130. * @var array $disabled_gateways. 
  131. */ 
  132. protected $disabled_gateways = array(); 
  133.  
  134. /** 
  135. * Membership period for finite access. 
  136. * Used for payment_type PAYMENT_TYPE_FINITE. 
  137. * @since 1.0.0 
  138. * @see $payment_type $payment_type property. 
  139. * @var array $period { 
  140. * @type int $period_unit The period of time quantity. 
  141. * @type string $period_type The period type (days, weeks, months, years). 
  142. * } 
  143. */ 
  144. protected $period = array( 'period_unit' => 1, 'period_type' => 'days' ); 
  145.  
  146. /** 
  147. * Membership payment recurring period cycle. 
  148. * Used for the payment_type PAYMENT_TYPE_RECURRING. 
  149. * @since 1.0.0 
  150. * @see $payment_type $payment_type property. 
  151. * @var array $pay_cycle_period {@see $period $period property}. 
  152. */ 
  153. protected $pay_cycle_period = array( 'period_unit' => 1, 'period_type' => 'days' ); 
  154.  
  155. /** 
  156. * Defines how many payments are made before the membership ends. 
  157. * Used for the payment_type PAYMENT_TYPE_RECURRING. 
  158. * @since 1.1.0 
  159. * @see $payment_type $payment_type property. 
  160. * @var int 
  161. */ 
  162. protected $pay_cycle_repetitions = 0; 
  163.  
  164. /** 
  165. * Membership start date for date range payment type. 
  166. * Used for the payment_type PAYMENT_TYPE_DATE_RANGE. 
  167. * @since 1.0.0 
  168. * @see $payment_type $payment_type property. 
  169. * @var string The membership start date. 
  170. */ 
  171. protected $period_date_start = ''; 
  172.  
  173. /** 
  174. * Membership end date for date range payment type. 
  175. * Used for the payment_type PAYMENT_TYPE_DATE_RANGE. 
  176. * @since 1.0.0 
  177. * @see $payment_type $payment_type property. 
  178. * @var string The membership end date. 
  179. */ 
  180. protected $period_date_end = ''; 
  181.  
  182. /** 
  183. * Membership trial period enabled indicator. 
  184. * Requires the Trial Period Add-on to work. 
  185. * @since 1.0.0 
  186. * @var bool $trial_period_enabled. 
  187. */ 
  188. protected $trial_period_enabled = false; 
  189.  
  190. /** 
  191. * Membership trial price value. 
  192. * Requires the Trial Period Add-on to work. 
  193. * @since 1.0.0 
  194. * @internal This property has no effect yet. 
  195. * @var float $trial_price. 
  196. */ 
  197. protected $trial_price = 0; 
  198.  
  199. /** 
  200. * Membership trial period. 
  201. * @since 1.0.0 
  202. * @var array $trial_period {@see $period $period property}. 
  203. */ 
  204. protected $trial_period = array( 'period_unit' => 1, 'period_type' => 'days' ); 
  205.  
  206. /** 
  207. * Move to Membership when the current one expires. 
  208. * After current membership expire move to the indicated membership_id. 
  209. * This membership is assigned when the current membership expires. 
  210. * @see MS_Model_Relationship::check_membership_status() 
  211. * @since 1.0.0 
  212. * @var int $on_end_membership_id. 
  213. */ 
  214. protected $on_end_membership_id = 0; 
  215.  
  216. /** 
  217. * Membership setup completed flag. 
  218. * We need this to determine if payment options of the membership are edited 
  219. * the first time during the setup assistant, or later via the membership 
  220. * list. 
  221. * @since 1.0.0 
  222. * @internal 
  223. * @var bool $is_setup_completed. 
  224. */ 
  225. protected $is_setup_completed = false; 
  226.  
  227. /** 
  228. * Where the data came from. Can only be changed by data import tool. 
  229. * @since 1.1.0 
  230. * @internal 
  231. * @var string 
  232. */ 
  233. protected $source = ''; 
  234.  
  235. /** 
  236. * Membership composite Rules. 
  237. * These are protection rules for this membership only. 
  238. * Network-wide mode: The rules stored in here are the rules that apply 
  239. * to the currently selected site in the network! 
  240. * Example: 
  241. * When the network has 10 sites then $rule_values will have 10 "page" rules 
  242. * which are stored as "1:page", "2:page", ... 
  243. * However, the $_rules property will only have ONE "page" rule, and that's 
  244. * the one for the currently visible site! 
  245. * @since 1.0.0 
  246. * @internal 
  247. * @var array MS_Rule[]. 
  248. */ 
  249. protected $_rules = array(); 
  250.  
  251. /** 
  252. * Only used for serialization of the membership. 
  253. * @see __sleep() 
  254. * @since 1.1.0 
  255. * @internal 
  256. * @var array 
  257. */ 
  258. protected $rule_values = array(); 
  259.  
  260. /** 
  261. * Used in simulation mode explaining why a page is allowed or denied. 
  262. * @since 1.0.1 
  263. * @internal 
  264. * @var array 
  265. */ 
  266. public $_access_reason = array(); 
  267.  
  268. /** 
  269. * Similar to $_access_reason, but only contains the rules that denied page 
  270. * access. 
  271. * @since 1.1.0 
  272. * @internal 
  273. * @var array 
  274. */ 
  275. public $_deny_rule = array(); 
  276.  
  277. /** 
  278. * Similar to $_access_reason, but only contains the rules that allowed page 
  279. * access. 
  280. * @since 1.1.0 
  281. * @internal 
  282. * @var array 
  283. */ 
  284. public $_allow_rule = array(); 
  285.  
  286. /** 
  287. * Stores the subscription-ID of the parent object. 
  288. * This value will only have a value when the Membership is loaded within 
  289. * the context of a subscription. 
  290. * @since 1.1.0.7 
  291. * @var int 
  292. */ 
  293. protected $subscription_id = 0; 
  294.  
  295.  
  296. // 
  297. // 
  298. // 
  299. // -------------------------------------------------------------- COLLECTION 
  300.  
  301.  
  302. /** 
  303. * Returns the post-type of the current object. 
  304. * @since 2.0.0 
  305. * @api 
  306. * @return string The post-type name. 
  307. */ 
  308. public static function get_post_type() { 
  309. return parent::_post_type( self::$POST_TYPE ); 
  310.  
  311. /** 
  312. * Get custom register post type args for this model. 
  313. * @since 1.0.0 
  314. * @internal 
  315. */ 
  316. public static function get_register_post_type_args() { 
  317. $args = array( 
  318. 'label' => __( 'Membership2 Memberships', MS_TEXT_DOMAIN ),  
  319. 'description' => __( 'Memberships user can join to.', MS_TEXT_DOMAIN ),  
  320. 'show_ui' => false,  
  321. 'show_in_menu' => false,  
  322. 'menu_position' => 70, // below Users 
  323. 'menu_icon' => 'dashicons-lock',  
  324. 'public' => true,  
  325. 'has_archive' => false,  
  326. 'publicly_queryable' => false,  
  327. 'supports' => false,  
  328. 'hierarchical' => false,  
  329. ); 
  330.  
  331. return apply_filters( 
  332. 'ms_customposttype_register_args',  
  333. $args,  
  334. self::get_post_type() 
  335. ); 
  336.  
  337. /** 
  338. * Get membership types. 
  339. * @since 1.0.0 
  340. * @internal 
  341. * @return array { 
  342. * Returns array of $type => $title. 
  343. * @type string $type The membership type 
  344. * @type string $title The membership type title 
  345. * } 
  346. */ 
  347. static public function get_types() { 
  348. $types = array( 
  349. self::TYPE_STANDARD => __( 'Standard Membership', MS_TEXT_DOMAIN ),  
  350. self::TYPE_DRIPPED => __( 'Dripped Content Membership', MS_TEXT_DOMAIN ),  
  351. self::TYPE_GUEST => __( 'Guest Membership', MS_TEXT_DOMAIN ),  
  352. self::TYPE_USER => __( 'Default Membership', MS_TEXT_DOMAIN ),  
  353. self::TYPE_BASE => __( 'System Membership', MS_TEXT_DOMAIN ),  
  354. ); 
  355.  
  356. return apply_filters( 'ms_model_membership_get_types', $types ); 
  357.  
  358. /** 
  359. * Get membership payment types. 
  360. * @since 1.0.0 
  361. * @internal 
  362. * @return array { 
  363. * Returns array of $type => $title. 
  364. * @type string $type The membership payment type 
  365. * @type string $title The membership payment type title 
  366. * } 
  367. */ 
  368. public static function get_payment_types( $type = 'paid' ) { 
  369. if ( 'free' == $type ) { 
  370. $payment_types = array( 
  371. self::PAYMENT_TYPE_PERMANENT => __( 'Permanent access', MS_TEXT_DOMAIN ),  
  372. self::PAYMENT_TYPE_FINITE => __( 'Finite access', MS_TEXT_DOMAIN ),  
  373. self::PAYMENT_TYPE_DATE_RANGE => __( 'Date range access', MS_TEXT_DOMAIN ),  
  374. ); 
  375. } else { 
  376. $payment_types = array( 
  377. self::PAYMENT_TYPE_PERMANENT => __( 'One payment for permanent access', MS_TEXT_DOMAIN ),  
  378. self::PAYMENT_TYPE_FINITE => __( 'One payment for finite access', MS_TEXT_DOMAIN ),  
  379. self::PAYMENT_TYPE_DATE_RANGE => __( 'One payment for date range access', MS_TEXT_DOMAIN ),  
  380. self::PAYMENT_TYPE_RECURRING => __( 'Recurring payments', MS_TEXT_DOMAIN ),  
  381. ); 
  382.  
  383. return apply_filters( 
  384. 'ms_model_membership_get_payment_types',  
  385. $payment_types,  
  386. $type 
  387. ); 
  388.  
  389. /** 
  390. * Get available Memberships count. 
  391. * @since 1.0.0 
  392. * @internal 
  393. * @param $args The query post args 
  394. * @see @link http://codex.wordpress.org/Class_Reference/WP_Query 
  395. * @return int The membership count. 
  396. */ 
  397. public static function get_membership_count( $args = null ) { 
  398. MS_Factory::select_blog(); 
  399. $args = self::get_query_args( $args ); 
  400. $query = new WP_Query( $args ); 
  401. MS_Factory::revert_blog(); 
  402.  
  403. $count = $query->found_posts; 
  404.  
  405. return apply_filters( 
  406. 'ms_model_membership_get_membership_count',  
  407. $count,  
  408. $args 
  409. ); 
  410.  
  411. /** 
  412. * Find out if the installation has at least one paid membership 
  413. * @since 1.1.0 
  414. * @internal 
  415. * @return bool 
  416. */ 
  417. public static function have_paid_membership() { 
  418. static $Have_Paid = null; 
  419.  
  420. if ( null === $Have_Paid ) { 
  421. global $wpdb; 
  422. // Using a custom WPDB query because building the meta-query is more 
  423. // complex than really required here... 
  424. $sql = " 
  425. SELECT COUNT( 1 ) 
  426. FROM {$wpdb->posts} p 
  427. INNER JOIN {$wpdb->postmeta} priv ON priv.post_id = p.ID AND priv.meta_key = %s 
  428. INNER JOIN {$wpdb->postmeta} free ON free.post_id = p.ID AND free.meta_key = %s 
  429. INNER JOIN {$wpdb->postmeta} pric ON pric.post_id = p.ID AND pric.meta_key = %s 
  430. WHERE 
  431. p.post_type = %s 
  432. AND priv.meta_value != '1' 
  433. AND NOT ( 
  434. free.meta_value = '1' 
  435. OR pric.meta_value = '0' 
  436. "; 
  437.  
  438. $sql = $wpdb->prepare( 
  439. $sql,  
  440. 'private', // INNER JOIN 
  441. 'is_free', // INNER JOIN 
  442. 'price', // INNER JOIN 
  443. self::get_post_type() // WHERE condition 
  444. ); 
  445.  
  446. $res = $wpdb->get_var( $sql ); 
  447.  
  448. $Have_Paid = apply_filters( 
  449. 'ms_model_membership_have_paid_membership',  
  450. intval( $res ) > 0 
  451. ); 
  452.  
  453. return $Have_Paid; 
  454.  
  455. /** 
  456. * Get WP_Query object arguments. 
  457. * Default search arguments for this custom post_type. 
  458. * @since 1.0.0 
  459. * @internal 
  460. * @param $args The query post args 
  461. * @see @link http://codex.wordpress.org/Class_Reference/WP_Query 
  462. * @return array $args The parsed args. 
  463. */ 
  464. public static function get_query_args( $args = null ) { 
  465. $defaults = apply_filters( 
  466. 'ms_model_membership_get_query_args_defaults',  
  467. array( 
  468. 'post_type' => self::get_post_type(),  
  469. 'order' => 'DESC',  
  470. 'orderby' => 'name',  
  471. 'post_status' => 'any',  
  472. 'post_per_page' => -1,  
  473. 'nopaging' => true,  
  474. 'include_base' => false,  
  475. 'include_guest' => true,  
  476. ); 
  477.  
  478. $args = wp_parse_args( $args, $defaults ); 
  479.  
  480. if ( ! lib2()->is_true( $args['include_base'] ) ) { 
  481. $args['meta_query']['base'] = array( 
  482. 'key' => 'type',  
  483. 'value' => self::TYPE_BASE,  
  484. 'compare' => '!=',  
  485. ); 
  486.  
  487. if ( ! lib2()->is_true( $args['include_guest'] ) ) { 
  488. $args['meta_query']['guest'] = array( 
  489. 'key' => 'type',  
  490. 'value' => self::TYPE_GUEST,  
  491. 'compare' => '!=',  
  492. ); 
  493. $args['meta_query']['user'] = array( 
  494. 'key' => 'type',  
  495. 'value' => self::TYPE_USER,  
  496. 'compare' => '!=',  
  497. ); 
  498.  
  499. return apply_filters( 
  500. 'ms_model_membership_get_query_args',  
  501. $args,  
  502. $defaults 
  503. ); 
  504.  
  505. /** 
  506. * Get Memberships models. 
  507. * When no $args are specified then all memberships except the base 
  508. * membership will be returned. 
  509. * To include the base membership use: 
  510. * $args = array( 'include_base' => 1 ) 
  511. * To exclude the guest membership use: 
  512. * $args = array( 'include_guest' => 0 ) 
  513. * @since 1.0.0 
  514. * @internal 
  515. * @param $args The query post args 
  516. * @see @link http://codex.wordpress.org/Class_Reference/WP_Query 
  517. * @return MS_Model_Membership[] The selected memberships. 
  518. */ 
  519. static public function get_memberships( $args = null ) { 
  520. $args = self::get_query_args( $args ); 
  521. $args['order'] = 'ASC'; 
  522.  
  523. MS_Factory::select_blog(); 
  524. $query = new WP_Query( $args ); 
  525. $items = $query->get_posts(); 
  526. MS_Factory::revert_blog(); 
  527.  
  528. $memberships = array(); 
  529. foreach ( $items as $item ) { 
  530. $memberships[] = MS_Factory::load( 
  531. 'MS_Model_Membership',  
  532. $item->ID 
  533. ); 
  534.  
  535. return apply_filters( 
  536. 'ms_model_membership_get_memberships',  
  537. $memberships,  
  538. $args 
  539. ); 
  540.  
  541. /** 
  542. * Returns a list of the dripped memberships. 
  543. * @since 1.1.0 
  544. * @internal 
  545. * @param $args The query post args 
  546. * @see @link http://codex.wordpress.org/Class_Reference/WP_Query 
  547. * @return MS_Model_Membership[] The selected memberships. 
  548. */ 
  549. static public function get_dripped_memberships( $args = null ) { 
  550. $drip_args = array( 
  551. 'meta_query' => array( 
  552. array( 
  553. 'key' => 'type',  
  554. 'value' => self::TYPE_DRIPPED,  
  555. ),  
  556. ),  
  557. ); 
  558.  
  559. $drip_args = wp_parse_args( $drip_args, $args ); 
  560. $memberships = self::get_memberships( $drip_args ); 
  561.  
  562. return apply_filters( 
  563. 'ms_model_membership_get_dripped_memberships',  
  564. $memberships,  
  565. $args 
  566. ); 
  567.  
  568. /** 
  569. * Get membership names. 
  570. * @since 1.0.0 
  571. * @internal 
  572. * @param $args The query post args 
  573. * @see @link http://codex.wordpress.org/Class_Reference/WP_Query 
  574. * @param bool $include_base_membership Include base membership from the list. 
  575. * @return array { 
  576. * Returns array of $membership_id => $name 
  577. * @type int $membership_id The membership Id. 
  578. * @type string $name The membership name; 
  579. * } 
  580. */ 
  581. public static function get_membership_names( $args = null ) { 
  582. $items = self::get_memberships( $args ); 
  583.  
  584. $memberships = array(); 
  585. foreach ( $items as $item ) { 
  586. $memberships[ $item->id ] = $item->name; 
  587.  
  588. return apply_filters( 
  589. 'ms_model_membership_get_membership_names',  
  590. $memberships,  
  591. $args 
  592. ); 
  593.  
  594. /** 
  595. * Get membership eligible to signup. 
  596. * @since 1.0.0 
  597. * @internal 
  598. * @param $args The query post args 
  599. * @see @link http://codex.wordpress.org/Class_Reference/WP_Query 
  600. * @param int[] $exclude_ids Optional. The membership ids to exclude. 
  601. * @param bool $only_names Optional. Return only array { 
  602. * @type int $membership_id The membership ID. 
  603. * @type string $membership_name The membership name. 
  604. * } 
  605. * @param bool $include_private If private memberships should be listed 
  606. * This param is only recognized in the admin section so admins can 
  607. * manually assign a private membership to a user. 
  608. * @return array { 
  609. * Returns array of $membership_id => $membership 
  610. * @type int $membership_id The membership Id. 
  611. * @type MS_Model_Membership The membership model object. 
  612. * } 
  613. */ 
  614. public static function get_signup_membership_list( 
  615. $args = null,  
  616. $exclude_ids = null,  
  617. $only_names = false,  
  618. $include_private = false 
  619. ) { 
  620. $not_in = array(); 
  621. if ( is_array( $exclude_ids ) ) { 
  622. $not_in = $exclude_ids; 
  623. $not_in[] = MS_Model_Membership::get_base()->id; 
  624. $not_in[] = MS_Model_Membership::get_guest()->id; 
  625. $not_in[] = MS_Model_Membership::get_user()->id; 
  626. $args['post__not_in'] = array_unique( $not_in ); 
  627.  
  628. if ( ! is_admin() ) { 
  629. $include_private = false; 
  630. // List of private memberships (they are grouped in own array). 
  631. $private = array(); 
  632.  
  633. // Retrieve memberships user is not part of, using selected args 
  634. $memberships = self::get_memberships( $args ); 
  635.  
  636. foreach ( $memberships as $key => $membership ) { 
  637. // Remove if not active. 
  638. if ( ! $membership->active ) { 
  639. unset( $memberships[ $key ] ); 
  640.  
  641. if ( $membership->private ) { 
  642. if ( $include_private ) { 
  643. // Move the private memberships to a option-group. 
  644. $private[ $key ] = $memberships[ $key ]; 
  645. unset( $memberships[ $key ] ); 
  646.  
  647. if ( $only_names ) { 
  648. $ms_names = array(); 
  649. foreach ( $memberships as $ms ) { 
  650. $ms_names[ $ms->id ] = $ms->name; 
  651. if ( ! empty( $private ) ) { 
  652. $priv_key = __( 'Private Memberships', MS_TEXT_DOMAIN ); 
  653. $ms_names[ $priv_key ] = array(); 
  654. foreach ( $private as $ms ) { 
  655. $ms_names[ $priv_key ][ $ms->id ] = $ms->name; 
  656. $memberships = $ms_names; 
  657. } else { 
  658. $memberships = array_merge( $memberships, $private ); 
  659.  
  660. return apply_filters( 
  661. 'ms_model_membership_get_signup_membership_list',  
  662. $memberships,  
  663. $exclude_ids,  
  664. $only_names 
  665. ); 
  666.  
  667. /** 
  668. * Verify if membership is valid. 
  669. * Verify if membership was not deleted, trying to load from DB. 
  670. * @since 1.0.0 
  671. * @api 
  672. * @param int $membership_id The membership id to verify. 
  673. * @return bool True if is valid. 
  674. */ 
  675. public static function is_valid_membership( $membership_id ) { 
  676. $membership = MS_Factory::load( 'MS_Model_Membership', $membership_id, '_is_valid_' ); 
  677. $valid = ( $membership->id > 0 ); 
  678.  
  679. return apply_filters( 
  680. 'ms_model_membership_is_valid_membership',  
  681. $valid,  
  682. $membership_id 
  683. ); 
  684.  
  685. /** 
  686. * Get Membership2 membership. 
  687. * Create a new membership if membership does not exist. 
  688. * @since 1.1.0 
  689. * @internal 
  690. * @param string $type The membership to load [protected_content|role] 
  691. * @param book $create_missing If set to false then missing special 
  692. * memberships are not created. 
  693. * @return MS_Model_Membership The Membership2. 
  694. */ 
  695. private static function _get_system_membership( $type, $create_missing = true ) { 
  696. static $Special_Membership = array(); 
  697. $comp_key = $type; 
  698. $membership = false; 
  699.  
  700. if ( ! isset( $Special_Membership[$comp_key] ) ) { 
  701. $membership = false; 
  702. global $wpdb; 
  703.  
  704. MS_Factory::select_blog(); 
  705. /** 
  706. * We are using a normal SQL query instead of using the WP_Query object 
  707. * here, because the WP_Query object does some strange things sometimes: 
  708. * In some cases new Membership2 memberships were created when a 
  709. * guest accessed the page. 
  710. * By using a manual query we are very certain that only one 
  711. * base-membership exists on the database. 
  712. */ 
  713. $sql = " 
  714. SELECT ID 
  715. FROM {$wpdb->posts} p 
  716. INNER JOIN {$wpdb->postmeta} m_type ON m_type.post_id = p.ID 
  717. WHERE 
  718. p.post_type = %s 
  719. AND m_type.meta_key = %s 
  720. AND m_type.meta_value = %s 
  721. "; 
  722. $values = array( 
  723. self::get_post_type(),  
  724. 'type',  
  725. $type,  
  726. ); 
  727.  
  728. $sql = $wpdb->prepare( $sql, $values ); 
  729. $item = $wpdb->get_results( $sql ); 
  730. $base = array_shift( $item ); // Remove the base membership from the results 
  731. MS_Factory::revert_blog(); 
  732.  
  733. if ( ! empty( $base ) ) { 
  734. $membership = MS_Factory::load( 'MS_Model_Membership', $base->ID ); 
  735. } else if ( $create_missing ) { 
  736. $names = self::get_types(); 
  737.  
  738. $description = __( 'Membership2 Core Membership', MS_TEXT_DOMAIN ); 
  739. $membership = MS_Factory::create( 'MS_Model_Membership' ); 
  740. $membership->name = $names[$type]; 
  741. $membership->title = $names[$type]; 
  742. $membership->description = $description; 
  743. $membership->type = $type; 
  744. $membership->save(); 
  745.  
  746. $Special_Membership[$comp_key] = apply_filters( 
  747. 'ms_model_membership_get_system_membership',  
  748. $membership,  
  749. $type 
  750. ); 
  751.  
  752. return $Special_Membership[$comp_key]; 
  753.  
  754. /** 
  755. * Get Membership2 base membership. 
  756. * Create a new membership if membership does not exist. 
  757. * This is an internal membership which is never displayed anywhere. 
  758. * @since 1.0.0 
  759. * @api 
  760. * @return MS_Model_Membership The base membership. 
  761. */ 
  762. public static function get_base() { 
  763. static $Base_Membership = null; 
  764.  
  765. if ( null === $Base_Membership ) { 
  766. $Base_Membership = self::_get_system_membership( 
  767. self::TYPE_BASE 
  768. ); 
  769.  
  770. foreach ( $Base_Membership->_rules as $key => $rule ) { 
  771. $Base_Membership->_rules[$key]->is_base_rule = true; 
  772.  
  773. $Base_Membership = apply_filters( 
  774. 'ms_model_membership_get_base',  
  775. $Base_Membership 
  776. ); 
  777.  
  778. return $Base_Membership; 
  779.  
  780. /** 
  781. * Get special membership that is assigned to all guests. 
  782. * Create a new membership if membership does not exist. 
  783. * @since 1.1.0 
  784. * @api 
  785. * @param string $role A WordPress user-role. 
  786. * @return MS_Model_Membership The guest membership. 
  787. */ 
  788. public static function get_guest() { 
  789. static $Guest_Membership = null; 
  790.  
  791. if ( null === $Guest_Membership ) { 
  792. $Guest_Membership = self::_get_system_membership( 
  793. self::TYPE_GUEST,  
  794. false // Don't create this membership automatically 
  795. ); 
  796.  
  797. $Guest_Membership = apply_filters( 
  798. 'ms_model_membership_get_guest',  
  799. $Guest_Membership 
  800. ); 
  801.  
  802. if ( ! $Guest_Membership ) { 
  803. $Guest_Membership = MS_Factory::create( 'MS_Model_Membership' ); 
  804.  
  805. return $Guest_Membership; 
  806.  
  807. /** 
  808. * Get default membership for all logged-in users that did not yet subscribe 
  809. * to any membership. 
  810. * Create a new membership if membership does not exist. 
  811. * @since 1.1.0 
  812. * @api 
  813. * @param string $role A WordPress user-role. 
  814. * @return MS_Model_Membership The guest membership. 
  815. */ 
  816. public static function get_user() { 
  817. static $User_Membership = null; 
  818.  
  819. if ( null === $User_Membership ) { 
  820. $User_Membership = self::_get_system_membership( 
  821. self::TYPE_USER,  
  822. false // Don't create this membership automatically 
  823. ); 
  824.  
  825. $User_Membership = apply_filters( 
  826. 'ms_model_membership_get_user',  
  827. $User_Membership 
  828. ); 
  829.  
  830. if ( ! $User_Membership ) { 
  831. $User_Membership = MS_Factory::create( 'MS_Model_Membership' ); 
  832.  
  833. return $User_Membership; 
  834.  
  835.  
  836. // 
  837. // 
  838. // 
  839. // ------------------------------------------------------------- SINGLE ITEM 
  840.  
  841.  
  842. /** 
  843. * Returns a list of variables that should be included in serialization,  
  844. * i.e. these values are the only ones that are stored in DB 
  845. * @since 1.1.0 
  846. * @internal 
  847. * @return array 
  848. */ 
  849. public function __sleep() { 
  850. /** 
  851. * Rule values are pre-processd before saving... 
  852. * Note: $this->_rules only contains rules for the *current* site, so 
  853. * all rules that are serialized here get the current-site prefix. 
  854. * Rules for the other sites are already in the $this->rule_values 
  855. * array and were not de-serialized on page load. 
  856. */ 
  857. $this->rule_values = lib2()->array->get( $this->rule_values ); 
  858. foreach ( $this->_rules as $rule_type => $rule ) { 
  859. $key = MS_Rule::rule_key( $rule_type ); 
  860.  
  861. $this->rule_values[$key] = $rule->serialize(); 
  862. if ( empty( $this->rule_values[$key] ) ) { 
  863. unset( $this->rule_values[$key] ); 
  864.  
  865. return array( 
  866. 'id',  
  867. 'name',  
  868. 'title',  
  869. 'description',  
  870. 'rule_values',  
  871. 'type',  
  872. 'payment_type',  
  873. 'active',  
  874. 'private',  
  875. 'is_free',  
  876. 'disabled_gateways',  
  877. 'price',  
  878. 'period',  
  879. 'pay_cycle_period',  
  880. 'pay_cycle_repetitions',  
  881. 'period_date_start',  
  882. 'period_date_end',  
  883. 'trial_period_enabled',  
  884. 'trial_price',  
  885. 'trial_period',  
  886. 'on_end_membership_id',  
  887. 'is_setup_completed',  
  888. 'custom_data',  
  889. ); 
  890.  
  891. /** 
  892. * Set rules membership_id before saving. 
  893. * @since 1.0.0 
  894. * @internal 
  895. */ 
  896. public function before_save() { 
  897. parent::before_save(); 
  898.  
  899. foreach ( $this->_rules as $rule ) { 
  900. $rule->membership_id = $this->id; 
  901.  
  902. /** 
  903. * Save model and move the object to the singleton cache if required. 
  904. * @since 2.0.0 
  905. */ 
  906. public function save() { 
  907. parent::save(); 
  908. parent::store_singleton(); 
  909.  
  910. /** 
  911. * After the membership was saved to DB we make sure that it is published. 
  912. * Network-wide mode: We are still in the switched blog (main site) so 
  913. * there is no need to call MS_Factory::select_blog() in this function. 
  914. * @since 2.0.0 
  915. * @internal 
  916. */ 
  917. public function after_save() { 
  918. // It is important! The Membership2 membership must be public 
  919. // so that the membership options are available for guest users. 
  920. wp_publish_post( $this->id ); 
  921.  
  922. /** 
  923. * Permanently delete the membership. 
  924. * @since 1.0.0 
  925. * @api 
  926. * @return bool 
  927. */ 
  928. public function delete() { 
  929. do_action( 'ms_model_membership_before_delete', $this ); 
  930. $res = false; 
  931.  
  932. if ( $this->is_base() ) { 
  933. throw new Exception( 
  934. 'Can not delete the system membership.' 
  935. ); 
  936.  
  937. if ( ! empty( $this->id ) ) { 
  938. if ( $this->get_members_count() > 0 ) { 
  939. $ms_relationships = MS_Model_Relationship::get_subscriptions( 
  940. array( 'membership_id' => $this->id ),  
  941. true 
  942. ); 
  943.  
  944. foreach ( $ms_relationships as $ms_relationship ) { 
  945. $ms_relationship->delete(); 
  946.  
  947. $res = ( false !== wp_delete_post( $this->id, true ) ); 
  948.  
  949. do_action( 'ms_model_membership_after_delete', $this, $res ); 
  950. return $res; 
  951.  
  952. /** 
  953. * Merge current rules to Membership2. 
  954. * Assure the membership rules get updated whenever Membership2 is changed. 
  955. * @since 1.0.0 
  956. * @internal 
  957. */ 
  958. public function prepare_obj() { 
  959. parent::prepare_obj(); 
  960.  
  961. if ( false !== strpos( $this->_factory_id, '_is_valid_' ) ) { 
  962. // This object only checks if the item ID is valid. 
  963. // No need to load any rules yet... 
  964. return; 
  965.  
  966. foreach ( $this->rule_values as $key => $values ) { 
  967. // Skip rules without any values. 
  968. if ( empty( $values ) ) { continue; } 
  969.  
  970. // Network-wide: Only instanciate rules for the *current* site! 
  971. if ( ! MS_Rule::is_current_site( $key ) ) { continue; } 
  972.  
  973. // Key could be "type" of "site:type" format. 
  974. $rule_type = MS_Rule::rule_type( $key ); 
  975.  
  976. // At this point we have an empty rule-instance 
  977. $rule = $this->get_rule( $rule_type ); 
  978.  
  979. // Now we populate that rule-instance with site-specific settings. 
  980. $rule->populate( $values ); 
  981.  
  982. // validate rules using Membership2 rules 
  983. if ( ! $this->is_base() && $this->is_valid() ) { 
  984. $this->merge_protection_rules(); 
  985.  
  986. /** 
  987. * Get current payment type description. 
  988. * Description to show in the admin list table. 
  989. * @since 1.0.0 
  990. * @api 
  991. * @return string The current payment type description. 
  992. */ 
  993. public function get_payment_type_desc() { 
  994. $desc = __( 'N/A', MS_TEXT_DOMAIN ); 
  995. $has_payment = ! $this->is_free(); 
  996.  
  997. switch ( $this->payment_type ) { 
  998. case self::PAYMENT_TYPE_FINITE: 
  999. $desc = sprintf( 
  1000. __( 'For %1$s', MS_TEXT_DOMAIN ),  
  1001. MS_Helper_Period::get_period_desc( $this->period ) 
  1002. ); 
  1003. break; 
  1004.  
  1005. case self::PAYMENT_TYPE_DATE_RANGE: 
  1006. $desc = sprintf( 
  1007. __( 'From %1$s to %2$s', MS_TEXT_DOMAIN ),  
  1008. $this->period_date_start,  
  1009. $this->period_date_end 
  1010. ); 
  1011. break; 
  1012.  
  1013. case self::PAYMENT_TYPE_RECURRING: 
  1014. $desc = __( 'Each %1$s', MS_TEXT_DOMAIN ); 
  1015.  
  1016. if ( $has_payment ) { 
  1017. if ( 1 == $this->pay_cycle_repetitions ) { 
  1018. $desc = __( 'Single payment', MS_TEXT_DOMAIN ); 
  1019. } elseif ( $this->pay_cycle_repetitions > 1 ) { 
  1020. $desc .= ', ' . __( '%2$s payments', MS_TEXT_DOMAIN ); 
  1021.  
  1022. $desc = sprintf( 
  1023. $desc,  
  1024. MS_Helper_Period::get_period_desc( $this->pay_cycle_period ),  
  1025. $this->pay_cycle_repetitions 
  1026. ); 
  1027. break; 
  1028.  
  1029. case self::PAYMENT_TYPE_PERMANENT: 
  1030. default: 
  1031. if ( $has_payment ) { 
  1032. $desc = __( 'Single payment', MS_TEXT_DOMAIN ); 
  1033. } else { 
  1034. $desc = __( 'Permanent access', MS_TEXT_DOMAIN ); 
  1035. break; 
  1036.  
  1037. return apply_filters( 
  1038. 'ms_model_membership_get_payment_type_desc',  
  1039. $desc,  
  1040. $this 
  1041. ); 
  1042.  
  1043. /** 
  1044. * Returns true if the current membership is free. 
  1045. * A membership is free when... 
  1046. * ... it is explicitely marked as "free" 
  1047. * ... the price is 0.00 
  1048. * ... it is a parent membership that cannot be signed up for 
  1049. * @since 1.0.4.7 
  1050. * @api 
  1051. * @return bool 
  1052. */ 
  1053. public function is_free() { 
  1054. $result = false; 
  1055.  
  1056. if ( $this->is_free ) { $result = true; } 
  1057. elseif ( empty( $this->price ) ) { $result = true; } 
  1058.  
  1059. $result = apply_filters( 
  1060. 'ms_model_membership_is_free',  
  1061. $result,  
  1062. $this 
  1063. ); 
  1064.  
  1065. if ( $result && $this->is_free ) { 
  1066. $this->is_free = $result; 
  1067.  
  1068. return $result; 
  1069.  
  1070. /** 
  1071. * Checks if a specific payment gateway is allowed for the current 
  1072. * membership. 
  1073. * @since 2.0.0 
  1074. * @param string $gateway_id The payment gateway ID. 
  1075. * @return bool 
  1076. */ 
  1077. public function can_use_gateway( $gateway_id ) { 
  1078. $result = true; 
  1079.  
  1080. $this->disabled_gateways = lib2()->array->get( $this->disabled_gateways ); 
  1081. if ( isset( $this->disabled_gateways[$gateway_id] ) ) { 
  1082. $state = $this->disabled_gateways[$gateway_id]; 
  1083. $result = ! lib2()->is_true( $state ); 
  1084.  
  1085. if ( $result ) { 
  1086. $gateway = MS_Model_Gateway::factory( $gateway_id ); 
  1087. $result = $gateway->payment_type_supported( $this ); 
  1088.  
  1089. $result = apply_filters( 
  1090. 'ms_model_membership_can_use_gateway',  
  1091. $result,  
  1092. $gateway_id,  
  1093. $this 
  1094. ); 
  1095. return $result; 
  1096.  
  1097. /** 
  1098. * Get protection Rule Model. 
  1099. * Note for network-wide mode: 
  1100. * In DB the rules for each site are stored in different objects. 
  1101. * When loading a membership we will always load 1 instance of each 
  1102. * rule_type, and this is the instance that belongs to the current site! 
  1103. * Instances for other sites are not accessible. 
  1104. * -> This is why we do not use/need a site_id or similar in this function. 
  1105. * @since 1.0.0 
  1106. * @api 
  1107. * @param string $rule_type The rule model type @see MS_Rule 
  1108. * @return MS_Rule The requested rule model. 
  1109. */ 
  1110. public function get_rule( $rule_type ) { 
  1111. if ( 'attachment' === $rule_type ) { 
  1112. $rule_type = MS_Rule_Media::RULE_ID; 
  1113.  
  1114. if ( ! isset( $this->_rules[ $rule_type ] ) 
  1115. || ! is_object( $this->_rules[ $rule_type ] ) // During plugin update. 
  1116. ) { 
  1117. // Create a new rule model object. 
  1118. $rule = MS_Rule::rule_factory( 
  1119. $rule_type,  
  1120. $this->id,  
  1121. $this->subscription_id 
  1122. ); 
  1123.  
  1124. $rule = apply_filters( 
  1125. 'ms_model_membership_get_rule',  
  1126. $rule,  
  1127. $rule_type,  
  1128. $this 
  1129. ); 
  1130.  
  1131. $this->_rules[ $rule_type ] = $rule; 
  1132. if ( ! is_array( $rule->rule_value ) ) { 
  1133. $rule->rule_value = array(); 
  1134.  
  1135. return $this->_rules[ $rule_type ]; 
  1136.  
  1137. /** 
  1138. * Set protection Rule Model. 
  1139. * Note for network-wide mode: 
  1140. * In DB the rules for each site are stored in different objects. 
  1141. * When loading a membership we will always load 1 instance of each 
  1142. * rule_type, and this is the instance that belongs to the current site! 
  1143. * Instances for other sites are not accessible. 
  1144. * -> This is why we do not use/need a site_id or similar in this function. 
  1145. * @since 1.0.0 
  1146. * @api 
  1147. * @param string The rule model type @see MS_Rule 
  1148. * @param MS_Rule $rule The protection rule to set. 
  1149. */ 
  1150. public function set_rule( $rule_type, $rule ) { 
  1151. $this->_rules[ $rule_type ] = apply_filters( 
  1152. 'ms_model_membership_set_rule',  
  1153. $rule,  
  1154. $rule_type,  
  1155. $this 
  1156. ); 
  1157.  
  1158. /** 
  1159. * Returns the unique HEX color for this membership. 
  1160. * The color is calculated from the membership-ID and therefore will never 
  1161. * change. 
  1162. * @since 1.1.0 
  1163. * @api 
  1164. * @return string Hex color, e.g. '#FFFFFF' 
  1165. */ 
  1166. public function get_color() { 
  1167. return MS_Helper_Utility::color_index( $this->type . $this->id ); 
  1168.  
  1169. /** 
  1170. * Returns a HTML tag that shows the membership name with the internal 
  1171. * membership color. 
  1172. * @since 2.0.0 
  1173. * @api 
  1174. */ 
  1175. public function get_name_tag() { 
  1176. $code = sprintf( 
  1177. '<span class="ms-membership" style="background:%2$s">%1$s</span>',  
  1178. esc_html( $this->name ),  
  1179. $this->get_color() 
  1180. ); 
  1181.  
  1182. return $code; 
  1183.  
  1184. /** 
  1185. * Echo a HTML tag that shows the membership name with the internal 
  1186. * membership color. 
  1187. * @since 2.0.0 
  1188. * @api 
  1189. */ 
  1190. public function name_tag() { 
  1191. echo $this->get_name_tag(); 
  1192.  
  1193. /** 
  1194. * Get current membership type description. 
  1195. * @since 1.0.0 
  1196. * @api 
  1197. * @return string The membership type description. 
  1198. */ 
  1199. public function get_type_description() { 
  1200. $types = self::get_types(); 
  1201. $desc = $types[ $this->type ]; 
  1202.  
  1203. return apply_filters( 
  1204. 'ms_model_membership_get_type_description',  
  1205. $desc,  
  1206. $this 
  1207. ); 
  1208.  
  1209. /** 
  1210. * Either creates or updates the value of a custom data field. 
  1211. * Note: Remember to prefix the $key with a unique string to prevent 
  1212. * conflicts with other plugins that also use this function. 
  1213. * @since 2.0.0 
  1214. * @api 
  1215. * @param string $key The field-key. 
  1216. * @param mixed $value The new value to assign to the field. 
  1217. */ 
  1218. public function set_custom_data( $key, $value ) { 
  1219. // Wrapper function, so this function shows up in API docs. 
  1220. parent::set_custom_data( $key, $value ); 
  1221.  
  1222. /** 
  1223. * Removes a custom data field from this object. 
  1224. * @since 2.0.0 
  1225. * @api 
  1226. * @param string $key The field-key. 
  1227. */ 
  1228. public function delete_custom_data( $key ) { 
  1229. // Wrapper function, so this function shows up in API docs. 
  1230. parent::delete_custom_data( $key ); 
  1231.  
  1232. /** 
  1233. * Returns the value of a custom data field. 
  1234. * @since 2.0.0 
  1235. * @api 
  1236. * @param string $key The field-key. 
  1237. * @return mixed The value that was previously assigned to the custom field 
  1238. * or false if no value was set for the field. 
  1239. */ 
  1240. public function get_custom_data( $key ) { 
  1241. // Wrapper function, so this function shows up in API docs. 
  1242. return parent::get_custom_data( $key ); 
  1243.  
  1244. /** 
  1245. * Merge Membership2 rules. 
  1246. * Merge every rule model with Membership2/visitor membership rules. 
  1247. * This ensure rules are consistent with Membership2 rules. 
  1248. * @since 1.0.0 
  1249. * @internal 
  1250. */ 
  1251. public function merge_protection_rules() { 
  1252. if ( $this->is_base() ) { 
  1253. // This is the visitor membership, no need to merge anything. 
  1254. return; 
  1255.  
  1256. $base_rules = self::get_base()->_rules; 
  1257.  
  1258. foreach ( $base_rules as $key => $base_rule ) { 
  1259. try { 
  1260. // Key could be "type" of "site:type" format. 
  1261. $rule_type = MS_Rule::rule_type( $key ); 
  1262.  
  1263. $rule = $this->get_rule( $rule_type ); 
  1264. $rule->protect_undefined_items( $base_rule, true ); 
  1265. $this->set_rule( $rule_type, $rule ); 
  1266. catch( Exception $e ) { 
  1267. MS_Helper_Debug::log( $e ); 
  1268.  
  1269. $this->_rules = apply_filters( 
  1270. 'ms_model_membership_merge_protection_rules',  
  1271. $this->_rules,  
  1272. $this 
  1273. ); 
  1274.  
  1275. /** 
  1276. * Get after membership expired options. 
  1277. * Memberships can be downgraded to the guest level protection. 
  1278. * @since 1.0.0 
  1279. * @api 
  1280. * @return array { 
  1281. * Returns array of $membership_id => $description. 
  1282. * @type int $membership_id The membership Id. 
  1283. * @type string $description The expired option description. 
  1284. * } 
  1285. */ 
  1286. public function get_after_ms_ends_options() { 
  1287. $options = array( 
  1288. 0 => __( 'Restrict access to Visitor-Level', MS_TEXT_DOMAIN ),  
  1289. ); 
  1290.  
  1291. $args = array( 
  1292. 'include_guest' => false,  
  1293. ); 
  1294. $options += $this->get_membership_names( $args ); 
  1295. unset( $options[$this->id] ); 
  1296.  
  1297. $label = __( 'Change to: %s', MS_TEXT_DOMAIN ); 
  1298. foreach ( $options as $id => $option ) { 
  1299. if ( $id > 0 ) { 
  1300. $options[$id] = sprintf( $label, $option ); 
  1301.  
  1302. return apply_filters( 
  1303. 'ms_model_membership_get_membership_names',  
  1304. $options,  
  1305. $this 
  1306. ); 
  1307.  
  1308. /** 
  1309. * Get members count of this membership. 
  1310. * @since 1.0.0 
  1311. * @api 
  1312. * @return int The members count. 
  1313. */ 
  1314. public function get_members_count() { 
  1315. $count = MS_Model_Relationship::get_subscription_count( 
  1316. array( 'membership_id' => $this->id ) 
  1317. ); 
  1318.  
  1319. return apply_filters( 
  1320. 'ms_model_membership_get_members_count',  
  1321. $count 
  1322. ); 
  1323.  
  1324. /** 
  1325. * Return membership has dripped content. 
  1326. * Verify post and page rules if there is a dripped content. 
  1327. * @since 1.0.0 
  1328. * @api 
  1329. * @return boolean 
  1330. */ 
  1331. public function has_dripped_content() { 
  1332. $has_dripped = false; 
  1333. $dripped = array( 'post', 'page' ); 
  1334.  
  1335. foreach ( $dripped as $rule_type ) { 
  1336. // using count() as !empty() never returned true 
  1337. if ( 0 < count( $this->get_rule( $rule_type )->dripped ) ) { 
  1338. $has_dripped = true; 
  1339.  
  1340. return apply_filters( 
  1341. 'ms_model_membership_has_dripped_content',  
  1342. $has_dripped,  
  1343. $this 
  1344. ); 
  1345.  
  1346. /** 
  1347. * Get protection rules sorted. 
  1348. * First one has priority over the last one. 
  1349. * These rules are used to determine access. 
  1350. * @since 1.0.0 
  1351. * @internal 
  1352. */ 
  1353. private function get_rules_hierarchy() { 
  1354. $rule_types = MS_Model_Rule::get_rule_types(); 
  1355. $rules = array(); 
  1356. $subscription = MS_Factory::load( 'MS_Model_Relationship', $this->subscription_id ); 
  1357.  
  1358. foreach ( $rule_types as $rule_type ) { 
  1359. $rule = $this->get_rule( $rule_type ); 
  1360.  
  1361. if ( $rule->rule_type != $rule_type ) { 
  1362. // This means that the $rule_type was not found... 
  1363. continue; 
  1364.  
  1365. // Sometimes the $subscription->id can be 0, which is intentional: 
  1366. // This is the case when the membership was auto-assigned to guest 
  1367. // or default membership. 
  1368. $rule->_subscription_id = $subscription->id; 
  1369.  
  1370. $rule->membership_id = $this->id; 
  1371. $rules[ $rule_type ] = $rule; 
  1372.  
  1373. return apply_filters( 
  1374. 'ms_model_membership_get_rules_hierarchy',  
  1375. $rules,  
  1376. $this 
  1377. ); 
  1378.  
  1379. /** 
  1380. * Mark membership setup as completed. 
  1381. * Only purpose of this flag is to display the correct update message to the 
  1382. * user: If setup_completed() returns true, then "Membership added" is 
  1383. * displayed, otherwise "Membership updated" 
  1384. * @since 1.0.0 
  1385. * @internal 
  1386. * @return bool $marked True in the first time setup is finished. 
  1387. */ 
  1388. public function setup_completed() { 
  1389. $marked = false; 
  1390.  
  1391. if ( ! $this->is_setup_completed ) { 
  1392. $this->is_setup_completed = true; 
  1393. $marked = true; 
  1394.  
  1395. return apply_filters( 
  1396. 'ms_model_membership_setup_completed',  
  1397. $marked,  
  1398. $this 
  1399. ); 
  1400.  
  1401. /** 
  1402. * Returns true if the membership the base membership. 
  1403. * @since 1.1.0 
  1404. * @see description of MS_Model_Membership::get_base() 
  1405. * @api 
  1406. * @return bool 
  1407. */ 
  1408. public function is_base() { 
  1409. $res = $this->type == self::TYPE_BASE; 
  1410.  
  1411. return apply_filters( 
  1412. 'ms_model_membership_is_base',  
  1413. $res,  
  1414. $this 
  1415. ); 
  1416.  
  1417. /** 
  1418. * Returns true if the membership the guest membership. 
  1419. * @since 1.1.0 
  1420. * @see description of MS_Model_Membership::get_guest() 
  1421. * @api 
  1422. * @return bool 
  1423. */ 
  1424. public function is_guest() { 
  1425. $res = $this->type == self::TYPE_GUEST; 
  1426.  
  1427. return apply_filters( 
  1428. 'ms_model_membership_is_guest',  
  1429. $res,  
  1430. $this 
  1431. ); 
  1432.  
  1433. /** 
  1434. * Returns true if the membership the user membership. 
  1435. * @since 1.1.0 
  1436. * @see description of MS_Model_Membership::get_user() 
  1437. * @api 
  1438. * @return bool 
  1439. */ 
  1440. public function is_user() { 
  1441. $res = $this->type == self::TYPE_USER; 
  1442.  
  1443. return apply_filters( 
  1444. 'ms_model_membership_is_user',  
  1445. $res,  
  1446. $this 
  1447. ); 
  1448.  
  1449. /** 
  1450. * Returns true if the membership a dripped membership. 
  1451. * @since 1.1.0 
  1452. * @api 
  1453. * @return bool 
  1454. */ 
  1455. public function is_dripped() { 
  1456. $res = $this->type == self::TYPE_DRIPPED; 
  1457.  
  1458. return apply_filters( 
  1459. 'ms_model_membership_is_dripped',  
  1460. $res,  
  1461. $this 
  1462. ); 
  1463.  
  1464. /** 
  1465. * Returns true if the membership the base or guest/user membership. 
  1466. * @since 1.1.0 
  1467. * @api 
  1468. * @return bool 
  1469. */ 
  1470. public function is_system() { 
  1471. $res = $this->is_base() || $this->is_guest() || $this->is_user(); 
  1472.  
  1473. return apply_filters( 
  1474. 'ms_model_membership_is_system',  
  1475. $res,  
  1476. $this 
  1477. ); 
  1478.  
  1479. /** 
  1480. * Can be used to validate if the current membership is actually loaded 
  1481. * from database. If this function returns false, then the specified 
  1482. * membership-ID does not exist in DB. 
  1483. * @since 1.1.0 
  1484. * @api 
  1485. * @return bool 
  1486. */ 
  1487. public function is_valid() { 
  1488. $res = ! empty( $this->id ); 
  1489.  
  1490. return apply_filters( 
  1491. 'ms_model_membership_is_valid',  
  1492. $res,  
  1493. $this 
  1494. ); 
  1495.  
  1496. /** 
  1497. * Verify access to current page. 
  1498. * Verify membership rules hierarchy for content accessed directly. 
  1499. * If 'has access' is found, it does have access. 
  1500. * Only for active memberships. 
  1501. * @since 1.0.0 
  1502. * @api 
  1503. * @param int $post_id 
  1504. * @return bool|null True if has access to current page. Default is false. 
  1505. * Null means: Rule not relevant for current page. 
  1506. */ 
  1507. public function has_access_to_current_page( $post_id = null ) { 
  1508. $has_access = null; 
  1509. $this->_access_reason = array(); 
  1510. $this->_deny_rule = array(); 
  1511. $this->_allow_rule = array(); 
  1512.  
  1513. // Only verify access if membership is Active. 
  1514. if ( $this->active ) { 
  1515.  
  1516. // If 'has access' is found in the hierarchy, it does have access. 
  1517. $rules = $this->get_rules_hierarchy(); 
  1518. foreach ( $rules as $rule ) { 
  1519. $rule_access = $rule->has_access( $post_id ); 
  1520.  
  1521. if ( null === $rule_access ) { 
  1522. $this->_access_reason[] = sprintf( 
  1523. __( 'Ignored: Rule "%s"', MS_TEXT_DOMAIN ),  
  1524. $rule->rule_type 
  1525. ); 
  1526. continue; 
  1527.  
  1528. $this->_access_reason[] = sprintf( 
  1529. __( '%s: Rule "%s"', MS_TEXT_DOMAIN ),  
  1530. $rule_access ? __( 'Allow', MS_TEXT_DOMAIN ) : __( 'Deny', MS_TEXT_DOMAIN ),  
  1531. $rule->rule_type 
  1532. ); 
  1533.  
  1534. if ( ! $rule_access ) { 
  1535. $this->_deny_rule[] = $rule->rule_type; 
  1536. } else { 
  1537. $this->_allow_rule[] = $rule->rule_type; 
  1538.  
  1539. // URL groups have final decission. 
  1540. if ( MS_Rule_Url::RULE_ID === $rule->rule_type ) { 
  1541. $has_access = $rule_access; 
  1542. break; 
  1543.  
  1544. // Special pages have final decission after URL groups. 
  1545. if ( MS_Rule_Special::RULE_ID === $rule->rule_type ) { 
  1546. $has_access = $rule_access; 
  1547. $this->_access_reason[] = $rule->matched_type; 
  1548. break; 
  1549.  
  1550. $has_access = ( $has_access || $rule_access ); 
  1551.  
  1552. if ( true === $has_access ) { 
  1553. break; 
  1554.  
  1555. return apply_filters( 
  1556. 'ms_model_membership_has_access_to_current_page',  
  1557. $has_access,  
  1558. $post_id,  
  1559. $this 
  1560. ); 
  1561.  
  1562. /** 
  1563. * Verify access to post. 
  1564. * Verify membership rules hierarchy for specific post or CPT. 
  1565. * @since 1.0.0 
  1566. * @api 
  1567. * @param int $post_id ID of specific post 
  1568. * @return boolean True if has access to current page. Default is false. 
  1569. */ 
  1570. public function has_access_to_post( $post_id ) { 
  1571. $has_access = null; 
  1572.  
  1573. if ( MS_Model_Member::is_normal_admin() ) { 
  1574. return true; 
  1575.  
  1576. if ( ! empty( $post_id ) ) { 
  1577. $post = get_post( $post_id ); 
  1578. if ( 'attachment' === $post->post_type ) { 
  1579. $post_id = get_post_field( 'post_parent', $post_id ); 
  1580.  
  1581. // If 'has access' is found in the hierarchy, it does have access. 
  1582. $rules = $this->get_rules_hierarchy(); 
  1583. foreach ( $rules as $rule ) { 
  1584. $rule->prepare_rule( $subscription ); 
  1585.  
  1586. // url groups have final decision 
  1587. if ( MS_Rule_Url::RULE_ID == $rule->rule_type 
  1588. && $rule->has_rule_for_post( $post_id ) 
  1589. ) { 
  1590. $has_access = $rule->has_access( $post_id ); 
  1591. break; 
  1592. } else { 
  1593. $rule_access = $rule->has_access( $post_id ); 
  1594. if ( null !== $rule_access ) { 
  1595. $has_access = $rule_access; 
  1596.  
  1597. if ( $has_access ) { 
  1598. break; 
  1599.  
  1600. if ( null === $has_access ) { 
  1601. // The post is not denied by any rule, so allow access. 
  1602. $has_access = true; 
  1603.  
  1604. return apply_filters( 
  1605. 'ms_model_membership_has_access_to_post',  
  1606. $has_access,  
  1607. $this 
  1608. ); 
  1609.  
  1610. /** 
  1611. * Set up the membership. This is always done, regardless if the user is 
  1612. * a normal user or an Admin user. 
  1613. * @since 1.0.0 
  1614. * @internal 
  1615. * @param MS_Model_Relationship $subscription The membership relationship. 
  1616. */ 
  1617. public function initialize( $subscription ) { 
  1618. do_action( 
  1619. 'ms_model_membership_initialize_before',  
  1620. $subscription,  
  1621. $this 
  1622. ); 
  1623.  
  1624. $this->subscription_id = $subscription->id; 
  1625. $rules = $this->get_rules_hierarchy(); 
  1626.  
  1627. // Apply protection settings of all rules (replace/hide contents, ...) 
  1628. foreach ( $rules as $rule ) { 
  1629. $rule->prepare_rule( $subscription ); 
  1630.  
  1631. do_action( 
  1632. 'ms_model_membership_initialize_after',  
  1633. $subscription,  
  1634. $this 
  1635. ); 
  1636.  
  1637. /** 
  1638. * Set initial protection for front-end. 
  1639. * This function is only executed when the current user is no Admin user. 
  1640. * Hide restricted content for this membership. 
  1641. * @since 1.0.0 
  1642. * @internal 
  1643. */ 
  1644. public function protect_content() { 
  1645. do_action( 
  1646. 'ms_model_membership_protect_content_before',  
  1647. $this 
  1648. ); 
  1649.  
  1650. $rules = $this->get_rules_hierarchy(); 
  1651.  
  1652. // Apply protection settings of all rules (replace/hide contents, ...) 
  1653. foreach ( $rules as $rule ) { 
  1654. $rule->protect_content(); 
  1655.  
  1656. do_action( 
  1657. 'ms_model_membership_protect_content_after',  
  1658. $this 
  1659. ); 
  1660.  
  1661. /** 
  1662. * Set initial protection for admin side. 
  1663. * Hide restricted content for this membership. 
  1664. * @since 1.1 
  1665. * @internal 
  1666. */ 
  1667. public function protect_admin_content() { 
  1668. do_action( 
  1669. 'ms_model_membership_protect_content_before',  
  1670. $this 
  1671. ); 
  1672.  
  1673. $rules = $this->get_rules_hierarchy(); 
  1674.  
  1675. foreach ( $rules as $rule ) { 
  1676. $rule->protect_admin_content(); 
  1677.  
  1678. do_action( 
  1679. 'ms_model_membership_protect_content_after',  
  1680. $this 
  1681. ); 
  1682.  
  1683. /** 
  1684. * Checks if the user is allowed to change the payment details for the 
  1685. * current membership. 
  1686. * Payment details can only be changed when 
  1687. * (A) no payment details were saved yet - OR - 
  1688. * (B) no members signed up for the memberhips 
  1689. * @since 1.0.4.5 
  1690. * @api 
  1691. * @return bool 
  1692. */ 
  1693. public function can_change_payment() { 
  1694. // Allow if Membership is new/unsaved. 
  1695. if ( empty( $this->id ) ) { return true; } 
  1696.  
  1697. // Allow if no payment detail was entered yet (incomplete setup). 
  1698. if ( empty( $this->payment_type ) ) { return true; } 
  1699.  
  1700. // Allow if no members signed up yet. 
  1701. $members = MS_Model_Relationship::get_subscription_count( 
  1702. array( 'membership_id' => $this->id ) 
  1703. ); 
  1704. if ( empty( $members ) ) { return true; } 
  1705.  
  1706. // Otherwise payment details cannot be changed anymore. 
  1707. return false; 
  1708.  
  1709. /** 
  1710. * Returns property associated with the render. 
  1711. * @since 1.0.0 
  1712. * @internal 
  1713. * @param string $property The name of a property. 
  1714. * @return mixed Returns mixed value of a property or NULL if a property doesn't exist. 
  1715. */ 
  1716. public function __get( $property ) { 
  1717. $value = null; 
  1718.  
  1719. switch ( $property ) { 
  1720. case 'type': 
  1721. switch ( $this->type ) { 
  1722. case self::TYPE_BASE: 
  1723. case self::TYPE_GUEST: 
  1724. case self::TYPE_USER: 
  1725. case self::TYPE_DRIPPED: 
  1726. break; 
  1727.  
  1728. default: 
  1729. $this->type = self::TYPE_STANDARD; 
  1730. break; 
  1731.  
  1732. $value = $this->type; 
  1733. break; 
  1734.  
  1735. case 'payment_type': 
  1736. $types = self::get_payment_types(); 
  1737. if ( ! array_key_exists( $this->payment_type, $types ) ) { 
  1738. $this->payment_type = self::PAYMENT_TYPE_PERMANENT; 
  1739. $value = $this->payment_type; 
  1740. break; 
  1741.  
  1742. case 'trial_period_enabled': 
  1743. case 'active': 
  1744. case 'private': 
  1745. case 'is_free': 
  1746. $value = lib2()->is_true( $this->$property ); 
  1747. break; 
  1748.  
  1749. case 'type_description': 
  1750. $value = $this->get_type_description(); 
  1751. break; 
  1752.  
  1753. case 'period_unit': 
  1754. $value = MS_Helper_Period::get_period_value( $this->period, 'period_unit' ); 
  1755. break; 
  1756.  
  1757. case 'period_type': 
  1758. $value = MS_Helper_Period::get_period_value( $this->period, 'period_type' ); 
  1759. break; 
  1760.  
  1761. case 'pay_cycle_period_unit': 
  1762. $value = MS_Helper_Period::get_period_value( $this->pay_cycle_period, 'period_unit' ); 
  1763. break; 
  1764.  
  1765. case 'pay_cycle_period_type': 
  1766. $value = MS_Helper_Period::get_period_value( $this->pay_cycle_period, 'period_type' ); 
  1767. break; 
  1768.  
  1769. case 'trial_period_unit': 
  1770. $value = MS_Helper_Period::get_period_value( $this->trial_period, 'period_unit' ); 
  1771. break; 
  1772.  
  1773. case 'trial_period_type': 
  1774. $value = MS_Helper_Period::get_period_value( $this->trial_period, 'period_type' ); 
  1775. break; 
  1776.  
  1777. case 'price': 
  1778. if ( $this->is_free() ) { 
  1779. $value = 0; 
  1780. } else { 
  1781. $value = $this->price; 
  1782. break; 
  1783.  
  1784. case 'total_price': 
  1785. if ( $this->is_free() ) { 
  1786. $value = 0; 
  1787. } else { 
  1788. $value = $this->price; 
  1789.  
  1790. $value = apply_filters( 
  1791. 'ms_apply_taxes',  
  1792. $value,  
  1793. $this 
  1794. ); 
  1795. break; 
  1796.  
  1797. case 'pay_cycle_repetitions': 
  1798. $value = absint( $this->pay_cycle_repetitions ); 
  1799. break; 
  1800.  
  1801. case 'disabled_gateways': 
  1802. $value = lib2()->array->get( $this->disabled_gateways ); 
  1803. break; 
  1804.  
  1805. default: 
  1806. if ( property_exists( $this, $property ) ) { 
  1807. $value = $this->$property; 
  1808. break; 
  1809.  
  1810. return apply_filters( 
  1811. 'ms_model_membership__get',  
  1812. $value,  
  1813. $property,  
  1814. $this 
  1815. ); 
  1816.  
  1817. /** 
  1818. * Validate specific property before set. 
  1819. * @since 1.0.0 
  1820. * @internal 
  1821. * @param string $property The name of a property to associate. 
  1822. * @param mixed $value The value of a property. 
  1823. */ 
  1824. public function __set( $property, $value ) { 
  1825. if ( property_exists( $this, $property ) ) { 
  1826. switch ( $property ) { 
  1827. case 'name': 
  1828. case 'title': 
  1829. $this->$property = sanitize_text_field( $value ); 
  1830. break; 
  1831.  
  1832. case 'description': 
  1833. $this->$property = wp_kses( $value, 'post' ); 
  1834. break; 
  1835.  
  1836. case 'type': 
  1837. switch ( $value ) { 
  1838. case self::TYPE_BASE: 
  1839. case self::TYPE_GUEST: 
  1840. case self::TYPE_USER: 
  1841. // Only one instance of these types can exist. 
  1842. $existing = $this->_get_system_membership( $value, false ); 
  1843.  
  1844. if ( $existing && $existing->id != $this->id ) { 
  1845. $value = self::TYPE_STANDARD; 
  1846. } else { 
  1847. $this->active = true; 
  1848. $this->private = true; 
  1849. $this->is_free = true; 
  1850. $this->price = 0; 
  1851. $this->post_name = sanitize_html_class( $this->title ); 
  1852. $this->payment_type = self::PAYMENT_TYPE_PERMANENT; 
  1853. $this->post_author = get_current_user_id(); 
  1854. break; 
  1855.  
  1856. case self::TYPE_DRIPPED: 
  1857. break; 
  1858.  
  1859. default: 
  1860. $value = self::TYPE_STANDARD; 
  1861. break; 
  1862.  
  1863. $this->type = $value; 
  1864. break; 
  1865.  
  1866. case 'payment_type': 
  1867. $types = self::get_payment_types(); 
  1868. if ( array_key_exists( $value, $types ) ) { 
  1869. if ( $this->can_change_payment() ) { 
  1870. $this->payment_type = $value; 
  1871. } elseif ( $this->payment_type != $value ) { 
  1872. $error = 'Payment type cannot be changed after members have signed up.'; 
  1873. MS_Helper_Debug::log( $error ); 
  1874. throw new Exception( $error ); 
  1875. } else { 
  1876. throw new Exception( 'Invalid membership type.' ); 
  1877. break; 
  1878.  
  1879. case 'trial_period_enabled': 
  1880. case 'active': 
  1881. case 'private': 
  1882. case 'is_free': 
  1883. $this->$property = lib2()->is_true( $value ); 
  1884. break; 
  1885.  
  1886. case 'price': 
  1887. case 'trial_price': 
  1888. $this->$property = floatval( $value ); 
  1889. break; 
  1890.  
  1891. case 'pay_cycle_repetitions': 
  1892. $this->$property = absint( $value ); 
  1893. break; 
  1894.  
  1895. case 'period': 
  1896. case 'pay_cycle_period': 
  1897. case 'trial_period': 
  1898. $this->$property = $this->validate_period( $value ); 
  1899. break; 
  1900.  
  1901. case 'period_date_start': 
  1902. case 'period_date_end': 
  1903. $this->$property = $this->validate_date( $value ); 
  1904. break; 
  1905.  
  1906. case 'on_end_membership_id': 
  1907. if ( 0 == $value ) { 
  1908. $this->$property = 0; 
  1909. } else if ( 0 < MS_Factory::load( 'MS_Model_Membership', $value )->id ) { 
  1910. $this->$property = $value; 
  1911. break; 
  1912.  
  1913. default: 
  1914. $this->$property = $value; 
  1915. break; 
  1916. } else { 
  1917. switch ( $property ) { 
  1918. case 'period_unit': 
  1919. $this->period['period_unit'] = $this->validate_period_unit( $value ); 
  1920. break; 
  1921.  
  1922. case 'period_type': 
  1923. $this->period['period_type'] = $this->validate_period_type( $value ); 
  1924. break; 
  1925.  
  1926. case 'pay_cycle_period_unit': 
  1927. $this->pay_cycle_period['period_unit'] = $this->validate_period_unit( $value ); 
  1928. break; 
  1929.  
  1930. case 'pay_cycle_period_type': 
  1931. $this->pay_cycle_period['period_type'] = $this->validate_period_type( $value ); 
  1932. break; 
  1933.  
  1934. case 'trial_period_unit': 
  1935. $this->trial_period['period_unit'] = $this->validate_period_unit( $value ); 
  1936. break; 
  1937.  
  1938. case 'trial_period_type': 
  1939. $this->trial_period['period_type'] = $this->validate_period_type( $value ); 
  1940. break; 
  1941.  
  1942. do_action( 
  1943. 'ms_model_membership__set_after',  
  1944. $property,  
  1945. $value,  
  1946. $this 
  1947. ); 
  1948.