/bp-xprofile-wp-user-sync.php

  1. <?php 
  2. /** 
  3. -------------------------------------------------------------------------------- 
  4. Plugin Name: BP XProfile WordPress User Sync 
  5. Plugin URI: https://github.com/christianwach/bp-xprofile-wp-user-sync 
  6. Description: Map BuddyPress xProfile fields to WordPress User fields. <strong>Note:</strong> because there is no way to hide xProfile fields, all field definitions are deleted when it is deactivated. The plugin tries to reconnect on reactivation, but always backup before deactivating. <strong>The best way to update this plugin is to replace the folder with the latest version via FTP or similar. This avoids the deactivate-reactivate process.</strong> 
  7. Author: Christian Wach 
  8. Version: 0.6.4 
  9. Author URI: http://haystack.co.uk 
  10. Text Domain: bp-xprofile-wp-user-sync 
  11. Domain Path: /languages 
  12. -------------------------------------------------------------------------------- 
  13. */ 
  14.  
  15.  
  16.  
  17. // set our version here 
  18. define( 'BP_XPROFILE_WP_USER_SYNC_VERSION', '0.6.4' ); 
  19.  
  20. // store reference to this file 
  21. if ( ! defined( 'BP_XPROFILE_WP_USER_SYNC_FILE' ) ) { 
  22. define( 'BP_XPROFILE_WP_USER_SYNC_FILE', __FILE__ ); 
  23.  
  24. // store URL to this plugin's directory 
  25. if ( ! defined( 'BP_XPROFILE_WP_USER_SYNC_URL' ) ) { 
  26. define( 'BP_XPROFILE_WP_USER_SYNC_URL', plugin_dir_url( BP_XPROFILE_WP_USER_SYNC_FILE ) ); 
  27. // store PATH to this plugin's directory 
  28. if ( ! defined( 'BP_XPROFILE_WP_USER_SYNC_PATH' ) ) { 
  29. define( 'BP_XPROFILE_WP_USER_SYNC_PATH', plugin_dir_path( BP_XPROFILE_WP_USER_SYNC_FILE ) ); 
  30.  
  31.  
  32.  
  33. /** 
  34. * BP XProfile WordPress User Sync Plugin Class. 
  35. * 
  36. * A class that encapsulates plugin functionality. 
  37. * 
  38. * @since 0.1 
  39. */ 
  40. class BpXProfileWordPressUserSync { 
  41.  
  42. /** 
  43. * Plugin Options. 
  44. * 
  45. * @since 0.1 
  46. * @access public 
  47. * @var array $options The plugin options array 
  48. */ 
  49. public $options = array(); 
  50.  
  51.  
  52.  
  53. /** 
  54. * Constructor. 
  55. * 
  56. * @since 0.1 
  57. */ 
  58. function __construct() { 
  59.  
  60. // get options array, if it exists 
  61. $this->options = get_option( 'bp_xp_wp_sync_options', array() ); 
  62.  
  63. // add action for plugin init 
  64. add_action( 'bp_init', array( $this, 'register_hooks' ) ); 
  65.  
  66. // use translation 
  67. add_action( 'plugins_loaded', array( $this, 'translation' ) ); 
  68.  
  69.  
  70.  
  71.  
  72. /** 
  73. * Loads translation, if present. 
  74. * 
  75. * @since 0.1 
  76. */ 
  77. public function translation() { 
  78.  
  79. // only use, if we have it... 
  80. if ( function_exists( 'load_plugin_textdomain' ) ) { 
  81.  
  82. // not used, as there are no translations as yet 
  83. load_plugin_textdomain( 
  84.  
  85. // unique name 
  86. 'bp-xprofile-wp-user-sync',  
  87.  
  88. // deprecated argument 
  89. false,  
  90.  
  91. // relative path to directory containing translation files 
  92. dirname( plugin_basename( BP_XPROFILE_WP_USER_SYNC_FILE ) ) . '/languages/' 
  93.  
  94. ); 
  95.  
  96.  
  97.  
  98.  
  99.  
  100. //########################################################################## 
  101.  
  102.  
  103.  
  104. /** 
  105. * Insert xProfile fields for "First Name" and "Last Name". 
  106. * 
  107. * @since 0.1 
  108. */ 
  109. public function activate() { 
  110.  
  111. // bail if BuddyPress xProfile not active 
  112. if ( ! bp_is_active( 'xprofile' ) ) return; 
  113.  
  114. // are we re-activating? 
  115. $reactivating = ( get_option( 'bp_xp_wp_sync_installed', 'false' ) === 'true' ) ? true : false; 
  116.  
  117. // before we create our fields, test if we're reactivating... 
  118. if ( $reactivating ) { 
  119.  
  120. // yes, get existing field data 
  121. $existing_fields = get_option( 'bp_xp_wp_sync_options_store', array() ); 
  122.  
  123. // if we're reactivating after an upgrade from a version that does not 
  124. // have the code to salvage the connection between fields and data... 
  125. if ( empty( $existing_fields ) ) { 
  126.  
  127. // hmm... 
  128.  
  129. } else { 
  130.  
  131. // first name field 
  132. $existing_first_name_field_id = $existing_fields['first_name_field_id']; 
  133. $existing_first_name_field_name = $existing_fields['first_name_field_name']; 
  134. $existing_first_name_field_desc = $existing_fields['first_name_field_desc']; 
  135.  
  136. // first name field 
  137. $existing_last_name_field_id = $existing_fields['last_name_field_id']; 
  138. $existing_last_name_field_name = $existing_fields['last_name_field_name']; 
  139. $existing_last_name_field_desc = $existing_fields['last_name_field_desc']; 
  140.  
  141.  
  142.  
  143.  
  144.  
  145. // "First Name" field 
  146.  
  147. // set field name 
  148. $name = __( 'First Name', 'bp-xprofile-wp-user-sync' ); 
  149. if ( isset( $existing_first_name_field_name ) ) { 
  150. $name = $existing_first_name_field_name; 
  151.  
  152. // set field description 
  153. $description = ''; 
  154. if ( isset( $existing_first_name_field_desc ) ) { 
  155. $description = $existing_first_name_field_desc; 
  156.  
  157. // get id of field 
  158. $first_name_field_id = $this->_create_field( $name, $description ); 
  159.  
  160.  
  161.  
  162. // "Last Name" field 
  163.  
  164. // set field name 
  165. $name = __( 'Last Name', 'bp-xprofile-wp-user-sync' ); 
  166. if ( isset( $existing_last_name_field_name ) ) { 
  167. $name = $existing_last_name_field_name; 
  168.  
  169. // set field description 
  170. $description = ''; 
  171. if ( isset( $existing_last_name_field_desc ) ) { 
  172. $description = $existing_last_name_field_desc; 
  173.  
  174. // get id of field 
  175. $last_name_field_id = $this->_create_field( $name, $description ); 
  176.  
  177.  
  178.  
  179. // are we re-activating? 
  180. if ( $reactivating ) { 
  181.  
  182. // reconnect data to fields 
  183. $this->_reconnect_field( $existing_first_name_field_id, $first_name_field_id ); 
  184. $this->_reconnect_field( $existing_last_name_field_id, $last_name_field_id ); 
  185.  
  186. // delete storage array 
  187. delete_option( 'bp_xp_wp_sync_options_store' ); 
  188.  
  189. // add to options 
  190. $this->options['first_name_field_id'] = $existing_first_name_field_id; 
  191. $this->options['last_name_field_id'] = $existing_last_name_field_id; 
  192.  
  193. // update options array 
  194. update_option( 'bp_xp_wp_sync_options', $this->options ); 
  195.  
  196. } else { 
  197.  
  198. // add to options 
  199. $this->options['first_name_field_id'] = $first_name_field_id; 
  200. $this->options['last_name_field_id'] = $last_name_field_id; 
  201.  
  202. // save options array 
  203. add_option( 'bp_xp_wp_sync_options', $this->options ); 
  204.  
  205. /** 
  206. * Set installed flag 
  207. * 
  208. * We can't retain fields when the plugin is deactivated, but the field 
  209. * data does survive and we'll try and reconnect it when the plugin is 
  210. * reactivated. 
  211. */ 
  212. add_option( 'bp_xp_wp_sync_installed', 'true' ); 
  213.  
  214.  
  215.  
  216.  
  217.  
  218. /** 
  219. * Actions to perform on plugin deactivation. (NOT deletion) 
  220. * 
  221. * @since 0.1 
  222. */ 
  223. public function deactivate() { 
  224.  
  225. /** 
  226. * There seems to be no way to hide the xProfile fields once they have 
  227. * been created, so we're left with no option but to lose the data when 
  228. * we deactivate the plugin :-( 
  229. * 
  230. * The 'bp_xp_wp_sync_options_store' option is an attempt at a workaround 
  231. * but will not work if an older version of the plugin is deactivated and 
  232. * a newer one is then activated, since no bridging data will have been 
  233. * saved. Have updated the readme to flag this. 
  234. * 
  235. * Also, we can't use BP's API because we can't set 'can_delete' in BP 1.7 
  236. * so we bypass it and manipulate the field directly 
  237. */ 
  238.  
  239. // init storage array 
  240. $options = array(); 
  241.  
  242. // bail if BuddyPress xProfile not active 
  243. if ( ! bp_is_active( 'xprofile' ) ) return; 
  244.  
  245. // get first_name xProfile field 
  246. $field = new BP_XProfile_Field( $this->options['first_name_field_id'] ); 
  247.  
  248. // store data about first name field 
  249. $options['first_name_field_id'] = $field->id; 
  250. $options['first_name_field_name'] = $field->name; 
  251. $options['first_name_field_desc'] = $field->description; 
  252.  
  253. // delete first_name xProfile field 
  254. $field->can_delete = 1; 
  255. $field->delete(); 
  256.  
  257. // get last_name xProfile field 
  258. $field = new BP_XProfile_Field( $this->options['last_name_field_id'] ); 
  259.  
  260. // store data about first name field 
  261. $options['last_name_field_id'] = $field->id; 
  262. $options['last_name_field_name'] = $field->name; 
  263. $options['last_name_field_desc'] = $field->description; 
  264.  
  265. // delete last_name xProfile field 
  266. $field->can_delete = 1; 
  267. $field->delete(); 
  268.  
  269. // save our storage array 
  270. add_option( 'bp_xp_wp_sync_options_store', $options ); 
  271.  
  272. // delete our options array 
  273. delete_option( 'bp_xp_wp_sync_options' ); 
  274.  
  275.  
  276.  
  277.  
  278. /** 
  279. * Register hooks when BuddyPress has initialised. 
  280. * 
  281. * @since 0.1 
  282. */ 
  283. public function register_hooks() { 
  284.  
  285. // do we have a version of BuddyPress capable of pre-filtering? 
  286. if ( function_exists( 'bp_parse_args' ) ) { 
  287.  
  288. // use bp_parse_args post-parse filter (available since BP 2.0) 
  289. add_filter( 'bp_after_has_profile_parse_args', array( $this, 'intercept_profile_query_args' ), 30, 1 ); 
  290.  
  291. } else { 
  292.  
  293. // exclude the default name field type on profile edit and registration 
  294. // screens and exclude our fields on profile view screen 
  295. add_filter( 'bp_has_profile', array( $this, 'intercept_profile_query' ), 30, 2 ); 
  296.  
  297.  
  298. // exclude the default name field type on profile fields admin screen (available since BP 2.1) 
  299. add_filter( 'bp_xprofile_get_groups', array( $this, 'intercept_profile_fields_query' ), 30, 2 ); 
  300.  
  301. // populate our fields on user registration and update by admins 
  302. add_action( 'user_register', array( $this, 'intercept_wp_user_update' ), 30, 1 ); 
  303. add_action( 'profile_update', array( $this, 'intercept_wp_user_update' ), 30, 1 ); 
  304.  
  305. // remove 'user_register' and 'profile_update' hooks and add post-update hooks 
  306. add_action( 'xprofile_updated_profile', array( $this, 'intercept_wp_profile_sync' ), 9, 3 ); 
  307. add_action( 'bp_core_signup_user', array( $this, 'intercept_wp_profile_sync' ), 9, 3 ); 
  308. add_action( 'bp_core_activated_user', array( $this, 'intercept_wp_profile_sync' ), 9, 3 ); 
  309.  
  310. // compatibility with "WP FB AutoConnect Premium" 
  311. add_filter( 'wpfb_xprofile_fields_received', array( $this, 'intercept_wp_fb_profile_sync' ), 10, 2 ); 
  312.  
  313. // compatibility with "WooCommerce" 
  314. add_action( 'woocommerce_save_account_details', array( $this, 'intercept_woo_user_update' ), 30, 1 ); 
  315.  
  316.  
  317.  
  318.  
  319. /** 
  320. * Intercept xProfile query process and manage display of fields. 
  321. * 
  322. * @since 0.1 
  323. * 
  324. * @param array $args The existing arguments used to query for fields 
  325. * @return array $args The modified arguments used to query for fields 
  326. */ 
  327. public function intercept_profile_query_args( $args ) { 
  328.  
  329. // if on profile view screen 
  330. if ( bp_is_user_profile() AND ! bp_is_user_profile_edit() ) { 
  331.  
  332. // get fields to exclude on profile view screen 
  333. $exclude_fields = $this->_get_excluded_fields(); 
  334.  
  335. // merge with existing if populated 
  336. $args['exclude_fields'] = $this->_merge_excluded_fields( $args['exclude_fields'], $exclude_fields ); 
  337.  
  338.  
  339. // if on profile edit screen 
  340. if ( bp_is_user_profile_edit() ) { 
  341.  
  342. // exclude name field (bp_xprofile_fullname_field_id is available since BP 2.0) 
  343. $exclude_fields = bp_xprofile_fullname_field_id(); 
  344.  
  345. // merge with existing if populated 
  346. $args['exclude_fields'] = $this->_merge_excluded_fields( $args['exclude_fields'], $exclude_fields ); 
  347.  
  348.  
  349. /** 
  350. * Apply to registration form whichever page it is displayed on, whilst avoiding 
  351. * splitting the Name field into First Name and Last Name fields in the profile 
  352. * display loop of the user. Note that we cannot determine if we are in the loop 
  353. * prior to the query, so we test for an empty user ID instead. 
  354. */ 
  355. if ( 
  356. ! is_user_logged_in() // user must be logged out 
  357. AND 
  358. ( ! bp_is_user_profile() OR ( bp_is_user_profile() AND empty( $args['user_id'] ) ) ) 
  359. ) { 
  360.  
  361. // query only group 1 
  362. $args['profile_group_id'] = 1; 
  363.  
  364. // exclude name field (bp_xprofile_fullname_field_id is available since BP 2.0) 
  365. $exclude_fields = bp_xprofile_fullname_field_id(); 
  366.  
  367. // merge with existing if populated 
  368. $args['exclude_fields'] = $this->_merge_excluded_fields( $args['exclude_fields'], $exclude_fields ); 
  369.  
  370.  
  371. // --< 
  372. return $args; 
  373.  
  374.  
  375.  
  376.  
  377. /** 
  378. * Intercept xProfile query process and manage display of fields. 
  379. * 
  380. * @since 0.1 
  381. * 
  382. * @param boolean $has_groups 
  383. * @param object $profile_template 
  384. * @return boolean $has_groups 
  385. */ 
  386. public function intercept_profile_query( $has_groups, $profile_template ) { 
  387.  
  388. // init args 
  389. $args = array(); 
  390.  
  391. // if on profile view screen 
  392. if ( bp_is_user_profile() AND ! bp_is_user_profile_edit() ) { 
  393.  
  394. // get fields to exclude on profile view screen 
  395. $args['exclude_fields'] = $this->_get_excluded_fields(); 
  396.  
  397.  
  398. // if on profile edit screen 
  399. if ( bp_is_user_profile_edit() ) { 
  400.  
  401. // check which profile group is being queried 
  402. if ( 
  403.  
  404. isset( $profile_template->groups ) AND 
  405. is_array( $profile_template->groups ) AND 
  406. count( $profile_template->groups ) > 0 
  407.  
  408. ) { 
  409.  
  410. // don't want to pop, so loop through them 
  411. foreach( $profile_template->groups AS $group ) { 
  412.  
  413. // is this the base group? 
  414. if ( $group->id == 1 ) { 
  415.  
  416. // query only group 1 
  417. $args['profile_group_id'] = 1; 
  418.  
  419. // get field id from name 
  420. $fullname_field_id = xprofile_get_field_id_from_name( bp_xprofile_fullname_field_name() ); 
  421.  
  422. // exclude name field 
  423. $args['exclude_fields'] = $fullname_field_id; 
  424.  
  425.  
  426. // only the first 
  427. break; 
  428.  
  429.  
  430.  
  431.  
  432. // determine if we are currently in the profile display loop 
  433. $in_loop = false; 
  434. if ( isset( $profile_template->in_the_loop ) AND $profile_template->in_the_loop === true ) { 
  435. $in_loop = true; 
  436.  
  437. /** 
  438. * Apply to registration form whichever page it is displayed on, whilst avoiding 
  439. * splitting the Name field into First Name and Last Name fields in the profile 
  440. * display loop of the user. Props https://github.com/sbrajesh 
  441. */ 
  442. if ( ! is_user_logged_in() AND ( ! bp_is_user_profile() OR bp_is_user_profile() AND ! $in_loop ) ) { 
  443.  
  444. // query only group 1 
  445. $args['profile_group_id'] = 1; 
  446.  
  447. // get field id from name 
  448. $fullname_field_id = xprofile_get_field_id_from_name( bp_xprofile_fullname_field_name() ); 
  449.  
  450. // exclude name field 
  451. $args['exclude_fields'] = $fullname_field_id; 
  452.  
  453.  
  454. // test for new BP xProfile admin screen 
  455.  
  456. // get BuddyPress instance 
  457. $bp = buddypress(); 
  458.  
  459. // test for new BP_Members_Admin object 
  460. if( isset( $bp->profile->admin ) ) { 
  461.  
  462. // check which profile group is being queried 
  463. if ( 
  464.  
  465. isset( $profile_template->groups ) AND 
  466. is_array( $profile_template->groups ) AND 
  467. count( $profile_template->groups ) > 0 
  468.  
  469. ) { 
  470.  
  471. // don't want to pop, so loop through them 
  472. foreach( $profile_template->groups AS $group ) { 
  473.  
  474. // is this the base group? 
  475. if ( $group->id == 1 ) { 
  476.  
  477. /** 
  478. * BP_XProfile_User_Admin queries prior to the loop, so 
  479. * do we have the fields populated? 
  480. * see BP_XProfile_User_Admin->register_metaboxes() 
  481. */ 
  482. if ( isset( $group->fields ) AND is_array( $group->fields ) ) { 
  483.  
  484. // get user ID 
  485. $user_id = isset( $_GET['user_id'] ) ? intval( $_GET['user_id'] ) : 0 ; 
  486.  
  487. // only edit other users profiles 
  488. if ( $user_id AND get_current_user_id() != $user_id ) { 
  489.  
  490. // query only group 1 
  491. $args['profile_group_id'] = 1; 
  492.  
  493. // query only for this user 
  494. $args['user_id'] = $user_id; 
  495.  
  496. // get field id from name 
  497. $fullname_field_id = xprofile_get_field_id_from_name( bp_xprofile_fullname_field_name() ); 
  498.  
  499. // exclude name field 
  500. $args['exclude_fields'] = $fullname_field_id; 
  501.  
  502.  
  503.  
  504.  
  505. // only the first 
  506. break; 
  507.  
  508.  
  509.  
  510.  
  511. // do we need to recreate query? 
  512. if ( count( $args ) > 0 ) { 
  513.  
  514. // ditch our filter so we don't create an endless loop 
  515. remove_filter( 'bp_has_profile', array( $this, 'intercept_profile_query' ), 30 ); 
  516.  
  517. // recreate profile_template 
  518. $has_groups = bp_has_profile( $args ); 
  519.  
  520. // add our filter again in case there are any other calls to bp_has_profile 
  521. add_filter( 'bp_has_profile', array( $this, 'intercept_profile_query' ), 30, 2 ); 
  522.  
  523.  
  524. // --< 
  525. return $has_groups; 
  526.  
  527.  
  528.  
  529.  
  530. /** 
  531. * Intercept xprofile query process and manage display of fields. 
  532. * 
  533. * @since 0.1 
  534. * 
  535. * @param array $groups The xProfile groups 
  536. * @param array $args The arguments 
  537. */ 
  538. public function intercept_profile_fields_query( $groups, $args ) { 
  539.  
  540. // bail if not in admin 
  541. if ( ! is_admin() ) return $groups; 
  542.  
  543. // exclude name field 
  544. $args['exclude_fields'] = bp_xprofile_fullname_field_id(); 
  545.  
  546. // re-query the groups 
  547. $groups = BP_XProfile_Group::get( $args ); 
  548.  
  549. // --< 
  550. return $groups; 
  551.  
  552.  
  553.  
  554.  
  555. /** 
  556. * Intercept WP user registration and profile updates in WP Admin screens. 
  557. * 
  558. * @since 0.1 
  559. * 
  560. * @param integer $user_id 
  561. */ 
  562. public function intercept_wp_user_update( $user_id ) { 
  563.  
  564. // only map data when the site admin is adding users, not on registration. 
  565. if ( ! is_admin() ) { return false; } 
  566.  
  567. // pass to method 
  568. $this->_user_update( $user_id ); 
  569.  
  570.  
  571.  
  572.  
  573. /** 
  574. * Intercept WooCommerce profile updates. 
  575. * 
  576. * @since 0.6.3 
  577. * 
  578. * @param integer $user_id 
  579. */ 
  580. public function intercept_woo_user_update( $user_id ) { 
  581.  
  582. // pass to method 
  583. $this->_user_update( $user_id ); 
  584.  
  585.  
  586.  
  587.  
  588. /** 
  589. * Intercept BP core's attempt to sync to WP user profile. 
  590. * 
  591. * @since 0.1 
  592. * 
  593. * @param integer $user_id 
  594. * @param array $posted_field_ids 
  595. * @param boolean $errors 
  596. */ 
  597. public function intercept_wp_profile_sync( $user_id = 0, $posted_field_ids, $errors ) { 
  598.  
  599. // bail if profile syncing is disabled 
  600. if ( bp_disable_profile_sync() ) { 
  601. return true; 
  602.  
  603. if ( empty( $user_id ) ) { 
  604. $user_id = bp_loggedin_user_id(); 
  605.  
  606. if ( empty( $user_id ) ) { 
  607. return false; 
  608.  
  609. // remove our hooks 
  610. remove_action( 'user_register', array( $this, 'intercept_wp_user_update' ), 30 ); 
  611. remove_action( 'profile_update', array( $this, 'intercept_wp_user_update' ), 30 ); 
  612.  
  613. // after xprofile_sync_wp_profile runs, re-sync with correct values 
  614. add_action( 'xprofile_updated_profile', array( $this, 'intercept_wp_profile_sync_patch' ), 11, 3 ); 
  615. add_action( 'bp_core_signup_user', array( $this, 'intercept_wp_profile_sync_patch' ), 11, 3 ); 
  616. add_action( 'bp_core_activated_user', array( $this, 'intercept_wp_profile_sync_patch' ), 11, 3 ); 
  617.  
  618.  
  619.  
  620.  
  621. /** 
  622. * Overwrite BP core's attempt to sync to WP user profile. 
  623. * 
  624. * @since 0.6.4 
  625. * 
  626. * @param integer $user_id 
  627. * @param array $posted_field_ids 
  628. * @param boolean $errors 
  629. * @return void 
  630. */ 
  631. public function intercept_wp_profile_sync_patch( $user_id = 0, $posted_field_ids, $errors ) { 
  632.  
  633. // bail if profile syncing is disabled 
  634. if ( bp_disable_profile_sync() ) { 
  635. return true; 
  636.  
  637. if ( empty( $user_id ) ) { 
  638. $user_id = bp_loggedin_user_id(); 
  639.  
  640. if ( empty( $user_id ) ) { 
  641. return false; 
  642.  
  643. // get our user's first name 
  644. $first_name = xprofile_get_field_data( 
  645. $this->options['first_name_field_id'],  
  646. $user_id 
  647. ); 
  648.  
  649. // get our user's last name 
  650. $last_name = xprofile_get_field_data( 
  651. $this->options['last_name_field_id'],  
  652. $user_id 
  653. ); 
  654.  
  655. // concatenate as per BP core 
  656. $name = $first_name . ' ' . $last_name; 
  657.  
  658. /** 
  659. * Overwrite default name field for this user. 
  660. * 
  661. * We have to do this because the BuddyPress sync routine does not always 
  662. * split names correctly, particularly when either the first name or the 
  663. * last name consists of multipe words, e.g. "Michael John" "Smith". 
  664. */ 
  665. xprofile_set_field_data( bp_xprofile_fullname_field_name(), $user_id, $name ); 
  666.  
  667. // remove our hooks 
  668. remove_action( 'user_register', array( $this, 'intercept_wp_user_update' ), 30 ); 
  669. remove_action( 'profile_update', array( $this, 'intercept_wp_user_update' ), 30 ); 
  670.  
  671. // now replicate BuddyPress sync procedure 
  672. bp_update_user_meta( $user_id, 'nickname', $name ); 
  673. bp_update_user_meta( $user_id, 'first_name', $first_name ); 
  674. bp_update_user_meta( $user_id, 'last_name', $last_name ); 
  675.  
  676. wp_update_user( array( 'ID' => $user_id, 'display_name' => $name ) ); 
  677. wp_cache_delete( 'bp_core_userdata_' . $user_id, 'bp' ); 
  678.  
  679.  
  680.  
  681.  
  682. /** 
  683. * Compatibility with "WP FB AutoConnect Premium". 
  684. * 
  685. * @since 0.3 
  686. * 
  687. * @param array $facebook_user 
  688. * @return array $facebook_user 
  689. */ 
  690. public function intercept_wp_fb_profile_sync( $facebook_user, $wp_user_id ) { 
  691.  
  692. /** 
  693. * When xProfiles are updated, BuddyPress sets user nickname and display name 
  694. * so WP FB AutoConnect Premium should do too. To do so, alter line 1315 or so: 
  695. * 
  696. * //A filter so 3rd party plugins can process any extra fields they might need 
  697. * $fbuser = apply_filters('wpfb_xprofile_fields_received', $fbuser, $args['WP_ID']); 
  698. */ 
  699.  
  700. // set user nickname 
  701. bp_update_user_meta( $wp_user_id, 'nickname', $facebook_user['name'] ); 
  702.  
  703. // access db 
  704. global $wpdb; 
  705.  
  706. // set user display name - see xprofile_sync_wp_profile() 
  707. $wpdb->query( 
  708. $wpdb->prepare( 
  709. "UPDATE {$wpdb->users} SET display_name = %s WHERE ID = %d",  
  710. $facebook_user['name'],  
  711. $wp_user_id 
  712. ); 
  713.  
  714. // pass it on 
  715. return $facebook_user; 
  716.  
  717.  
  718.  
  719.  
  720. //########################################################################## 
  721.  
  722.  
  723.  
  724. /** 
  725. * Do WP user update process and populate our fields. 
  726. * 
  727. * Note that BuddyPress updates the "Name" field before wp_insert_user or 
  728. * wp_update_user get called - it hooks into 'user_profile_update_errors' 
  729. * instead. So, there are two options: either hook into the same action or 
  730. * call the same function below. Until I raise this as an issue (ie, why do 
  731. * database operations via an action designed to collate errors) I'll 
  732. * temporarily call the same function. 
  733. * 
  734. * @since 0.6.3 
  735. * 
  736. * @param integer $user_id 
  737. */ 
  738. private function _user_update( $user_id ) { 
  739.  
  740. // populate the user's first and last names 
  741. if ( bp_is_active( 'xprofile' ) ) { 
  742.  
  743. // get first name 
  744. $first_name = bp_get_user_meta( $user_id, 'first_name', true ); 
  745.  
  746. // get last name 
  747. $last_name = bp_get_user_meta( $user_id, 'last_name', true ); 
  748.  
  749. // if nothing set... 
  750. if ( empty( $first_name ) AND empty( $last_name ) ) { 
  751.  
  752. // get nickname instead 
  753. $nickname = bp_get_user_meta( $user_id, 'nickname', true ); 
  754.  
  755. // does it have a space in it? (use core BP logic) 
  756. $space = strpos( $nickname, ' ' ); 
  757. if ( false === $space ) { 
  758. $first_name = $nickname; 
  759. $last_name = ''; 
  760. } else { 
  761. $first_name = substr( $nickname, 0, $space ); 
  762. $last_name = trim( substr( $nickname, $space, strlen( $nickname ) ) ); 
  763.  
  764.  
  765. /** 
  766. * In multisite when not on the main blog, our options are not loaded 
  767. * because I mistakenly failed to use a site_option instead of a blog 
  768. * option. At the moment, there's little I can do except to switch to 
  769. * the BP root blog and grab the values from there. I assume this 
  770. * won't be a common occurrence and therefore that this won't cause 
  771. * too much of an overhead. 
  772. */ 
  773.  
  774. // test for site other than main site 
  775. if ( is_multisite() AND ! is_main_site() ) { 
  776.  
  777. // switch to main blog 
  778. switch_to_blog( bp_get_root_blog_id() ); 
  779.  
  780. // get options array, if it exists 
  781. $this->options = get_option( 'bp_xp_wp_sync_options', array() ); 
  782.  
  783. // switch back 
  784. restore_current_blog(); 
  785.  
  786.  
  787. // test for one of our options 
  788. if ( isset( $this->options['first_name_field_id'] ) ) { 
  789.  
  790. // update first_name field 
  791. xprofile_set_field_data( 
  792. $this->options['first_name_field_id'],  
  793. $user_id,  
  794. $first_name 
  795. ); 
  796.  
  797. // update last_name field 
  798. xprofile_set_field_data( 
  799. $this->options['last_name_field_id'],  
  800. $user_id,  
  801. $last_name 
  802. ); 
  803.  
  804.  
  805. /** 
  806. * When xProfiles are updated, BuddyPress sets user nickname and 
  807. * display name so we should too... 
  808. */ 
  809.  
  810. // construct full name 
  811. $full_name = $first_name . ' ' . $last_name; 
  812.  
  813. // set user nickname 
  814. bp_update_user_meta( $user_id, 'nickname', $full_name ); 
  815.  
  816. // remove our hooks 
  817. remove_action( 'user_register', array( $this, 'intercept_wp_user_update' ), 30 ); 
  818. remove_action( 'profile_update', array( $this, 'intercept_wp_user_update' ), 30 ); 
  819.  
  820. // set user display name - see xprofile_sync_wp_profile() 
  821. wp_update_user( array( 'ID' => $user_id, 'display_name' => $full_name ) ); 
  822.  
  823. // see notes above regarding when BuddyPress updates the "Name" field 
  824.  
  825. // update BuddyPress "Name" field directly 
  826. xprofile_set_field_data( 
  827. bp_xprofile_fullname_field_name(),  
  828. $user_id,  
  829. $full_name 
  830. ); 
  831.  
  832.  
  833.  
  834.  
  835.  
  836. /** 
  837. * Create a field with a given name. 
  838. * 
  839. * @since 0.1 
  840. * 
  841. * @param str $field_name The name of the field 
  842. * @param str $field_description The field description 
  843. * @return int $field_id True on success, false on failure 
  844. */ 
  845. private function _create_field( $field_name, $field_description = '' ) { 
  846.  
  847. // common field attributes 
  848.  
  849. // default group 
  850. $field_group_id = 1; 
  851.  
  852. // no parent 
  853. $parent_id = 0; 
  854.  
  855. // text 
  856. $type = 'textbox'; 
  857.  
  858. // name from passed value 
  859. $name = $field_name; 
  860.  
  861. // description 
  862. $description = $field_description; 
  863.  
  864. // required (note: super admins can always edit) 
  865. $is_required = 1; 
  866.  
  867. // cannot be deleted 
  868. $can_delete = 0; 
  869.  
  870. // construct data to save 
  871. $data = compact( 
  872. array( 'field_group_id', 'parent_id', 'type', 'name', 'description', 'is_required', 'can_delete' ) 
  873. ); 
  874.  
  875. // use bp function to get new field ID 
  876. $field_id = xprofile_insert_field( $data ); 
  877.  
  878. // die if unsuccessful 
  879. if ( ! is_numeric( $field_id ) ) { 
  880.  
  881. // construct message 
  882. $msg = __( 
  883. 'BP XProfile WordPress User Sync plugin: Could not create xProfile field',  
  884. 'bp-xprofile-wp-user-sync' 
  885. ); 
  886.  
  887. // use var_dump as this seems to display in the iframe 
  888. var_dump( $msg ); die(); 
  889.  
  890.  
  891. // disable custom visibility 
  892. bp_xprofile_update_field_meta( 
  893. $field_id,  
  894. 'allow_custom_visibility',  
  895. 'disabled' 
  896. ); 
  897.  
  898. // BuddyPress 1.7 seems to overlook our 'can_delete' setting 
  899. // Fixed in BuddyPress 1.9, but leave the check below for older versions 
  900. $field = new BP_XProfile_Field( $field_id ); 
  901.  
  902. // let's see if our new field is correctly set 
  903. if ( $field->can_delete != 0 ) { 
  904.  
  905. // we'll need these to manually update, because the API can't do it 
  906. global $wpdb, $bp; 
  907.  
  908. // construct query 
  909. $sql = $wpdb->prepare( 
  910. "UPDATE {$bp->profile->table_name_fields} SET can_delete = %d WHERE id = %d",  
  911. 0,  
  912. $field_id 
  913. ); 
  914.  
  915. // we must have one row affected 
  916. if ( $wpdb->query( $sql ) !== 1 ) { 
  917.  
  918. // construct message 
  919. $msg = __( 
  920. 'BP XProfile WordPress User Sync plugin: Could not set "can_delete" for xProfile field',  
  921. 'bp-xprofile-wp-user-sync' 
  922. ); 
  923.  
  924. // use var_dump as this seems to display in the iframe 
  925. var_dump( $msg ); die(); 
  926.  
  927.  
  928.  
  929. // --< 
  930. return $field_id; 
  931.  
  932.  
  933.  
  934.  
  935. /** 
  936. * Update a field with a given ID. 
  937. * 
  938. * The idea here is to try and reconnect the "orphaned" field data with the 
  939. * field definition by changing the auto-incremented ID of the field back to 
  940. * its original value. This should work because the original value should not 
  941. * have been reused unless the table has been truncated and the auto-increment 
  942. * value reset. 
  943. * 
  944. * @since 0.5 
  945. * 
  946. * @param int $old_field_id The previous ID of the field 
  947. * @param int $new_field_id The new ID of the field 
  948. * @return bool True if update successful 
  949. */ 
  950. private function _reconnect_field( $old_field_id, $new_field_id ) { 
  951.  
  952. // we'll need these to manually update 
  953. global $wpdb, $bp; 
  954.  
  955. // check if old field exists 
  956. $field = new BP_XProfile_Field( $old_field_id ); 
  957.  
  958. // if it does, we've got a bigger problem... 
  959. if ( isset( $field->id ) AND $field->id == $old_field_id ) { 
  960.  
  961. // construct message 
  962. $msg = __( 
  963. 'BP XProfile WordPress User Sync plugin: An xProfile field with that ID already exists. Cannot reconnect data.',  
  964. 'bp-xprofile-wp-user-sync' 
  965. ); 
  966.  
  967. // use var_dump as this seems to display in the iframe 
  968. var_dump( $msg ); die(); 
  969.  
  970.  
  971. // construct query 
  972. $sql = $wpdb->prepare( 
  973. "UPDATE {$bp->profile->table_name_fields} SET id = %d WHERE id = %d",  
  974. $old_field_id,  
  975. $new_field_id 
  976. ); 
  977.  
  978. // we must have one row affected 
  979. if ( $wpdb->query( $sql ) !== 1 ) { 
  980.  
  981. // construct message 
  982. $msg = __( 
  983. 'BP XProfile WordPress User Sync plugin: Could not update "ID" for xProfile field. SQL = ' . $sql,  
  984. 'bp-xprofile-wp-user-sync' 
  985. ); 
  986.  
  987. // use var_dump as this seems to display in the iframe 
  988. var_dump( $msg ); die(); 
  989.  
  990.  
  991. // --< 
  992. return true; 
  993.  
  994.  
  995.  
  996.  
  997. /** 
  998. * Get excluded fields on Profile View. 
  999. * 
  1000. * @since 0.5.2 
  1001. * 
  1002. * @return string $exclude_fields Comma-separated list of field IDs 
  1003. */ 
  1004. private function _get_excluded_fields() { 
  1005.  
  1006. // comma-delimit our fields 
  1007. $exclude_fields = implode( ', ', $this->options ); 
  1008.  
  1009. /** 
  1010. * Exclude our xprofile fields, but allow filtering. The relevant params 
  1011. * are passed to the filter so that other plugins can make an informed 
  1012. * choice of what to return. 
  1013. * 
  1014. * To retain the first name and last name fields, an appropriate way to 
  1015. * do this would look something like: 
  1016. * 
  1017. * add_filter( 'bp_xprofile_wp_user_sync_exclude_fields', 'my_function' ); 
  1018. * function my_function( $exclude_fields ) { 
  1019. * return bp_xprofile_fullname_field_id(); 
  1020. * } 
  1021. * 
  1022. * @param string $exclude_fields Comma-delimited pseudo-array of custom fields 
  1023. * @param array $options Array of custom field IDs 
  1024. */ 
  1025. return apply_filters( 
  1026. 'bp_xprofile_wp_user_sync_exclude_fields',  
  1027. $exclude_fields,  
  1028. $this->options 
  1029. ); 
  1030.  
  1031.  
  1032.  
  1033.  
  1034. /** 
  1035. * Merge excluded fields on Profile View. 
  1036. * 
  1037. * @since 0.5.3 
  1038. * 
  1039. * @param string $excluded_fields Comma-delimited list of fields already excluded 
  1040. * @param string $exclude_fields Comma-delimited list of fields requiring exclusion 
  1041. * @return string $excluded_fields Comma-delimited list of all fields to be excluded 
  1042. */ 
  1043. private function _merge_excluded_fields( $excluded_fields, $exclude_fields ) { 
  1044.  
  1045. // if params are not arrays already, convert them 
  1046. if ( ! is_array( $excluded_fields ) ) $excluded_fields = explode( ', ', $excluded_fields ); 
  1047. if ( ! is_array( $exclude_fields ) ) $exclude_fields = explode( ', ', $exclude_fields ); 
  1048.  
  1049. // merge with existing if populated 
  1050. if ( ! empty( $excluded_fields ) ) { 
  1051. $excluded_fields = array_unique( array_merge( $excluded_fields, $exclude_fields ) ); 
  1052. } else { 
  1053. $excluded_fields = $exclude_fields; 
  1054.  
  1055. // --< 
  1056. return implode( ', ', $excluded_fields ); 
  1057.  
  1058.  
  1059.  
  1060.  
  1061. } // class ends 
  1062.  
  1063.  
  1064.  
  1065.  
  1066.  
  1067. // declare as global 
  1068. global $bp_xprofile_wordpress_user_sync; 
  1069.  
  1070. // init plugin 
  1071. $bp_xprofile_wordpress_user_sync = new BpXProfileWordPressUserSync; 
  1072.  
  1073. // activation 
  1074. register_activation_hook( __FILE__, array( $bp_xprofile_wordpress_user_sync, 'activate' ) ); 
  1075.  
  1076. // deactivation 
  1077. register_deactivation_hook( __FILE__, array( $bp_xprofile_wordpress_user_sync, 'deactivate' ) ); 
  1078.  
  1079. // uninstall will use the 'uninstall.php' method when xProfile fields can be "deactivated" 
  1080. // see: http://codex.wordpress.org/Function_Reference/register_uninstall_hook 
.