/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'; 
  27.  
  28. /** 
  29. * Members search constants. 
  30. * 
  31. * @since 1.0.0 
  32. * @internal 
  33. */ 
  34. const SEARCH_NOT_MEMBERS = 'not_members'; 
  35.  
  36. /** 
  37. * Members search constants. 
  38. * 
  39. * @since 1.0.0 
  40. * @internal 
  41. */ 
  42. const SEARCH_ALL_USERS = 'all_users'; 
  43.  
  44. /** 
  45. * Cache for function is_admin_user() 
  46. * 
  47. * @since 1.1.0 
  48. * @internal 
  49. * @var bool[] 
  50. */ 
  51. static protected $_is_admin_user = array(); 
  52.  
  53. /** 
  54. * Cache for function is_normal_admin() 
  55. * 
  56. * @since 1.1.0 
  57. * @internal 
  58. * @var bool[] 
  59. */ 
  60. static protected $_is_normal_admin = array(); 
  61.  
  62. /** 
  63. * Cache for function is_simulated_user() 
  64. * 
  65. * @since 1.1.0 
  66. * @internal 
  67. * @var bool[] 
  68. */ 
  69. static protected $_is_simulated_user = array(); 
  70.  
  71. /** 
  72. * Cache for function is_normal_user() 
  73. * 
  74. * @since 1.1.0 
  75. * @internal 
  76. * @var bool[] 
  77. */ 
  78. static protected $_is_normal_user = array(); 
  79.  
  80. /** 
  81. * Member's active subscriptions. 
  82. * 
  83. * Note: This field is populated by MS_Factory when the Member instance is 
  84. * created. 
  85. * 
  86. * @since 1.0.0 
  87. * @var array { 
  88. * @type int $membership_id The membership ID. 
  89. * @type MS_Model_Relationship The membership relationship model object. 
  90. * } 
  91. */ 
  92. protected $subscriptions = array(); 
  93.  
  94. /** 
  95. * Indicator if the user is a valid Member. 
  96. * 
  97. * Only members have access to memberships. 
  98. * False indicates blocked members (if signed up for a membership). 
  99. * 
  100. * @since 1.0.0 
  101. * @var boolean 
  102. */ 
  103. protected $is_member = false; 
  104.  
  105. /** 
  106. * Active status. 
  107. * 
  108. * Staus to activate or deactivate a user independently of the membership 
  109. * status. False indicates blocked members (if signed up for a membership). 
  110. * For further use. (For temporary member blocking). 
  111. * 
  112. * @since 1.0.0 
  113. * @var boolean 
  114. */ 
  115. protected $active = true; 
  116.  
  117. /** 
  118. * Member's username. 
  119. * 
  120. * Mapped from wordpress $wp_user object. 
  121. * @see WP_User $user_login. 
  122. * 
  123. * @since 1.0.0 
  124. * @var string 
  125. */ 
  126. protected $username; 
  127.  
  128. /** 
  129. * Member's email. 
  130. * 
  131. * Mapped from wordpress $wp_user object. 
  132. * @see WP_User $user_email. 
  133. * 
  134. * @since 1.0.0 
  135. * @var string 
  136. */ 
  137. protected $email; 
  138.  
  139. /** 
  140. * Member's name. 
  141. * 
  142. * Mapped from wordpress $wp_user object. 
  143. * @see WP_User $user_nicename. 
  144. * 
  145. * @since 1.0.0 
  146. * @var string 
  147. */ 
  148. protected $name; 
  149.  
  150. /** 
  151. * Member's first name. 
  152. * 
  153. * Mapped from wordpress $wp_user object. 
  154. * @see WP_User $first_name 
  155. * 
  156. * @since 1.0.0 
  157. * @var string 
  158. */ 
  159. protected $first_name; 
  160.  
  161. /** 
  162. * Member's last name. 
  163. * 
  164. * Mapped from wordpress $wp_user object. 
  165. * @see WP_User $last_name. 
  166. * 
  167. * @since 1.0.0 
  168. * @var string 
  169. */ 
  170. protected $last_name; 
  171.  
  172. /** 
  173. * Member's password. 
  174. * 
  175. * Used when registering. 
  176. * 
  177. * @since 1.0.0 
  178. * @internal 
  179. * @var string 
  180. */ 
  181. protected $password; 
  182.  
  183. /** 
  184. * Member's password confirmation. 
  185. * 
  186. * Used when registering. 
  187. * 
  188. * @since 1.0.0 
  189. * @internal 
  190. * @var string 
  191. */ 
  192. protected $password2; 
  193.  
  194. /** 
  195. * Member's gateway profiles info. 
  196. * 
  197. * Save gateway IDs. 
  198. * 
  199. * @since 1.0.0 
  200. * @var array { 
  201. * Return structure: $gateway[ $field ] => $value; 
  202. * 
  203. * @type string $gateway_id The gateway id. 
  204. * @type string $field The field to store. 
  205. * @type mixed $value The field value to store. 
  206. * } 
  207. */ 
  208. protected $gateway_profiles; 
  209.  
  210. /** 
  211. * The associated WP_User object 
  212. * 
  213. * @since 1.1 
  214. * @internal 
  215. * @var WP_User 
  216. */ 
  217. protected $wp_user; 
  218.  
  219. /** 
  220. * Custom data can be used by other plugins via the set_custom_data() and 
  221. * get_custom_data() functions. 
  222. * 
  223. * This can be used to store additional information on user-level, e.g. 
  224. * settings needed by some Add-ons or even by other plugins. 
  225. * 
  226. * @since 2.0.0 
  227. * 
  228. * @var array 
  229. */ 
  230. protected $custom_data = array(); 
  231.  
  232.  
  233. // 
  234. // 
  235. // 
  236. // -------------------------------------------------------------- COLLECTION 
  237.  
  238. /** 
  239. * Get current member. 
  240. * 
  241. * @since 1.0.0 
  242. * @internal 
  243. * 
  244. * @return MS_Model_Member The current member. 
  245. */ 
  246. static public function get_current_member() { 
  247. return MS_Factory::load( 'MS_Model_Member', get_current_user_id() ); 
  248.  
  249. /** 
  250. * Checks if user-signup is enabled for this site or not. 
  251. * 
  252. * @since 1.1.0 
  253. * @api 
  254. * 
  255. * @return bool 
  256. */ 
  257. static public function can_register() { 
  258. static $Signup_Allowed = null; 
  259.  
  260. if ( null === $Signup_Allowed ) { 
  261. $Signup_Allowed = false; 
  262.  
  263. if ( is_multisite() ) { 
  264. $reg_option = get_site_option( 'registration', 'none' ); 
  265. if ( in_array( $reg_option, array( 'all', 'user' ) ) ) { 
  266. $Signup_Allowed = true; 
  267. } else { 
  268. if ( get_option( 'users_can_register' ) ) { 
  269. $Signup_Allowed = true; 
  270.  
  271. return apply_filters( 
  272. 'ms_member_can_register',  
  273. $Signup_Allowed 
  274. ); 
  275.  
  276. /** 
  277. * Allows users to register for this site. 
  278. * 
  279. * @since 1.1.0 
  280. * @api 
  281. * 
  282. * @return bool 
  283. */ 
  284. static public function allow_registration() { 
  285. if ( self::can_register() ) { return; } 
  286.  
  287. if ( is_multisite() ) { 
  288. $reg_option = get_site_option( 'registration', 'none' ); 
  289. if ( 'blog' == $reg_option ) { 
  290. // Creation of new blogs is allowd. Add User-Registration. 
  291. update_site_option( 'registration', 'all' ); 
  292. } else { 
  293. // Only enable user registration and keep blogs disabled. 
  294. update_site_option( 'registration', 'user' ); 
  295. } else { 
  296. // Simply enable registration on single sites. 
  297. update_option( 'users_can_register', true ); 
  298.  
  299. /** 
  300. * Get members total count. 
  301. * 
  302. * @since 1.0.0 
  303. * @internal 
  304. * 
  305. * @param $args The query user args 
  306. * @see @link http://codex.wordpress.org/Class_Reference/WP_User_Query 
  307. * @return int The count. 
  308. */ 
  309. public static function get_members_count( $args = null ) { 
  310. $args = self::get_query_args( $args, self::SEARCH_ALL_USERS ); 
  311. $args['number'] = 0; 
  312. $args['count_total'] = true; 
  313. $wp_user_search = new WP_User_Query( $args ); 
  314.  
  315. return apply_filters( 
  316. 'ms_model_member_get_members_count',  
  317. $wp_user_search->get_total() 
  318. ); 
  319.  
  320. /** 
  321. * Get members IDs. 
  322. * The IDs are cached and only fetched once for each set of $args. 
  323. * 
  324. * @since 1.0.4.4 
  325. * @internal 
  326. * 
  327. * @param $args The query user args 
  328. * @see @link http://codex.wordpress.org/Class_Reference/WP_User_Query 
  329. * @return array List of member IDs 
  330. */ 
  331. public static function get_member_ids( $args = null, $search_option = self::SEARCH_ALL_USERS ) { 
  332. static $Members = array(); 
  333. $key = json_encode( $args ); 
  334.  
  335. if ( ! isset( $Members[$key] ) ) { 
  336. $args = self::get_query_args( $args, $search_option ); 
  337. $wp_user_search = new WP_User_Query( $args ); 
  338. $users = $wp_user_search->get_results(); 
  339. $members = array(); 
  340.  
  341. foreach ( $users as $user_id ) { 
  342. $members[] = $user_id; 
  343.  
  344. $Members[$key] = apply_filters( 
  345. 'ms_model_member_get_member_ids',  
  346. $members,  
  347. $args 
  348. ); 
  349.  
  350. return $Members[$key]; 
  351.  
  352. /** 
  353. * Get members. 
  354. * 
  355. * @since 1.0.0 
  356. * @internal 
  357. * 
  358. * @param $args The query user args 
  359. * @see @link http://codex.wordpress.org/Class_Reference/WP_User_Query 
  360. * @return MS_Model_Member[] The selected members. 
  361. */ 
  362. public static function get_members( $args = null, $search_option = self::SEARCH_ALL_USERS ) { 
  363. $members = array(); 
  364. $ids = self::get_member_ids( $args, $search_option ); 
  365.  
  366. foreach ( $ids as $user_id ) { 
  367. $members[] = MS_Factory::load( 'MS_Model_Member', $user_id ); 
  368.  
  369. return apply_filters( 
  370. 'ms_model_member_get_members',  
  371. $members,  
  372. $ids,  
  373. $args 
  374. ); 
  375.  
  376. /** 
  377. * Get usernames. 
  378. * 
  379. * @since 1.0.0 
  380. * @internal 
  381. * 
  382. * @param $args The query user args 
  383. * @see @link http://codex.wordpress.org/Class_Reference/WP_User_Query 
  384. * @param string $search_option The search options (only members, not members, all users). 
  385. * @return array { 
  386. * @type int $id The user_id. 
  387. * @type string $username The username. 
  388. * } 
  389. */ 
  390. public static function get_usernames( $args = null, $search_option = self::SEARCH_ONLY_MEMBERS, $return_array = true ) { 
  391. $members = array(); 
  392.  
  393. if ( $return_array ) { 
  394. $members[0] = __( 'Select a user', MS_TEXT_DOMAIN ); 
  395.  
  396. $args['fields'] = array( 'ID', 'user_login' ); 
  397. $args['number'] = 0; 
  398. $args = self::get_query_args( $args, $search_option ); 
  399. $wp_user_search = new WP_User_Query( $args ); 
  400. $users = $wp_user_search->get_results(); 
  401.  
  402. foreach ( $users as $user ) { 
  403. if ( ! self::is_admin_user( $user->ID ) ) { 
  404. if ( $return_array ) { 
  405. $members[ $user->ID ] = $user->user_login; 
  406. } else { 
  407. $members[] = array( 
  408. 'id' => $user->ID,  
  409. 'text' => $user->user_login,  
  410. ); 
  411.  
  412. return apply_filters( 
  413. 'ms_model_member_get_members_usernames',  
  414. $members,  
  415. $return_array 
  416. ); 
  417.  
  418. /** 
  419. * Get WP_Query object arguments. 
  420. * 
  421. * Default search arguments for this model. 
  422. * 
  423. * @since 1.0.0 
  424. * @internal 
  425. * 
  426. * @param $args The query user args 
  427. * @see @link http://codex.wordpress.org/Class_Reference/WP_User_Query 
  428. * @param string $search_option The search options (only members, not members, all users). 
  429. * @return array $args The parsed args. 
  430. */ 
  431. public static function get_query_args( $args = null, $search_option = self::SEARCH_ONLY_MEMBERS ) { 
  432. $defaults = apply_filters( 
  433. 'ms_model_member_get_query_args_defaults',  
  434. array( 
  435. 'order' => 'DESC',  
  436. 'orderby' => 'ID',  
  437. 'number' => 20,  
  438. 'offset' => 0,  
  439. 'fields' => 'ID',  
  440. ); 
  441.  
  442. $args = lib2()->array->get( $args ); 
  443. lib2()->array->equip( $args, 'meta_query' ); 
  444.  
  445. if ( 'none' !== $args['meta_query'] ) { 
  446. $args['meta_query'] = lib2()->array->get( $args['meta_query'] ); 
  447.  
  448. switch ( $search_option ) { 
  449. case self::SEARCH_ONLY_MEMBERS: 
  450. $args['meta_query'] = array( 
  451. array( 
  452. 'key' => 'ms_is_member',  
  453. 'value' => true,  
  454. ),  
  455. ); 
  456. break; 
  457.  
  458. case self::SEARCH_NOT_MEMBERS: 
  459. /** 
  460. * This does a recursive call to first get all member IDs 
  461. */ 
  462. $members = self::get_member_ids( 
  463. null,  
  464. self::SEARCH_ONLY_MEMBERS 
  465. ); 
  466.  
  467. $args['exclude'] = $members; 
  468. break; 
  469.  
  470. case self::SEARCH_ALL_USERS: 
  471. default: 
  472. break; 
  473. } else { 
  474. unset( $args['meta_query'] ); 
  475.  
  476. $args = wp_parse_args( $args, $defaults ); 
  477.  
  478. return apply_filters( 
  479. 'ms_model_member_get_query_args',  
  480. $args,  
  481. $defaults 
  482. ); 
  483.  
  484. /** 
  485. * Returns the current user ID. 
  486. * This function can be called before the init action hook. 
  487. * 
  488. * Much of this logic is taken from wp-includes/pluggable.php 
  489. * 
  490. * @since 1.1.1.4 
  491. * @internal 
  492. * @return int|false 
  493. */ 
  494. public static function get_user_id() { 
  495. static $User_id = false; 
  496.  
  497. if ( $User_id ) { 
  498. // We already found the user-id, no need to do it again. 
  499. return $User_id; 
  500.  
  501. if ( defined( 'DOING_CRON' ) && DOING_CRON ) { 
  502. // A cron request has no user credentials... 
  503. return 0; 
  504.  
  505. $cookie = wp_parse_auth_cookie(); 
  506.  
  507. if ( ! $cookie ) { 
  508. // Missing, expired or corrupt cookie. 
  509. return 0; 
  510.  
  511. $scheme = $cookie['scheme']; 
  512. $username = $cookie['username']; 
  513. $hmac = $cookie['hmac']; 
  514. $token = $cookie['token']; 
  515. $expiration = $cookie['expiration']; 
  516.  
  517. $user = get_user_by( 'login', $username ); 
  518.  
  519. if ( ! $user ) { 
  520. // Invalid username. 
  521. return 0; 
  522.  
  523. $pass_frag = substr( $user->user_pass, 8, 4 ); 
  524. $key = wp_hash( $username . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme ); 
  525. $algo = function_exists( 'hash' ) ? 'sha256' : 'sha1'; 
  526. $hash = hash_hmac( $algo, $username . '|' . $expiration . '|' . $token, $key ); 
  527.  
  528. if ( ! hash_equals( $hash, $hmac ) ) { 
  529. // Forged/expired cookie value. 
  530. return 0; 
  531.  
  532. // Remember the user-ID so we don't have to validate everything again. 
  533. $User_id = $user->ID; 
  534.  
  535. return $User_id; 
  536.  
  537. /** 
  538. * Verify is user is logged in. 
  539. * 
  540. * @since 1.0.0 
  541. * @internal 
  542. * 
  543. * @return boolean True if user is logged in. 
  544. */ 
  545. public static function is_logged_in() { 
  546. $logged = is_user_logged_in(); 
  547.  
  548. return apply_filters( 'ms_member_is_logged_in', $logged ); 
  549.  
  550. /** 
  551. * Verify is user is Admin user. 
  552. * 
  553. * @since 1.0.0 
  554. * @api 
  555. * 
  556. * @param int|false $user_id Optional. The user ID. Default to current user. 
  557. * @param string $capability The capability to check for admin users. 
  558. * @return boolean True if user is admin. 
  559. */ 
  560. static public function is_admin_user( $user_id = false, $capability = 'manage_options' ) { 
  561. if ( ! isset( self::$_is_admin_user[ $user_id ] ) ) { 
  562. $is_admin = false; 
  563. $default_user_id = null; 
  564.  
  565. if ( empty( $user_id ) ) { 
  566. $default_user_id = $user_id; 
  567. $user_id = self::get_user_id(); 
  568.  
  569. if ( is_super_admin( $user_id ) ) { 
  570. $is_admin = true; 
  571.  
  572. $capability = apply_filters( 
  573. 'ms_model_member_is_admin_user_capability',  
  574. $capability 
  575. ); 
  576.  
  577. if ( ! empty( $capability ) ) { 
  578. if ( empty( $user_id ) ) { 
  579. $is_admin = current_user_can( $capability ); 
  580. } else { 
  581. $is_admin = user_can( $user_id, $capability ); 
  582.  
  583. $res = apply_filters( 
  584. 'ms_model_member_is_admin_user',  
  585. $is_admin,  
  586. $user_id 
  587. ); 
  588. self::$_is_admin_user[ $user_id ] = $res; 
  589.  
  590. if ( null !== $default_user_id ) { 
  591. self::$_is_admin_user[ $default_user_id ] = $res; 
  592.  
  593. return self::$_is_admin_user[ $user_id ]; 
  594.  
  595. /** 
  596. * Verify is user is Admin user and simulation mode is deactivated. 
  597. * 
  598. * @since 1.1.0 
  599. * @api 
  600. * 
  601. * @param int|false $user_id Optional. The user ID. Default to current user. 
  602. * @return boolean 
  603. */ 
  604. static public function is_normal_admin( $user_id = false ) { 
  605. if ( ! isset( self::$_is_normal_admin[$user_id] ) ) { 
  606. $res = self::is_admin_user( $user_id ) 
  607. && ! MS_Factory::load( 'MS_Model_Simulate' )->is_simulating(); 
  608. self::$_is_normal_admin[$user_id] = $res; 
  609.  
  610. if ( empty( $user_id ) ) { 
  611. self::$_is_normal_admin[ get_current_user_id() ] = $res; 
  612.  
  613. return self::$_is_normal_admin[$user_id]; 
  614.  
  615. /** 
  616. * Verify is user is Admin user and simulation mode is active. 
  617. * 
  618. * @since 1.1.0 
  619. * @api 
  620. * 
  621. * @param int|false $user_id Optional. The user ID. Default to current user. 
  622. * @return boolean 
  623. */ 
  624. static public function is_simulated_user( $user_id = false ) { 
  625. if ( ! isset( self::$_is_simulated_user[$user_id] ) ) { 
  626. $res = self::is_admin_user( $user_id ) 
  627. && MS_Factory::load( 'MS_Model_Simulate' )->is_simulating(); 
  628. self::$_is_simulated_user[$user_id] = $res; 
  629.  
  630. if ( empty( $user_id ) ) { 
  631. self::$_is_simulated_user[ get_current_user_id() ] = $res; 
  632.  
  633. return self::$_is_simulated_user[$user_id]; 
  634.  
  635. /** 
  636. * Verify is user is not Admin user and simulation mode is deactivated. 
  637. * 
  638. * @since 1.1.0 
  639. * @api 
  640. * 
  641. * @param int|false $user_id Optional. The user ID. Default to current user. 
  642. * @return boolean 
  643. */ 
  644. static public function is_normal_user( $user_id = false ) { 
  645. if ( ! isset( self::$_is_normal_user[$user_id] ) ) { 
  646. // Simlation is only activated when the current user is an Admin. 
  647. $res = ! self::is_admin_user( $user_id ); 
  648. self::$_is_normal_user[$user_id] = $res; 
  649.  
  650. if ( empty( $user_id ) ) { 
  651. self::$_is_normal_user[ get_current_user_id() ] = $res; 
  652.  
  653. return self::$_is_normal_user[$user_id]; 
  654.  
  655. /** 
  656. * Get email addresses of all admin users. 
  657. * 
  658. * @since 1.0.0 
  659. * @internal 
  660. * 
  661. * @return string[] The admin emails. 
  662. */ 
  663. public static function get_admin_user_emails() { 
  664. $admins = array(); 
  665.  
  666. $args = array( 
  667. 'role' => 'administrator',  
  668. 'fields' => array( 'ID', 'user_email' ),  
  669. ); 
  670.  
  671. $wp_user_search = new WP_User_Query( $args ); 
  672. $users = $wp_user_search->get_results(); 
  673.  
  674. if ( ! empty ($users ) ) { 
  675. foreach ( $users as $user ) { 
  676. $admins[ $user->user_email ] = $user->user_email; 
  677.  
  678. return apply_filters( 
  679. 'ms_model_member_get_admin_user_emails',  
  680. $admins 
  681. ); 
  682.  
  683. /** 
  684. * Get username from user_id. 
  685. * 
  686. * @since 1.0.0 
  687. * @api 
  688. * 
  689. * @param int $user_id The user ID to get username. 
  690. * @return string The username. 
  691. */ 
  692. public static function get_username( $user_id ) { 
  693. $member = MS_Factory::load( 'MS_Model_Member', $user_id ); 
  694.  
  695. return apply_filters( 
  696. 'ms_model_member_get_username',  
  697. $member->username,  
  698. $user_id 
  699. ); 
  700.  
  701. /** 
  702. * Search for orphaned relationships and remove them. 
  703. * 
  704. * We write a custom SQL query for this, as solving it with a meta-query 
  705. * structure is very performance intense and requires at least two queries 
  706. * and a loop... 
  707. * 
  708. * For additional performance we will only do this check once every hour. 
  709. * 
  710. * Note: We cannot use the hook 'delete_user' to do this, because in 
  711. * Multisite users are deleted via the Main network admin; however, there 
  712. * we do not have access to the site data; especially if Plugin is not 
  713. * network enabled... 
  714. * 
  715. * @todo Change this to use WP-Cron instead of own implementation... 
  716. * 
  717. * @since 1.0.4.4 
  718. * @internal 
  719. */ 
  720. static public function clean_db() { 
  721. $timestamp = absint( MS_Factory::get_transient( 'ms_member_clean_db' ) ); 
  722. $elapsed = time() - $timestamp; 
  723.  
  724. if ( $elapsed > 3600 ) { 
  725. // Last check is longer than 1 hour ago. Check again. 
  726. MS_Factory::set_transient( 'ms_member_clean_db', time(), 3600 ); 
  727. } else { 
  728. // Last check was within past hour. Do nothing yet... 
  729. return; 
  730.  
  731. global $wpdb; 
  732.  
  733. // Find all Relationships that have no post-author. 
  734. $sql = " 
  735. SELECT p.ID 
  736. FROM {$wpdb->posts} p 
  737. WHERE p.post_type=%s 
  738. AND NOT EXISTS ( 
  739. SELECT 1 
  740. FROM {$wpdb->users} u 
  741. WHERE u.ID = p.post_author 
  742. ); 
  743. "; 
  744.  
  745. $sql = $wpdb->prepare( 
  746. $sql,  
  747. MS_Model_Relationship::get_post_type() 
  748. ); 
  749.  
  750. // Delete these Relationships! 
  751. $items = $wpdb->get_results( $sql ); 
  752. foreach ( $items as $item ) { 
  753. $junk = MS_Factory::load( 'MS_Model_Relationship', $item->ID ); 
  754. $junk->delete(); 
  755.  
  756. // 
  757. // 
  758. // 
  759. // ------------------------------------------------------------- SINGLE ITEM 
  760.  
  761.  
  762. /** 
  763. * Returns a list of variables that should be included in serialization,  
  764. * i.e. these values are the only ones that are stored in DB 
  765. * 
  766. * @since 1.1.0 
  767. * @internal 
  768. * @return array 
  769. */ 
  770. public function __sleep() { 
  771. return array( 
  772. 'is_member',  
  773. 'active',  
  774. 'gateway_profiles',  
  775. 'custom_data',  
  776. ); 
  777.  
  778. /** 
  779. * Validates the object right after it was loaded/initialized. 
  780. * 
  781. * We ensure that the custom_data field is an array. 
  782. * 
  783. * @since 2.0.0 
  784. */ 
  785. public function prepare_obj() { 
  786. parent::prepare_obj(); 
  787.  
  788. if ( ! is_array( $this->custom_data ) ) { 
  789. $this->custom_data = array(); 
  790.  
  791. /** 
  792. * Save member. 
  793. * 
  794. * Create a new user is id is empty. 
  795. * Save member fields to wp_user and wp_usermeta tables. 
  796. * Set cache for further use in MS_Factory::load. 
  797. * The usermeta are prefixed with 'ms_'. 
  798. * 
  799. * @since 1.0.0 
  800. * @api 
  801. * 
  802. * @return MS_Model_Member The saved member object. 
  803. */ 
  804. public function save() { 
  805. $class = get_class( $this ); 
  806.  
  807. if ( empty( $this->id ) ) { 
  808. $this->create_new_user(); 
  809.  
  810. if ( isset( $this->username ) ) { 
  811. $wp_user = new stdClass(); 
  812. $wp_user->ID = $this->id; 
  813. $wp_user->nickname = $this->username; 
  814. $wp_user->user_nicename = $this->username; 
  815. $wp_user->first_name = $this->first_name; 
  816. $wp_user->last_name = $this->last_name; 
  817. $wp_user->display_name = $this->username; 
  818.  
  819. if ( ! empty( $this->password ) 
  820. && $this->password == $this->password2 
  821. ) { 
  822. $wp_user->user_pass = $this->password; 
  823. wp_update_user( get_object_vars( $wp_user ) ); 
  824.  
  825. // Serialize our plugin meta data 
  826. $data = MS_Factory::serialize_model( $this ); 
  827.  
  828. // Then update all meta fields that are inside the collection 
  829. foreach ( $data as $field => $val ) { 
  830. update_user_meta( $this->id, 'ms_' . $field, $val ); 
  831.  
  832. wp_cache_set( $this->id, $this, $class ); 
  833. return apply_filters( 'ms_model_member_save', $this ); 
  834.  
  835. /** 
  836. * Create new WP user. 
  837. * 
  838. * @since 1.0.0 
  839. * @internal 
  840. * @throws Exception 
  841. */ 
  842. private function create_new_user() { 
  843. // Check if the WordPress settings allow user registration. 
  844. if ( ! MS_Model_Member::can_register() ) { 
  845. throw new Exception( __( 'Registration is currently not allowed.', MS_TEXT_DOMAIN ), 1 ); 
  846. return; 
  847.  
  848. $validation_errors = new WP_Error(); 
  849.  
  850. $required = array( 
  851. 'username' => __( 'Username', MS_TEXT_DOMAIN ),  
  852. 'email' => __( 'Email address', MS_TEXT_DOMAIN ),  
  853. 'password' => __( 'Password', MS_TEXT_DOMAIN ),  
  854. 'password2' => __( 'Password confirmation', MS_TEXT_DOMAIN ),  
  855. ); 
  856.  
  857. foreach ( $required as $field => $message ) { 
  858. if ( empty( $this->$field ) ) { 
  859. $validation_errors->add( 
  860. $field,  
  861. sprintf( 
  862. __( 'Please ensure that the <span class="ms-bold">%s</span> information is completed.', MS_TEXT_DOMAIN ),  
  863. $message 
  864. ); 
  865.  
  866. if ( $this->password != $this->password2 ) { 
  867. $validation_errors->add( 
  868. 'passmatch',  
  869. __( 'Please ensure the passwords match.', MS_TEXT_DOMAIN ) 
  870. ); 
  871.  
  872. if ( ! validate_username( $this->username ) ) { 
  873. $validation_errors->add( 
  874. 'usernamenotvalid',  
  875. __( 'The username is not valid, sorry.', MS_TEXT_DOMAIN ) 
  876. ); 
  877.  
  878. if ( username_exists( $this->username ) ) { 
  879. $validation_errors->add( 
  880. 'usernameexists',  
  881. __( 'That username is already taken, sorry.', MS_TEXT_DOMAIN ) 
  882. ); 
  883.  
  884. if ( ! is_email( $this->email ) ) { 
  885. $validation_errors->add( 
  886. 'emailnotvalid',  
  887. __( 'The email address is not valid, sorry.', MS_TEXT_DOMAIN ) 
  888. ); 
  889.  
  890. if ( email_exists( $this->email ) ) { 
  891. $validation_errors->add( 
  892. 'emailexists',  
  893. __( 'That email address is already taken, sorry.', MS_TEXT_DOMAIN ) 
  894. ); 
  895.  
  896. $validation_errors = apply_filters( 
  897. 'ms_model_membership_create_new_user_validation_errors',  
  898. $validation_errors 
  899. ); 
  900.  
  901. // Compatibility with WangGuard 
  902. $_POST['user_email'] = $this->email; 
  903.  
  904. $result = apply_filters( 
  905. 'wpmu_validate_user_signup',  
  906. array( 
  907. 'user_name' => $this->username,  
  908. 'orig_username' => $this->username,  
  909. 'user_email' => $this->email,  
  910. 'errors' => $validation_errors,  
  911. ); 
  912.  
  913. $validation_errors = $result['errors']; 
  914. $errors = $validation_errors->get_error_messages(); 
  915.  
  916. if ( ! empty( $errors ) ) { 
  917. throw new Exception( implode( '<br/>', $errors ) ); 
  918. } else { 
  919. $user_id = wp_create_user( $this->username, $this->password, $this->email ); 
  920.  
  921. if ( is_wp_error( $user_id ) ) { 
  922. $validation_errors->add( 
  923. 'userid',  
  924. $user_id->get_error_message() 
  925. ); 
  926.  
  927. throw new Exception( 
  928. implode( 
  929. '<br/>',  
  930. $validation_errors->get_error_messages() 
  931. ); 
  932. $this->id = $user_id; 
  933.  
  934. do_action( 'ms_model_member_create_new_user', $this ); 
  935.  
  936. /** 
  937. * Marks the current user as "confirmed" 
  938. * 
  939. * @since 1.1.0 
  940. * @internal 
  941. */ 
  942. public function confirm() { 
  943. global $wpdb; 
  944.  
  945. $sql = "UPDATE $wpdb->users SET user_status = 0 WHERE ID = %d"; 
  946. $sql = $wpdb->prepare( $sql, $this->id ); 
  947. $wpdb->query( $sql ); 
  948.  
  949. /** 
  950. * Sign on user. 
  951. * 
  952. * @since 1.0.0 
  953. * @api 
  954. */ 
  955. public function signon_user() { 
  956. $user = new WP_User( $this->id ); 
  957.  
  958. if ( ! headers_sent() ) { 
  959. $user = @wp_signon( 
  960. array( 
  961. 'user_login' => $this->username,  
  962. 'user_password' => $this->password,  
  963. 'remember' => true,  
  964. ); 
  965.  
  966. // Stop here in case the login failed. 
  967. if ( is_wp_error( $user ) ) { 
  968. return $user; 
  969.  
  970. // Also used in class-ms-controller-dialog.php (Ajax login) 
  971. wp_set_current_user( $this->id ); 
  972. wp_set_auth_cookie( $this->id ); 
  973. do_action( 'wp_login', $this->username, $user ); 
  974. do_action( 'ms_model_member_signon_user', $user, $this ); 
  975.  
  976. /** 
  977. * Either creates or updates the value of a custom data field. 
  978. * 
  979. * Note: Remember to prefix the $key with a unique string to prevent 
  980. * conflicts with other plugins that also use this function. 
  981. * 
  982. * @since 2.0.0 
  983. * @api 
  984. * 
  985. * @param string $key The field-key. 
  986. * @param mixed $value The new value to assign to the field. 
  987. */ 
  988. public function set_custom_data( $key, $value ) { 
  989. $this->custom_data[ $key ] = $value; 
  990.  
  991. /** 
  992. * Removes a custom data field from this object. 
  993. * 
  994. * @since 2.0.0 
  995. * @api 
  996. * 
  997. * @param string $key The field-key. 
  998. */ 
  999. public function delete_custom_data( $key ) { 
  1000. unset( $this->custom_data[ $key ] ); 
  1001.  
  1002. /** 
  1003. * Returns the value of a custom data field. 
  1004. * 
  1005. * @since 2.0.0 
  1006. * @api 
  1007. * 
  1008. * @param string $key The field-key. 
  1009. * @return mixed The value that was previously assigned to the custom field 
  1010. * or false if no value was set for the field. 
  1011. */ 
  1012. public function get_custom_data( $key ) { 
  1013. $res = false; 
  1014. if ( isset( $this->custom_data[ $key ] ) ) { 
  1015. $res = $this->custom_data[ $key ]; 
  1016. return $res; 
  1017.  
  1018. /** 
  1019. * Returns a list of all membership IDs of the current user. 
  1020. * 
  1021. * @since 1.1.0 
  1022. * @api 
  1023. * 
  1024. * @return array 
  1025. */ 
  1026. public function get_membership_ids() { 
  1027. $result = array(); 
  1028.  
  1029. foreach ( $this->subscriptions as $subscription ) { 
  1030. $result[] = $subscription->membership_id; 
  1031.  
  1032. return $result; 
  1033.  
  1034. /** 
  1035. * Add a new membership. 
  1036. * 
  1037. * If multiple membership is disabled, may move existing membership. 
  1038. * 
  1039. * Only add a membership if a user is not already a member. 
  1040. * 
  1041. * @since 1.0.0 
  1042. * @api 
  1043. * 
  1044. * @param int $membership_id The membership id to add to. 
  1045. * @param string $gateway_id Optional. The gateway used to add the membership. 
  1046. * @param int $move_from_id Optional. The membership id to move from if any. 
  1047. * 
  1048. * @return object|null $subscription 
  1049. */ 
  1050. public function add_membership( $membership_id, $gateway_id = 'admin', $move_from_id = 0 ) { 
  1051. $subscription = null; 
  1052.  
  1053. if ( MS_Model_Membership::is_valid_membership( $membership_id ) ) { 
  1054. if ( ! array_key_exists( $membership_id, $this->subscriptions ) ) { 
  1055. $subscription = MS_Model_Relationship::create_ms_relationship( 
  1056. $membership_id,  
  1057. $this->id,  
  1058. $gateway_id,  
  1059. $move_from_id 
  1060. ); 
  1061.  
  1062. if ( 'admin' != $gateway_id ) { 
  1063. $subscription->get_current_invoice(); 
  1064.  
  1065. if ( MS_Model_Relationship::STATUS_PENDING !== $subscription->status ) { 
  1066. $this->subscriptions[ $membership_id ] = $subscription; 
  1067. } else { 
  1068. $subscription = $this->subscriptions[ $membership_id ]; 
  1069.  
  1070. return apply_filters( 
  1071. 'ms_model_member_add_membership',  
  1072. $subscription,  
  1073. $membership_id,  
  1074. $gateway_id,  
  1075. $move_from_id,  
  1076. $this 
  1077. ); 
  1078.  
  1079. /** 
  1080. * Drop a membership. 
  1081. * 
  1082. * Only update the status to deactivated. 
  1083. * 
  1084. * @since 1.0.0 
  1085. * @api 
  1086. * 
  1087. * @param int $membership_id The membership id to drop. 
  1088. */ 
  1089. public function drop_membership( $membership_id ) { 
  1090. if ( array_key_exists( $membership_id, $this->subscriptions ) ) { 
  1091. do_action( 
  1092. 'ms_model_membership_drop_membership',  
  1093. $this->subscriptions[ $membership_id ],  
  1094. $this 
  1095. ); 
  1096.  
  1097. $this->subscriptions[ $membership_id ]->deactivate_membership(); 
  1098. unset( $this->subscriptions[ $membership_id ] ); 
  1099.  
  1100. do_action( 
  1101. 'ms_model_membership_drop_membership',  
  1102. $membership_id,  
  1103. $this 
  1104. ); 
  1105.  
  1106. /** 
  1107. * Cancel a membership. 
  1108. * 
  1109. * The membership remains valid until expiration date. 
  1110. * 
  1111. * @since 1.0.0 
  1112. * @api 
  1113. * 
  1114. * @param int $membership_id The membership id to drop. 
  1115. */ 
  1116. public function cancel_membership( $membership_id ) { 
  1117. if ( array_key_exists( $membership_id, $this->subscriptions ) ) { 
  1118. do_action( 
  1119. 'ms_model_membership_cancel_membership',  
  1120. $this->subscriptions[ $membership_id ],  
  1121. $this 
  1122. ); 
  1123.  
  1124. $this->subscriptions[ $membership_id ]->cancel_membership(); 
  1125. } else { 
  1126. // The membership might be on status "PENDING" which is not included 
  1127. // in $this->subscriptions. 
  1128. $relationship = MS_Model_Relationship::get_subscription( 
  1129. $this->id,  
  1130. $membership_id 
  1131. ); 
  1132.  
  1133. if ( $relationship->user_id == $this->id ) { 
  1134. $relationship->cancel_membership(); 
  1135.  
  1136. do_action( 
  1137. 'ms_model_membership_cancel_membership',  
  1138. $membership_id,  
  1139. $this 
  1140. ); 
  1141.  
  1142. /** 
  1143. * Move a membership. 
  1144. * 
  1145. * @since 1.0.0 
  1146. * @api 
  1147. * 
  1148. * @param int $old_membership_id The membership id to move from. 
  1149. * @param int $mew_membership_id The membership id to move to. 
  1150. */ 
  1151. public function move_membership( $old_membership_id, $mew_membership_id ) { 
  1152. if ( array_key_exists( $old_membership_id, $this->subscriptions ) ) { 
  1153. $move_from = $this->subscriptions[ $old_membership_id ]; 
  1154. $ms_relationship = MS_Model_Relationship::create_ms_relationship( 
  1155. $mew_membership_id,  
  1156. $this->id,  
  1157. $move_from->gateway_id,  
  1158. $old_membership_id 
  1159. ); 
  1160.  
  1161. $this->cancel_membership( $old_membership_id ); 
  1162. $this->subscriptions[ $mew_membership_id ] = $ms_relationship; 
  1163.  
  1164. MS_Model_Event::save_event( 
  1165. MS_Model_Event::TYPE_MS_MOVED,  
  1166. $this->subscriptions[ $mew_membership_id ] 
  1167. ); 
  1168.  
  1169. do_action( 
  1170. 'ms_model_membership_move_membership',  
  1171. $old_membership_id,  
  1172. $mew_membership_id,  
  1173. $this 
  1174. ); 
  1175.  
  1176. /** 
  1177. * Check membership relationship status. 
  1178. * 
  1179. * Canceled status is allowed until it expires. 
  1180. * 
  1181. * @since 1.0.0 
  1182. * @api 
  1183. * 
  1184. * @param int $membership_id Optional. The specific membership to verify. 
  1185. * If empty, verify against all memberships. 
  1186. * @return bool True if has a valid membership. 
  1187. */ 
  1188. public function has_membership( $membership_id = 0 ) { 
  1189. $has_membership = false; 
  1190.  
  1191. // Allowed membership status to have access 
  1192. $allowed_status = apply_filters( 
  1193. 'ms_model_member_allowed_status',  
  1194. array( 
  1195. MS_Model_Relationship::STATUS_ACTIVE,  
  1196. MS_Model_Relationship::STATUS_TRIAL,  
  1197. MS_Model_Relationship::STATUS_CANCELED,  
  1198. ); 
  1199.  
  1200. if ( self::is_normal_admin( $this->id ) ) { 
  1201. $has_membership = true; 
  1202.  
  1203. if ( ! empty( $membership_id ) ) { 
  1204. // Membership-ID specified: Check if user has this membership 
  1205. if ( array_key_exists( $membership_id, $this->subscriptions ) 
  1206. && in_array( $this->subscriptions[ $membership_id ]->get_status(), $allowed_status ) 
  1207. ) { 
  1208. $has_membership = true; 
  1209. } elseif ( ! empty ( $this->subscriptions ) ) { 
  1210. // No membership-ID: Check if user has *any* membership 
  1211. foreach ( $this->subscriptions as $membership_relationship ) { 
  1212. if ( $membership_relationship->is_system() ) { continue; } 
  1213. if ( in_array( $membership_relationship->get_status(), $allowed_status ) ) { 
  1214. $has_membership = true; 
  1215. break; 
  1216.  
  1217. return apply_filters( 
  1218. 'ms_model_member_has_membership',  
  1219. $has_membership,  
  1220. $membership_id,  
  1221. $this 
  1222. ); 
  1223.  
  1224. /** 
  1225. * Return the subscription object for the specified membership. 
  1226. * 
  1227. * @since 1.1.0 
  1228. * @api 
  1229. * 
  1230. * @param int $membership_id The specific membership to return. 
  1231. * @return MS_Model_Relationship The subscription object. 
  1232. */ 
  1233. public function get_subscription( $membership_id ) { 
  1234. $subscription = null; 
  1235.  
  1236. if ( ! empty( $membership_id ) ) { 
  1237. // Membership-ID specified: Check if user has this membership 
  1238. if ( array_key_exists( $membership_id, $this->subscriptions ) ) { 
  1239. $subscription = $this->subscriptions[$membership_id]; 
  1240.  
  1241. return apply_filters( 
  1242. 'ms_model_member_get_subscription',  
  1243. $subscription,  
  1244. $membership_id,  
  1245. $this 
  1246. ); 
  1247.  
  1248. /** 
  1249. * Delete member usermeta. 
  1250. * 
  1251. * Delete all plugin related usermeta. 
  1252. * 
  1253. * @since 1.0.0 
  1254. * @internal 
  1255. */ 
  1256. public function delete_all_membership_usermeta() { 
  1257. $this->subscriptions = array(); 
  1258. $this->gateway_profiles = array(); 
  1259. $this->is_member = false; 
  1260.  
  1261. do_action( 
  1262. 'ms_model_membership_delete_all_membership_usermeta',  
  1263. $this 
  1264. ); 
  1265.  
  1266. /** 
  1267. * Returns the WP_User object that is linked to the current member 
  1268. * 
  1269. * @since 1.1.0 
  1270. * @api 
  1271. * 
  1272. * @return WP_User 
  1273. */ 
  1274. public function get_user() { 
  1275. return $this->wp_user; 
  1276.  
  1277. /** 
  1278. * Verify if current object is valid. 
  1279. * 
  1280. * @since 1.0.0 
  1281. * @api 
  1282. * 
  1283. * @return boolean True if is valid. 
  1284. */ 
  1285. public function is_valid() { 
  1286. $valid = ( $this->id > 0 ); 
  1287.  
  1288. return apply_filters( 'ms_model_member_is_valid', $valid, $this ); 
  1289.  
  1290. /** 
  1291. * Get gateway profile. 
  1292. * 
  1293. * @since 1.0.0 
  1294. * @api 
  1295. * 
  1296. * @param string $gateway The gateway ID. 
  1297. * @param string $field Optional. The field to retrive. Default to null,  
  1298. * returning all profile info. 
  1299. * 
  1300. * @return mixed The gateway profile info. 
  1301. */ 
  1302. public function get_gateway_profile( $gateway, $field = null ) { 
  1303. $profile = null; 
  1304.  
  1305. if ( ! isset( $this->gateway_profiles[ $gateway ] ) ) { 
  1306. $this->gateway_profiles[ $gateway ] = array(); 
  1307.  
  1308. if ( empty( $field ) ) { 
  1309. $profile = $this->gateway_profiles[ $gateway ]; 
  1310. } else { 
  1311. if ( ! isset( $this->gateway_profiles[ $gateway ][ $field ] ) ) { 
  1312. $this->gateway_profiles[ $gateway ][ $field ] = ''; 
  1313. $profile = $this->gateway_profiles[ $gateway ][ $field ]; 
  1314.  
  1315. return apply_filters( 'ms_model_member_get_gateway_profile', $profile ); 
  1316.  
  1317. /** 
  1318. * Set gateway profile. 
  1319. * 
  1320. * @since 1.0.0 
  1321. * @api 
  1322. * 
  1323. * @param string $gateway The gateway ID. 
  1324. * @param string $field The field name to save. 
  1325. * @param mixed $value The field value to save. 
  1326. */ 
  1327. public function set_gateway_profile( $gateway, $field, $value ) { 
  1328. $this->gateway_profiles[ $gateway ][ $field ] = $value; 
  1329.  
  1330. do_action( 
  1331. 'ms_model_member_set_gateway_profile',  
  1332. $gateway,  
  1333. $field,  
  1334. $value,  
  1335. $this 
  1336. ); 
  1337.  
  1338. /** 
  1339. * Validate member info. 
  1340. * 
  1341. * @since 1.0.0 
  1342. * @internal 
  1343. * 
  1344. * @return boolean True if validated. 
  1345. * @throws Exception if not validated. 
  1346. */ 
  1347. public function validate_member_info() { 
  1348. $validation_errors = new WP_Error(); 
  1349.  
  1350. if ( ! is_email( $this->email ) ) { 
  1351. $validation_errors->add( 
  1352. 'emailnotvalid',  
  1353. __( 'The email address is not valid, sorry.', MS_TEXT_DOMAIN ) 
  1354. ); 
  1355.  
  1356. if ( $this->password != $this->password2 ) { 
  1357. $validation_errors->add( 
  1358. 'passmatch',  
  1359. __( 'Please ensure the passwords match.', MS_TEXT_DOMAIN ) 
  1360. ); 
  1361.  
  1362. $errors = apply_filters( 
  1363. 'ms_model_member_validate_member_info_errors',  
  1364. $validation_errors->get_error_messages() 
  1365. ); 
  1366.  
  1367. if ( ! empty( $errors ) ) { 
  1368. throw new Exception( implode( '<br/>', $errors ) ); 
  1369. } else { 
  1370. return true; 
  1371.  
  1372. /** 
  1373. * Get specific property. 
  1374. * 
  1375. * @since 1.1.0 
  1376. * @internal 
  1377. * 
  1378. * @param string $name The name of a property to associate. 
  1379. * @return mixed The value of a property. 
  1380. */ 
  1381. public function __get( $property ) { 
  1382. $value = null; 
  1383.  
  1384. if ( property_exists( $this, $property ) ) { 
  1385. $value = $this->$property; 
  1386. } else { 
  1387. switch ( $property ) { 
  1388. case 'full_name': 
  1389. if ( ! empty( $this->first_name ) || ! empty( $this->last_name ) ) { 
  1390. $value = trim( $this->first_name . ' ' . $this->last_name ); 
  1391. } elseif ( ! empty( $this->name ) ) { 
  1392. $value = trim( $this->name ); 
  1393. } else { 
  1394. $value = trim( $this->username ); 
  1395. break; 
  1396.  
  1397. return $value; 
  1398.  
  1399. /** 
  1400. * Set specific property. 
  1401. * 
  1402. * @since 1.0.0 
  1403. * @internal 
  1404. * 
  1405. * @param string $name The name of a property to associate. 
  1406. * @param mixed $value The value of a property. 
  1407. */ 
  1408. public function __set( $property, $value ) { 
  1409. if ( property_exists( $this, $property ) ) { 
  1410. switch ( $property ) { 
  1411. case 'email': 
  1412. if ( is_email( $value ) ) { 
  1413. $this->$property = $value; 
  1414. break; 
  1415.  
  1416. case 'username': 
  1417. $this->$property = sanitize_user( $value ); 
  1418. break; 
  1419.  
  1420. case 'name': 
  1421. case 'first_name': 
  1422. case 'last_name': 
  1423. $this->$property = sanitize_text_field( $value ); 
  1424. break; 
  1425.  
  1426. default: 
  1427. $this->$property = $value; 
  1428. break; 
.