MS_Model_Member

Member model.

Defined (1)

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

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