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

  1. <?php 
  2. /** 
  3. * Member model. 
  4. * 
  5. * Defines several details about a WordPress user. 
  6. * The Member object allows us to quickly check if the user did subscribe to a 
  7. * certain membership, and other useful stuff. 
  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_Member extends MS_Model { 
  19.  
  20. /** 
  21. * Members search constants. 
  22. * 
  23. * @since 1.0.0 
  24. * @internal 
  25. */ 
  26. const SEARCH_ONLY_MEMBERS = 'only members'; //TODO: replace space with _ !!!! 
  27.  
  28. /** 
  29. * Members search constants. 
  30. * 
  31. * @since 1.0.0 
  32. * @internal 
  33. */ 
  34. const SEARCH_ALL_USERS = 'all_users'; 
  35.  
  36. /** 
  37. * Cache for function is_admin_user() 
  38. * 
  39. * @since 1.0.0 
  40. * @internal 
  41. * @var bool[] 
  42. */ 
  43. static protected $_is_admin_user = array(); 
  44.  
  45. /** 
  46. * Cache for function is_normal_admin() 
  47. * 
  48. * @since 1.0.0 
  49. * @internal 
  50. * @var bool[] 
  51. */ 
  52. static protected $_is_normal_admin = array(); 
  53.  
  54. /** 
  55. * Cache for function is_simulated_user() 
  56. * 
  57. * @since 1.0.0 
  58. * @internal 
  59. * @var bool[] 
  60. */ 
  61. static protected $_is_simulated_user = array(); 
  62.  
  63. /** 
  64. * Cache for function is_normal_user() 
  65. * 
  66. * @since 1.0.0 
  67. * @internal 
  68. * @var bool[] 
  69. */ 
  70. static protected $_is_normal_user = array(); 
  71.  
  72. /** 
  73. * Member's active subscriptions. 
  74. * 
  75. * Note: This field is populated by MS_Factory when the Member instance is 
  76. * created. 
  77. * 
  78. * @since 1.0.0 
  79. * @var array { 
  80. * @type int $membership_id The membership ID. 
  81. * @type MS_Model_Relationship The membership relationship model object. 
  82. * } 
  83. */ 
  84. protected $subscriptions = array(); 
  85.  
  86. /** 
  87. * Indicator if the user is an active M2 Member. 
  88. * 
  89. * This is a convenience/redudant flag to speed up SQL queries. 
  90. * Actually everyone that has an active or trial status membership is 
  91. * considered an active member. 
  92. * 
  93. * This flag is set when: 
  94. * - In MS_Model_Relationship, when a payment is recorded 
  95. * via add_payment() 
  96. * 
  97. * This flag is reset when: 
  98. * - In MS_Model_Relationship, when a subscription is deactivated 
  99. * via check_membership_status() 
  100. * 
  101. * @since 1.0.0 
  102. * @var boolean 
  103. */ 
  104. protected $is_member = false; 
  105.  
  106. /** 
  107. * Member's username. 
  108. * 
  109. * Mapped from wordpress $wp_user object. 
  110. * @see WP_User $user_login. 
  111. * 
  112. * @since 1.0.0 
  113. * @var string 
  114. */ 
  115. protected $username = ''; 
  116.  
  117. /** 
  118. * Member's email. 
  119. * 
  120. * Mapped from wordpress $wp_user object. 
  121. * @see WP_User $user_email. 
  122. * 
  123. * @since 1.0.0 
  124. * @var string 
  125. */ 
  126. protected $email = ''; 
  127.  
  128. /** 
  129. * Member's name. 
  130. * 
  131. * Mapped from wordpress $wp_user object. 
  132. * @see WP_User $user_nicename. 
  133. * 
  134. * @since 1.0.0 
  135. * @var string 
  136. */ 
  137. protected $name = ''; 
  138.  
  139. /** 
  140. * Member's first name. 
  141. * 
  142. * Mapped from wordpress $wp_user object. 
  143. * @see WP_User $first_name 
  144. * 
  145. * @since 1.0.0 
  146. * @var string 
  147. */ 
  148. protected $first_name = ''; 
  149.  
  150. /** 
  151. * Member's last name. 
  152. * 
  153. * Mapped from wordpress $wp_user object. 
  154. * @see WP_User $last_name. 
  155. * 
  156. * @since 1.0.0 
  157. * @var string 
  158. */ 
  159. protected $last_name = ''; 
  160.  
  161. /** 
  162. * Member's password. 
  163. * 
  164. * Used when registering. 
  165. * 
  166. * @since 1.0.0 
  167. * @internal 
  168. * @var string 
  169. */ 
  170. protected $password = ''; 
  171.  
  172. /** 
  173. * Member's password confirmation. 
  174. * 
  175. * Used when registering. 
  176. * 
  177. * @since 1.0.0 
  178. * @internal 
  179. * @var string 
  180. */ 
  181. protected $password2 = ''; 
  182.  
  183. /** 
  184. * Member's gateway profiles info. 
  185. * 
  186. * Save gateway IDs. 
  187. * 
  188. * @since 1.0.0 
  189. * @var array { 
  190. * Return structure: $gateway[ $field ] => $value; 
  191. * 
  192. * @type string $gateway_id The gateway id. 
  193. * @type string $field The field to store. 
  194. * @type mixed $value The field value to store. 
  195. * } 
  196. */ 
  197. protected $gateway_profiles = array(); 
  198.  
  199. /** 
  200. * The associated WP_User object 
  201. * 
  202. * @since 1.0.0 
  203. * @internal 
  204. * @var WP_User 
  205. */ 
  206. protected $wp_user = null; 
  207.  
  208. /** 
  209. * Custom data can be used by other plugins via the set_custom_data() and 
  210. * get_custom_data() functions. 
  211. * 
  212. * This can be used to store additional information on user-level, e.g. 
  213. * settings needed by some Add-ons or even by other plugins. 
  214. * 
  215. * @since 1.0.0 
  216. * 
  217. * @var array 
  218. */ 
  219. protected $custom_data = array(); 
  220.  
  221.  
  222. // 
  223. // 
  224. // 
  225. // -------------------------------------------------------------- COLLECTION 
  226.  
  227. /** 
  228. * Get current member. 
  229. * 
  230. * @since 1.0.0 
  231. * @internal 
  232. * 
  233. * @return MS_Model_Member The current member. 
  234. */ 
  235. static public function get_current_member() { 
  236. return MS_Factory::load( 'MS_Model_Member', get_current_user_id() ); 
  237.  
  238. /** 
  239. * Checks if user-signup is enabled for this site or not. 
  240. * 
  241. * @since 1.0.0 
  242. * @api 
  243. * 
  244. * @return bool 
  245. */ 
  246. static public function can_register() { 
  247. static $Signup_Allowed = null; 
  248.  
  249. if ( null === $Signup_Allowed ) { 
  250. $Signup_Allowed = false; 
  251.  
  252. if ( is_multisite() ) { 
  253. $reg_option = get_site_option( 'registration', 'none' ); 
  254. if ( in_array( $reg_option, array( 'all', 'user' ) ) ) { 
  255. $Signup_Allowed = true; 
  256. } else { 
  257. if ( get_option( 'users_can_register' ) ) { 
  258. $Signup_Allowed = true; 
  259.  
  260. return apply_filters( 
  261. 'ms_member_can_register',  
  262. $Signup_Allowed 
  263. ); 
  264.  
  265. /** 
  266. * Allows users to register for this site. 
  267. * 
  268. * @since 1.0.0 
  269. * @api 
  270. * 
  271. * @return bool 
  272. */ 
  273. static public function allow_registration() { 
  274. if ( self::can_register() ) { return; } 
  275.  
  276. if ( is_multisite() ) { 
  277. $reg_option = get_site_option( 'registration', 'none' ); 
  278. if ( 'blog' == $reg_option ) { 
  279. // Creation of new blogs is allowd. Add User-Registration. 
  280. update_site_option( 'registration', 'all' ); 
  281. } else { 
  282. // Only enable user registration and keep blogs disabled. 
  283. update_site_option( 'registration', 'user' ); 
  284. } else { 
  285. // Simply enable registration on single sites. 
  286. update_option( 'users_can_register', true ); 
  287.  
  288. /** 
  289. * Get members total count. 
  290. * 
  291. * @since 1.0.0 
  292. * @internal 
  293. * 
  294. * @param $args The query user args 
  295. * @see @link http://codex.wordpress.org/Class_Reference/WP_User_Query 
  296. * @return int The count. 
  297. */ 
  298. public static function get_members_count( $args = null ) { 
  299. $args = self::get_query_args( $args, self::SEARCH_ALL_USERS ); 
  300. $args['number'] = 0; 
  301. $args['count_total'] = true; 
  302. $wp_user_search = new WP_User_Query( $args ); 
  303.  
  304. return apply_filters( 
  305. 'ms_model_member_get_members_count',  
  306. $wp_user_search->get_total() 
  307. ); 
  308.  
  309. /** 
  310. * Get members IDs. 
  311. * The IDs are cached and only fetched once for each set of $args. 
  312. * 
  313. * @since 1.0.0 
  314. * @internal 
  315. * 
  316. * @param $args The query user args 
  317. * @see @link http://codex.wordpress.org/Class_Reference/WP_User_Query 
  318. * @return array List of member IDs 
  319. */ 
  320. public static function get_member_ids( $args = null, $search_option = self::SEARCH_ALL_USERS ) { 
  321. static $Members = array(); 
  322. if ( ! isset( $args['number'] ) ) { 
  323. $args['number'] = 0; 
  324.  
  325. $key = json_encode( $args ); 
  326.  
  327. if ( ! isset( $Members[$key] ) ) { 
  328. $args = self::get_query_args( $args, $search_option ); 
  329. $wp_user_search = new WP_User_Query( $args ); 
  330. $users = $wp_user_search->get_results(); 
  331. $members = array(); 
  332.  
  333. foreach ( $users as $user_id ) { 
  334. $members[] = $user_id; 
  335.  
  336. $Members[$key] = apply_filters( 
  337. 'ms_model_member_get_member_ids',  
  338. $members,  
  339. $args 
  340. ); 
  341.  
  342. return $Members[$key]; 
  343.  
  344. /** 
  345. * Get members. 
  346. * 
  347. * @since 1.0.0 
  348. * @internal 
  349. * 
  350. * @param $args The query user args 
  351. * @see @link http://codex.wordpress.org/Class_Reference/WP_User_Query 
  352. * @return MS_Model_Member[] The selected members. 
  353. */ 
  354. public static function get_members( $args = null, $search_option = self::SEARCH_ALL_USERS ) { 
  355. $members = array(); 
  356. $ids = self::get_member_ids( $args, $search_option ); 
  357.  
  358. foreach ( $ids as $user_id ) { 
  359. $members[] = MS_Factory::load( 'MS_Model_Member', $user_id ); 
  360.  
  361. return apply_filters( 
  362. 'ms_model_member_get_members',  
  363. $members,  
  364. $ids,  
  365. $args 
  366. ); 
  367.  
  368. /** 
  369. * Get usernames. 
  370. * 
  371. * @since 1.0.0 
  372. * @internal 
  373. * 
  374. * @param $args The query user args 
  375. * @see @link http://codex.wordpress.org/Class_Reference/WP_User_Query 
  376. * @param string $search_option The search options (only members, not members, all users). 
  377. * @return array { 
  378. * @type int $id The user_id. 
  379. * @type string $username The username. 
  380. * } 
  381. */ 
  382. public static function get_usernames( $args = null, $search_option = self::SEARCH_ONLY_MEMBERS, $return_array = true ) { 
  383. $members = array(); 
  384.  
  385. if ( $return_array ) { 
  386. $members[0] = __( 'Select a user', 'membership2' ); 
  387.  
  388. $args['fields'] = array( 'ID', 'user_login' ); 
  389. $args['number'] = 0; 
  390. $args = self::get_query_args( $args, $search_option ); 
  391. $wp_user_search = new WP_User_Query( $args ); 
  392. $users = $wp_user_search->get_results(); 
  393.  
  394. foreach ( $users as $user ) { 
  395. if ( ! self::is_admin_user( $user->ID ) ) { 
  396. if ( $return_array ) { 
  397. $members[ $user->ID ] = $user->user_login; 
  398. } else { 
  399. $members[] = array( 
  400. 'id' => $user->ID,  
  401. 'text' => $user->user_login,  
  402. ); 
  403.  
  404. return apply_filters( 
  405. 'ms_model_member_get_members_usernames',  
  406. $members,  
  407. $return_array 
  408. ); 
  409.  
  410. /** 
  411. * Get WP_Query object arguments. 
  412. * 
  413. * Default search arguments for this model. 
  414. * 
  415. * @since 1.0.0 
  416. * @internal 
  417. * 
  418. * @param $args The query user args 
  419. * @see @link http://codex.wordpress.org/Class_Reference/WP_User_Query 
  420. * @param string $search_option The search options (only members, not members, all users). 
  421. * @return array $args The parsed args. 
  422. */ 
  423. public static function get_query_args( $args = null, $search_option = self::SEARCH_ONLY_MEMBERS ) { 
  424. global $wpdb; 
  425.  
  426. $defaults = apply_filters( 
  427. 'ms_model_member_get_query_args_defaults',  
  428. array( 
  429. 'order' => 'DESC',  
  430. 'orderby' => 'ID',  
  431. 'number' => 20,  
  432. 'offset' => 0,  
  433. 'fields' => 'ID',  
  434. ); 
  435.  
  436. $args = lib3()->array->get( $args ); 
  437. lib3()->array->equip( $args, 'meta_query', 'membership_id', 'subscription_status' ); 
  438.  
  439. if ( 'none' !== $args['meta_query'] ) { 
  440. $args['meta_query'] = lib3()->array->get( $args['meta_query'] ); 
  441.  
  442. switch ( $search_option ) { 
  443. case self::SEARCH_ONLY_MEMBERS: 
  444. $args['meta_query'] = array( 
  445. array( 
  446. 'key' => 'ms_is_member',  
  447. 'value' => true,  
  448. ),  
  449. ); 
  450. break; 
  451.  
  452. case self::SEARCH_ALL_USERS: 
  453. default: 
  454. break; 
  455. } else { 
  456. unset( $args['meta_query'] ); 
  457.  
  458. // For performance reasons we execute a custom SQL to get relevant user_ids. 
  459. if ( ! empty( $args['membership_id'] ) 
  460. || ! empty( $args['subscription_status'] ) 
  461. && 'all' != $args['subscription_status'] 
  462. ) { 
  463. $membership_id = intval( $args['membership_id'] ); 
  464. $status = $args['subscription_status']; 
  465.  
  466. switch ( $status ) { 
  467. case 'expired': 
  468. $status_val = implode( 
  469. ', ',  
  470. array( 
  471. "'" . MS_Model_Relationship::STATUS_TRIAL_EXPIRED . "'",  
  472. "'" . MS_Model_Relationship::STATUS_EXPIRED . "'",  
  473. ); 
  474. break; 
  475.  
  476. default: 
  477. $status_val = $wpdb->prepare( " '%s' ", $status ); 
  478. break; 
  479.  
  480. $sql = " 
  481. SELECT DISTINCT usr.meta_value 
  482. FROM {$wpdb->posts} p 
  483. INNER JOIN {$wpdb->postmeta} mem ON mem.post_id=p.ID AND mem.meta_key='membership_id' 
  484. INNER JOIN {$wpdb->postmeta} sta ON sta.post_id=p.ID AND sta.meta_key='status' 
  485. INNER JOIN {$wpdb->postmeta} usr ON usr.post_id=p.ID AND usr.meta_key='user_id' 
  486. WHERE 
  487. p.post_type = %s 
  488. AND ('0' = %s OR mem.meta_value = %s) 
  489. AND ('' = %s OR sta.meta_value IN ({$status_val})) 
  490. "; 
  491. $sql = $wpdb->prepare( 
  492. $sql,  
  493. MS_Model_Relationship::get_post_type(),  
  494. $membership_id,  
  495. $membership_id,  
  496. $status 
  497. ); 
  498. $ids = $wpdb->get_col( $sql ); 
  499. if ( empty( $ids ) || ! is_array( $ids ) ) { $ids = array( 0 ); } 
  500. $args['include'] = $ids; 
  501.  
  502. if ( MS_Plugin::is_network_wide() ) { 
  503. $defaults['blog_id'] = false; 
  504.  
  505. $args = wp_parse_args( $args, $defaults ); 
  506.  
  507. return apply_filters( 
  508. 'ms_model_member_get_query_args',  
  509. $args,  
  510. $defaults 
  511. ); 
  512.  
  513. /** 
  514. * Returns the current user ID. 
  515. * This function can be called before the init action hook. 
  516. * 
  517. * Much of this logic is taken from wp-includes/pluggable.php 
  518. * 
  519. * @since 1.0.0 
  520. * @internal 
  521. * @return int|false 
  522. */ 
  523. public static function get_user_id() { 
  524. static $User_id = false; 
  525.  
  526. if ( $User_id ) { 
  527. // We already found the user-id, no need to do it again. 
  528. return $User_id; 
  529.  
  530. if ( defined( 'DOING_CRON' ) && DOING_CRON ) { 
  531. // A cron request has no user credentials... 
  532. return 0; 
  533.  
  534. $cookie = wp_parse_auth_cookie(); 
  535.  
  536. if ( ! $cookie ) { 
  537. // Missing, expired or corrupt cookie. 
  538. return 0; 
  539.  
  540. $scheme = $cookie['scheme']; 
  541. $username = $cookie['username']; 
  542. $hmac = $cookie['hmac']; 
  543. $token = $cookie['token']; 
  544. $expiration = $cookie['expiration']; 
  545.  
  546. $user = get_user_by( 'login', $username ); 
  547.  
  548. if ( ! $user ) { 
  549. // Invalid username. 
  550. return 0; 
  551.  
  552. $pass_frag = substr( $user->user_pass, 8, 4 ); 
  553. $key = wp_hash( $username . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme ); 
  554. $algo = function_exists( 'hash' ) ? 'sha256' : 'sha1'; 
  555. $hash = hash_hmac( $algo, $username . '|' . $expiration . '|' . $token, $key ); 
  556.  
  557. if ( ! hash_equals( $hash, $hmac ) ) { 
  558. // Forged/expired cookie value. 
  559. return 0; 
  560.  
  561. // Remember the user-ID so we don't have to validate everything again. 
  562. $User_id = $user->ID; 
  563.  
  564. return $User_id; 
  565.  
  566. /** 
  567. * Verify is user is logged in. 
  568. * 
  569. * @since 1.0.0 
  570. * @internal 
  571. * 
  572. * @return boolean True if user is logged in. 
  573. */ 
  574. public static function is_logged_in() { 
  575. $logged = is_user_logged_in(); 
  576.  
  577. return apply_filters( 'ms_member_is_logged_in', $logged ); 
  578.  
  579. /** 
  580. * Verify is user is Admin user. 
  581. * 
  582. * @since 1.0.0 
  583. * @api 
  584. * 
  585. * @param int|false $user_id Optional. The user ID. Default to current user. 
  586. * @param bool $deprecated Do not use. 
  587. * @return boolean True if user is admin. 
  588. */ 
  589. static public function is_admin_user( $user_id = false ) { 
  590. $cache_result = true; 
  591.  
  592. if ( ! isset( self::$_is_admin_user[ $user_id ] ) ) { 
  593. $is_admin = false; 
  594. $default_user_id = null; 
  595.  
  596. if ( empty( $user_id ) ) { 
  597. $default_user_id = $user_id; 
  598. $user_id = self::get_user_id(); 
  599.  
  600. if ( is_super_admin( $user_id ) ) { 
  601. // Superadmin always is considered admin user, no discussion... 
  602. $is_admin = true; 
  603. } else { 
  604. /** 
  605. * Use the capability defined by the main plugin controller. 
  606. * 
  607. * This capability defines which user is considered admin user. 
  608. * An Admin user has full permissions to edit M2 settings. 
  609. * 
  610. * To modify the capability: 
  611. * Use filter `ms_admin_user_capability` or 
  612. * define( 'MS_ADMIN_CAPABILITY', '...' ) 
  613. * 
  614. * @var string|bool A WordPress capability or boolean false. 
  615. */ 
  616. $controller = MS_Plugin::instance()->controller; 
  617. if ( $controller ) { 
  618. $capability = $controller->capability; 
  619. } else { 
  620. // This is used in case the function is called too early. 
  621. $capability = 'manage_options'; 
  622. $cache_result = false; 
  623.  
  624. if ( ! empty( $capability ) ) { 
  625. if ( empty( $user_id ) ) { 
  626. $is_admin = current_user_can( $capability ); 
  627. } else { 
  628. $is_admin = user_can( $user_id, $capability ); 
  629.  
  630. $is_admin = apply_filters( 
  631. 'ms_model_member_is_admin_user',  
  632. $is_admin,  
  633. $user_id,  
  634. $capability 
  635. ); 
  636.  
  637. if ( $cache_result ) { 
  638. self::$_is_admin_user[ $user_id ] = $is_admin; 
  639.  
  640. if ( null !== $default_user_id ) { 
  641. self::$_is_admin_user[ $default_user_id ] = $is_admin; 
  642. } else { 
  643. $is_admin = self::$_is_admin_user[ $user_id ]; 
  644.  
  645. return $is_admin; 
  646.  
  647. /** 
  648. * Verify is user is Admin user and simulation mode is deactivated. 
  649. * 
  650. * @since 1.0.0 
  651. * @api 
  652. * 
  653. * @param int|false $user_id Optional. The user ID. Default to current user. 
  654. * @return boolean 
  655. */ 
  656. static public function is_normal_admin( $user_id = false ) { 
  657. if ( ! isset( self::$_is_normal_admin[$user_id] ) ) { 
  658. $res = self::is_admin_user( $user_id ) 
  659. && ! MS_Factory::load( 'MS_Model_Simulate' )->is_simulating(); 
  660. self::$_is_normal_admin[$user_id] = $res; 
  661.  
  662. if ( empty( $user_id ) ) { 
  663. self::$_is_normal_admin[ get_current_user_id() ] = $res; 
  664.  
  665. return self::$_is_normal_admin[$user_id]; 
  666.  
  667. /** 
  668. * Verify is user is Admin user and simulation mode is active. 
  669. * 
  670. * @since 1.0.0 
  671. * @api 
  672. * 
  673. * @param int|false $user_id Optional. The user ID. Default to current user. 
  674. * @return boolean 
  675. */ 
  676. static public function is_simulated_user( $user_id = false ) { 
  677. if ( ! isset( self::$_is_simulated_user[$user_id] ) ) { 
  678. $res = self::is_admin_user( $user_id ) 
  679. && MS_Factory::load( 'MS_Model_Simulate' )->is_simulating(); 
  680. self::$_is_simulated_user[$user_id] = $res; 
  681.  
  682. if ( empty( $user_id ) ) { 
  683. self::$_is_simulated_user[ get_current_user_id() ] = $res; 
  684.  
  685. return self::$_is_simulated_user[$user_id]; 
  686.  
  687. /** 
  688. * Verify is user is not Admin user and simulation mode is deactivated. 
  689. * 
  690. * @since 1.0.0 
  691. * @api 
  692. * 
  693. * @param int|false $user_id Optional. The user ID. Default to current user. 
  694. * @return boolean 
  695. */ 
  696. static public function is_normal_user( $user_id = false ) { 
  697. if ( ! isset( self::$_is_normal_user[$user_id] ) ) { 
  698. // Simlation is only activated when the current user is an Admin. 
  699. $res = ! self::is_admin_user( $user_id ); 
  700. self::$_is_normal_user[$user_id] = $res; 
  701.  
  702. if ( empty( $user_id ) ) { 
  703. self::$_is_normal_user[ get_current_user_id() ] = $res; 
  704.  
  705. return self::$_is_normal_user[$user_id]; 
  706.  
  707. /** 
  708. * Get email addresses of all admin users. 
  709. * 
  710. * @since 1.0.0 
  711. * @internal 
  712. * 
  713. * @return string[] The admin emails. 
  714. */ 
  715. public static function get_admin_user_emails() { 
  716. $admins = array(); 
  717.  
  718. $args = array( 
  719. 'role' => 'administrator',  
  720. 'fields' => array( 'ID', 'user_email' ),  
  721. ); 
  722.  
  723. $wp_user_search = new WP_User_Query( $args ); 
  724. $users = $wp_user_search->get_results(); 
  725.  
  726. if ( ! empty ($users ) ) { 
  727. foreach ( $users as $user ) { 
  728. $admins[ $user->user_email ] = $user->user_email; 
  729.  
  730. return apply_filters( 
  731. 'ms_model_member_get_admin_user_emails',  
  732. $admins 
  733. ); 
  734.  
  735. /** 
  736. * Get username from user_id. 
  737. * 
  738. * @since 1.0.0 
  739. * @api 
  740. * 
  741. * @param int $user_id The user ID to get username. 
  742. * @return string The username. 
  743. */ 
  744. public static function get_username( $user_id ) { 
  745. $member = MS_Factory::load( 'MS_Model_Member', $user_id ); 
  746.  
  747. return apply_filters( 
  748. 'ms_model_member_get_username',  
  749. $member->username,  
  750. $user_id 
  751. ); 
  752.  
  753. /** 
  754. * Search for orphaned relationships and remove them. 
  755. * 
  756. * We write a custom SQL query for this, as solving it with a meta-query 
  757. * structure is very performance intense and requires at least two queries 
  758. * and a loop... 
  759. * 
  760. * For additional performance we will only do this check once every hour. 
  761. * 
  762. * Note: We cannot use the hook 'delete_user' to do this, because in 
  763. * Multisite users are deleted via the Main network admin; however, there 
  764. * we do not have access to the site data; especially if Plugin is not 
  765. * network enabled... 
  766. * 
  767. * @todo Change this to use WP-Cron instead of own implementation... 
  768. * 
  769. * @since 1.0.0 
  770. * @internal 
  771. */ 
  772. static public function clean_db() { 
  773. $timestamp = absint( MS_Factory::get_transient( 'ms_member_clean_db' ) ); 
  774. $elapsed = time() - $timestamp; 
  775.  
  776. if ( $elapsed > 3600 ) { 
  777. // Last check is longer than 1 hour ago. Check again. 
  778. MS_Factory::set_transient( 'ms_member_clean_db', time(), 3600 ); 
  779. } else { 
  780. // Last check was within past hour. Do nothing yet... 
  781. return; 
  782.  
  783. global $wpdb; 
  784.  
  785. // Find all Relationships that have no post-author. 
  786. $sql = " 
  787. SELECT p.ID 
  788. FROM {$wpdb->posts} p 
  789. WHERE p.post_type=%s 
  790. AND NOT EXISTS ( 
  791. SELECT 1 
  792. FROM {$wpdb->users} u 
  793. WHERE u.ID = p.post_author 
  794. ); 
  795. "; 
  796.  
  797. $sql = $wpdb->prepare( 
  798. $sql,  
  799. MS_Model_Relationship::get_post_type() 
  800. ); 
  801.  
  802. // Delete these Relationships! 
  803. $items = $wpdb->get_results( $sql ); 
  804. foreach ( $items as $item ) { 
  805. $junk = MS_Factory::load( 'MS_Model_Relationship', $item->ID ); 
  806. $junk->delete(); 
  807.  
  808. // 
  809. // 
  810. // 
  811. // ------------------------------------------------------------- SINGLE ITEM 
  812.  
  813.  
  814. /** 
  815. * Returns a list of variables that should be included in serialization,  
  816. * i.e. these values are the only ones that are stored in DB 
  817. * 
  818. * @since 1.0.0 
  819. * @internal 
  820. * @return array 
  821. */ 
  822. public function __sleep() { 
  823. return array( 
  824. 'id',  
  825. 'username',  
  826. 'email',  
  827. 'name',  
  828. 'first_name',  
  829. 'last_name',  
  830. 'subscriptions',  
  831. 'is_member',  
  832. 'gateway_profiles',  
  833. 'custom_data',  
  834. ); 
  835.  
  836. /** 
  837. * Validates the object right after it was loaded/initialized. 
  838. * 
  839. * We ensure that the custom_data field is an array. 
  840. * 
  841. * @since 1.0.0 
  842. */ 
  843. public function prepare_obj() { 
  844. parent::prepare_obj(); 
  845.  
  846. if ( ! is_array( $this->custom_data ) ) { 
  847. $this->custom_data = array(); 
  848.  
  849. /** 
  850. * Save member. 
  851. * 
  852. * Create a new user is id is empty. 
  853. * Save member fields to wp_user and wp_usermeta tables. 
  854. * Set cache for further use in MS_Factory::load. 
  855. * The usermeta are prefixed with 'ms_'. 
  856. * 
  857. * @since 1.0.0 
  858. * @api 
  859. * 
  860. * @return MS_Model_Member The saved member object. 
  861. */ 
  862. public function save() { 
  863. $class = get_class( $this ); 
  864.  
  865. /** 
  866. * Tell WordPress core that we do NOT want to trigger the 
  867. * Password-Reset email while updating the user now. 
  868. * New since WordPress 4.3.0 
  869. * 
  870. * @since 1.0.2.2 
  871. */ 
  872. add_filter( 'send_password_change_email', '__return_false' ); 
  873.  
  874. if ( empty( $this->id ) ) { 
  875. $this->create_new_user(); 
  876.  
  877. if ( isset( $this->username ) ) { 
  878. $wp_user = new stdClass(); 
  879. $wp_user->ID = $this->id; 
  880. $wp_user->nickname = $this->username; 
  881. $wp_user->user_nicename = $this->username; 
  882. $wp_user->first_name = $this->first_name; 
  883. $wp_user->last_name = $this->last_name; 
  884. $wp_user->user_email = $this->email; 
  885.  
  886. if ( ! empty( $this->password ) 
  887. && $this->password == $this->password2 
  888. ) { 
  889. $wp_user->user_pass = $this->password; 
  890. wp_update_user( get_object_vars( $wp_user ) ); 
  891.  
  892. // Serialize our plugin meta data 
  893. $data = MS_Factory::serialize_model( $this ); 
  894.  
  895. // Then update all meta fields that are inside the collection 
  896. foreach ( $data as $field => $val ) { 
  897. update_user_meta( $this->id, 'ms_' . $field, $val ); 
  898.  
  899. wp_cache_set( $this->id, $this, $class ); 
  900.  
  901. // Remove our "mute-password-reset-email" trigger again. 
  902. remove_filter( 'send_password_change_email', '__return_false' ); 
  903.  
  904. return apply_filters( 'ms_model_member_save', $this ); 
  905.  
  906. /** 
  907. * Create new WP user. 
  908. * 
  909. * @since 1.0.0 
  910. * @internal 
  911. * @throws Exception 
  912. */ 
  913. private function create_new_user() { 
  914. // Check if the WordPress settings allow user registration. 
  915. if ( ! MS_Model_Member::can_register() ) { 
  916. throw new Exception( __( 'Registration is currently not allowed.', 'membership2' ), 1 ); 
  917. return; 
  918.  
  919. if ( is_user_logged_in() ) { 
  920. throw new Exception( __( 'You cannot register a new account, because you are already logged in.', 'membership2' ), 1 ); 
  921. return; 
  922.  
  923. $validation_errors = new WP_Error(); 
  924.  
  925. $required = array( 
  926. 'username' => __( 'Username', 'membership2' ),  
  927. 'email' => __( 'Email address', 'membership2' ),  
  928. 'password' => __( 'Password', 'membership2' ),  
  929. 'password2' => __( 'Password confirmation', 'membership2' ),  
  930. ); 
  931.  
  932. /** 
  933. * Filter the required field list to customize the fields that are 
  934. * mandatory. 
  935. * 
  936. * @since 1.0.1.0 
  937. * @var array 
  938. */ 
  939. $required = apply_filters( 
  940. 'ms_model_member_create_user_required_fields',  
  941. $required 
  942. ); 
  943.  
  944. foreach ( $required as $field => $message ) { 
  945. if ( empty( $this->$field ) && empty( $_POST[$field] ) ) { 
  946. $validation_errors->add( 
  947. $field,  
  948. sprintf( 
  949. __( 'Please ensure that the <span class="ms-bold">%s</span> information is completed.', 'membership2' ),  
  950. $message 
  951. ); 
  952.  
  953. if ( $this->password != $this->password2 ) { 
  954. $validation_errors->add( 
  955. 'passmatch',  
  956. __( 'Please ensure the passwords match.', 'membership2' ) 
  957. ); 
  958.  
  959. if ( ! validate_username( $this->username ) ) { 
  960. $validation_errors->add( 
  961. 'usernamenotvalid',  
  962. __( 'The username is not valid, sorry.', 'membership2' ) 
  963. ); 
  964.  
  965. if ( username_exists( $this->username ) ) { 
  966. $validation_errors->add( 
  967. 'usernameexists',  
  968. __( 'That username is already taken, sorry.', 'membership2' ) 
  969. ); 
  970.  
  971. if ( ! is_email( $this->email ) ) { 
  972. $validation_errors->add( 
  973. 'emailnotvalid',  
  974. __( 'The email address is not valid, sorry.', 'membership2' ) 
  975. ); 
  976.  
  977. if ( email_exists( $this->email ) ) { 
  978. $validation_errors->add( 
  979. 'emailexists',  
  980. __( 'That email address is already taken, sorry.', 'membership2' ) 
  981. ); 
  982.  
  983. // Check the multisite Email-Domain limitation for new registrations. 
  984. if ( is_multisite() ) { 
  985. $illegal_names = get_site_option( 'illegal_names' ); 
  986. $limited_domains = get_site_option( 'limited_email_domains' ); 
  987. $banned_domains = get_site_option( 'banned_email_domains' ); 
  988. $email_domain = substr( strrchr( $this->email, '@' ), 1 ); 
  989.  
  990. if ( $illegal_names && is_array( $illegal_names ) ) { 
  991. if ( in_array( $this->username, $illegal_names ) ) { 
  992. $validation_errors->add( 
  993. 'illegalname',  
  994. __( 'The username is not valid, sorry.', 'membership2' ) 
  995. ); 
  996.  
  997. if ( $limited_domains && is_array( $limited_domains ) ) { 
  998. if ( ! in_array( $email_domain, $limited_domains ) ) { 
  999. $validation_errors->add( 
  1000. 'emaildomain',  
  1001. __( 'That email domain is not allowed for registration, sorry.', 'membership2' ) 
  1002. ); 
  1003.  
  1004. if ( $banned_domains && is_array( $banned_domains ) ) { 
  1005. if ( in_array( $email_domain, $banned_domains ) ) { 
  1006. $validation_errors->add( 
  1007. 'emaildomain',  
  1008. __( 'That email domain is not allowed for registration, sorry.', 'membership2' ) 
  1009. ); 
  1010.  
  1011. $validation_errors = apply_filters( 
  1012. 'ms_model_membership_create_new_user_validation_errors',  
  1013. $validation_errors,  
  1014. $this 
  1015. ); 
  1016.  
  1017. // Compatibility with WangGuard 
  1018. $_POST['user_email'] = $this->email; 
  1019.  
  1020. $user_data = array( 
  1021. 'user_name' => $this->username,  
  1022. 'orig_username' => $this->username,  
  1023. 'user_email' => $this->email,  
  1024. 'errors' => $validation_errors,  
  1025. ); 
  1026.  
  1027. $user_data = apply_filters( 
  1028. 'wpmu_validate_user_signup',  
  1029. $user_data 
  1030. ); 
  1031.  
  1032. if ( is_wp_error( $user_data ) ) { 
  1033. /** 
  1034. * Some plugins incorrectly return a WP_Error object as result of 
  1035. * the wpmu_validate_user_signup filter. 
  1036. */ 
  1037. $validation_errors = $user_data; 
  1038. } else { 
  1039. $validation_errors = $user_data['errors']; 
  1040.  
  1041. $errors = $validation_errors->get_error_messages(); 
  1042.  
  1043. if ( ! empty( $errors ) ) { 
  1044. throw new Exception( implode( '<br/>', $errors ) ); 
  1045. } else { 
  1046. if ( ! $this->password ) { 
  1047. /** 
  1048. * For some reason the user did not provide a password in the 
  1049. * registration form. We help out here by creating a password 
  1050. * for the little bugger and send him a password-reset email. 
  1051. * 
  1052. * So: Generate a STRONG password for the new user. 
  1053. * 
  1054. * Important: This password should be sent to the user via the 
  1055. * Email template "User Account Created" 
  1056. */ 
  1057. $this->password = wp_generate_password( 24 ); 
  1058. $this->password2 = $this->password; 
  1059.  
  1060. $user_id = wp_create_user( 
  1061. $this->username,  
  1062. $this->password,  
  1063. $this->email 
  1064. ); 
  1065.  
  1066. if ( is_wp_error( $user_id ) ) { 
  1067. $validation_errors->add( 
  1068. 'userid',  
  1069. $user_id->get_error_message() 
  1070. ); 
  1071.  
  1072. throw new Exception( 
  1073. implode( 
  1074. '<br/>',  
  1075. $validation_errors->get_error_messages() 
  1076. ); 
  1077. else 
  1078. if( isset( $_POST['display_name'] ) && $_POST['display_name'] != '' ) 
  1079. wp_update_user( array( 'ID' => $user_id, 'display_name' => $_POST['display_name'] ) ); 
  1080.  
  1081. $this->id = $user_id; 
  1082.  
  1083. do_action( 'ms_model_member_create_new_user', $this ); 
  1084.  
  1085. /** 
  1086. * Marks the current user as "confirmed" 
  1087. * 
  1088. * @since 1.0.0 
  1089. * @internal 
  1090. */ 
  1091. public function confirm() { 
  1092. global $wpdb; 
  1093.  
  1094. $auto_confirm = apply_filters( 
  1095. 'ms_model_member_auto_confirm',  
  1096. true,  
  1097. $this 
  1098. ); 
  1099.  
  1100. if ( ! $auto_confirm ) { return; } 
  1101.  
  1102. $sql = "UPDATE $wpdb->users SET user_status = 0 WHERE ID = %d"; 
  1103. $sql = $wpdb->prepare( $sql, $this->id ); 
  1104. $wpdb->query( $sql ); 
  1105.  
  1106. /** 
  1107. * Sign on user. 
  1108. * 
  1109. * @since 1.0.0 
  1110. * @api 
  1111. */ 
  1112. public function signon_user() { 
  1113. $user = new WP_User( $this->id ); 
  1114.  
  1115. if ( ! headers_sent() ) { 
  1116. $user = @wp_signon( 
  1117. array( 
  1118. 'user_login' => $this->username,  
  1119. 'user_password' => $this->password,  
  1120. 'remember' => true,  
  1121. ); 
  1122.  
  1123. // Stop here in case the login failed. 
  1124. if ( is_wp_error( $user ) ) { 
  1125. return $user; 
  1126.  
  1127. // Also used in class-ms-controller-dialog.php (Ajax login) 
  1128. wp_set_current_user( $this->id ); 
  1129. wp_set_auth_cookie( $this->id ); 
  1130. do_action( 'wp_login', $this->username, $user ); 
  1131. do_action( 'ms_model_member_signon_user', $user, $this ); 
  1132.  
  1133. /** 
  1134. * Either creates or updates the value of a custom data field. 
  1135. * 
  1136. * Note: Remember to prefix the $key with a unique string to prevent 
  1137. * conflicts with other plugins that also use this function. 
  1138. * 
  1139. * @since 1.0.0 
  1140. * @api 
  1141. * 
  1142. * @param string $key The field-key. 
  1143. * @param mixed $value The new value to assign to the field. 
  1144. */ 
  1145. public function set_custom_data( $key, $value ) { 
  1146. $this->custom_data[ $key ] = $value; 
  1147.  
  1148. /** 
  1149. * Removes a custom data field from this object. 
  1150. * 
  1151. * @since 1.0.0 
  1152. * @api 
  1153. * 
  1154. * @param string $key The field-key. 
  1155. */ 
  1156. public function delete_custom_data( $key ) { 
  1157. unset( $this->custom_data[ $key ] ); 
  1158.  
  1159. /** 
  1160. * Returns the value of a custom data field. 
  1161. * 
  1162. * @since 1.0.0 
  1163. * @api 
  1164. * 
  1165. * @param string $key The field-key. 
  1166. * @return mixed The value that was previously assigned to the custom field 
  1167. * or false if no value was set for the field. 
  1168. */ 
  1169. public function get_custom_data( $key ) { 
  1170. $res = false; 
  1171. if ( isset( $this->custom_data[ $key ] ) ) { 
  1172. $res = $this->custom_data[ $key ]; 
  1173. return $res; 
  1174.  
  1175. /** 
  1176. * Returns a list of all membership IDs of the current user. 
  1177. * 
  1178. * @since 1.0.0 
  1179. * @api 
  1180. * 
  1181. * @return array 
  1182. */ 
  1183. public function get_membership_ids() { 
  1184. $result = array(); 
  1185.  
  1186. foreach ( $this->subscriptions as $subscription ) { 
  1187. $result[] = $subscription->membership_id; 
  1188.  
  1189. return $result; 
  1190.  
  1191. /** 
  1192. * Add a new membership. 
  1193. * 
  1194. * If multiple membership is disabled, may move existing membership. 
  1195. * 
  1196. * Only add a membership if a user is not already a member. 
  1197. * 
  1198. * @since 1.0.0 
  1199. * @api 
  1200. * 
  1201. * @param int $membership_id The membership id to add to. 
  1202. * @param string $gateway_id Optional. The gateway used to add the membership. 
  1203. * @param int|string $move_from_id Optional. The membership id(s) to cancel. 
  1204. * 
  1205. * @return object|null $subscription 
  1206. */ 
  1207. public function add_membership( $membership_id, $gateway_id = 'admin', $move_from_id = 0 ) { 
  1208. $subscription = null; 
  1209.  
  1210. if ( MS_Model_Membership::is_valid_membership( $membership_id ) ) { 
  1211. if ( ! $this->get_subscription( $membership_id ) ) { 
  1212. $subscription = MS_Model_Relationship::create_ms_relationship( 
  1213. $membership_id,  
  1214. $this->id,  
  1215. $gateway_id,  
  1216. $move_from_id 
  1217. ); 
  1218.  
  1219. if ( 'admin' != $gateway_id ) { 
  1220. $subscription->get_current_invoice(); 
  1221.  
  1222. if ( MS_Model_Relationship::STATUS_PENDING !== $subscription->status ) { 
  1223. $this->subscriptions[] = $subscription; 
  1224.  
  1225. usort( 
  1226. $this->subscriptions,  
  1227. array( 'MS_Model_Relationship', 'sort_by_priority' ) 
  1228. ); 
  1229. } else { 
  1230. $subscription = $this->get_subscription( $membership_id ); 
  1231.  
  1232. // Reset the status and start/expire dates when added by admin. 
  1233. if ( 'admin' == $gateway_id ) { 
  1234. $subscription->start_date = null; // Will calculate correct date. 
  1235. $subscription->trial_expire_date = null; 
  1236. $subscription->expire_date = null; 
  1237. $subscription->status = MS_Model_Relationship::STATUS_ACTIVE; 
  1238. $subscription->save(); 
  1239.  
  1240. $this->is_member = true; 
  1241.  
  1242. return apply_filters( 
  1243. 'ms_model_member_add_membership',  
  1244. $subscription,  
  1245. $membership_id,  
  1246. $gateway_id,  
  1247. $move_from_id,  
  1248. $this 
  1249. ); 
  1250.  
  1251. /** 
  1252. * Drop a membership. 
  1253. * 
  1254. * Only update the status to deactivated. 
  1255. * 
  1256. * @since 1.0.0 
  1257. * @api 
  1258. * 
  1259. * @param int $membership_id The membership id to drop. 
  1260. */ 
  1261. public function drop_membership( $membership_id ) { 
  1262. $subscription = $this->get_subscription( $membership_id, $key ); 
  1263. if ( $subscription ) { 
  1264. do_action( 
  1265. 'ms_model_membership_drop_membership',  
  1266. $subscription,  
  1267. $this 
  1268. ); 
  1269.  
  1270. $subscription->deactivate_membership(); 
  1271. unset( $this->subscriptions[$key] ); 
  1272.  
  1273. $is_member = false; 
  1274. foreach ( $this->subscriptions as $subscription ) { 
  1275. if ( ! $subscription->is_system() ) { 
  1276. $is_member = true; 
  1277. break; 
  1278. $this->is_member = $is_member; 
  1279.  
  1280. do_action( 
  1281. 'ms_model_membership_drop_membership',  
  1282. $membership_id,  
  1283. $this 
  1284. ); 
  1285.  
  1286. /** 
  1287. * Cancel a membership. 
  1288. * 
  1289. * The membership remains valid until expiration date. 
  1290. * 
  1291. * @since 1.0.0 
  1292. * @api 
  1293. * 
  1294. * @param int $membership_id The membership id to drop. 
  1295. */ 
  1296. public function cancel_membership( $membership_id ) { 
  1297. $subscription = $this->get_subscription( $membership_id ); 
  1298. if ( $subscription ) { 
  1299. $subscription->cancel_membership(); 
  1300. } else { 
  1301. // The membership might be on status "PENDING" which is not included 
  1302. // in $this->subscriptions. 
  1303. $subscription = MS_Model_Relationship::get_subscription( 
  1304. $this->id,  
  1305. $membership_id 
  1306. ); 
  1307.  
  1308. if ( $subscription->user_id == $this->id ) { 
  1309. $subscription->cancel_membership(); 
  1310.  
  1311. do_action( 
  1312. 'ms_model_membership_cancel_membership',  
  1313. $membership_id,  
  1314. $this 
  1315. ); 
  1316.  
  1317. /** 
  1318. * Move a membership. 
  1319. * 
  1320. * @since 1.0.0 
  1321. * @api 
  1322. * 
  1323. * @param int $old_membership_id The membership id to move from. 
  1324. * @param int $mew_membership_id The membership id to move to. 
  1325. */ 
  1326. public function move_membership( $old_membership_id, $mew_membership_id ) { 
  1327. $old_subscription = $this->get_subscription( $old_membership_id ); 
  1328. if ( $old_subscription ) { 
  1329. $new_subscription = MS_Model_Relationship::create_ms_relationship( 
  1330. $mew_membership_id,  
  1331. $this->id,  
  1332. $old_subscription->gateway_id,  
  1333. $old_membership_id 
  1334. ); 
  1335.  
  1336. $this->cancel_membership( $old_membership_id ); 
  1337. $this->subscriptions[] = $new_subscription; 
  1338.  
  1339. MS_Model_Event::save_event( 
  1340. MS_Model_Event::TYPE_MS_MOVED,  
  1341. $new_subscription 
  1342. ); 
  1343.  
  1344. do_action( 
  1345. 'ms_model_membership_move_membership',  
  1346. $old_membership_id,  
  1347. $mew_membership_id,  
  1348. $this 
  1349. ); 
  1350.  
  1351. /** 
  1352. * Check membership relationship status. 
  1353. * 
  1354. * Canceled status is allowed until it expires. 
  1355. * 
  1356. * @since 1.0.0 
  1357. * @api 
  1358. * 
  1359. * @param int $membership_id Optional. The specific membership to verify. 
  1360. * If empty, verify against all memberships. 
  1361. * @return bool True if has a valid membership. 
  1362. */ 
  1363. public function has_membership( $membership_id = 0 ) { 
  1364. $has_membership = false; 
  1365.  
  1366. // Allowed membership status to have access 
  1367. $allowed_status = apply_filters( 
  1368. 'ms_model_member_allowed_status',  
  1369. array( 
  1370. MS_Model_Relationship::STATUS_ACTIVE,  
  1371. MS_Model_Relationship::STATUS_TRIAL,  
  1372. MS_Model_Relationship::STATUS_CANCELED,  
  1373. ); 
  1374.  
  1375. if ( self::is_normal_admin( $this->id ) ) { 
  1376. $has_membership = true; 
  1377.  
  1378. if ( ! empty( $membership_id ) ) { 
  1379. $subscription = $this->get_subscription( $membership_id ); 
  1380. // Membership-ID specified: Check if user has this membership 
  1381. if ( $subscription 
  1382. && in_array( $subscription->get_status(), $allowed_status ) 
  1383. ) { 
  1384. $has_membership = true; 
  1385. } elseif ( ! empty ( $this->subscriptions ) ) { 
  1386. // No membership-ID: Check if user has *any* membership 
  1387. foreach ( $this->subscriptions as $subscription ) { 
  1388. if ( $subscription->is_system() ) { continue; } 
  1389. if ( in_array( $subscription->get_status(), $allowed_status ) ) { 
  1390. $has_membership = true; 
  1391. break; 
  1392.  
  1393. return apply_filters( 
  1394. 'ms_model_member_has_membership',  
  1395. $has_membership,  
  1396. $membership_id,  
  1397. $this 
  1398. ); 
  1399.  
  1400. /** 
  1401. * Return the subscription object for the specified membership. 
  1402. * 
  1403. * @since 1.0.0 
  1404. * @api 
  1405. * 
  1406. * @param int|string $membership_id The specific membership to return. 
  1407. * Value 'priority' will return the subcription with lowest priority. 
  1408. * @return MS_Model_Relationship The subscription object. 
  1409. */ 
  1410. public function get_subscription( $membership_id, &$key = -1 ) { 
  1411. $subscription = null; 
  1412. $key = -1; 
  1413.  
  1414. if ( 'priority' == $membership_id ) { 
  1415. // Find subscription with the lowest priority. 
  1416. $cur_priority = -1; 
  1417. foreach ( $this->subscriptions as $ind => $item ) { 
  1418. $membership = $item->get_membership(); 
  1419. if ( ! $membership->active ) { continue; } 
  1420. if ( $cur_priority < 0 || $membership->priority < $cur_priority ) { 
  1421. $subscription = $item; 
  1422. $cur_priority = $membership->priority; 
  1423. $key = $ind; 
  1424. } elseif ( ! empty( $membership_id ) ) { 
  1425. // Membership-ID specified: Check if user has this membership 
  1426. foreach ( $this->subscriptions as $ind => $item ) { 
  1427. if ( $item->membership_id == $membership_id ) { 
  1428. $subscription = $item; 
  1429. $key = $ind; 
  1430. break; 
  1431.  
  1432. return apply_filters( 
  1433. 'ms_model_member_get_subscription',  
  1434. $subscription,  
  1435. $membership_id,  
  1436. $this 
  1437. ); 
  1438.  
  1439. /** 
  1440. * Returns a list of memberships for all active subscriptions of the member. 
  1441. * 
  1442. * @since 1.0.1.0 
  1443. * @return array 
  1444. */ 
  1445. protected function get_active_memberships() { 
  1446. $active_memberships = array(); 
  1447.  
  1448. $active_status = array( 
  1449. MS_Model_Relationship::STATUS_ACTIVE,  
  1450. MS_Model_Relationship::STATUS_TRIAL,  
  1451. MS_Model_Relationship::STATUS_CANCELED,  
  1452. ); 
  1453.  
  1454. foreach ( $this->subscriptions as $sub ) { 
  1455. if ( $sub->is_base() ) { continue; } 
  1456. if ( ! in_array( $sub->status, $active_status ) ) { continue; } 
  1457.  
  1458. $membership = $sub->get_membership(); 
  1459. $active_memberships[$membership->id] = $membership; 
  1460.  
  1461. return $active_memberships; 
  1462.  
  1463. /** 
  1464. * Checks if the current user is allowed to subscribe to the specified 
  1465. * membership. 
  1466. * 
  1467. * @since 1.0.1.0 
  1468. * @api 
  1469. * @param int $membership_id A membership_id. 
  1470. * @return bool Whether subscription is allowed or not. 
  1471. */ 
  1472. public function can_subscribe_to( $membership_id ) { 
  1473. static $Access_Flags = null; 
  1474.  
  1475. if ( null === $Access_Flags ) { 
  1476. $Access_Flags = array(); 
  1477. $active_memberships = $this->get_active_memberships(); 
  1478. $all_memberships = MS_Model_Membership::get_memberships(); 
  1479.  
  1480. /** 
  1481. * Controls how to handle conflicts in upgrade path settings when a 
  1482. * member has multiple memberships. 
  1483. * 
  1484. * Default is true: 
  1485. * If one membership forbids the upgrade, then that's it. 
  1486. * 
  1487. * Custom set to false: 
  1488. * If one membership allows the upgrade, then allow it. 
  1489. * 
  1490. * @since 1.0.1.0 
  1491. * @var bool 
  1492. */ 
  1493. $prefer_forbidden = apply_filters( 
  1494. 'ms_model_member_can_subscribe_to_prefer_forbidden',  
  1495. true 
  1496. ); 
  1497.  
  1498. foreach ( $active_memberships as $membership ) { 
  1499. $base_id = $membership->id; 
  1500. if ( $membership->is_guest() || $membership->is_user() ) { 
  1501. $base_id = 'guest'; 
  1502.  
  1503. foreach ( $all_memberships as $ms ) { 
  1504. if ( isset( $active_memberships[$ms->id] ) ) { continue; } 
  1505.  
  1506. $is_allowed = $ms->update_allowed( $base_id ); 
  1507.  
  1508. if ( ! isset( $Access_Flags[$ms->id] ) ) { 
  1509. $Access_Flags[$ms->id] = $is_allowed; 
  1510. } else { 
  1511. if ( $prefer_forbidden && ! $is_allowed ) { 
  1512. $Access_Flags[$ms->id] = $is_allowed; 
  1513. } elseif ( ! $prefer_forbidden && $is_allowed ) { 
  1514. $Access_Flags[$ms->id] = $is_allowed; 
  1515.  
  1516. $result = true; 
  1517. if ( isset( $Access_Flags[$membership_id] ) ) { 
  1518. $result = $Access_Flags[$membership_id]; 
  1519.  
  1520. return apply_filters( 
  1521. 'ms_model_member_can_subscribe_to',  
  1522. $result,  
  1523. $membership_id 
  1524. ); 
  1525.  
  1526. /** 
  1527. * Returns an array of existing subscriptions that should be cancelled when 
  1528. * the user signs up to the specified membership. 
  1529. * 
  1530. * @since 1.0.1.0 
  1531. * @param int $membership_id A membership ID. 
  1532. * @return array Might be an empty array or a list of membership IDs. 
  1533. */ 
  1534. public function cancel_ids_on_subscription( $membership_id ) { 
  1535. $result = array(); 
  1536.  
  1537. $membership = MS_Factory::load( 'MS_Model_Membership', $membership_id ); 
  1538. $active_memberships = $this->get_active_memberships(); 
  1539.  
  1540. foreach ( $active_memberships as $ms ) { 
  1541. if ( $membership->update_replaces( $ms->id ) ) { 
  1542. $result[] = $ms->id; 
  1543.  
  1544. return $result; 
  1545.  
  1546. /** 
  1547. * Delete member usermeta. 
  1548. * 
  1549. * Delete all plugin related usermeta. 
  1550. * 
  1551. * @since 1.0.0 
  1552. * @internal 
  1553. */ 
  1554. public function delete_all_membership_usermeta() { 
  1555. $this->subscriptions = array(); 
  1556. $this->gateway_profiles = array(); 
  1557. $this->is_member = false; 
  1558.  
  1559. do_action( 
  1560. 'ms_model_membership_delete_all_membership_usermeta',  
  1561. $this 
  1562. ); 
  1563.  
  1564. /** 
  1565. * Returns the WP_User object that is linked to the current member 
  1566. * 
  1567. * @since 1.0.0 
  1568. * @api 
  1569. * 
  1570. * @return WP_User 
  1571. */ 
  1572. public function get_user() { 
  1573. return $this->wp_user; 
  1574.  
  1575. /** 
  1576. * Returns a value from the user-meta table. 
  1577. * 
  1578. * @since 1.0.1.0 
  1579. * @api 
  1580. * @param string $key The meta-key. 
  1581. * @return mixed The meta-value. 
  1582. */ 
  1583. public function get_meta( $key ) { 
  1584. return get_user_meta( $this->id, $key, true ); 
  1585.  
  1586. /** 
  1587. * Updates a value in the user-meta table. 
  1588. * 
  1589. * @since 1.0.1.0 
  1590. * @api 
  1591. * @param string $key The meta-key. 
  1592. * @param mixed $value The new meta-value. 
  1593. */ 
  1594. public function set_meta( $key, $value ) { 
  1595. update_user_meta( $this->id, $key, $value ); 
  1596.  
  1597. /** 
  1598. * Verify if current object is valid. 
  1599. * 
  1600. * @since 1.0.0 
  1601. * @api 
  1602. * 
  1603. * @return boolean True if is valid. 
  1604. */ 
  1605. public function is_valid() { 
  1606. $valid = ( $this->id > 0 ); 
  1607.  
  1608. return apply_filters( 'ms_model_member_is_valid', $valid, $this ); 
  1609.  
  1610. /** 
  1611. * Get gateway profile. 
  1612. * 
  1613. * @since 1.0.0 
  1614. * @api 
  1615. * 
  1616. * @param string $gateway The gateway ID. 
  1617. * @param string $field Optional. The field to retrive. Default to null,  
  1618. * returning all profile info. 
  1619. * 
  1620. * @return mixed The gateway profile info. 
  1621. */ 
  1622. public function get_gateway_profile( $gateway, $field = null ) { 
  1623. $profile = null; 
  1624.  
  1625. if ( ! isset( $this->gateway_profiles[ $gateway ] ) ) { 
  1626. $this->gateway_profiles[ $gateway ] = array(); 
  1627.  
  1628. if ( empty( $field ) ) { 
  1629. $profile = $this->gateway_profiles[ $gateway ]; 
  1630. } else { 
  1631. if ( ! isset( $this->gateway_profiles[ $gateway ][ $field ] ) ) { 
  1632. $this->gateway_profiles[ $gateway ][ $field ] = ''; 
  1633. $profile = $this->gateway_profiles[ $gateway ][ $field ]; 
  1634.  
  1635. return apply_filters( 'ms_model_member_get_gateway_profile', $profile ); 
  1636.  
  1637. /** 
  1638. * Set gateway profile. 
  1639. * 
  1640. * @since 1.0.0 
  1641. * @api 
  1642. * 
  1643. * @param string $gateway The gateway ID. 
  1644. * @param string $field The field name to save. 
  1645. * @param mixed $value The field value to save. 
  1646. */ 
  1647. public function set_gateway_profile( $gateway, $field, $value ) { 
  1648. $this->gateway_profiles[ $gateway ][ $field ] = $value; 
  1649.  
  1650. do_action( 
  1651. 'ms_model_member_set_gateway_profile',  
  1652. $gateway,  
  1653. $field,  
  1654. $value,  
  1655. $this 
  1656. ); 
  1657.  
  1658. /** 
  1659. * Validate member info. 
  1660. * 
  1661. * @since 1.0.0 
  1662. * @internal 
  1663. * 
  1664. * @return boolean True if validated. 
  1665. * @throws Exception if not validated. 
  1666. */ 
  1667. public function validate_member_info() { 
  1668. $validation_errors = new WP_Error(); 
  1669.  
  1670. if ( ! is_email( $this->email ) ) { 
  1671. $validation_errors->add( 
  1672. 'emailnotvalid',  
  1673. __( 'The email address is not valid, sorry.', 'membership2' ) 
  1674. ); 
  1675.  
  1676. if ( $this->password != $this->password2 ) { 
  1677. $validation_errors->add( 
  1678. 'passmatch',  
  1679. __( 'Please ensure the passwords match.', 'membership2' ) 
  1680. ); 
  1681.  
  1682. $validation_errors = apply_filters( 
  1683. 'ms_model_member_validate_member_info_errors_obj',  
  1684. $validation_errors,  
  1685. $this 
  1686. ); 
  1687.  
  1688. $errors = apply_filters( 
  1689. 'ms_model_member_validate_member_info_errors',  
  1690. $validation_errors->get_error_messages(),  
  1691. $validation_errors,  
  1692. $this 
  1693. ); 
  1694.  
  1695. if ( ! empty( $errors ) ) { 
  1696. throw new Exception( implode( '<br/>', $errors ) ); 
  1697. } else { 
  1698. return true; 
  1699.  
  1700. /** 
  1701. * Creates a new password-reset key and stores it in the DB. 
  1702. * 
  1703. * @since 1.0.2.3 
  1704. * @return object { 
  1705. * The reset-key that can be sent to the user. 
  1706. * 
  1707. * @var key The reset key 
  1708. * @var url The full reset URL 
  1709. * } 
  1710. */ 
  1711. public function new_password_reset_key() { 
  1712. global $wpdb, $wp_hasher; 
  1713.  
  1714. // Generate something random for a password reset key. 
  1715. $key = wp_generate_password( 20, false ); 
  1716.  
  1717. do_action( 'retrieve_password_key', $this->username, $key ); 
  1718.  
  1719. // Now insert a hashed version of the key into the DB. 
  1720. // Important: The has needs to include the time() value! 
  1721. if ( empty( $wp_hasher ) ) { 
  1722. require_once ABSPATH . WPINC . '/class-phpass.php'; 
  1723. $wp_hasher = new PasswordHash( 8, true ); 
  1724. $hashed = time() . ':' . $wp_hasher->HashPassword( $key ); 
  1725. $wpdb->update( 
  1726. $wpdb->users,  
  1727. array( 'user_activation_key' => $hashed ),  
  1728. array( 'user_login' => $this->username ) 
  1729. ); 
  1730.  
  1731. MS_Model_Pages::create_missing_pages(); 
  1732. $reset_url = MS_Model_Pages::get_page_url( MS_Model_Pages::MS_PAGE_ACCOUNT ); 
  1733. $reset_url = esc_url_raw( 
  1734. add_query_arg( 
  1735. array( 
  1736. 'action' => MS_Controller_Frontend::ACTION_VIEW_RESETPASS,  
  1737. 'key' => $key,  
  1738. 'login' => rawurlencode( $this->username ),  
  1739. ),  
  1740. $reset_url 
  1741. ); 
  1742.  
  1743. return (object) array( 
  1744. 'key' => $key,  
  1745. 'url' => $reset_url,  
  1746. ); 
  1747.  
  1748. /** 
  1749. * Get specific property. 
  1750. * 
  1751. * @since 1.0.0 
  1752. * @internal 
  1753. * 
  1754. * @param string $name The name of a property to associate. 
  1755. * @return mixed The value of a property. 
  1756. */ 
  1757. public function __get( $property ) { 
  1758. $value = null; 
  1759.  
  1760. if ( property_exists( $this, $property ) ) { 
  1761. $value = $this->$property; 
  1762. } else { 
  1763. switch ( $property ) { 
  1764. case 'full_name': 
  1765. if ( ! empty( $this->first_name ) || ! empty( $this->last_name ) ) { 
  1766. $value = trim( $this->first_name . ' ' . $this->last_name ); 
  1767. } elseif ( ! empty( $this->name ) ) { 
  1768. $value = trim( $this->name ); 
  1769. } else { 
  1770. $value = trim( $this->username ); 
  1771. break; 
  1772.  
  1773. case 'display_name': 
  1774. $user = get_userdata( $this->id ); 
  1775. $value = $user->display_name; 
  1776. break; 
  1777.  
  1778. return $value; 
  1779.  
  1780. /** 
  1781. * Set specific property. 
  1782. * 
  1783. * @since 1.0.0 
  1784. * @internal 
  1785. * 
  1786. * @param string $name The name of a property to associate. 
  1787. * @param mixed $value The value of a property. 
  1788. */ 
  1789. public function __set( $property, $value ) { 
  1790. if ( property_exists( $this, $property ) ) { 
  1791. switch ( $property ) { 
  1792. case 'email': 
  1793. if ( is_email( $value ) ) { 
  1794. $this->$property = $value; 
  1795. break; 
  1796.  
  1797. case 'username': 
  1798. $this->$property = sanitize_user( $value ); 
  1799. break; 
  1800.  
  1801. case 'name': 
  1802. case 'first_name': 
  1803. case 'last_name': 
  1804. $this->$property = sanitize_text_field( $value ); 
  1805. break; 
  1806.  
  1807. default: 
  1808. $this->$property = $value; 
  1809. break; 
  1810. }else{ 
  1811. if( $property == 'display_name' ) 
  1812. wp_update_user( array( 'ID' => $this->id, 'display_name' => $value ) ); 
  1813.  
  1814.  
  1815. /** 
  1816. * Check if property isset. 
  1817. * 
  1818. * @since 1.0.0 
  1819. * @internal 
  1820. * 
  1821. * @param string $property The name of a property. 
  1822. * @return mixed Returns true/false. 
  1823. */ 
  1824. public function __isset( $property ) { 
  1825. return isset($this->$property); 
  1826. }  
.