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

  1. <?php 
  2. /** 
  3. * Controller for managing Members and Membership relationships. 
  4. * 
  5. * Manages the Member and the member's Memberships. 
  6. * 
  7. * @since 1.0.0 
  8. * 
  9. * @package Membership2 
  10. * @subpackage Controller 
  11. */ 
  12. class MS_Controller_Member extends MS_Controller { 
  13.  
  14. /** 
  15. * AJAX action constant: Edit subscriptions of a single member. 
  16. * 
  17. * @since 1.0.0 
  18. * 
  19. * @var string 
  20. */ 
  21. const AJAX_ACTION_CHANGE_MEMBERSHIPS = 'member_subscriptions'; 
  22.  
  23. /** 
  24. * AJAX action constant: Validate a user field before creating the user. 
  25. * 
  26. * @since 1.0.1.0 
  27. * 
  28. * @var string 
  29. */ 
  30. const AJAX_ACTION_VALIDATE_FIELD = 'member_validate_field'; 
  31.  
  32. /** 
  33. * AJAX action constant: Search users via Ajax. 
  34. * 
  35. * @since 1.0.1.0 
  36. * 
  37. * @var string 
  38. */ 
  39. const AJAX_ACTION_SEARCH = 'member_search'; 
  40.  
  41. /** 
  42. * Used on the Add Member screen to indicate that a new WP User should be 
  43. * created and added to M2. 
  44. * 
  45. * @since 1.0.1.0 
  46. * 
  47. * @var string 
  48. */ 
  49. const ACTION_ADD_MEMBER = 'member_add'; 
  50.  
  51. /** 
  52. * Used on the Add Member screen to indicate that the submitted form details 
  53. * should update an existing user. 
  54. * 
  55. * @since 1.0.1.0 
  56. * 
  57. * @var string 
  58. */ 
  59. const ACTION_UPDATE_MEMBER = 'member_update'; 
  60.  
  61. /** 
  62. * Used on the Add Member screen to trigger a new subscription action for an 
  63. * existing user (user subscribes to one or multiple memberships) 
  64. * 
  65. * @since 1.0.1.0 
  66. * 
  67. * @var string 
  68. */ 
  69. const ACTION_MODIFY_SUBSCRIPTIONS = 'member_subscription'; 
  70.  
  71. /** 
  72. * Used on the Add Member screen to indicate that an existing WP User should 
  73. * be added to M2. 
  74. * 
  75. * @since 1.0.1.0 
  76. * 
  77. * @var string 
  78. */ 
  79. const ACTION_SELECT_MEMBER = 'member_select'; 
  80.  
  81. /** 
  82. * Prepare the Member manager. 
  83. * 
  84. * @since 1.0.0 
  85. */ 
  86. public function __construct() { 
  87. parent::__construct(); 
  88.  
  89. $this->add_action( 
  90. 'ms_controller_membership_setup_completed',  
  91. 'add_current_user' 
  92. ); 
  93.  
  94. $this->add_ajax_action( 
  95. self::AJAX_ACTION_CHANGE_MEMBERSHIPS,  
  96. 'ajax_action_change_memberships' 
  97. ); 
  98.  
  99. $this->add_ajax_action( 
  100. self::AJAX_ACTION_VALIDATE_FIELD,  
  101. 'ajax_action_validate_field' 
  102. ); 
  103.  
  104. $this->add_ajax_action( 
  105. self::AJAX_ACTION_SEARCH,  
  106. 'ajax_action_search' 
  107. ); 
  108.  
  109. /** 
  110. * Initialize the admin-side functions. 
  111. * 
  112. * @since 1.0.0 
  113. */ 
  114. public function admin_init() { 
  115. $hooks = array( 
  116. 'list' => MS_Controller_Plugin::admin_page_hook( 'members' ),  
  117. 'editor' => MS_Controller_Plugin::admin_page_hook( 'add-member' ),  
  118. ); 
  119.  
  120. foreach ( $hooks as $key => $hook ) { 
  121. $this->run_action( 'load-' . $hook, 'members_admin_page_process_' . $key ); 
  122. $this->run_action( 'admin_print_scripts-' . $hook, 'enqueue_scripts_' . $key ); 
  123. $this->run_action( 'admin_print_styles-' . $hook, 'enqueue_styles' ); 
  124.  
  125. /** 
  126. * Add the current user to the Members-List. 
  127. * 
  128. * This does NOT assign any membership to the user, but ensures that the 
  129. * admin user appears in the Members-List 
  130. * 
  131. * @since 1.0.0 
  132. */ 
  133. public function add_current_user() { 
  134. $member = MS_Factory::load( 
  135. 'MS_Model_Member',  
  136. get_current_user_id() 
  137. ); 
  138.  
  139. $member->is_member = true; 
  140. $member->save(); 
  141.  
  142. /** 
  143. * Manages membership actions. 
  144. * 
  145. * Verifies GET and POST requests to manage members 
  146. * 
  147. * @since 1.0.0 
  148. */ 
  149. public function members_admin_page_process_list() { 
  150. $msg = 0; 
  151. $redirect = false; 
  152.  
  153. if ( $this->is_admin_user() ) { 
  154. $fields_new = array( 'new_member', 'action' ); 
  155. $fields_edit = array( 'member_id', 'action' ); 
  156.  
  157. // Execute list table single action. 
  158. if ( $this->verify_nonce( null, 'GET' ) 
  159. && self::validate_required( $fields_edit, 'GET' ) 
  160. ) { 
  161. $msg = $this->member_list_do_action( 
  162. $_GET['action'],  
  163. array( $_GET['member_id'] ) 
  164. ); 
  165.  
  166. $redirect = esc_url_raw( 
  167. add_query_arg( 
  168. array( 'msg' => $msg ),  
  169. remove_query_arg( 
  170. array( 'member_id', 'action', '_wpnonce' ) 
  171. ); 
  172.  
  173. // Execute list table bulk actions. 
  174. elseif ( $this->verify_nonce( 'bulk' ) ) { 
  175. lib3()->array->equip_post( 'action', 'action2', 'member_id' ); 
  176. $action = $_POST['action']; 
  177. if ( empty( $action ) || $action == '-1' ) { 
  178. $action = $_POST['action2']; 
  179. $members = $_POST['member_id']; 
  180.  
  181. /** 
  182. * The Bulk-Edit action is built like 'cmd-id' 
  183. * e.g. 'add-123' will add membership 123 to the selected items. 
  184. */ 
  185. if ( empty( $action ) ) { 
  186. $cmd = array(); 
  187. } elseif ( empty( $members ) ) { 
  188. $cmd = array(); 
  189. } elseif ( '-1' == $action ) { 
  190. $cmd = array(); 
  191. } else { 
  192. $cmd = explode( '-', $action ); 
  193.  
  194. if ( 2 == count( $cmd ) ) { 
  195. $action = $cmd[0]; 
  196. $action_id = $cmd[1]; 
  197.  
  198. // Get a list of specified memberships... 
  199. if ( is_numeric( $action_id ) ) { 
  200. // ... either a single membership. 
  201. $memberships = array( 
  202. MS_Factory::load( 'MS_Model_Membership', $action_id ),  
  203. ); 
  204. } elseif ( 'all' == $action_id ) { 
  205. // ... or all memberships. 
  206. $memberships = MS_Model_Membership::get_memberships(); 
  207.  
  208. // Loop defined memberships and add/remove members. 
  209. foreach ( $memberships as $membership ) { 
  210. $msg = $this->member_list_do_action( 
  211. $action,  
  212. $members,  
  213. $membership->id 
  214. ); 
  215.  
  216. $redirect = esc_url_raw( 
  217. add_query_arg( array( 'msg' => $msg ) ) 
  218. ); 
  219.  
  220. // Execute edit view page action submit. 
  221. elseif ( isset( $_POST['submit'] ) 
  222. && $this->verify_nonce() 
  223. && self::validate_required( $fields_edit, 'POST' ) 
  224. ) { 
  225. if ( is_array( $_POST['member_id'] ) ) { 
  226. $member_ids = $_POST['member_id']; 
  227. } else { 
  228. $member_ids = explode( ', ', $_POST['member_id'] ); 
  229.  
  230. $msg = $this->member_list_do_action( 
  231. $_POST['action'],  
  232. $member_ids,  
  233. $_POST['membership_id'] 
  234. ); 
  235.  
  236. $redirect = esc_url_raw( 
  237. add_query_arg( array( 'msg' => $msg ) ) 
  238. ); 
  239.  
  240. if ( $redirect ) { 
  241. wp_safe_redirect( $redirect ); 
  242. exit; 
  243.  
  244. /** 
  245. * Manages membership actions for the ADD/EDIT screen. 
  246. * 
  247. * @since 1.0.1.0 
  248. */ 
  249. public function members_admin_page_process_editor() { 
  250. $msg = 0; 
  251. $redirect = false; 
  252.  
  253. if ( $this->is_admin_user() ) { 
  254. $fields_add = array( 'username', 'email' ); 
  255. $fields_select = array( 'user_id' ); 
  256. $fields_update = array( 'user_id', 'email' ); 
  257. $fields_modify = array( 'user_id', 'memberships' ); 
  258. $fields_subscribe = array( 'user_id', 'subscribe' ); 
  259.  
  260. // Process Action: Create new user. 
  261. if ( isset( $_POST['btn_create'] ) 
  262. && $this->verify_nonce() 
  263. && self::validate_required( $fields_add, 'POST' ) 
  264. ) { 
  265. $data = array( 
  266. 'user_login' => $_POST['username'],  
  267. 'user_email' => $_POST['email'],  
  268. 'first_name' => $_POST['first_name'],  
  269. 'last_name' => $_POST['last_name'],  
  270. 'user_pass' => $_POST['password'],  
  271. ); 
  272. $user_id = wp_insert_user( $data ); 
  273.  
  274. if ( ! is_wp_error( $user_id ) ) { 
  275. $redirect = esc_url_raw( 
  276. add_query_arg( array( 'user_id' => $user_id ) ) 
  277. ); 
  278.  
  279. // Process Action: Select existing user. 
  280. elseif ( isset( $_POST['btn_select'] ) 
  281. && $this->verify_nonce() 
  282. && self::validate_required( $fields_select, 'POST' ) 
  283. ) { 
  284. $user_id = intval( $_POST['user_id'] ); 
  285.  
  286. $redirect = esc_url_raw( 
  287. add_query_arg( array( 'user_id' => $user_id ) ) 
  288. ); 
  289.  
  290. // Process Action: Update existing user. 
  291. elseif ( isset( $_POST['btn_save'] ) 
  292. && $this->verify_nonce() 
  293. && self::validate_required( $fields_update, 'POST' ) 
  294. ) { 
  295. $data = array( 
  296. 'ID' => intval( $_POST['user_id'] ),  
  297. 'user_email' => $_POST['email'],  
  298. 'first_name' => $_POST['first_name'],  
  299. 'last_name' => $_POST['last_name'],  
  300. 'display_name' => $_POST['displayname'],  
  301. ); 
  302. wp_update_user( $data ); 
  303.  
  304. // Process Action: Subscribe to a new membership. 
  305. elseif ( isset( $_POST['btn_modify'] ) 
  306. && $this->verify_nonce() 
  307. ) { 
  308. $user_id = intval( $_POST['user_id'] ); 
  309. $user = MS_Factory::load( 'MS_Model_Member', $user_id ); 
  310.  
  311. // Modify existing subscriptions. 
  312. if ( self::validate_required( $fields_modify, 'POST' ) ) { 
  313. $memberships = lib3()->array->get( $_POST['memberships'] ); 
  314.  
  315. foreach ( $memberships as $membership_id ) { 
  316. if ( empty( $_POST['mem_' . $membership_id] ) ) { continue; } 
  317.  
  318. $subscription = $user->get_subscription( $membership_id ); 
  319. $data = $_POST['mem_' . $membership_id]; 
  320.  
  321. $subscription->start_date = $data['start']; 
  322. $subscription->expire_date = $data['expire']; 
  323. $subscription->status = $data['status']; 
  324. $subscription->save(); 
  325.  
  326. // Add new subscriptions. 
  327. if ( self::validate_required( $fields_subscribe, 'POST' ) ) { 
  328. $subscribe_to = $_POST['subscribe']; 
  329.  
  330. if ( MS_Model_Addon::is_enabled( MS_Model_Addon::ADDON_MULTI_MEMBERSHIPS ) ) { 
  331. // Memberships is an array. 
  332. foreach ( $subscribe_to as $membership_id ) { 
  333. $user->add_membership( $membership_id, 'admin' ); 
  334. } else { 
  335. // Memberships is a single ID. 
  336. foreach ( $user->subscriptions as $subscription ) { 
  337. $subscription->deactivate_membership( false ); 
  338. $user->add_membership( $subscribe_to, 'admin' ); 
  339.  
  340. $user->save(); 
  341.  
  342. if ( $redirect ) { 
  343. wp_safe_redirect( $redirect ); 
  344. exit; 
  345.  
  346. /** 
  347. * Show member list. 
  348. * 
  349. * Menu "All Members", show all members available. 
  350. * Called by MS_Controller_Plugin::route_submenu_request() 
  351. * 
  352. * @since 1.0.0 
  353. */ 
  354. public function admin_page() { 
  355. $data = array(); 
  356.  
  357. $view = MS_Factory::create( 'MS_View_Member_List' ); 
  358. $view->data = apply_filters( 'ms_view_member_list_data', $data ); 
  359. $view->render(); 
  360.  
  361. /** 
  362. * Show member editor. 
  363. * 
  364. * Menu "Add Member", add or edit a single member. 
  365. * Called by MS_Controller_Plugin::route_submenu_request() 
  366. * 
  367. * @since 1.0.1.0 
  368. */ 
  369. public function admin_page_editor() { 
  370. $data = array(); 
  371.  
  372. if ( ! empty( $_REQUEST['user_id'] ) && intval( $_REQUEST['user_id'] ) ) { 
  373. $data['user_id'] = intval( $_REQUEST['user_id'] ); 
  374. $data['action'] = 'edit'; 
  375. } else { 
  376. $data['user_id'] = 0; 
  377. $data['action'] = 'add'; 
  378.  
  379. $view = MS_Factory::create( 'MS_View_Member_Editor' ); 
  380. $view->data = apply_filters( 'ms_view_member_editor_data', $data ); 
  381. $view->render(); 
  382.  
  383. /** 
  384. * Handle Ajax change-memberships action. 
  385. * 
  386. * This action handler is only called by admin users via the Members admin 
  387. * page, so all memberships added here have gateway_id 'admin'. 
  388. * 
  389. * Related Action Hooks: 
  390. * - wp_ajax_change_memberships 
  391. * 
  392. * @since 1.0.0 
  393. */ 
  394. public function ajax_action_change_memberships() { 
  395. $msg = 0; 
  396. $this->_resp_reset(); 
  397.  
  398. $required = array( 'member' ); 
  399. if ( $this->_resp_ok() && ! $this->is_admin_user() ) { 
  400. $this->_resp_err( 'permission denied' ); 
  401. } elseif ( $this->_resp_ok() && ! $this->verify_nonce() ) { 
  402. $this->_resp_err( 'subscribe: nonce' ); 
  403. } elseif ( $this->_resp_ok() && ! self::validate_required( $required ) ) { 
  404. $this->_resp_err( 'subscribe: required' ); 
  405.  
  406. if ( $this->_resp_ok() ) { 
  407. $values = array(); 
  408. if ( isset( $_POST['values'] ) && is_array( $_POST['values'] ) ) { 
  409. $values = $_POST['values']; 
  410.  
  411. $msg = $this->assign_memberships( 
  412. $_POST['member'],  
  413. $values 
  414. ); 
  415. $msg .= $this->_resp_code(); 
  416.  
  417. echo $msg; 
  418. exit; 
  419.  
  420. /** 
  421. * Handle Ajax validate field action. 
  422. * 
  423. * This function should validate the field value before the user is created 
  424. * to make sure that the value is unique/valid. 
  425. * 
  426. * Related Action Hooks: 
  427. * - wp_ajax_validate_field 
  428. * 
  429. * @since 1.0.1.0 
  430. */ 
  431. public function ajax_action_validate_field() { 
  432. $msg = 0; 
  433. $this->_resp_reset(); 
  434.  
  435. $required = array( 'field', 'value' ); 
  436. if ( $this->_resp_ok() && ! $this->is_admin_user() ) { 
  437. $this->_resp_err( 'permission denied' ); 
  438. } elseif ( $this->_resp_ok() && ! self::validate_required( $required ) ) { 
  439. $this->_resp_err( 'validate: required' ); 
  440.  
  441. if ( $this->_resp_ok() ) { 
  442. $field = $_POST['field']; 
  443. $value = $_POST['value']; 
  444.  
  445. if ( 'email' == $field ) { 
  446. if ( ! is_email( $value ) ) { 
  447. $msg = __( 'Invalid Email address', 'membership2' ); 
  448. } elseif ( email_exists( $value ) ) { 
  449. $msg = __( 'Email already taken', 'membership2' ); 
  450. } else { 
  451. $msg = 1; 
  452. } elseif ( 'username' == $field ) { 
  453. if ( username_exists( $value ) ) { 
  454. $msg = __( 'Username already taken', 'membership2' ); 
  455. } else { 
  456. $msg = 1; 
  457. $msg .= $this->_resp_code(); 
  458.  
  459. echo $msg; 
  460. exit; 
  461.  
  462. /** 
  463. * Handle Ajax search users action. 
  464. * 
  465. * Related Action Hooks: 
  466. * - wp_ajax_search 
  467. * 
  468. * @since 1.0.1.0 
  469. */ 
  470. public function ajax_action_search() { 
  471. $res = (object) array( 
  472. 'items' => array(),  
  473. 'more' => false,  
  474. ); 
  475. $this->_resp_reset(); 
  476. $items_per_page = 20; 
  477.  
  478. $required = array( 'q' ); 
  479. if ( $this->_resp_ok() && ! $this->is_admin_user() ) { 
  480. $this->_resp_err( 'permission denied' ); 
  481. } elseif ( $this->_resp_ok() && ! self::validate_required( $required, 'any' ) ) { 
  482. $this->_resp_err( 'search: required' ); 
  483. if ( empty( $_REQUEST['p'] ) ) { $_REQUEST['p'] = 0; } 
  484.  
  485. if ( $this->_resp_ok() ) { 
  486. $term = $_REQUEST['q']; 
  487. $page = max( intval( $_REQUEST['p'] ) - 1, 0 ); 
  488. $offset = $page * $items_per_page; 
  489.  
  490. $args = array( 
  491. 'search' => '*' . $term . '*',  
  492. 'offset' => $offset,  
  493. 'number' => $items_per_page + 1,  
  494. 'fields' => array( 
  495. 'ID',  
  496. 'user_login',  
  497. 'display_name',  
  498. ),  
  499. 'orderby' => 'display_name',  
  500. ); 
  501. $users = get_users( $args ); 
  502.  
  503. if ( count( $users ) > $items_per_page ) { 
  504. $res->more = true; 
  505. array_pop( $users ); 
  506.  
  507. foreach ( $users as $user ) { 
  508. $res->items[] = array( 
  509. 'id' => $user->ID,  
  510. 'text' => sprintf( 
  511. '%s (%s)',  
  512. $user->display_name,  
  513. $user->user_login 
  514. ),  
  515. ); 
  516.  
  517. echo json_encode( $res ); 
  518. exit; 
  519.  
  520. /** 
  521. * Assigns (or removes) memberships to a Member. 
  522. * 
  523. * @since 1.0.0 
  524. * 
  525. * @param string $user_id 
  526. * @param array $memberships Memberships that will be assigned to the 
  527. * rule-item. Memberships that are not mentioned are removed. 
  528. * @return string [description] 
  529. */ 
  530. private function assign_memberships( $user_id, $memberships ) { 
  531. $member = MS_Factory::load( 'MS_Model_Member', $user_id ); 
  532.  
  533. $memberships = apply_filters( 
  534. 'ms_controller_member_assign_memberships',  
  535. $memberships,  
  536. $member,  
  537. $this 
  538. ); 
  539.  
  540. // Drop memberships that are not specified 
  541. foreach ( $member->get_membership_ids() as $old_id ) { 
  542. if ( in_array( $old_id, $memberships ) ) { continue; } 
  543. $member->drop_membership( $old_id ); 
  544.  
  545. // Add new memberships 
  546. foreach ( $memberships as $membership_id ) { 
  547. $subscription = $member->add_membership( $membership_id ); 
  548.  
  549. if ( $member->has_membership() ) { 
  550. $member->is_member = true; 
  551. } else { 
  552. $member->is_member = false; 
  553. $member->save(); 
  554.  
  555. do_action( 
  556. 'ms_controller_member_assign_memberships_done',  
  557. $member,  
  558. $memberships,  
  559. $this 
  560. ); 
  561.  
  562. return MS_Helper_Membership::MEMBERSHIP_MSG_UPDATED; 
  563.  
  564. /** 
  565. * Handles Member list actions. 
  566. * 
  567. * @since 1.0.0 
  568. * 
  569. * @param string $action The action to execute. 
  570. * @param object[] $members Array of members. 
  571. * @param int $membership_id The Membership to apply action to. 
  572. */ 
  573. public function member_list_do_action( $action, $members, $membership_id = null ) { 
  574. $msg = MS_Helper_Member::MSG_MEMBER_NOT_UPDATED; 
  575. if ( ! $this->is_admin_user() ) { 
  576. return $msg; 
  577.  
  578. foreach ( $members as $member_id ) { 
  579. // Member Model 
  580. $member = MS_Factory::load( 'MS_Model_Member', $member_id ); 
  581. switch ( $action ) { 
  582. case 'add': 
  583. $member->add_membership( $membership_id ); 
  584. $msg = MS_Helper_Member::MSG_MEMBER_ADDED; 
  585. break; 
  586.  
  587. case 'drop': 
  588. $member->drop_membership( $membership_id ); 
  589. $msg = MS_Helper_Member::MSG_MEMBER_DELETED; 
  590. break; 
  591.  
  592. case 'move': 
  593. if ( ! empty( $_POST['membership_move_from_id'] ) ) { 
  594. $member->move_membership( 
  595. $_POST['membership_move_from_id'],  
  596. $_POST['membership_id'] 
  597. ); 
  598. $msg = MS_Helper_Member::MSG_MEMBER_UPDATED; 
  599. break; 
  600.  
  601. case 'edit_date': 
  602. if ( is_array( $membership_id ) ) { 
  603. foreach ( $membership_id as $id ) { 
  604. $subscription = $member->get_subscriptions( $id ); 
  605. if ( ! empty( $_POST[ 'start_date_' . $id ] ) ) { 
  606. $subscription->start_date = $_POST[ 'start_date_' . $id ]; 
  607. $subscription->set_trial_expire_date(); 
  608.  
  609. if ( ! empty( $_POST[ 'expire_date_' . $id ] ) ) { 
  610. $subscription->expire_date = $_POST[ 'expire_date_' . $id ]; 
  611. $subscription->save(); 
  612. $msg = MS_Helper_Member::MSG_MEMBER_UPDATED; 
  613. break; 
  614. $member->save(); 
  615.  
  616. return apply_filters( 
  617. 'ms_controller_member_member_list_do_action',  
  618. $msg,  
  619. $action,  
  620. $members,  
  621. $membership_id,  
  622. $this 
  623. ); 
  624.  
  625. /** 
  626. * Load Member manager specific styles. 
  627. * 
  628. * @since 1.0.0 
  629. */ 
  630. public function enqueue_styles() { 
  631. lib3()->ui->add( 'jquery-ui' ); 
  632.  
  633. /** 
  634. * Load Member specific scripts for the LIST view. 
  635. * 
  636. * @since 1.0.0 
  637. */ 
  638. public function enqueue_scripts_list() { 
  639. $data = array( 
  640. 'ms_init' => array(),  
  641. ); 
  642. lib3()->array->equip_get( 'action' ); 
  643.  
  644. if ( 'edit_date' == $_GET['action'] ) { 
  645. // Start and expire date edit 
  646. wp_enqueue_script( 'jquery-ui-datepicker' ); 
  647. $data['ms_init'][] = 'view_member_date'; 
  648. } else { 
  649. // Members list 
  650. $data['ms_init'][] = 'view_member_list'; 
  651. $data['lang'] = array( 
  652. 'select_user' => __( 'Select an User', 'membership2' ),  
  653. ); 
  654.  
  655. lib3()->ui->data( 'ms_data', $data ); 
  656. wp_enqueue_script( 'ms-admin' ); 
  657.  
  658. /** 
  659. * Load Member specific scripts for ADD/EDIT screen. 
  660. * 
  661. * @since 1.0.1.0 
  662. */ 
  663. public function enqueue_scripts_editor() { 
  664. $data = array( 
  665. 'ms_init' => array(),  
  666. ); 
  667.  
  668. $data['ms_init'][] = 'view_member_editor'; 
  669.  
  670. lib3()->ui->data( 'ms_data', $data ); 
  671. wp_enqueue_script( 'ms-admin' ); 
  672.  
.