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