/app/controller/class-ms-controller-api.php

  1. <?php 
  2. /** 
  3. * Exposes the public API. 
  4. * 
  5. * The simplest way to use the API is via an WordPress action `ms_init` that 
  6. * runs some code as soon as the API becomes available: 
  7. * 
  8. * // Run some code as early as possible. 
  9. * add_action( 'ms_init', 'my_api_hook' ); 
  10. * function my_api_hook( $api ) { 
  11. * $memberships = $api->list_memberships(); 
  12. * } 
  13. * 
  14. * **Recommended implementation structure** 
  15. * 
  16. * class My_Membership2_Implementation { 
  17. * protected $api = null; 
  18. * 
  19. * // Function is always executed. Will create 1 Implementation object. 
  20. * static public function setup() { 
  21. * static $Inst = null; 
  22. * if ( null === $Inst ) { 
  23. * $Inst = new My_Membership2_Implementation(); 
  24. * } 
  25. * } 
  26. * 
  27. * // Function set up the api hook. 
  28. * private function __construct() { 
  29. * add_action( 'ms_init', array( $this, 'init' ) ); 
  30. * } 
  31. * 
  32. * // Function is only run when Membership2 is present + active. 
  33. * public function init( $api ) { 
  34. * $this->api = $api; 
  35. * // The main init code should come here now! 
  36. * } 
  37. * 
  38. * // Add other event handlers and helper functions. 
  39. * // You can use $this->api in other functions to access the API object. 
  40. * } 
  41. * My_Membership2_Implementation::setup(); 
  42. * 
  43. * ---------------- 
  44. * 
  45. * We also add the WordPress filter `ms_active` to check if the plugin is 
  46. * enabled and loaded. As long as this filter returns `false` the API cannot 
  47. * be used: 
  48. * 
  49. * // Check if the API object is available. 
  50. * if ( apply_filters( 'ms_active', false ) ) { ... } 
  51. * 
  52. * Different ways to access the M2 API object: 
  53. * 
  54. * // 1. Our recommendation: 
  55. * add_action( 'ms_init', 'my_api_hook' ); 
  56. * function my_api_hook( $api ) { 
  57. * } 
  58. * 
  59. * // 2. Use a procedural approach. Use in init hook or later! 
  60. * $api = ms_api(); 
  61. * 
  62. * // 3. Not recommended: Direct access to the `$api` property: 
  63. * $api = MS_Plugin::$api; 
  64. * 
  65. * // 4. Not recommended: Singleton access: 
  66. * $api = MS_Controller_Api::instance(); 
  67. * 
  68. * @since 1.0.0 
  69. * 
  70. * @package Membership2 
  71. * @subpackage Controller 
  72. */ 
  73. class MS_Controller_Api extends MS_Hooker { 
  74.  
  75. /** 
  76. * A reference to the Membership2 settings object. 
  77. * 
  78. * @since 1.0.0 
  79. * @api 
  80. * @var MS_Model_Settings 
  81. */ 
  82. public $settings = null; 
  83.  
  84. /** 
  85. * Stores a list of custom payment gateways. 
  86. * 
  87. * @since 1.0.1.0 
  88. * @internal 
  89. * @var array 
  90. */ 
  91. protected $gateways = array(); 
  92.  
  93. /** 
  94. * Returns the singleton object. 
  95. * 
  96. * @since 1.0.1.2 
  97. * @return MS_Controller_Api 
  98. */ 
  99. static public function instance() { 
  100. static $Inst = null; 
  101.  
  102. if ( null === $Inst ) { 
  103. $Inst = new MS_Controller_Api(); 
  104.  
  105. return $Inst; 
  106.  
  107. /** 
  108. * Private constructor: Singleton pattern. 
  109. * 
  110. * @since 1.0.0 
  111. * @internal 
  112. */ 
  113. protected function __construct() { 
  114. $this->settings = MS_Plugin::instance()->settings; 
  115.  
  116. /** 
  117. * Simple check to allow other plugins to quickly find out if 
  118. * Membership2 is loaded and the API was initialized. 
  119. * 
  120. * Example: 
  121. * if ( apply_filters( 'ms_active', false ) ) { ... } 
  122. * 
  123. * @since 1.0.0 
  124. */ 
  125. add_filter( 'ms_active', '__return_true' ); 
  126.  
  127. /** 
  128. * Make the API controller accessible via static property. 
  129. * 
  130. * Example: 
  131. * $api = MS_Plugin::$api; 
  132. * 
  133. * Alternative: 
  134. * $api = apply_filters( 'ms_api', false ); 
  135. * 
  136. * @since 1.0.0 
  137. */ 
  138. MS_Plugin::set_api( $this ); 
  139.  
  140. /** 
  141. * Notify other plugins that Membership2 is ready. 
  142. * 
  143. * @since 1.0.0 
  144. */ 
  145. do_action( 'ms_init', $this ); 
  146.  
  147. /** 
  148. * Maintain compatibility with MS_Controller interface. 
  149. * 
  150. * @since 1.0.1.2 
  151. */ 
  152. public function admin_init() { } 
  153.  
  154. /** 
  155. * Returns either the current member or the member with the specified id. 
  156. * 
  157. * If the specified user does not exist then false is returned. 
  158. * 
  159. * // Useful functions of the Member object: 
  160. * $member->has_membership( $membership_id ) 
  161. * $member->get_subscription( $membership_id ) 
  162. * 
  163. * @since 1.0.0 
  164. * @api 
  165. * 
  166. * @param int $user_id User_id 
  167. * @return MS_Model_Member|false The Member model. 
  168. */ 
  169. public function get_member( $user_id ) { 
  170. $user_id = absint( $user_id ); 
  171. $member = MS_Factory::load( 'MS_Model_Member', $user_id ); 
  172.  
  173. if ( ! $member->is_valid() ) { 
  174. $member = false; 
  175.  
  176. return $member; 
  177.  
  178. /** 
  179. * Returns the Member object of the current user. 
  180. * 
  181. * @since 1.0.0 
  182. * @api 
  183. * 
  184. * @return MS_Model_Member The Member model. 
  185. */ 
  186. public function get_current_member() { 
  187. $member = MS_Model_Member::get_current_member(); 
  188.  
  189. return $member; 
  190.  
  191. /** 
  192. * Returns a single membership object. 
  193. * 
  194. * Other plugins can store and accuess custom settings for each membership: 
  195. * 
  196. * // Create a custom value in the membership 
  197. * $membership->set_custom_data( 'the_key', 'the_value' ); 
  198. * $membership->save(); // Custom data is now saved to database. 
  199. * 
  200. * // Access and delete the custom value 
  201. * $value = $membership->get_custom_data( 'the_key' ); 
  202. * $membership->delete_custom_data( 'the_key' ); 
  203. * $membership->save(); // Custom data is now deleted from database. 
  204. * 
  205. * @since 1.0.0 
  206. * @api 
  207. * 
  208. * @param int|string $membership_id The membership-ID or name/slug. 
  209. * @return MS_Model_Membership The membership object. 
  210. */ 
  211. public function get_membership( $membership_id ) { 
  212. if ( ! is_numeric( $membership_id ) ) { 
  213. $membership_id = $this->get_membership_id( $membership_id ); 
  214.  
  215. $membership = MS_Factory::load( 'MS_Model_Membership', intval( $membership_id ) ); 
  216.  
  217. return $membership; 
  218.  
  219. /** 
  220. * Returns the membership-ID that matches the specified Membership name or 
  221. * slug. 
  222. * 
  223. * If multiple memberships have the same name then the one with the lowest 
  224. * ID (= the oldest) will be returned. 
  225. * 
  226. * Name or slug are case-IN-sensitive ('slug' and 'SLUG' are identical) 
  227. * Wildcards are not allowed, the string must match exactly. 
  228. * 
  229. * @since 1.0.1.2 
  230. * @param string $name_or_slug The Membership name or slug to search. 
  231. * @return int|false The membership ID or false. 
  232. */ 
  233. public function get_membership_id( $name_or_slug ) { 
  234. global $wpdb; 
  235. $res = false; 
  236.  
  237. $sql = " 
  238. SELECT ID 
  239. FROM {$wpdb->posts} p 
  240. INNER JOIN {$wpdb->postmeta} m ON m.post_id = p.ID AND m.meta_key = %s 
  241. WHERE 
  242. p.post_type = %s 
  243. AND ( m.meta_value = %s OR p.post_name = %s ) 
  244. ORDER BY ID 
  245. ;"; 
  246.  
  247. MS_Factory::select_blog(); 
  248. $sql = $wpdb->prepare( 
  249. $sql,  
  250. 'name',  
  251. MS_Model_Membership::get_post_type(),  
  252. $name_or_slug,  
  253. $name_or_slug 
  254. ); 
  255.  
  256. $ids = $wpdb->get_col( $sql ); 
  257. MS_Factory::revert_blog(); 
  258.  
  259. if ( is_array( $ids ) && count( $ids ) ) { 
  260. $res = reset( $ids ); 
  261.  
  262. return $res; 
  263.  
  264. /** 
  265. * Returns a single subscription object of the specified user. 
  266. * 
  267. * If the user did not subscribe to the given membership then false is 
  268. * returned. 
  269. * 
  270. * Each subscription also offers custom data fields 
  271. * (see the details in get_membership() for details) 
  272. * 
  273. * @since 1.0.0 
  274. * @api 
  275. * 
  276. * @param int $user_id The user ID. 
  277. * @param int|string $membership_id The membership-ID or name/slug. 
  278. * @return MS_Model_Relationship|false The subscription object. 
  279. */ 
  280. public function get_subscription( $user_id, $membership_id ) { 
  281. $subscription = false; 
  282.  
  283. $membership = $this->get_membership( $membership_id ); 
  284.  
  285. $member = MS_Factory::load( 'MS_Model_Member', $user_id ); 
  286. if ( $member && $member->has_membership( $membership->id ) ) { 
  287. $subscription = $member->get_subscription( $membership->id ); 
  288.  
  289. return $subscription; 
  290.  
  291. /** 
  292. * Add a new subscription for the specified user. 
  293. * 
  294. * If the membership is free the subscription instantly is ACTIVE. 
  295. * Otherwise the subscription is set to PENDING until the user makes the 
  296. * payment via the M2 checkout page. 
  297. * 
  298. * @since 1.0.1.2 
  299. * @param int $user_id The User-ID. 
  300. * @param int|string $membership_id The membership-ID or name/slug. 
  301. * @return MS_Model_Relationship|null The new subscription object. 
  302. */ 
  303. public function add_subscription( $user_id, $membership_id ) { 
  304. $subscription = null; 
  305. $membership = $this->get_membership( $membership_id ); 
  306.  
  307. $member = MS_Factory::load( 'MS_Model_Member', $user_id ); 
  308. if ( $member ) { 
  309. $subscription = $member->add_membership( $membership->id, '' ); 
  310.  
  311. // Activate free memberships instantly. 
  312. if ( $membership->is_free() ) { 
  313. $subscription->add_payment( 0, MS_Gateway_Free::ID, 'free' ); 
  314.  
  315. return $subscription; 
  316.  
  317. /** 
  318. * Returns a list of all available Memberships. 
  319. * 
  320. * @since 1.0.0 
  321. * @api 
  322. * 
  323. * @param bool $list_all If set to true then also private and internal 
  324. * Memberships (e.g. Guest Membership) are included. 
  325. * Default is false which returns only memberships that a guest user 
  326. * can subscribe to. 
  327. * @return MS_Model_Membership[] List of all available Memberships. 
  328. */ 
  329. public function list_memberships( $list_all = false ) { 
  330. $args = array( 
  331. 'include_base' => false,  
  332. 'include_guest' => true,  
  333. ); 
  334. $list = MS_Model_Membership::get_memberships( $args ); 
  335.  
  336. if ( ! $list_all ) { 
  337. foreach ( $list as $key => $item ) { 
  338. if ( ! $item->active ) { unset( $list[$key] ); } 
  339. elseif ( $item->private ) { unset( $list[$key] ); } 
  340.  
  341. return $list; 
  342.  
  343. /** 
  344. * Tries to determine the currently displayed membership. 
  345. * 
  346. * Detection logic: 
  347. * 1. If a valid preferred value was specified then this value is used. 
  348. * 2. Examine REQUEST data and look for membership/subscription/invoice. 
  349. * 3. Check currently logged in user and use the top-priority subscription. 
  350. * 4. If no membership could be detected the response value is bool FALSE. 
  351. * 
  352. * @since 1.0.1.0 
  353. * @return false|MS_Model_Membership The detected Membership or false. 
  354. */ 
  355. public function detect_membership() { 
  356. $result = false; 
  357.  
  358. $membership_id = apply_filters( 'ms_detect_membership_id', $membership_id ); 
  359. if ( $membership_id ) { 
  360. $result = MS_Factory::load( 'MS_Model_Membership', $membership_id ); 
  361.  
  362. return apply_filters( 'ms_detect_membership_result', $result, $membership_id ); 
  363.  
  364. /** 
  365. * Create your own payment gateway and hook it up with Membership 2 by using 
  366. * this function! 
  367. * 
  368. * Creating your own payment gateway requires good php skills. To get you 
  369. * started follow these steps: 
  370. * 
  371. * 1. Copy the folder "/app/gateway/manual" to the "wp-contents/plugins" 
  372. * folder, and name it "membership-mygateway" 
  373. * 
  374. * 2. Rename all files inside the "membership-mygateway" and replace the 
  375. * term "manual" with "mygateway" 
  376. * 
  377. * 3. Edit all files, rename the class names inside the files to 
  378. * "_Mygateway" (replacing "_Manual") 
  379. * 
  380. * 4. In class MS_Gateway_Mygateway make following changes: 
  381. * - Set the value of const ID to "mygateway" 
  382. * 
  383. * - Change the assigned name in function "after_load" 
  384. * 
  385. * - Add a plugin header to the file, e.g. 
  386. * /** 
  387. * * Plugin name: Membership 2 Mygateway 
  388. * * / 
  389. * 
  390. * - Add the following line to the bottom of the file: 
  391. * add_action( 'ms_init', 'mygateway_register' ); 
  392. * function mygateway_register( $api ) { 
  393. * $api->register_payment_gateway( 
  394. * MS_Gateway_Mygateway::ID,  
  395. * 'MS_Gateway_Mygateway' 
  396. * ) 
  397. * } 
  398. * 
  399. * Now you have created a new plugin that registers a custom payment gateway 
  400. * for Membership 2! Implementing the payment logic is up to you - you can 
  401. * get a lot of insight by reviewing the existing payment gateways. 
  402. * 
  403. * @since 1.0.1.0 
  404. * @api 
  405. * 
  406. * @param string $id The ID of the new gateway. 
  407. * @param string $class The Class-name of the new gateway. 
  408. */ 
  409. public function register_payment_gateway( $id, $class ) { 
  410. $this->gateways[$id] = $class; 
  411. $this->add_action( 'ms_model_gateway_register', '_register_gateways' ); 
  412.  
  413. /** 
  414. * Internal filter callback function that registers custom payment gateways. 
  415. * 
  416. * @since 1.0.1.0 
  417. * @internal 
  418. * 
  419. * @param array $gateways List of payment gateways. 
  420. * @return array New list of payment gateways. 
  421. */ 
  422. public function _register_gateways( $gateways ) { 
  423. foreach ( $this->gateways as $id => $class ) { 
  424. $gateways[$id] = $class; 
  425.  
  426. return $gateways; 
  427.  
  428. /** 
  429. * Membership2 has a nice integrated debugging feature. This feature can be 
  430. * helpful for other developers so this API offers a simple way to access 
  431. * the debugging feature. 
  432. * 
  433. * Also note that all membership objects come with the built-in debug 
  434. * function `$obj->dump()` to quickly analyze the object. 
  435. * 
  436. * // Example of $obj->dump() usage 
  437. * $user = MS_Plugin::$api->current_member(); 
  438. * $user->dump(); 
  439. * 
  440. * @since 1.0.0 
  441. * @api 
  442. * 
  443. * @param mixed $data The value to dump to the output stream. 
  444. */ 
  445. public function debug( $data ) { 
  446. lib3()->debug->enable(); 
  447. // Intended debug output, leave it here. 
  448. lib3()->debug->dump( $data ); 
  449.  
  450.  
  451.  
  452. /** 
  453. * TEMPLATE TAG FUNCTIONS. 
  454. */ 
  455.  
  456. if ( ! function_exists( 'ms_has_membership' ) ) { 
  457. /** 
  458. * Template tag: Check if the current user has a specific membership. 
  459. * 
  460. * Multiple memberships can be specified by adding more parameters to the 
  461. * function call. 
  462. * 
  463. * Examples: 
  464. * 
  465. * <?php if ( ms_has_membership() ) : ?> Current user has *any* membership? 
  466. * 
  467. * <?php if ( ms_has_membership(100) ) : ?> Current user has membership 100? 
  468. * 
  469. * <?php if ( ms_has_membership(100, 110) ) : ?> Current user has membership 100 or 110? 
  470. * 
  471. * @since 1.0.1.0 
  472. * @api Template Tag 
  473. * @param int $id Optional. Membership-ID to check. 
  474. * If no value is specified the function will check if the member 
  475. * has any membership at all. Guest/Default memberships are ignored. 
  476. * @param int $id2 Optional. You can specify multiple membership-IDs. Just 
  477. * add more parameters to the function call. 
  478. * @return bool True if the current member has any/the specified membership. 
  479. */ 
  480. function ms_has_membership( $id = 0 ) { 
  481. $result = false; 
  482. $current_member = MS_Plugin::$api->get_current_member(); 
  483.  
  484. // Check all params and return true if the member has any membership. 
  485. foreach ( func_get_args() as $check_id ) { 
  486. if ( $current_member->has_membership( $check_id ) ) { 
  487. $result = true; 
  488. break; 
  489.  
  490. return $result; 
  491.  
  492. if ( ! function_exists( 'ms_api' ) ) { 
  493. /** 
  494. * Procedural way to load the API instance. 
  495. * 
  496. * Call this function inside the init hook or later. Using it earlier might 
  497. * cause problems because other parts of M2 might not be completely 
  498. * initialized. 
  499. * 
  500. * @since 1.0.1.2 
  501. * @api 
  502. * @return MS_Controller_Api 
  503. */ 
  504. function ms_api() { 
  505. if ( ! did_action( 'init' ) ) { 
  506. _doing_it_wrong( 
  507. 'ms_api',  
  508. 'ms_api() is called before the "init" hook, this is too early!',  
  509. '1.0.1.2' 
  510. ); 
  511.  
  512. return MS_Controller_Api::instance(); 
.