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

  1. <?php 
  2. /** 
  3. * Subscription model (former "Membership Relationship"). 
  4. * 
  5. * The Subscription defines which user has access to which membership and for 
  6. * how long. Do not confuse this with an invoice - a single subscription can 
  7. * have multiple invoices! 
  8. * 
  9. * Note that all properties are declared protected but they can be access 
  10. * directly (e.g. `$membership->type` to get the type value). 
  11. * There are magic methods \_\_get() and \_\_set() that do some validation before 
  12. * accessing the properties. 
  13. * 
  14. * @since 1.0.0 
  15. * @package Membership2 
  16. * @subpackage Model 
  17. */ 
  18. class MS_Model_Relationship extends MS_Model_CustomPostType { 
  19.  
  20. /** 
  21. * Model custom post type. 
  22. * 
  23. * @since 1.0.0 
  24. * @internal Use self::get_post_type() instead! 
  25. * @var string $POST_TYPE 
  26. */ 
  27. protected static $POST_TYPE = 'ms_relationship'; 
  28.  
  29. /** 
  30. * Membership Relationship Status constants. 
  31. * 
  32. * @since 1.0.0 
  33. * @see $status $status property. 
  34. */ 
  35. const STATUS_PENDING = 'pending'; 
  36.  
  37. /** 
  38. * Membership Relationship Status constants. 
  39. * 
  40. * @since 1.0.0 
  41. * @see $status $status property. 
  42. */ 
  43. const STATUS_ACTIVE = 'active'; 
  44.  
  45. /** 
  46. * Membership Relationship Status constants. 
  47. * 
  48. * @since 1.0.0 
  49. * @see $status $status property. 
  50. */ 
  51. const STATUS_TRIAL = 'trial'; 
  52.  
  53. /** 
  54. * Membership Relationship Status constants. 
  55. * 
  56. * @since 1.0.0 
  57. * @see $status $status property. 
  58. */ 
  59. const STATUS_TRIAL_EXPIRED = 'trial_expired'; 
  60.  
  61. /** 
  62. * Membership Relationship Status constants. 
  63. * Start-Date not reached yet. 
  64. * 
  65. * @since 1.0.0 
  66. * @see $status $status property. 
  67. */ 
  68. const STATUS_WAITING = 'waiting'; 
  69.  
  70. /** 
  71. * Membership Relationship Status constants. 
  72. * End-Date reached. 
  73. * 
  74. * @since 1.0.0 
  75. * @see $status $status property. 
  76. */ 
  77. const STATUS_EXPIRED = 'expired'; 
  78.  
  79. /** 
  80. * Membership Relationship Status constants. 
  81. * 
  82. * @since 1.0.0 
  83. * @see $status $status property. 
  84. */ 
  85. const STATUS_DEACTIVATED = 'deactivated'; 
  86.  
  87. /** 
  88. * Membership Relationship Status constants. 
  89. * 
  90. * @since 1.0.0 
  91. * @see $status $status property. 
  92. */ 
  93. const STATUS_CANCELED = 'canceled'; 
  94.  
  95. /** 
  96. * The Membership ID. 
  97. * 
  98. * @since 1.0.0 
  99. * @var string $membership_id 
  100. */ 
  101. protected $membership_id; 
  102.  
  103. /** 
  104. * The Payment Gateway ID. 
  105. * 
  106. * @since 1.0.0 
  107. * @var string $gateway_id 
  108. */ 
  109. protected $gateway_id; 
  110.  
  111. /** 
  112. * The start date of the membership relationship. 
  113. * 
  114. * @since 1.0.0 
  115. * @var string $start_date 
  116. */ 
  117. protected $start_date; 
  118.  
  119. /** 
  120. * The expire date of the membership relationship. 
  121. * 
  122. * @since 1.0.0 
  123. * @var string $expire_date 
  124. */ 
  125. protected $expire_date; 
  126.  
  127. /** 
  128. * The trial expire date of the membership relationship. 
  129. * 
  130. * @since 1.0.0 
  131. * @var string $trial_expire_date 
  132. */ 
  133. protected $trial_expire_date; 
  134.  
  135. /** 
  136. * Trial period completed flag. 
  137. * 
  138. * Indicates if already used a trial period and can't have another trial period. 
  139. * 
  140. * @since 1.0.0 
  141. * @var string $trial_period_completed 
  142. */ 
  143. protected $trial_period_completed; 
  144.  
  145. /** 
  146. * The status of the membership relationship. 
  147. * 
  148. * @since 1.0.0 
  149. * @var string $status 
  150. */ 
  151. protected $status; 
  152.  
  153. /** 
  154. * Current invoice count. 
  155. * This is NOT the public invoice-number but an counter to determine how 
  156. * many invoices were generated for this subscription already. 
  157. * 
  158. * @since 1.0.0 
  159. * @var $current_invoice_number 
  160. */ 
  161. protected $current_invoice_number = 1; 
  162.  
  163. /** 
  164. * The moving/change/downgrade/upgrade from membership ID. 
  165. * 
  166. * @since 1.0.0 
  167. * @internal 
  168. * @var string $move_from_id 
  169. */ 
  170. protected $move_from_id; 
  171.  
  172. /** 
  173. * Where the data came from. Can only be changed by data import tool 
  174. * 
  175. * @since 1.1.0 
  176. * @internal 
  177. * @var string 
  178. */ 
  179. protected $source = ''; 
  180.  
  181. /** 
  182. * The number of successful payments that were made for this subscription. 
  183. * 
  184. * We use this value to determine the end of a recurring payment plan. 
  185. * Also this information is displayed in the member-info popup (only for 
  186. * admins; see MS_View_Member_Dialog) 
  187. * 
  188. * @since 1.1.0 
  189. * @var array { 
  190. * A list of all payments that were made [since 1.1.0] 
  191. * 
  192. * string $date Payment date/time. 
  193. * number $amount Payment-Amount. 
  194. * string $gateway Gateway that confirmed payment. 
  195. * } 
  196. */ 
  197. protected $payments = array(); 
  198.  
  199. /** 
  200. * Flag that keeps track, if this subscription is a simulated or a real one. 
  201. * 
  202. * @since 1.1.0 
  203. * @internal 
  204. * @var bool 
  205. */ 
  206. protected $is_simulated = false; 
  207.  
  208. /** 
  209. * The related membership model object. 
  210. * 
  211. * @since 1.0.0 
  212. * @var MS_Model_Membership $membership 
  213. */ 
  214. private $membership; 
  215.  
  216. // 
  217. // 
  218. // 
  219. // -------------------------------------------------------------- COLLECTION 
  220.  
  221. /** 
  222. * Returns the post-type of the current object. 
  223. * 
  224. * @since 2.0.0 
  225. * @api 
  226. * 
  227. * @return string The post-type name. 
  228. */ 
  229. public static function get_post_type() { 
  230. return parent::_post_type( self::$POST_TYPE ); 
  231.  
  232. /** 
  233. * Get custom register post type args for this model. 
  234. * 
  235. * @since 1.0.0 
  236. * @internal 
  237. */ 
  238. public static function get_register_post_type_args() { 
  239. $args = array( 
  240. 'label' => __( 'Membership2 Subscriptions', MS_TEXT_DOMAIN ),  
  241. ); 
  242.  
  243. return apply_filters( 
  244. 'ms_customposttype_register_args',  
  245. $args,  
  246. self::get_post_type() 
  247. ); 
  248.  
  249. /** 
  250. * Don't persist this fields. 
  251. * 
  252. * @since 1.0.0 
  253. * @internal 
  254. * 
  255. * @var string[] The fields to ignore when persisting. 
  256. */ 
  257. static public $ignore_fields = array( 
  258. 'membership',  
  259. 'post_type',  
  260. ); 
  261.  
  262. /** 
  263. * Return existing status types and names. 
  264. * 
  265. * @since 1.0.0 
  266. * @internal 
  267. * 
  268. * @return array{ 
  269. * Return array of ( $type => name ); 
  270. * @type string $type The status type. 
  271. * @type string $name The status name. 
  272. * } 
  273. */ 
  274. public static function get_status_types() { 
  275. $status_types = array( 
  276. self::STATUS_PENDING => __( 'Pending', MS_TEXT_DOMAIN ),  
  277. self::STATUS_ACTIVE => __( 'Active', MS_TEXT_DOMAIN ),  
  278. self::STATUS_TRIAL => __( 'Trial', MS_TEXT_DOMAIN ),  
  279. self::STATUS_TRIAL_EXPIRED => __( 'Trial Expired', MS_TEXT_DOMAIN ),  
  280. self::STATUS_EXPIRED => __( 'Expired', MS_TEXT_DOMAIN ),  
  281. self::STATUS_DEACTIVATED => __( 'Deactivated', MS_TEXT_DOMAIN ),  
  282. self::STATUS_CANCELED => __( 'Canceled', MS_TEXT_DOMAIN ),  
  283. self::STATUS_WAITING => __( 'Not yet active', MS_TEXT_DOMAIN ),  
  284. ); 
  285.  
  286. return apply_filters( 
  287. 'ms_model_relationship_get_status_types',  
  288. $status_types 
  289. ); 
  290.  
  291. /** 
  292. * Create a new membership relationship. 
  293. * 
  294. * Search for existing relationship (unique object), creating if not exists. 
  295. * Set initial status. 
  296. * 
  297. * @since 1.0.0 
  298. * @internal 
  299. * 
  300. * @return MS_Model_Relationship The created relationship. 
  301. */ 
  302. public static function create_ms_relationship( 
  303. $membership_id = 0,  
  304. $user_id = 0,  
  305. $gateway_id = 'admin',  
  306. $move_from_id = 0 
  307. ) { 
  308. do_action( 
  309. 'ms_model_relationship_create_ms_relationship_before',  
  310. $membership_id,  
  311. $user_id,  
  312. $gateway_id,  
  313. $move_from_id 
  314. ); 
  315.  
  316. if ( MS_Model_Membership::is_valid_membership( $membership_id ) ) { 
  317. $subscription = self::_create_ms_relationship( 
  318. $membership_id,  
  319. $user_id,  
  320. $gateway_id,  
  321. $move_from_id 
  322. ); 
  323. } else { 
  324. $subscription = null; 
  325. MS_Helper_Debug::log( 
  326. 'Invalid membership_id: ' . 
  327. "$membership_id, ms_relationship not created for $user_id, $gateway_id, $move_from_id" 
  328. ); 
  329. MS_Helper_Debug::debug_trace(); 
  330.  
  331. return apply_filters( 
  332. 'ms_model_relationship_create_ms_relationship',  
  333. $subscription,  
  334. $membership_id,  
  335. $user_id,  
  336. $gateway_id,  
  337. $move_from_id 
  338. ); 
  339.  
  340. /** 
  341. * Helper function called by create_ms_relationship() 
  342. * 
  343. * @since 1.0.0 
  344. * @internal 
  345. * 
  346. * @return MS_Model_Relationship The created relationship. 
  347. */ 
  348. private static function _create_ms_relationship( $membership_id, $user_id, $gateway_id, $move_from_id ) { 
  349. $is_simulated = false; 
  350.  
  351. // Try to reuse existing db record to keep history. 
  352. $subscription = self::get_subscription( $user_id, $membership_id ); 
  353.  
  354. if ( 'simulation' == $gateway_id ) { 
  355. $is_simulated = true; 
  356. $gateway_id = 'admin'; 
  357. $subscription = false; 
  358.  
  359. // Not found, create a new one. 
  360. if ( empty( $subscription ) ) { 
  361. $subscription = MS_Factory::create( 'MS_Model_Relationship' ); 
  362. $subscription->membership_id = $membership_id; 
  363. $subscription->user_id = $user_id; 
  364. $subscription->status = self::STATUS_PENDING; 
  365. $subscription->is_simulated = $is_simulated; 
  366.  
  367. if ( $is_simulated ) { 
  368. $subscription->id = -1; 
  369.  
  370. // Always update these fields. 
  371. $subscription->move_from_id = $move_from_id; 
  372. $subscription->gateway_id = $gateway_id; 
  373. $subscription->expire_date = ''; 
  374.  
  375. // Set initial state. 
  376. switch ( $subscription->status ) { 
  377. case self::STATUS_DEACTIVATED: 
  378. $subscription->status = self::STATUS_PENDING; 
  379. break; 
  380.  
  381. case self::STATUS_TRIAL: 
  382. case self::STATUS_TRIAL_EXPIRED: 
  383. case self::STATUS_ACTIVE: 
  384. case self::STATUS_EXPIRED: 
  385. case self::STATUS_CANCELED: 
  386. /** Once a member or have tried the membership, not 
  387. * eligible to another trial period, unless the relationship 
  388. * is permanetly deleted. 
  389. */ 
  390. $subscription->trial_period_completed = true; 
  391. break; 
  392.  
  393. case self::STATUS_PENDING: 
  394. default: 
  395. // Initial status 
  396. $subscription->name = "user_id: $user_id, membership_id: $membership_id"; 
  397. $subscription->description = $subscription->name; 
  398. $subscription->trial_period_completed = false; 
  399. break; 
  400.  
  401. // Set the start/expire dates. Do this *after* the set_status() call! 
  402. $subscription->config_period(); 
  403. $membership = $subscription->get_membership(); 
  404.  
  405. if ( 'admin' == $gateway_id || $membership->is_free() ) { 
  406. $subscription->trial_period_completed = true; 
  407. $subscription->status = self::STATUS_ACTIVE; 
  408.  
  409. // Set the start/expire dates. Do this *after* the set_status() call! 
  410. $subscription->config_period(); 
  411.  
  412. if ( ! $subscription->is_system() && ! $is_simulated ) { 
  413. // Create event. 
  414. MS_Model_Event::save_event( MS_Model_Event::TYPE_MS_SIGNED_UP, $subscription ); 
  415.  
  416. $subscription->save(); 
  417.  
  418. return $subscription; 
  419.  
  420. /** 
  421. * Retrieve membership relationships. 
  422. * 
  423. * By default returns a list of relationships that are not "pending" or 
  424. * "deactivated". To get a list of all relationships use this: 
  425. * $args = array( 'status' => 'all' ) 
  426. * 
  427. * @since 1.0.0 
  428. * @internal 
  429. * 
  430. * @param array $args The query post args 
  431. * @see @link http://codex.wordpress.org/Class_Reference/WP_Query 
  432. * @param bool $include_system Whether to include the base/guest memberships. 
  433. * @return MS_Model_Relationship[] The array of membership relationships. 
  434. */ 
  435. public static function get_subscriptions( $args = null, $include_system = false ) { 
  436. $args = self::get_query_args( $args ); 
  437.  
  438. MS_Factory::select_blog(); 
  439. $query = new WP_Query( $args ); 
  440. $posts = $query->get_posts(); 
  441. MS_Factory::revert_blog(); 
  442. $subscriptions = array(); 
  443.  
  444. if ( ! empty( $posts ) ) { 
  445. foreach ( $posts as $post_id ) { 
  446. $ms_relationship = MS_Factory::load( 
  447. 'MS_Model_Relationship',  
  448. $post_id 
  449. ); 
  450.  
  451. // Remove System-Memberships 
  452. if ( $ms_relationship->is_system() && ! $include_system ) { 
  453. continue; 
  454.  
  455. if ( ! empty( $args['author'] ) ) { 
  456. $subscriptions[ $ms_relationship->membership_id ] = $ms_relationship; 
  457. } else { 
  458. $subscriptions[ $post_id ] = $ms_relationship; 
  459.  
  460. $subscriptions = apply_filters( 
  461. 'ms_model_relationship_get_subscriptions',  
  462. $subscriptions,  
  463. $args 
  464. ); 
  465.  
  466. return $subscriptions; 
  467.  
  468. /** 
  469. * Retrieve membership relationship count. 
  470. * 
  471. * @since 1.0.0 
  472. * @internal 
  473. * 
  474. * @param $args The query post args 
  475. * @see @link http://codex.wordpress.org/Class_Reference/WP_Query 
  476. * @return int The membership relationship count. 
  477. */ 
  478. public static function get_subscription_count( $args = null ) { 
  479. $args = apply_filters( 
  480. 'ms_model_relationship_get_subscription_count_args',  
  481. self::get_query_args( $args ) 
  482. ); 
  483. MS_Factory::select_blog(); 
  484. $query = new WP_Query( $args ); 
  485. $count = $query->found_posts; 
  486. MS_Factory::revert_blog(); 
  487.  
  488. return apply_filters( 
  489. 'ms_model_relationship_get_subscription_count',  
  490. $count,  
  491. $args 
  492. ); 
  493.  
  494. /** 
  495. * Retrieve membership relationship. 
  496. * 
  497. * @since 1.0.0 
  498. * @internal 
  499. * 
  500. * @param int $user_id The user id 
  501. * @return int $membership_id The membership id. 
  502. */ 
  503. public static function get_subscription( $user_id, $membership_id ) { 
  504. $args = apply_filters( 
  505. 'ms_model_relationship_get_subscription_args',  
  506. self::get_query_args( 
  507. array( 
  508. 'user_id' => $user_id,  
  509. 'membership_id' => $membership_id,  
  510. 'status' => 'all',  
  511. ); 
  512.  
  513. MS_Factory::select_blog(); 
  514. $query = new WP_Query( $args ); 
  515. $post = $query->get_posts(); 
  516. MS_Factory::revert_blog(); 
  517.  
  518. $subscription = null; 
  519.  
  520. if ( ! empty( $post[0] ) ) { 
  521. $subscription = MS_Factory::load( 
  522. 'MS_Model_Relationship',  
  523. $post[0] 
  524. ); 
  525.  
  526. return apply_filters( 
  527. 'ms_model_relationship_get_subscription',  
  528. $subscription,  
  529. $args 
  530. ); 
  531.  
  532. /** 
  533. * Create default args to search posts. 
  534. * 
  535. * Merge received args to default ones. 
  536. * 
  537. * @since 1.0.0 
  538. * @internal 
  539. * 
  540. * @param $args The query post args 
  541. * @see @link http://codex.wordpress.org/Class_Reference/WP_Query 
  542. * @return array The args. 
  543. */ 
  544. public static function get_query_args( $args = null ) { 
  545. $defaults = apply_filters( 
  546. 'ms_model_relationship_get_query_args_defaults',  
  547. array( 
  548. 'post_type' => self::get_post_type(),  
  549. 'post_status' => 'any',  
  550. 'fields' => 'ids',  
  551. 'nopaging' => true,  
  552. ); 
  553.  
  554. $args = wp_parse_args( $args, $defaults ); 
  555.  
  556. // Set filter arguments 
  557. if ( ! empty( $args['user_id'] ) ) { 
  558. $args['author'] = $args['user_id']; 
  559. unset( $args['user_id'] ); 
  560.  
  561. if ( ! empty( $args['membership_id'] ) ) { 
  562. $args['meta_query']['membership_id'] = array( 
  563. 'key' => 'membership_id',  
  564. 'value' => $args['membership_id'],  
  565. ); 
  566. unset( $args['membership_id'] ); 
  567.  
  568. if ( ! empty( $args['gateway_id'] ) ) { 
  569. $args['meta_query']['gateway_id'] = array( 
  570. 'key' => 'gateway_id',  
  571. 'value' => $args['gateway_id'],  
  572. ); 
  573. unset( $args['gateway_id'] ); 
  574.  
  575. if ( ! empty( $args['status'] ) ) { 
  576. // Allowed status filters: 
  577. // 'valid' .. all status values except Deactivated 
  578. // <any other value except 'all'> 
  579. if ( 'valid' === $args['status'] ) { 
  580. $args['meta_query']['status'] = array( 
  581. 'key' => 'status',  
  582. 'value' => self::STATUS_DEACTIVATED,  
  583. 'compare' => 'NOT LIKE',  
  584. ); 
  585. } elseif ( 'all' !== $args['status'] ) { 
  586. $args['meta_query']['status'] = array( 
  587. 'key' => 'status',  
  588. 'value' => $args['status'],  
  589. 'compare' => 'LIKE',  
  590. ); 
  591.  
  592. // This is only reached when status === 'all' 
  593. unset( $args['status'] ); 
  594. } else { 
  595. $args['meta_query']['status'] = array( 
  596. 'key' => 'status',  
  597. 'value' => array( self::STATUS_DEACTIVATED, self::STATUS_PENDING ),  
  598. 'compare' => 'NOT IN',  
  599. ); 
  600.  
  601. return apply_filters( 
  602. 'ms_model_relationship_get_query_args',  
  603. $args,  
  604. $defaults 
  605. ); 
  606.  
  607.  
  608. // 
  609. // 
  610. // 
  611. // ------------------------------------------------------------- SINGLE ITEM 
  612.  
  613.  
  614. /** 
  615. * Cancel membership. 
  616. * 
  617. * @since 1.0.0 
  618. * @api 
  619. * 
  620. * @param bool $generate_event Optional. Defines if cancel events are generated. 
  621. */ 
  622. public function cancel_membership( $generate_event = true ) { 
  623. do_action( 
  624. 'ms_model_relationship_cancel_membership_before',  
  625. $this,  
  626. $generate_event 
  627. ); 
  628.  
  629. if ( self::STATUS_CANCELED == $this->status ) { return; } 
  630. if ( self::STATUS_DEACTIVATED == $this->status ) { return; } 
  631.  
  632. try { 
  633. // Canceling in trial period -> change the expired date. 
  634. if ( self::STATUS_TRIAL == $this->status ) { 
  635. $this->expire_date = $this->trial_expire_date; 
  636.  
  637. $this->status = $this->calculate_status( self::STATUS_CANCELED ); 
  638. $this->save(); 
  639.  
  640. // Cancel subscription in the gateway. 
  641. if ( $gateway = $this->get_gateway() ) { 
  642. $gateway->cancel_membership( $this ); 
  643.  
  644. // Remove any unpaid invoices. 
  645. $this->remove_unpaid_invoices(); 
  646.  
  647. if ( $generate_event ) { 
  648. MS_Model_Event::save_event( MS_Model_Event::TYPE_MS_CANCELED, $this ); 
  649. catch ( Exception $e ) { 
  650. MS_Helper_Debug::log( '[Error canceling membership]: '. $e->getMessage() ); 
  651.  
  652. do_action( 
  653. 'ms_model_relationship_cancel_membership_after',  
  654. $this,  
  655. $generate_event 
  656. ); 
  657.  
  658. /** 
  659. * Deactivate membership. 
  660. * 
  661. * Cancel membership and move to deactivated state. 
  662. * 
  663. * @since 1.0.0 
  664. * @api 
  665. */ 
  666. public function deactivate_membership() { 
  667. do_action( 
  668. 'ms_model_relationship_deactivate_membership_before',  
  669. $this 
  670. ); 
  671.  
  672. /** 
  673. * Documented in check_membership_status() 
  674. * 
  675. * @since 1.1.0.5 
  676. */ 
  677. if ( MS_Plugin::get_modifier( 'MS_LOCK_SUBSCRIPTIONS' ) ) { 
  678. return false; 
  679.  
  680. if ( self::STATUS_DEACTIVATED == $this->status ) { return; } 
  681.  
  682. try { 
  683. $this->cancel_membership( false ); 
  684. $this->status = self::STATUS_DEACTIVATED; 
  685. $this->save(); 
  686.  
  687. MS_Model_Event::save_event( 
  688. MS_Model_Event::TYPE_MS_DEACTIVATED,  
  689. $this 
  690. ); 
  691. catch( Exception $e ) { 
  692. MS_Helper_Debug::log( 
  693. '[Error deactivating membership]: '. $e->getMessage() 
  694. ); 
  695.  
  696. do_action( 
  697. 'ms_model_relationship_deactivate_membership_after',  
  698. $this 
  699. ); 
  700.  
  701. /** 
  702. * Save model. 
  703. * 
  704. * Only saves if is not admin user and not a visitor. 
  705. * Don't save automatically assigned visitor/system memberships. 
  706. * 
  707. * @since 1.0.0 
  708. * @api 
  709. */ 
  710. public function save() { 
  711. do_action( 'ms_model_relationship_save_before', $this ); 
  712.  
  713. if ( ! empty( $this->user_id ) 
  714. && ! MS_Model_Member::is_admin_user( $this->user_id ) 
  715. ) { 
  716. if ( ! $this->is_system() ) { 
  717. parent::save(); 
  718. parent::store_singleton(); 
  719.  
  720. do_action( 'ms_model_relationship_after', $this ); 
  721.  
  722. /** 
  723. * Removes any unpaid invoice that belongs to this subscription. 
  724. * 
  725. * @since 1.1.1.3 
  726. * @internal 
  727. */ 
  728. public function remove_unpaid_invoices() { 
  729. $invoices = $this->get_invoices(); 
  730.  
  731. foreach ( $invoices as $invoice ) { 
  732. if ( 'paid' != $invoice->status ) { 
  733. $invoice->delete(); 
  734.  
  735. /** 
  736. * Verify if the member can use the trial period. 
  737. * 
  738. * It returns FALSE if 
  739. * .. Trial Add-on is disabled 
  740. * .. The membership does not allow a trial period 
  741. * .. The current user already consumed trial period or is in trial period 
  742. * 
  743. * @since 1.0.0 
  744. * @api 
  745. * 
  746. * @return bool True if trial eligible. 
  747. */ 
  748. public function is_trial_eligible() { 
  749. $membership = $this->get_membership(); 
  750.  
  751. $trial_eligible_status = apply_filters( 
  752. 'ms_model_relationship_trial_eligible_status',  
  753. array( 
  754. self::STATUS_PENDING,  
  755. self::STATUS_DEACTIVATED,  
  756. ); 
  757.  
  758. $eligible = false; 
  759.  
  760. if ( ! MS_Model_Addon::is_enabled( MS_Model_Addon::ADDON_TRIAL ) ) { 
  761. // Trial Membership is globally disabled. 
  762. $eligible = false; 
  763. } elseif ( self::STATUS_TRIAL == $this->status ) { 
  764. // Subscription IS already in trial, so it's save to assume true. 
  765. $eligible = true; 
  766. } elseif ( ! in_array( $this->status, $trial_eligible_status ) ) { 
  767. // Current Subscription is not allowed for a trial membership anymore. 
  768. $eligible = false; 
  769. } elseif ( $this->trial_period_completed ) { 
  770. // Trial membership already consumed. 
  771. $eligible = false; 
  772. } elseif ( ! $membership->trial_period_enabled ) { 
  773. // Trial mode for this membership is disabled. 
  774. $eligible = false; 
  775. } else { 
  776. // All other cases: User can sign up for trial! 
  777. $eligible = true; 
  778.  
  779. return apply_filters( 
  780. 'ms_model_relationship_is_trial_eligible',  
  781. $eligible,  
  782. $this 
  783. ); 
  784.  
  785. /** 
  786. * Checks if the current subscription consumes a trial period. 
  787. * 
  788. * When the subscription either is currently in trial or was in trial before 
  789. * then this function returns true. 
  790. * If the subscription never was in trial status it returns false. 
  791. * 
  792. * @since 1.1.1.4 
  793. * @api 
  794. * 
  795. * @return bool 
  796. */ 
  797. public function has_trial() { 
  798. $result = false; 
  799.  
  800. if ( ! MS_Model_Addon::is_enabled( MS_Model_Addon::ADDON_TRIAL ) ) { 
  801. $result = false; 
  802. } elseif ( ! $this->trial_expire_date ) { 
  803. $result = false; 
  804. } elseif ( $this->trial_expire_date == $this->start_date ) { 
  805. $result = false; 
  806. } else { 
  807. $result = true; 
  808.  
  809. return $result; 
  810.  
  811. /** 
  812. * Set Membership Relationship start date. 
  813. * 
  814. * @since 1.0.0 
  815. * @api 
  816. * 
  817. * @param string $start_date Optional. The start date to set. 
  818. * Default will be calculated/current date. 
  819. */ 
  820. public function set_start_date( $start_date = null ) { 
  821. $membership = $this->get_membership(); 
  822.  
  823. if ( empty( $start_date ) ) { 
  824. if ( MS_Model_Membership::PAYMENT_TYPE_DATE_RANGE == $membership->payment_type ) { 
  825. $start_date = $membership->period_date_start; 
  826. } else { 
  827. /** 
  828. * Note that we pass TRUE as second param to current_date 
  829. * This is needed so that we 100% use the current date, which 
  830. * is required to successfully do simulation. 
  831. */ 
  832. $start_date = MS_Helper_Period::current_date( null, true ); 
  833.  
  834. $this->start_date = apply_filters( 
  835. 'ms_model_relationship_set_start_date',  
  836. $start_date,  
  837. $this 
  838. ); 
  839.  
  840. /** 
  841. * Set trial expire date. 
  842. * 
  843. * Validate to a date greater than start date. 
  844. * 
  845. * @since 1.0.0 
  846. * @api 
  847. * 
  848. * @param string $trial_expire_date Optional. The trial expire date to set. 
  849. * Default will be calculated based on start_date. 
  850. */ 
  851. public function set_trial_expire_date( $trial_expire_date = null ) { 
  852. if ( $this->is_trial_eligible() ) { 
  853. $valid_date = MS_Helper_Period::is_after( 
  854. $trial_expire_date,  
  855. $this->start_date 
  856. ); 
  857.  
  858. if ( ! $valid_date ) { 
  859. $trial_expire_date = $this->calc_trial_expire_date( $this->start_date ); 
  860.  
  861. /** 
  862. * When payment-type is DATE-RANGE make sure that the trial period 
  863. * is not longer than the specified end-date 
  864. */ 
  865. $membership = $this->get_membership(); 
  866. if ( MS_Model_Membership::PAYMENT_TYPE_DATE_RANGE == $membership->payment_type ) { 
  867. if ( $membership->period_date_end < $trial_expire_date ) { 
  868. $trial_expire_date = $membership->period_date_end; 
  869. } else { 
  870. // Do NOT set any trial-expire-date when trial period is not available! 
  871. $trial_expire_date = ''; 
  872.  
  873. $this->trial_expire_date = apply_filters( 
  874. 'ms_model_relationship_set_trial_start_date',  
  875. $trial_expire_date,  
  876. $this 
  877. ); 
  878.  
  879. // Subscriptions with this status have no valid expire-date. 
  880. $no_expire_date = array( 
  881. self::STATUS_DEACTIVATED,  
  882. self::STATUS_PENDING,  
  883. self::STATUS_TRIAL,  
  884. self::STATUS_TRIAL_EXPIRED,  
  885. ); 
  886.  
  887. if ( $this->trial_expire_date && in_array( $this->status, $no_expire_date ) ) { 
  888. // Set the expire date to trial-expire date 
  889. $this->expire_date = $this->trial_expire_date; 
  890.  
  891. /** 
  892. * Set trial expire date. 
  893. * 
  894. * Validate to a date greater than start date and trial expire date. 
  895. * 
  896. * @since 1.0.0 
  897. * @api 
  898. * 
  899. * @param string $expire_date Optional. The expire date to set. 
  900. * Default will be calculated based on start_date. 
  901. */ 
  902. public function set_expire_date( $expire_date = null ) { 
  903. $no_expire_date = array( 
  904. self::STATUS_DEACTIVATED,  
  905. self::STATUS_PENDING,  
  906. ); 
  907.  
  908. if ( ! in_array( $this->status, $no_expire_date ) ) { 
  909. $valid_date = MS_Helper_Period::is_after( 
  910. $expire_date,  
  911. $this->start_date,  
  912. $this->trial_expire_date 
  913. ); 
  914.  
  915. if ( ! $valid_date ) { 
  916. $expire_date = $this->calc_expire_date( $this->start_date ); 
  917. } else { 
  918. // Do NOT set any expire-date when subscription is not active! 
  919. $expire_date = ''; 
  920.  
  921. $this->expire_date = apply_filters( 
  922. 'ms_model_relationship_set_expire_date',  
  923. $expire_date,  
  924. $this 
  925. ); 
  926.  
  927. /** 
  928. * Calculate trial expire date. 
  929. * 
  930. * Based in the membership definition. 
  931. * 
  932. * @since 1.0.0 
  933. * @internal 
  934. * 
  935. * @param string $start_date Optional. The start date to calculate date from. 
  936. * @return string The calculated trial expire date. 
  937. */ 
  938. public function calc_trial_expire_date( $start_date = null ) { 
  939. $membership = $this->get_membership(); 
  940. $trial_expire_date = null; 
  941.  
  942. if ( empty( $start_date ) ) { 
  943. $start_date = $this->start_date; 
  944. if ( empty( $start_date ) ) { 
  945. $start_date = MS_Helper_Period::current_date(); 
  946.  
  947. if ( $this->is_trial_eligible() ) { 
  948. // Trial period was not consumed yet, calculate the expiration date. 
  949.  
  950. if ( MS_Model_Membership::PAYMENT_TYPE_DATE_RANGE == $membership->payment_type ) { 
  951. $from_date = $membership->period_date_start; 
  952. } else { 
  953. $from_date = $start_date; 
  954.  
  955. $period_unit = MS_Helper_Period::get_period_value( 
  956. $membership->trial_period,  
  957. 'period_unit' 
  958. ); 
  959. $period_type = MS_Helper_Period::get_period_value( 
  960. $membership->trial_period,  
  961. 'period_type' 
  962. ); 
  963.  
  964. $trial_expire_date = MS_Helper_Period::add_interval( 
  965. $period_unit,  
  966. $period_type,  
  967. $from_date 
  968. ); 
  969. } else { 
  970. // Subscription not entitled for trial anymore. Trial expires instantly. 
  971. $trial_expire_date = $start_date; 
  972.  
  973. return apply_filters( 
  974. 'ms_model_relationship_calc_trial_expire_date',  
  975. $trial_expire_date,  
  976. $start_date,  
  977. $this 
  978. ); 
  979.  
  980. /** 
  981. * Calculate expire date. 
  982. * 
  983. * Based in the membership definition 
  984. * 
  985. * @since 1.0.0 
  986. * @internal 
  987. * 
  988. * @param string $start_date Optional. The start date to calculate date from. 
  989. * @param bool $paid If the user made a payment to extend the expire date. 
  990. * @return string The calculated expire date. 
  991. */ 
  992. public function calc_expire_date( $start_date = null, $paid = false ) { 
  993. $membership = $this->get_membership(); 
  994. $gateway = $this->get_gateway(); 
  995.  
  996. $start_date = $this->calc_trial_expire_date( $start_date ); 
  997. $expire_date = null; 
  998.  
  999. /** 
  1000. * When in trial period and gateway does not send automatic recurring 
  1001. * payment notifications, the expire date is equal to trial expire date. 
  1002. */ 
  1003. if ( $this->is_trial_eligible() ) { 
  1004. $expire_date = $start_date; 
  1005. } else { 
  1006. if ( $paid ) { 
  1007. /** 
  1008. * Always extend the membership from current date or later, even if 
  1009. * the specified start-date is in the past. 
  1010. * 
  1011. * Example: User does not pay for 3 days (subscription set "pending") 
  1012. * Then he pays: The 3 days without access are for free; 
  1013. * his subscriptions is extended from current date! 
  1014. */ 
  1015. $today = MS_Helper_Period::current_date(); 
  1016. if ( MS_Helper_Period::is_after( $today, $start_date ) ) { 
  1017. $start_date = $today; 
  1018.  
  1019. /** 
  1020. * The gatway calls the payment handler URL automatically: 
  1021. * This means that the user does not need to re-authorize each 
  1022. * payment. 
  1023. */ 
  1024. switch ( $membership->payment_type ) { 
  1025. case MS_Model_Membership::PAYMENT_TYPE_PERMANENT: 
  1026. $expire_date = false; 
  1027. break; 
  1028.  
  1029. case MS_Model_Membership::PAYMENT_TYPE_FINITE: 
  1030. $period_unit = MS_Helper_Period::get_period_value( 
  1031. $membership->period,  
  1032. 'period_unit' 
  1033. ); 
  1034. $period_type = MS_Helper_Period::get_period_value( 
  1035. $membership->period,  
  1036. 'period_type' 
  1037. ); 
  1038. $expire_date = MS_Helper_Period::add_interval( 
  1039. $period_unit,  
  1040. $period_type,  
  1041. $start_date 
  1042. ); 
  1043. break; 
  1044.  
  1045. case MS_Model_Membership::PAYMENT_TYPE_DATE_RANGE: 
  1046. $expire_date = $membership->period_date_end; 
  1047. break; 
  1048.  
  1049. case MS_Model_Membership::PAYMENT_TYPE_RECURRING: 
  1050. $period_unit = MS_Helper_Period::get_period_value( 
  1051. $membership->pay_cycle_period,  
  1052. 'period_unit' 
  1053. ); 
  1054. $period_type = MS_Helper_Period::get_period_value( 
  1055. $membership->pay_cycle_period,  
  1056. 'period_type' 
  1057. ); 
  1058. $expire_date = MS_Helper_Period::add_interval( 
  1059. $period_unit,  
  1060. $period_type,  
  1061. $start_date 
  1062. ); 
  1063. break; 
  1064.  
  1065. return apply_filters( 
  1066. 'ms_model_relationship_calc_expire_date',  
  1067. $expire_date,  
  1068. $this 
  1069. ); 
  1070.  
  1071. /** 
  1072. * Configure the membership period dates based on the current subscription 
  1073. * status. 
  1074. * 
  1075. * Set initial membership period or renew periods. 
  1076. * 
  1077. * @since 1.0.0 
  1078. * @internal 
  1079. */ 
  1080. public function config_period() { // Needed because of status change. 
  1081. do_action( 
  1082. 'ms_model_relationship_config_period_before',  
  1083. $this 
  1084. ); 
  1085.  
  1086. switch ( $this->status ) { 
  1087. case self::STATUS_DEACTIVATED: 
  1088. case self::STATUS_PENDING: 
  1089. // Set initial start, trial and expire date. 
  1090. $this->set_start_date(); 
  1091. $this->set_trial_expire_date(); 
  1092. $this->set_expire_date(); 
  1093. break; 
  1094.  
  1095. case self::STATUS_EXPIRED: 
  1096. case self::STATUS_CANCELED: 
  1097. case self::STATUS_ACTIVE: 
  1098. /** 
  1099. * If no expire date is set yet then add it now. 
  1100. * This case happens when the subscription was added via the 
  1101. * Members admin page (by an admin and not by the member) 
  1102. * 
  1103. * Usually nothing is done here as the expire date is only 
  1104. * changed by add_payment(). 
  1105. */ 
  1106. if ( empty( $this->expire_date ) ) { 
  1107. $membership = $this->get_membership(); 
  1108. if ( MS_Model_Membership::PAYMENT_TYPE_PERMANENT != $membership->payment_type ) { 
  1109. $this->set_expire_date(); 
  1110. break; 
  1111.  
  1112. case self::STATUS_TRIAL: 
  1113. case self::STATUS_TRIAL_EXPIRED: 
  1114. $this->set_trial_expire_date(); 
  1115. break; 
  1116.  
  1117. default: 
  1118. do_action( 
  1119. 'ms_model_relationship_config_period_for_status_' . $this->status,  
  1120. $this 
  1121. ); 
  1122. break; 
  1123.  
  1124. do_action( 
  1125. 'ms_model_relationship_config_period_after',  
  1126. $this 
  1127. ); 
  1128.  
  1129. /** 
  1130. * Returns the number of days since the subscription started. 
  1131. * 
  1132. * Example: If the start_date is 14 days ago it will return the value 14. 
  1133. * 
  1134. * @since 1.0.0 
  1135. * @api 
  1136. * 
  1137. * @return int Remaining days. 
  1138. */ 
  1139. public function get_current_period() { 
  1140. $period_days = MS_Helper_Period::subtract_dates( 
  1141. MS_Helper_Period::current_date(),  
  1142. $this->start_date 
  1143. ); 
  1144.  
  1145. return apply_filters( 
  1146. 'ms_model_relationship_get_current_period',  
  1147. $period_days,  
  1148. $this 
  1149. ); 
  1150.  
  1151. /** 
  1152. * Returns the number of days until trial period ends. 
  1153. * 
  1154. * @since 1.0.0 
  1155. * @api 
  1156. * 
  1157. * @return int Remaining days. 
  1158. */ 
  1159. public function get_remaining_trial_period() { 
  1160. $period_days = MS_Helper_Period::subtract_dates( 
  1161. $this->trial_expire_date,  
  1162. MS_Helper_Period::current_date() 
  1163. ); 
  1164.  
  1165. return apply_filters( 
  1166. 'ms_model_relationship_get_remaining_trial_period',  
  1167. $period_days,  
  1168. $this 
  1169. ); 
  1170.  
  1171. /** 
  1172. * Get number of days until this membership expires. 
  1173. * 
  1174. * @since 1.0.0 
  1175. * @api 
  1176. * 
  1177. * @return int Remaining days. 
  1178. */ 
  1179. public function get_remaining_period() { 
  1180. $period_days = MS_Helper_Period::subtract_dates( 
  1181. $this->expire_date,  
  1182. MS_Helper_Period::current_date() 
  1183. ); 
  1184.  
  1185. return apply_filters( 
  1186. 'ms_model_relationship_get_remaining_period',  
  1187. $period_days,  
  1188. $this 
  1189. ); 
  1190.  
  1191. /** 
  1192. * Get Member model of the subscription owner. 
  1193. * 
  1194. * @since 1.0.0 
  1195. * @api 
  1196. * 
  1197. * @return MS_Model_Member The member object. 
  1198. */ 
  1199. public function get_member() { 
  1200. $member = null; 
  1201.  
  1202. if ( ! empty( $this->user_id ) ) { 
  1203. $member = MS_Factory::load( 'MS_Model_Member', $this->user_id ); 
  1204.  
  1205. return apply_filters( 
  1206. 'ms_model_relationship_get_member',  
  1207. $member 
  1208. ); 
  1209.  
  1210. /** 
  1211. * Convenience function to access current invoice for this subscription. 
  1212. * 
  1213. * @since 1.1.1.4 
  1214. * @api 
  1215. * 
  1216. * @return MS_Model_Invoice 
  1217. */ 
  1218. public function get_current_invoice( $create_missing = true ) { 
  1219. return MS_Model_Invoice::get_current_invoice( $this, $create_missing ); 
  1220.  
  1221. /** 
  1222. * Convenience function to access next invoice for this subscription. 
  1223. * 
  1224. * @since 1.1.1.4 
  1225. * @api 
  1226. * 
  1227. * @return MS_Model_Invoice 
  1228. */ 
  1229. public function get_next_invoice( $create_missing = true ) { 
  1230. return MS_Model_Invoice::get_next_invoice( $this, $create_missing ); 
  1231.  
  1232. /** 
  1233. * Convenience function to access previous invoice for this subscription. 
  1234. * 
  1235. * @since 1.1.1.4 
  1236. * @api 
  1237. * 
  1238. * @return MS_Model_Invoice 
  1239. */ 
  1240. public function get_previous_invoice( $status = null ) { 
  1241. return MS_Model_Invoice::get_previous_invoice( $this, $status ); 
  1242.  
  1243. /** 
  1244. * Get a list of all invoices linked to this relationship 
  1245. * 
  1246. * @since 1.1.0 
  1247. * @api 
  1248. * 
  1249. * @return MS_Model_Invoice[] 
  1250. */ 
  1251. public function get_invoices() { 
  1252. $invoices = MS_Model_Invoice::get_invoices( 
  1253. array( 
  1254. 'nopaging' => true,  
  1255. 'meta_query' => array( 
  1256. array( 
  1257. 'key' => 'ms_relationship_id',  
  1258. 'value' => $this->id,  
  1259. ),  
  1260. ),  
  1261. ); 
  1262.  
  1263. return apply_filters( 
  1264. 'ms_model_relationship_get_invoices',  
  1265. $invoices 
  1266. ); 
  1267.  
  1268. /** 
  1269. * Get related Membership model. 
  1270. * 
  1271. * @since 1.0.0 
  1272. * @api 
  1273. * 
  1274. * @return MS_Model_Membership The membership model. 
  1275. */ 
  1276. public function get_membership() { 
  1277. if ( empty( $this->membership->id ) ) { 
  1278. $this->membership = MS_Factory::load( 
  1279. 'MS_Model_Membership',  
  1280. $this->membership_id,  
  1281. $this->id 
  1282. ); 
  1283.  
  1284. // Set the context of the membership to current subscription. 
  1285. $this->membership->subscription_id = $this->id; 
  1286.  
  1287. return apply_filters( 
  1288. 'ms_model_relationship_get_membership',  
  1289. $this->membership 
  1290. ); 
  1291.  
  1292. /** 
  1293. * Returns true if the related membership is the base-membership. 
  1294. * 
  1295. * @since 1.1.0 
  1296. * @api 
  1297. * 
  1298. * @return bool 
  1299. */ 
  1300. public function is_base() { 
  1301. return $this->get_membership()->is_base(); 
  1302.  
  1303. /** 
  1304. * Returns true if the related membership is the guest-membership. 
  1305. * 
  1306. * @since 1.1.0 
  1307. * @api 
  1308. * 
  1309. * @return bool 
  1310. */ 
  1311. public function is_guest() { 
  1312. return $this->get_membership()->is_guest(); 
  1313.  
  1314. /** 
  1315. * Returns true if the related membership is the user-membership. 
  1316. * 
  1317. * @since 1.1.0 
  1318. * @api 
  1319. * 
  1320. * @return bool 
  1321. */ 
  1322. public function is_user() { 
  1323. return $this->get_membership()->is_user(); 
  1324.  
  1325. /** 
  1326. * Returns true if the related membership is a system membership. 
  1327. * 
  1328. * @since 1.1.0 
  1329. * @api 
  1330. * 
  1331. * @return bool 
  1332. */ 
  1333. public function is_system() { 
  1334. return $this->get_membership()->is_system(); 
  1335.  
  1336. /** 
  1337. * Get related gateway model. 
  1338. * 
  1339. * @since 1.0.0 
  1340. * @api 
  1341. * 
  1342. * @return MS_Model_Gateway 
  1343. */ 
  1344. public function get_gateway() { 
  1345. $gateway = MS_Model_Gateway::factory( $this->gateway_id ); 
  1346.  
  1347. return apply_filters( 
  1348. 'ms_model_relationship_get_gateway',  
  1349. $gateway 
  1350. ); 
  1351.  
  1352. /** 
  1353. * Either creates or updates the value of a custom data field. 
  1354. * 
  1355. * Note: Remember to prefix the $key with a unique string to prevent 
  1356. * conflicts with other plugins that also use this function. 
  1357. * 
  1358. * @since 2.0.0 
  1359. * @api 
  1360. * 
  1361. * @param string $key The field-key. 
  1362. * @param mixed $value The new value to assign to the field. 
  1363. */ 
  1364. public function set_custom_data( $key, $value ) { 
  1365. // Wrapper function, so this function shows up in API docs. 
  1366. parent::set_custom_data( $key, $value ); 
  1367.  
  1368. /** 
  1369. * Removes a custom data field from this object. 
  1370. * 
  1371. * @since 2.0.0 
  1372. * @api 
  1373. * 
  1374. * @param string $key The field-key. 
  1375. */ 
  1376. public function delete_custom_data( $key ) { 
  1377. // Wrapper function, so this function shows up in API docs. 
  1378. parent::delete_custom_data( $key ); 
  1379.  
  1380. /** 
  1381. * Returns the value of a custom data field. 
  1382. * 
  1383. * @since 2.0.0 
  1384. * @api 
  1385. * 
  1386. * @param string $key The field-key. 
  1387. * @return mixed The value that was previously assigned to the custom field 
  1388. * or false if no value was set for the field. 
  1389. */ 
  1390. public function get_custom_data( $key ) { 
  1391. // Wrapper function, so this function shows up in API docs. 
  1392. return parent::get_custom_data( $key ); 
  1393.  
  1394. /** 
  1395. * Get textual payment information description. 
  1396. * 
  1397. * @since 1.0.0 
  1398. * @api 
  1399. * 
  1400. * @param MS_Model_Invoice $invoice Optional. Specific invoice that defines 
  1401. * the price. Default is the price defined in the membership. 
  1402. * @param bool $short Optional. Default is false. If set to true then a 
  1403. * hort sumary is returned 
  1404. * @return string The description. 
  1405. */ 
  1406. public function get_payment_description( $invoice = null, $short = false ) { 
  1407. $currency = MS_Plugin::instance()->settings->currency; 
  1408. $membership = $this->get_membership(); 
  1409. $desc = ''; 
  1410.  
  1411. if ( null !== $invoice ) { 
  1412. $total_price = $invoice->total; // Includes Tax 
  1413. $trial_price = $invoice->trial_price; // Includes Tax 
  1414. } else { 
  1415. $total_price = $membership->total_price; // Excludes Tax 
  1416. $trial_price = $membership->trial_price; // Excludes Tax 
  1417.  
  1418. $total_price = MS_Helper_Billing::format_price( $total_price ); 
  1419. $trial_price = MS_Helper_Billing::format_price( $trial_price ); 
  1420.  
  1421. switch ( $membership->payment_type ) { 
  1422. case MS_Model_Membership::PAYMENT_TYPE_PERMANENT: 
  1423. if ( 0 == $total_price ) { 
  1424. if ( $short ) { 
  1425. $lbl = __( 'Nothing (for ever)', MS_TEXT_DOMAIN ); 
  1426. } else { 
  1427. $lbl = __( 'You will pay nothing for permanent access.', MS_TEXT_DOMAIN ); 
  1428. } else { 
  1429. if ( $short ) { 
  1430. $lbl = __( '<span class="price">%1$s %2$s</span> (for ever)', MS_TEXT_DOMAIN ); 
  1431. } else { 
  1432. $lbl = __( 'You will pay <span class="price">%1$s %2$s</span> for permanent access.', MS_TEXT_DOMAIN ); 
  1433.  
  1434. $desc = sprintf( 
  1435. $lbl,  
  1436. $currency,  
  1437. $total_price 
  1438. ); 
  1439. break; 
  1440.  
  1441. case MS_Model_Membership::PAYMENT_TYPE_FINITE: 
  1442. if ( 0 == $total_price ) { 
  1443. if ( $short ) { 
  1444. $lbl = __( 'Nothing (until %4$s)', MS_TEXT_DOMAIN ); 
  1445. } else { 
  1446. $lbl = __( 'You will pay nothing for access until %3$s.', MS_TEXT_DOMAIN ); 
  1447. } else { 
  1448. if ( $short ) { 
  1449. $lbl = __( '<span class="price">%1$s %2$s</span> (until %4$s)', MS_TEXT_DOMAIN ); 
  1450. } else { 
  1451. $lbl = __( 'You will pay <span class="price">%1$s %2$s</span> for access until %3$s.', MS_TEXT_DOMAIN ); 
  1452.  
  1453. $desc .= sprintf( 
  1454. $lbl,  
  1455. $currency,  
  1456. $total_price,  
  1457. MS_Helper_Period::format_date( $this->calc_expire_date( $this->expire_date ) ),  
  1458. $this->calc_expire_date( $this->expire_date ) 
  1459. ); 
  1460. break; 
  1461.  
  1462. case MS_Model_Membership::PAYMENT_TYPE_DATE_RANGE: 
  1463. if ( 0 == $total_price ) { 
  1464. if ( $short ) { 
  1465. $lbl = __( 'Nothing (%5$s - %6$s)', MS_TEXT_DOMAIN ); 
  1466. } else { 
  1467. $lbl = __( 'You will pay nothing for access from %3$s until %4$s.', MS_TEXT_DOMAIN ); 
  1468. } else { 
  1469. if ( $short ) { 
  1470. $lbl = __( '<span class="price">%1$s %2$s</span> (%5$s - %6$s)', MS_TEXT_DOMAIN ); 
  1471. } else { 
  1472. $lbl = __( 'You will pay <span class="price">%1$s %2$s</span> to access from %3$s until %4$s.', MS_TEXT_DOMAIN ); 
  1473.  
  1474. $desc .= sprintf( 
  1475. $lbl,  
  1476. $currency,  
  1477. $total_price,  
  1478. MS_Helper_Period::format_date( $membership->period_date_start ),  
  1479. MS_Helper_Period::format_date( $membership->period_date_end ),  
  1480. $membership->period_date_start,  
  1481. $membership->period_date_end 
  1482. ); 
  1483. break; 
  1484.  
  1485. case MS_Model_Membership::PAYMENT_TYPE_RECURRING: 
  1486. if ( 1 == $membership->pay_cycle_repetitions ) { 
  1487. // Exactly 1 payment. Actually same as the "finite" type. 
  1488. if ( $short ) { 
  1489. $lbl = __( '<span class="price">%1$s %2$s</span> (once)', MS_TEXT_DOMAIN ); 
  1490. } else { 
  1491. $lbl = __( 'You will pay <span class="price">%1$s %2$s</span> once.', MS_TEXT_DOMAIN ); 
  1492. } else { 
  1493. if ( $membership->pay_cycle_repetitions > 1 ) { 
  1494. // Fixed number of payments (more than 1) 
  1495. if ( $short ) { 
  1496. $lbl = __( '%4$s times <span class="price">%1$s %2$s</span> (each %3$s)', MS_TEXT_DOMAIN ); 
  1497. } else { 
  1498. $lbl = __( 'You will make %4$s payments of <span class="price">%1$s %2$s</span>, one each %3$s.', MS_TEXT_DOMAIN ); 
  1499. } else { 
  1500. // Indefinite number of payments 
  1501. if ( $short ) { 
  1502. $lbl = __( '<span class="price">%1$s %2$s</span> (each %3$s)', MS_TEXT_DOMAIN ); 
  1503. } else { 
  1504. $lbl = __( 'You will pay <span class="price">%1$s %2$s</span> each %3$s.', MS_TEXT_DOMAIN ); 
  1505.  
  1506. $desc .= sprintf( 
  1507. $lbl,  
  1508. $currency,  
  1509. $total_price,  
  1510. MS_Helper_Period::get_period_desc( $membership->pay_cycle_period ),  
  1511. $membership->pay_cycle_repetitions 
  1512. ); 
  1513. break; 
  1514.  
  1515. if ( $this->is_trial_eligible() && 0 != $total_price ) { 
  1516. if ( 0 == absint( $trial_price ) ) { 
  1517. if ( $short ) { 
  1518. if ( MS_Model_Membership::PAYMENT_TYPE_RECURRING == $membership->payment_type ) { 
  1519. $lbl = __( 'after %4$s', MS_TEXT_DOMAIN ); 
  1520. } else { 
  1521. $lbl = __( 'on %4$s', MS_TEXT_DOMAIN ); 
  1522. } else { 
  1523. $trial_price = __( 'nothing', MS_TEXT_DOMAIN ); 
  1524. $lbl = __( 'The trial period of %1$s is for free.', MS_TEXT_DOMAIN ); 
  1525. } else { 
  1526. $trial_price = MS_Helper_Billing::format_price( $trial_price ); 
  1527. $lbl = __( 'For the trial period of %1$s you only pay <span class="price">%2$s %3$s</span>.', MS_TEXT_DOMAIN ); 
  1528.  
  1529. $desc .= sprintf( 
  1530. ' <br />' . $lbl,  
  1531. MS_Helper_Period::get_period_desc( $membership->trial_period, true ),  
  1532. $currency,  
  1533. $trial_price,  
  1534. MS_Helper_Period::format_date( $invoice->due_date, __( 'M j', MS_TEXT_DOMAIN ) ) 
  1535. ); 
  1536.  
  1537. return apply_filters( 
  1538. 'ms_model_relationship_get_payment_description',  
  1539. $desc,  
  1540. $membership 
  1541. ); 
  1542.  
  1543. /** 
  1544. * Saves information on a payment that was made. 
  1545. * This function also extends the expire_date for one period if the 
  1546. * membership payment-type is recurring or limited 
  1547. * 
  1548. * @since 1.1.0 
  1549. * @api 
  1550. * 
  1551. * @param float $amount The payment amount. Set to 0 for free subscriptions. 
  1552. * @param string $gateway The payment gateway-ID. 
  1553. * @return bool True if the subscription has ACTIVE status after payment. 
  1554. * If the amount was 0 and membership uses a trial period the status 
  1555. * could also be TRIAL, in which case the returnv alue is false. 
  1556. */ 
  1557. public function add_payment( $amount, $gateway ) { 
  1558. if ( ! is_array( $this->payments ) ) { 
  1559. $this->payments = array(); 
  1560.  
  1561. // Update the payment-gateway. 
  1562. $this->gateway_id = $gateway; 
  1563.  
  1564. if ( $amount > 0 ) { 
  1565. $this->payments[] = array( 
  1566. 'date' => MS_Helper_Period::current_date( MS_Helper_Period::DATE_TIME_FORMAT ),  
  1567. 'amount' => $amount,  
  1568. 'gateway' => $gateway,  
  1569. ); 
  1570.  
  1571. // Upon first payment set the start date to current date. 
  1572. if ( 1 == count( $this->payments ) && ! $this->trial_expire_date ) { 
  1573. $this->set_start_date(); 
  1574.  
  1575. // Updates the subscription status. 
  1576. if ( MS_Gateway_Free::ID == $gateway && $this->is_trial_eligible() ) { 
  1577. // Calculate the final trial expire date. 
  1578.  
  1579. /** 
  1580. * Important: 
  1581. * FIRST set the TRIAL EXPIRE DATE, otherwise status is set to 
  1582. * active instead of trial! 
  1583. */ 
  1584. $this->set_trial_expire_date(); 
  1585.  
  1586. $this->set_status( self::STATUS_TRIAL ); 
  1587. } else { 
  1588. /** 
  1589. * Important: 
  1590. * FIRST set the SUBSCRIPTION STATUS, otherwise the expire date is 
  1591. * based on start_date instead of trial_expire_date! 
  1592. */ 
  1593. $this->set_status( self::STATUS_ACTIVE ); 
  1594.  
  1595. /** 
  1596. * Renew period. Every time this function is called, the expire 
  1597. * date is extended for 1 period 
  1598. */ 
  1599. $this->expire_date = $this->calc_expire_date( 
  1600. $this->expire_date, // Extend past the current expire date. 
  1601. true // Grant the user a full payment interval. 
  1602. ); 
  1603.  
  1604. $this->save(); 
  1605.  
  1606. // Return true if the subscription is active. 
  1607. $is_active = self::STATUS_ACTIVE == $this->status; 
  1608. return $is_active; 
  1609.  
  1610. /** 
  1611. * Set membership relationship status. 
  1612. * 
  1613. * Validates every time. 
  1614. * Check for status that need membership verification for trial, active and expired. 
  1615. * 
  1616. * @since 1.0.0 
  1617. * @internal Use $this->status instead! 
  1618. * 
  1619. * @param string $status The status to set. 
  1620. */ 
  1621. public function set_status( $status ) { 
  1622. // These status are not validated, and promptly assigned 
  1623. $ignored_status = apply_filters( 
  1624. 'ms_model_relationship_unvalidated_status',  
  1625. array( 
  1626. self::STATUS_DEACTIVATED,  
  1627. self::STATUS_TRIAL_EXPIRED,  
  1628. ),  
  1629. 'set' 
  1630. ); 
  1631. $membership = $this->get_membership(); 
  1632.  
  1633. if ( self::STATUS_PENDING == $this->status && $membership->is_free() ) { 
  1634. // Skip "Pending" for free memberships. 
  1635. $status = self::STATUS_ACTIVE; 
  1636. $this->handle_status_change( $status ); 
  1637. } elseif ( in_array( $status, $ignored_status ) ) { 
  1638. // No validation for this status. 
  1639. $this->status = $status; 
  1640. } else { 
  1641. // Check if this status is still valid. 
  1642. $calc_status = $this->calculate_status( $status ); 
  1643. $this->handle_status_change( $calc_status ); 
  1644.  
  1645. $this->status = apply_filters( 
  1646. 'ms_model_relationship_set_status',  
  1647. $this->status,  
  1648. $this 
  1649. ); 
  1650.  
  1651. /** 
  1652. * Get membership relationship status. 
  1653. * 
  1654. * Validates every time. 
  1655. * 
  1656. * Verifies start and end date of a membership and updates status if expired. 
  1657. * 
  1658. * @since 1.0.0 
  1659. * @internal Use $this->status instead! 
  1660. * 
  1661. * @return string The current status. 
  1662. */ 
  1663. public function get_status() { 
  1664. return apply_filters( 
  1665. 'membership_model_relationship_get_status',  
  1666. $this->status,  
  1667. $this 
  1668. ); 
  1669.  
  1670. /** 
  1671. * Returns an i18n translated version of the subscription status. 
  1672. * 
  1673. * @since 1.1.1.4 
  1674. * @api 
  1675. * 
  1676. * @return string 
  1677. */ 
  1678. public function status_text() { 
  1679. static $Status = null; 
  1680.  
  1681. if ( null === $Status ) { 
  1682. $Status = self::get_status_types(); 
  1683.  
  1684. $result = $this->status; 
  1685.  
  1686. if ( isset( $Status[$this->status] ) ) { 
  1687. $result = $Status[$this->status]; 
  1688.  
  1689. return apply_filters( 
  1690. 'ms_subscription_status_text',  
  1691. $result,  
  1692. $this->status 
  1693. ); 
  1694.  
  1695. /** 
  1696. * Calculate the membership status. 
  1697. * 
  1698. * Calculate status for the membership verifying the start date,  
  1699. * trial exire date and expire date. 
  1700. * 
  1701. * @since 1.0.0 
  1702. * @internal 
  1703. * 
  1704. * @param string $set_status The set status to compare. 
  1705. * @return string The calculated status. 
  1706. */ 
  1707. protected function calculate_status( $set_status = null ) { 
  1708. /** 
  1709. * Documented in check_membership_status() 
  1710. * 
  1711. * @since 1.1.0.5 
  1712. */ 
  1713. if ( MS_Plugin::get_modifier( 'MS_LOCK_SUBSCRIPTIONS' ) ) { 
  1714. return $set_status; 
  1715.  
  1716. $membership = $this->get_membership(); 
  1717. $calc_status = null; 
  1718. $check_trial = $this->is_trial_eligible(); 
  1719.  
  1720. if ( ! empty( $this->payments ) ) { 
  1721. /** 
  1722. * The user already paid for this membership, so don't check for 
  1723. * trial status anymore 
  1724. */ 
  1725. $check_trial = false; 
  1726.  
  1727. // If the start-date is not reached yet, then set membership to Pending. 
  1728. if ( empty( $calc_status ) 
  1729. && ! empty( $this->start_date ) 
  1730. && strtotime( $this->start_date ) > strtotime( MS_Helper_Period::current_date() ) 
  1731. ) { 
  1732. $calc_status = self::STATUS_WAITING; 
  1733.  
  1734. if ( $check_trial ) { 
  1735. if ( empty( $calc_status ) 
  1736. && strtotime( $this->trial_expire_date ) >= strtotime( MS_Helper_Period::current_date() ) 
  1737. ) { 
  1738. $calc_status = self::STATUS_TRIAL; 
  1739.  
  1740. if ( empty( $calc_status ) 
  1741. && strtotime( $this->trial_expire_date ) < strtotime( MS_Helper_Period::current_date() ) 
  1742. ) { 
  1743. $calc_status = self::STATUS_TRIAL_EXPIRED; 
  1744.  
  1745. // Permanent memberships grant instant access, no matter what. 
  1746. if ( empty( $calc_status ) 
  1747. && MS_Model_Membership::PAYMENT_TYPE_PERMANENT == $membership->payment_type 
  1748. ) { 
  1749. $calc_status = self::STATUS_ACTIVE; 
  1750.  
  1751. // If expire date is empty and Active-state is requests then use active. 
  1752. if ( empty( $calc_status ) 
  1753. && empty( $this->expire_date ) 
  1754. && self::STATUS_ACTIVE == $set_status 
  1755. ) { 
  1756. $calc_status = self::STATUS_ACTIVE; 
  1757.  
  1758. // If expire date is not reached then membership obviously is active. 
  1759. if ( empty( $calc_status ) 
  1760. && ! empty( $this->expire_date ) 
  1761. && strtotime( $this->expire_date ) >= strtotime( MS_Helper_Period::current_date() ) 
  1762. ) { 
  1763. $calc_status = self::STATUS_ACTIVE; 
  1764.  
  1765. // If no other condition was true then the expire date was reached. 
  1766. if ( empty( $calc_status ) ) { 
  1767. $calc_status = self::STATUS_EXPIRED; 
  1768.  
  1769. // Did the user cancel the membership? 
  1770. $cancel_it = self::STATUS_CANCELED == $set_status 
  1771. || ( 
  1772. self::STATUS_CANCELED == $this->status 
  1773. && self::STATUS_ACTIVE != $set_status 
  1774. && self::STATUS_TRIAL != $set_status 
  1775. ); 
  1776. if ( $cancel_it ) { 
  1777. /** 
  1778. * When a membership is cancelled then it will stay "Cancelled" 
  1779. * until the expiration date is reached. A user has access to the 
  1780. * contents of a cancelled membership until it expired. 
  1781. */ 
  1782.  
  1783. if ( self::STATUS_EXPIRED == $calc_status ) { 
  1784. // Membership has expired. Finally deactivate it! 
  1785. // (possibly it was cancelled a few days earlier) 
  1786. $calc_status = self::STATUS_DEACTIVATED; 
  1787. } elseif ( self::STATUS_TRIAL_EXPIRED == $calc_status ) { 
  1788. // Trial period has expired. Finally deactivate it! 
  1789. $calc_status = self::STATUS_DEACTIVATED; 
  1790. } elseif ( self::STATUS_TRIAL == $calc_status ) { 
  1791. // User can keep access until trial period finishes... 
  1792. $calc_status = self::STATUS_CANCELED; 
  1793. } elseif ( MS_Model_Membership::PAYMENT_TYPE_PERMANENT == $membership->payment_type ) { 
  1794. // This membership has no expiration-time. Deactivate it! 
  1795. $calc_status = self::STATUS_DEACTIVATED; 
  1796. } else { 
  1797. // Wait until the expiration date is reached... 
  1798. $calc_status = self::STATUS_CANCELED; 
  1799.  
  1800. return apply_filters( 
  1801. 'membership_model_relationship_calculate_status',  
  1802. $calc_status,  
  1803. $this 
  1804. ); 
  1805.  
  1806. /** 
  1807. * Handle status change. 
  1808. * 
  1809. * Save news when status change. 
  1810. * 
  1811. * @since 1.0.0 
  1812. * @internal 
  1813. * 
  1814. * @param string $new_status The status to change to. 
  1815. */ 
  1816. public function handle_status_change( $new_status ) { 
  1817. do_action( 
  1818. 'ms_model_relationship_handle_status_change_before',  
  1819. $new_status,  
  1820. $this 
  1821. ); 
  1822.  
  1823. if ( empty( $new_status ) ) { return false; } 
  1824. if ( $new_status == $this->status ) { return false; } 
  1825. if ( ! array_key_exists( $new_status, self::get_status_types() ) ) { return false; } 
  1826.  
  1827. if ( $this->is_simulated ) { 
  1828. // Do not trigger any events for simulated relationships. 
  1829. } elseif ( self::STATUS_DEACTIVATED == $new_status ) { 
  1830. /** 
  1831. * Deactivated manually or automatically after a limited 
  1832. * expiration-period or trial period ended. 
  1833. */ 
  1834. MS_Model_Event::save_event( 
  1835. MS_Model_Event::TYPE_MS_DEACTIVATED,  
  1836. $this 
  1837. ); 
  1838. } else { 
  1839. // Current status to change from. 
  1840. switch ( $this->status ) { 
  1841. case self::STATUS_PENDING: 
  1842. // signup 
  1843. if ( in_array( $new_status, array( self::STATUS_TRIAL, self::STATUS_ACTIVE ) ) ) { 
  1844. MS_Model_Event::save_event( MS_Model_Event::TYPE_MS_SIGNED_UP, $this ); 
  1845.  
  1846. // When changing from Pending -> Trial set trial_period_completed to true. 
  1847. $this->trial_period_completed = true; 
  1848. break; 
  1849.  
  1850. case self::STATUS_TRIAL: 
  1851. // Trial finished 
  1852. if ( self::STATUS_TRIAL_EXPIRED == $new_status ) { 
  1853. MS_Model_Event::save_event( MS_Model_Event::TYPE_MS_TRIAL_FINISHED, $this ); 
  1854. } elseif ( self::STATUS_ACTIVE == $new_status ) { 
  1855. MS_Model_Event::save_event( MS_Model_Event::TYPE_MS_RENEWED, $this ); 
  1856. } elseif ( self::STATUS_CANCELED == $new_status ) { 
  1857. MS_Model_Event::save_event( MS_Model_Event::TYPE_MS_CANCELED, $this ); 
  1858. break; 
  1859.  
  1860. case self::STATUS_TRIAL_EXPIRED: 
  1861. if ( self::STATUS_ACTIVE == $new_status ) { 
  1862. MS_Model_Event::save_event( MS_Model_Event::TYPE_MS_RENEWED, $this ); 
  1863. break; 
  1864.  
  1865. case self::STATUS_ACTIVE: 
  1866. if ( self::STATUS_CANCELED == $new_status ) { 
  1867. MS_Model_Event::save_event( MS_Model_Event::TYPE_MS_CANCELED, $this ); 
  1868. } elseif ( self::STATUS_EXPIRED == $new_status ) { 
  1869. MS_Model_Event::save_event( MS_Model_Event::TYPE_MS_EXPIRED, $this ); 
  1870. break; 
  1871.  
  1872. case self::STATUS_EXPIRED: 
  1873. case self::STATUS_CANCELED: 
  1874. if ( self::STATUS_ACTIVE == $new_status ) { 
  1875. MS_Model_Event::save_event( MS_Model_Event::TYPE_MS_RENEWED, $this ); 
  1876. break; 
  1877.  
  1878. case self::STATUS_DEACTIVATED: 
  1879. break; 
  1880.  
  1881. case self::STATUS_WAITING: 
  1882. // Start date is not reached yet, so don't do anything. 
  1883. break; 
  1884.  
  1885. $this->status = apply_filters( 
  1886. 'ms_model_relationship_set_status',  
  1887. $new_status 
  1888. ); 
  1889. $this->save(); 
  1890.  
  1891. do_action( 
  1892. 'ms_model_relationship_handle_status_change_after',  
  1893. $new_status,  
  1894. $this 
  1895. ); 
  1896.  
  1897. /** 
  1898. * Get a detailled status description. 
  1899. * 
  1900. * @since 1.0.0 
  1901. * @api 
  1902. * 
  1903. * @return string The status description. 
  1904. */ 
  1905. public function get_status_description() { 
  1906. $desc = ''; 
  1907.  
  1908. switch ( $this->status ) { 
  1909. case self::STATUS_PENDING: 
  1910. $desc = __( 'Pending payment.', MS_TEXT_DOMAIN ); 
  1911. break; 
  1912.  
  1913. case self::STATUS_TRIAL: 
  1914. $desc = sprintf( 
  1915. '%s <span class="ms-date">%s</span>',  
  1916. __( 'Membership Trial expires on ', MS_TEXT_DOMAIN ),  
  1917. MS_Helper_Period::format_date( $this->trial_expire_date ) 
  1918. ); 
  1919. break; 
  1920.  
  1921. case self::STATUS_ACTIVE: 
  1922. if ( ! empty( $this->expire_date ) ) { 
  1923. $desc = sprintf( 
  1924. '%s <span class="ms-date">%s</span>',  
  1925. __( 'Membership expires on ', MS_TEXT_DOMAIN ),  
  1926. MS_Helper_Period::format_date( $this->expire_date ) 
  1927. ); 
  1928. else { 
  1929. $desc = __( 'Permanent access.', MS_TEXT_DOMAIN ); 
  1930. break; 
  1931.  
  1932. case self::STATUS_TRIAL_EXPIRED: 
  1933. case self::STATUS_EXPIRED: 
  1934. $desc = sprintf( 
  1935. '%s <span class="ms-date">%s</span>',  
  1936. __( 'Membership expired since ', MS_TEXT_DOMAIN ),  
  1937. MS_Helper_Period::format_date( $this->expire_date ) 
  1938. ); 
  1939. break; 
  1940.  
  1941. case self::STATUS_CANCELED: 
  1942. $desc = sprintf( 
  1943. '%s <span class="ms-date">%s</span>',  
  1944. __( 'Membership canceled, valid until it expires on ', MS_TEXT_DOMAIN ),  
  1945. MS_Helper_Period::format_date( $this->expire_date ) 
  1946. ); 
  1947. break; 
  1948.  
  1949. case self::STATUS_DEACTIVATED: 
  1950. $desc = __( 'Membership deactivated.', MS_TEXT_DOMAIN ); 
  1951. break; 
  1952.  
  1953. return apply_filters( 
  1954. 'ms_model_relationship_get_status_description',  
  1955. $desc 
  1956. ); 
  1957.  
  1958. /** 
  1959. * Check membership status. 
  1960. * 
  1961. * Execute actions when time/period condition are met. 
  1962. * E.g. change membership status, add communication to queue, create invoices. 
  1963. * 
  1964. * This check is called via a cron job. 
  1965. * 
  1966. * @since 1.0.0 
  1967. * @internal Used by Cron 
  1968. * @see MS_Model_Plugin::check_membership_status() 
  1969. */ 
  1970. public function check_membership_status() { 
  1971. do_action( 
  1972. 'ms_model_relationship_check_membership_status_before',  
  1973. $this 
  1974. ); 
  1975.  
  1976. /** 
  1977. * Use `define( 'MS_LOCK_SUBSCRIPTIONS', true );` in wp-config.php to prevent 
  1978. * Membership2 from sending *any* emails to users. 
  1979. * Also any currently enqueued message is removed from the queue 
  1980. * 
  1981. * @since 1.1.0.5 
  1982. */ 
  1983. if ( MS_Plugin::get_modifier( 'MS_LOCK_SUBSCRIPTIONS' ) ) { 
  1984. return false; 
  1985.  
  1986. $comms = MS_Model_Communication::load_communications(); 
  1987. $invoice_before_days = 5;//@todo create a setting to configure this period. 
  1988. $deactivate_expired_after_days = 30; //@todo create a setting to configure this period. 
  1989. $deactivate_pending_after_days = 30; //@todo create a setting to configure this period. 
  1990. $deactivate_trial_expired_after_days = 5; //@todo create a setting to configure this period. 
  1991.  
  1992. $membership = $this->get_membership(); 
  1993. $remaining_days = $this->get_remaining_period(); 
  1994. $remaining_trial_days = $this->get_remaining_trial_period(); 
  1995.  
  1996. //@todo: Add a flag to subscriptions with sent communications. Then improve the conditions below to prevent multiple emails. 
  1997.  
  1998. do_action( 
  1999. 'ms_check_membership_status-' . $this->status,  
  2000. $this,  
  2001. $remaining_days,  
  2002. $remaining_trial_days 
  2003. ); 
  2004.  
  2005. // Update the Subscription status. 
  2006. $cur_status = $this->calculate_status(); 
  2007.  
  2008. switch ( $cur_status ) { 
  2009. case self::STATUS_TRIAL: 
  2010. if ( MS_Model_Addon::is_enabled( MS_Model_Addon::ADDON_TRIAL ) 
  2011. && MS_Model_Addon::is_enabled( MS_Model_Addon::ADDON_AUTO_MSGS_PLUS ) 
  2012. ) { 
  2013.  
  2014. // Send trial end communication. 
  2015. $comm = $comms[ MS_Model_Communication::COMM_TYPE_BEFORE_TRIAL_FINISHES ]; 
  2016.  
  2017. if ( $comm->enabled ) { 
  2018. $days = MS_Helper_Period::get_period_in_days( 
  2019. $comm->period['period_unit'],  
  2020. $comm->period['period_type'] 
  2021. ); 
  2022. //@todo: This will send out the reminder multiple times on the reminder-day (4 times or more often) 
  2023. if ( $days == $remaining_trial_days ) { 
  2024. $comm->add_to_queue( $this->id ); 
  2025. MS_Model_Event::save_event( 
  2026. MS_Model_Event::TYPE_MS_BEFORE_TRIAL_FINISHES,  
  2027. $this 
  2028. ); 
  2029.  
  2030. // Check for card expiration 
  2031. $gateway = $this->get_gateway(); 
  2032. $gateway->check_card_expiration( $this ); 
  2033. break; 
  2034.  
  2035. case self::STATUS_TRIAL_EXPIRED: 
  2036. if ( MS_Model_Addon::is_enabled( MS_Model_Addon::ADDON_TRIAL ) ) { 
  2037. // Mark the trial period as completed. $this->save() is below. 
  2038. $this->trial_period_completed = true; 
  2039.  
  2040. // Request payment to the gateway (for gateways that allows it). 
  2041. $gateway = $this->get_gateway(); 
  2042.  
  2043. /** 
  2044. * The subscription will be either automatically activated 
  2045. * or set to pending. 
  2046. * 
  2047. * Important: Set trial_period_completed=true before calling 
  2048. * request_payment()! 
  2049. */ 
  2050. if ( $gateway->request_payment( $this ) ) { 
  2051. $cur_status = self::STATUS_ACTIVE; 
  2052. $this->status = $cur_status; 
  2053. $this->config_period(); // Needed because of status change. 
  2054.  
  2055. // Check for card expiration 
  2056. $gateway->check_card_expiration( $this ); 
  2057.  
  2058. // Deactivate expired memberships after a period of time. 
  2059. if ( $deactivate_trial_expired_after_days < - $remaining_trial_days ) { 
  2060. $this->deactivate_membership(); 
  2061. break; 
  2062.  
  2063. case self::STATUS_ACTIVE: 
  2064. case self::STATUS_EXPIRED: 
  2065. case self::STATUS_CANCELED: 
  2066. /** 
  2067. * Send period end communication. 
  2068. * Deactivate expired memberships after $deactivate_expired_after_days. 
  2069. * Create invoice. 
  2070. */ 
  2071.  
  2072. do_action( 
  2073. 'ms_check_membership_status-' . $this->status,  
  2074. $this 
  2075. ); 
  2076.  
  2077. /** 
  2078. * Only "Recurring" memberships will ever try to automatically 
  2079. * renew the subscription. All other types will expire when the 
  2080. * end date is reached. 
  2081. */ 
  2082. $auto_renew = $membership->payment_type == MS_Model_Membership::PAYMENT_TYPE_RECURRING; 
  2083. $deactivate = false; 
  2084. $invoice = null; 
  2085.  
  2086. if ( $auto_renew && $membership->pay_cycle_repetitions > 0 ) { 
  2087. /** 
  2088. * The membership has a payment-repetition limit. 
  2089. * When this limit is reached then we do not auto-renew the 
  2090. * subscription but expire it. 
  2091. */ 
  2092. $payments = lib2()->array->get( $this->payments ); 
  2093. if ( count( $payments ) >= $membership->pay_cycle_repetitions ) { 
  2094. $auto_renew = false; 
  2095.  
  2096. if ( $auto_renew ) { 
  2097. if ( $remaining_days < $invoice_before_days ) { 
  2098. // Create a new invoice. 
  2099. $invoice = $this->get_next_invoice(); 
  2100. } else { 
  2101. $invoice = $this->get_current_invoice(); 
  2102. } else { 
  2103. $invoice = $this->get_current_invoice(); 
  2104.  
  2105. // Advanced communications Add-on. 
  2106. if ( MS_Model_Addon::is_enabled( MS_Model_Addon::ADDON_AUTO_MSGS_PLUS ) ) { 
  2107. // Before finishes communication. 
  2108. $comm = $comms[ MS_Model_Communication::COMM_TYPE_BEFORE_FINISHES ]; 
  2109. $days = MS_Helper_Period::get_period_in_days( 
  2110. $comm->period['period_unit'],  
  2111. $comm->period['period_type'] 
  2112. ); 
  2113. if ( $days == $remaining_days ) { 
  2114. $comm->add_to_queue( $this->id ); 
  2115. MS_Model_Event::save_event( 
  2116. MS_Model_Event::TYPE_MS_BEFORE_FINISHES,  
  2117. $this 
  2118. ); 
  2119.  
  2120. // After finishes communication. 
  2121. $comm = $comms[ MS_Model_Communication::COMM_TYPE_AFTER_FINISHES ]; 
  2122. $days = MS_Helper_Period::get_period_in_days( 
  2123. $comm->period['period_unit'],  
  2124. $comm->period['period_type'] 
  2125. ); 
  2126.  
  2127. if ( $remaining_days < 0 && $days == abs( $remaining_days ) ) { 
  2128. $comm->add_to_queue( $this->id ); 
  2129. MS_Model_Event::save_event( 
  2130. MS_Model_Event::TYPE_MS_AFTER_FINISHES,  
  2131. $this 
  2132. ); 
  2133.  
  2134. // Before payment due. 
  2135. $comm = $comms[ MS_Model_Communication::COMM_TYPE_BEFORE_PAYMENT_DUE ]; 
  2136. $days = MS_Helper_Period::get_period_in_days( 
  2137. $comm->period['period_unit'],  
  2138. $comm->period['period_type'] 
  2139. ); 
  2140. $invoice_days = MS_Helper_Period::subtract_dates( 
  2141. $invoice->due_date,  
  2142. MS_Helper_Period::current_date() 
  2143. ); 
  2144.  
  2145. if ( MS_Model_Invoice::STATUS_BILLED == $invoice->status 
  2146. && $days == $invoice_days 
  2147. ) { 
  2148. $comm->add_to_queue( $this->id ); 
  2149. MS_Model_Event::save_event( MS_Model_Event::TYPE_PAYMENT_BEFORE_DUE, $this ); 
  2150.  
  2151. // After payment due event 
  2152. $comm = $comms[ MS_Model_Communication::COMM_TYPE_AFTER_PAYMENT_DUE ]; 
  2153. $days = MS_Helper_Period::get_period_in_days( 
  2154. $comm->period['period_unit'],  
  2155. $comm->period['period_type'] 
  2156. ); 
  2157. $invoice_days = MS_Helper_Period::subtract_dates( 
  2158. $invoice->due_date,  
  2159. MS_Helper_Period::current_date() 
  2160. ); 
  2161.  
  2162. if ( MS_Model_Invoice::STATUS_BILLED == $invoice->status 
  2163. && $days == $invoice_days 
  2164. ) { 
  2165. $comm->add_to_queue( $this->id ); 
  2166. MS_Model_Event::save_event( MS_Model_Event::TYPE_PAYMENT_AFTER_DUE, $this ); 
  2167. } // -- End of advanced communications Add-on 
  2168.  
  2169. // Subscription ended. See if we can renew it. 
  2170. if ( $remaining_days <= 0 ) { 
  2171. if ( $auto_renew ) { 
  2172. /** 
  2173. * The membership can be renewed. Try to renew it 
  2174. * automatically by requesting the next payment from the 
  2175. * payment gateway (only works if gateway supports this) 
  2176. */ 
  2177. $gateway = $this->get_gateway(); 
  2178. $gateway->check_card_expiration( $this ); 
  2179. $gateway->request_payment( $this ); 
  2180.  
  2181. // Check if the payment was successful. 
  2182. $remaining_days = $this->get_remaining_period(); 
  2183.  
  2184. /** 
  2185. * User did not renew the membership. Give him some time 
  2186. * to react before restricting his access. 
  2187. */ 
  2188. if ( $deactivate_expired_after_days < - $remaining_days ) { 
  2189. $deactivate = true; 
  2190. } else { 
  2191. $deactivate = true; 
  2192.  
  2193. if ( $deactivate ) { 
  2194. $this->deactivate_membership(); 
  2195.  
  2196. // Move membership to configured membership. 
  2197. $membership = $this->get_membership(); 
  2198.  
  2199. $new_membership = MS_Factory::load( 
  2200. 'MS_Model_Membership',  
  2201. $membership->on_end_membership_id 
  2202. ); 
  2203.  
  2204. if ( $new_membership->is_valid() ) { 
  2205. $member = MS_Factory::load( 'MS_Model_Member', $this->user_id ); 
  2206. $new_subscription = $member->add_membership( 
  2207. $membership->on_end_membership_id,  
  2208. $this->gateway_id 
  2209. ); 
  2210.  
  2211. MS_Model_Event::save_event( 
  2212. MS_Model_Event::TYPE_MS_MOVED,  
  2213. $new_subscription 
  2214. ); 
  2215.  
  2216. /** 
  2217. * If the new membership is paid we want that the user 
  2218. * confirms the payment in his account. So we set it 
  2219. * to "Pending" first. 
  2220. */ 
  2221. if ( $new_membership->is_free() ) { 
  2222. $new_subscription->status = self::STATUS_PENDING; 
  2223. break; 
  2224.  
  2225. case self::STATUS_PENDING: 
  2226. case self::STATUS_DEACTIVATED: 
  2227. default: 
  2228. break; 
  2229.  
  2230. // Save the new status. 
  2231. $this->status = $cur_status; 
  2232. $this->save(); 
  2233.  
  2234. // Save the changed email queue. 
  2235. foreach ( $comms as $comm ) { 
  2236. $comm->save(); 
  2237.  
  2238. do_action( 
  2239. 'ms_model_relationship_check_membership_status_after',  
  2240. $this 
  2241. ); 
  2242.  
  2243. /** 
  2244. * Returns property. 
  2245. * 
  2246. * @since 1.0.0 
  2247. * @internal 
  2248. * 
  2249. * @param string $property The name of a property. 
  2250. * @return mixed Returns mixed value of a property or NULL if a property doesn't exist. 
  2251. */ 
  2252. public function __get( $property ) { 
  2253. $value = null; 
  2254.  
  2255. switch ( $property ) { 
  2256. case 'status': 
  2257. $value = $this->get_status(); 
  2258. break; 
  2259.  
  2260. default: 
  2261. if ( ! property_exists( $this, $property ) ) { 
  2262. MS_Helper_Debug::log( 'Property does not exist: ' . $property ); 
  2263. } else { 
  2264. $value = $this->$property; 
  2265. break; 
  2266.  
  2267. return apply_filters( 
  2268. 'ms_model_relationship__get',  
  2269. $value,  
  2270. $property,  
  2271. $this 
  2272. ); 
  2273.  
  2274. /** 
  2275. * Set specific property. 
  2276. * 
  2277. * @since 1.0.0 
  2278. * @internal 
  2279. * 
  2280. * @param string $property The name of a property to associate. 
  2281. * @param mixed $value The value of a property. 
  2282. */ 
  2283. public function __set( $property, $value ) { 
  2284. switch ( $property ) { 
  2285. case 'start_date': 
  2286. $this->set_start_date( $value ); 
  2287. break; 
  2288.  
  2289. case 'trial_expire_date': 
  2290. $this->set_trial_expire_date( $value ); 
  2291. break; 
  2292.  
  2293. case 'expire_date': 
  2294. $this->set_expire_date( $value ); 
  2295. break; 
  2296.  
  2297. case 'status': 
  2298. $this->set_status( $value ); 
  2299. break; 
  2300.  
  2301. default: 
  2302. if ( property_exists( $this, $property ) ) { 
  2303. $this->$property = $value; 
  2304. break; 
  2305.  
  2306. do_action( 
  2307. 'ms_model_relationship__set_after',  
  2308. $property,  
  2309. $value,  
  2310. $this 
  2311. ); 
  2312.  
.