/bp-core/admin/bp-core-admin-tools.php

  1. <?php 
  2. /** 
  3. * BuddyPress Tools panel. 
  4. * 
  5. * @package BuddyPress 
  6. * @subpackage Core 
  7. * @since 2.0.0 
  8. */ 
  9.  
  10. // Exit if accessed directly. 
  11. defined( 'ABSPATH' ) || exit; 
  12.  
  13. /** 
  14. * Render the BuddyPress Tools page. 
  15. * 
  16. * @since 2.0.0 
  17. */ 
  18. function bp_core_admin_tools() { 
  19. ?> 
  20. <div class="wrap"> 
  21.  
  22. <h1><?php esc_html_e( 'BuddyPress Tools', 'buddypress' ) ?></h1> 
  23.  
  24. <p><?php esc_html_e( 'BuddyPress keeps track of various relationships between members, groups, and activity items. Occasionally these relationships become out of sync, most often after an import, update, or migration.', 'buddypress' ); ?></p> 
  25. <p><?php esc_html_e( 'Use the tools below to manually recalculate these relationships.', 'buddypress' ); ?> 
  26. </p> 
  27. <p class="description"><?php esc_html_e( 'Some of these tools create substantial database overhead. Avoid running more than one repair job at a time.', 'buddypress' ); ?></p> 
  28.  
  29. <form class="settings" method="post" action=""> 
  30.  
  31. <fieldset> 
  32. <legend><?php esc_html_e( 'Repair tools', 'buddypress' ) ?></legend> 
  33.  
  34. <div class="checkbox"> 
  35. <?php foreach ( bp_admin_repair_list() as $item ) : ?> 
  36. <label for="<?php echo esc_attr( str_replace( '_', '-', $item[0] ) ); ?>"><input type="checkbox" class="checkbox" name="<?php echo esc_attr( $item[0] ) . '" id="' . esc_attr( str_replace( '_', '-', $item[0] ) ); ?>" value="1" /> <?php echo esc_html( $item[1] ); ?></label> 
  37. <?php endforeach; ?> 
  38. </div> 
  39.  
  40. <p class="submit"> 
  41. <input class="button-primary" type="submit" name="bp-tools-submit" value="<?php esc_attr_e( 'Repair Items', 'buddypress' ); ?>" /> 
  42. <?php wp_nonce_field( 'bp-do-counts' ); ?> 
  43. </p> 
  44.  
  45. </fieldset> 
  46.  
  47. </form> 
  48.  
  49. </div> 
  50.  
  51. <?php 
  52.  
  53. /** 
  54. * Handle the processing and feedback of the admin tools page. 
  55. * 
  56. * @since 2.0.0 
  57. */ 
  58. function bp_admin_repair_handler() { 
  59. if ( ! bp_is_post_request() || empty( $_POST['bp-tools-submit'] ) ) { 
  60. return; 
  61.  
  62. check_admin_referer( 'bp-do-counts' ); 
  63.  
  64. // Bail if user cannot moderate. 
  65. $capability = bp_core_do_network_admin() ? 'manage_network_options' : 'manage_options'; 
  66. if ( ! bp_current_user_can( $capability ) ) { 
  67. return; 
  68.  
  69. wp_cache_flush(); 
  70. $messages = array(); 
  71.  
  72. foreach ( (array) bp_admin_repair_list() as $item ) { 
  73. if ( isset( $item[2] ) && isset( $_POST[$item[0]] ) && 1 === absint( $_POST[$item[0]] ) && is_callable( $item[2] ) ) { 
  74. $messages[] = call_user_func( $item[2] ); 
  75.  
  76. if ( count( $messages ) ) { 
  77. foreach ( $messages as $message ) { 
  78. bp_admin_tools_feedback( $message[1] ); 
  79. add_action( bp_core_admin_hook(), 'bp_admin_repair_handler' ); 
  80.  
  81. /** 
  82. * Get the array of the repair list. 
  83. * 
  84. * @return array 
  85. */ 
  86. function bp_admin_repair_list() { 
  87. $repair_list = array(); 
  88.  
  89. // Members: 
  90. // - member count 
  91. // - last_activity migration (2.0). 
  92. $repair_list[20] = array( 
  93. 'bp-total-member-count',  
  94. __( 'Repair total members count.', 'buddypress' ),  
  95. 'bp_admin_repair_count_members',  
  96. ); 
  97.  
  98. $repair_list[25] = array( 
  99. 'bp-last-activity',  
  100. __( 'Repair member "last activity" data.', 'buddypress' ),  
  101. 'bp_admin_repair_last_activity',  
  102. ); 
  103.  
  104. // Friends: 
  105. // - user friend count. 
  106. if ( bp_is_active( 'friends' ) ) { 
  107. $repair_list[0] = array( 
  108. 'bp-user-friends',  
  109. __( 'Repair total friends count for each member.', 'buddypress' ),  
  110. 'bp_admin_repair_friend_count',  
  111. ); 
  112.  
  113. // Groups: 
  114. // - user group count. 
  115. if ( bp_is_active( 'groups' ) ) { 
  116. $repair_list[10] = array( 
  117. 'bp-group-count',  
  118. __( 'Repair total groups count for each member.', 'buddypress' ),  
  119. 'bp_admin_repair_group_count',  
  120. ); 
  121.  
  122. // Blogs: 
  123. // - user blog count. 
  124. if ( bp_is_active( 'blogs' ) ) { 
  125. $repair_list[90] = array( 
  126. 'bp-blog-records',  
  127. __( 'Repopulate site tracking records.', 'buddypress' ),  
  128. 'bp_admin_repair_blog_records',  
  129. ); 
  130.  
  131. // Emails: 
  132. // - reinstall emails. 
  133. $repair_list[100] = array( 
  134. 'bp-reinstall-emails',  
  135. __( 'Reinstall emails (delete and restore from defaults).', 'buddypress' ),  
  136. 'bp_admin_reinstall_emails',  
  137. ); 
  138.  
  139. ksort( $repair_list ); 
  140.  
  141. /** 
  142. * Filters the array of the repair list. 
  143. * 
  144. * @since 2.0.0 
  145. * 
  146. * @param array $repair_list Array of values for the Repair list options. 
  147. */ 
  148. return (array) apply_filters( 'bp_repair_list', $repair_list ); 
  149.  
  150. /** 
  151. * Recalculate friend counts for each user. 
  152. * 
  153. * @since 2.0.0 
  154. * 
  155. * @return array 
  156. */ 
  157. function bp_admin_repair_friend_count() { 
  158. global $wpdb; 
  159.  
  160. if ( ! bp_is_active( 'friends' ) ) { 
  161. return; 
  162.  
  163. $statement = __( 'Counting the number of friends for each user… %s', 'buddypress' ); 
  164. $result = __( 'Failed!', 'buddypress' ); 
  165.  
  166. $sql_delete = "DELETE FROM {$wpdb->usermeta} WHERE meta_key IN ( 'total_friend_count' );"; 
  167. if ( is_wp_error( $wpdb->query( $sql_delete ) ) ) { 
  168. return array( 1, sprintf( $statement, $result ) ); 
  169.  
  170. $bp = buddypress(); 
  171.  
  172. // Walk through all users on the site. 
  173. $total_users = $wpdb->get_row( "SELECT count(ID) as c FROM {$wpdb->users}" )->c; 
  174.  
  175. $updated = array(); 
  176. if ( $total_users > 0 ) { 
  177. $per_query = 500; 
  178. $offset = 0; 
  179. while ( $offset < $total_users ) { 
  180. // Only bother updating counts for users who actually have friendships. 
  181. $friendships = $wpdb->get_results( $wpdb->prepare( "SELECT initiator_user_id, friend_user_id FROM {$bp->friends->table_name} WHERE is_confirmed = 1 AND ( ( initiator_user_id > %d AND initiator_user_id <= %d ) OR ( friend_user_id > %d AND friend_user_id <= %d ) )", $offset, $offset + $per_query, $offset, $offset + $per_query ) ); 
  182.  
  183. // The previous query will turn up duplicates, so we 
  184. // filter them here. 
  185. foreach ( $friendships as $friendship ) { 
  186. if ( ! isset( $updated[ $friendship->initiator_user_id ] ) ) { 
  187. BP_Friends_Friendship::total_friend_count( $friendship->initiator_user_id ); 
  188. $updated[ $friendship->initiator_user_id ] = 1; 
  189.  
  190. if ( ! isset( $updated[ $friendship->friend_user_id ] ) ) { 
  191. BP_Friends_Friendship::total_friend_count( $friendship->friend_user_id ); 
  192. $updated[ $friendship->friend_user_id ] = 1; 
  193.  
  194. $offset += $per_query; 
  195. } else { 
  196. return array( 2, sprintf( $statement, $result ) ); 
  197.  
  198. return array( 0, sprintf( $statement, __( 'Complete!', 'buddypress' ) ) ); 
  199.  
  200. /** 
  201. * Recalculate group counts for each user. 
  202. * 
  203. * @since 2.0.0 
  204. * 
  205. * @return array 
  206. */ 
  207. function bp_admin_repair_group_count() { 
  208. global $wpdb; 
  209.  
  210. if ( ! bp_is_active( 'groups' ) ) { 
  211. return; 
  212.  
  213. $statement = __( 'Counting the number of groups for each user… %s', 'buddypress' ); 
  214. $result = __( 'Failed!', 'buddypress' ); 
  215.  
  216. $sql_delete = "DELETE FROM {$wpdb->usermeta} WHERE meta_key IN ( 'total_group_count' );"; 
  217. if ( is_wp_error( $wpdb->query( $sql_delete ) ) ) { 
  218. return array( 1, sprintf( $statement, $result ) ); 
  219.  
  220. $bp = buddypress(); 
  221.  
  222. // Walk through all users on the site. 
  223. $total_users = $wpdb->get_row( "SELECT count(ID) as c FROM {$wpdb->users}" )->c; 
  224.  
  225. if ( $total_users > 0 ) { 
  226. $per_query = 500; 
  227. $offset = 0; 
  228. while ( $offset < $total_users ) { 
  229. // But only bother to update counts for users that have groups. 
  230. $users = $wpdb->get_col( $wpdb->prepare( "SELECT user_id FROM {$bp->groups->table_name_members} WHERE is_confirmed = 1 AND is_banned = 0 AND user_id > %d AND user_id <= %d", $offset, $offset + $per_query ) ); 
  231.  
  232. foreach ( $users as $user ) { 
  233. BP_Groups_Member::refresh_total_group_count_for_user( $user ); 
  234.  
  235. $offset += $per_query; 
  236. } else { 
  237. return array( 2, sprintf( $statement, $result ) ); 
  238.  
  239. return array( 0, sprintf( $statement, __( 'Complete!', 'buddypress' ) ) ); 
  240.  
  241. /** 
  242. * Recalculate user-to-blog relationships and useful blog meta data. 
  243. * 
  244. * @since 2.1.0 
  245. * 
  246. * @return array 
  247. */ 
  248. function bp_admin_repair_blog_records() { 
  249.  
  250. // Description of this tool, displayed to the user. 
  251. $statement = __( 'Repopulating Blogs records… %s', 'buddypress' ); 
  252.  
  253. // Default to failure text. 
  254. $result = __( 'Failed!', 'buddypress' ); 
  255.  
  256. // Default to unrepaired. 
  257. $repair = false; 
  258.  
  259. // Run function if blogs component is active. 
  260. if ( bp_is_active( 'blogs' ) ) { 
  261. $repair = bp_blogs_record_existing_blogs(); 
  262.  
  263. // Setup success/fail messaging. 
  264. if ( true === $repair ) { 
  265. $result = __( 'Complete!', 'buddypress' ); 
  266.  
  267. // All done! 
  268. return array( 0, sprintf( $statement, $result ) ); 
  269.  
  270. /** 
  271. * Recalculate the total number of active site members. 
  272. * 
  273. * @since 2.0.0 
  274. */ 
  275. function bp_admin_repair_count_members() { 
  276. $statement = __( 'Counting the number of active members on the site… %s', 'buddypress' ); 
  277. delete_transient( 'bp_active_member_count' ); 
  278. bp_core_get_active_member_count(); 
  279. return array( 0, sprintf( $statement, __( 'Complete!', 'buddypress' ) ) ); 
  280.  
  281. /** 
  282. * Repair user last_activity data. 
  283. * 
  284. * Re-runs the migration from usermeta introduced in BP 2.0. 
  285. * 
  286. * @since 2.0.0 
  287. */ 
  288. function bp_admin_repair_last_activity() { 
  289. $statement = __( 'Determining last activity dates for each user… %s', 'buddypress' ); 
  290. bp_last_activity_migrate(); 
  291. return array( 0, sprintf( $statement, __( 'Complete!', 'buddypress' ) ) ); 
  292.  
  293. /** 
  294. * Assemble admin notices relating success/failure of repair processes. 
  295. * 
  296. * @since 2.0.0 
  297. * 
  298. * @param string $message Feedback message. 
  299. * @param string|bool $class Unused. 
  300. * @return bool 
  301. */ 
  302. function bp_admin_tools_feedback( $message, $class = false ) { 
  303. if ( is_string( $message ) ) { 
  304. $message = '<p>' . $message . '</p>'; 
  305. $class = $class ? $class : 'updated'; 
  306. } elseif ( is_wp_error( $message ) ) { 
  307. $errors = $message->get_error_messages(); 
  308.  
  309. switch ( count( $errors ) ) { 
  310. case 0: 
  311. return false; 
  312.  
  313. case 1: 
  314. $message = '<p>' . $errors[0] . '</p>'; 
  315. break; 
  316.  
  317. default: 
  318. $message = '<ul>' . "\n\t" . '<li>' . implode( '</li>' . "\n\t" . '<li>', $errors ) . '</li>' . "\n" . '</ul>'; 
  319. break; 
  320.  
  321. $class = $class ? $class : 'error'; 
  322. } else { 
  323. return false; 
  324.  
  325. $message = '<div id="message" class="' . esc_attr( $class ) . '">' . $message . '</div>'; 
  326. $message = str_replace( "'", "\'", $message ); 
  327. $lambda = function() use ( $message ) { echo $message; }; 
  328.  
  329. add_action( bp_core_do_network_admin() ? 'network_admin_notices' : 'admin_notices', $lambda ); 
  330.  
  331. return $lambda; 
  332.  
  333. /** 
  334. * Render the Available Tools page. 
  335. * 
  336. * We register this page on Network Admin as a top-level home for our 
  337. * BuddyPress tools. This displays the default content. 
  338. * 
  339. * @since 2.0.0 
  340. */ 
  341. function bp_core_admin_available_tools_page() { 
  342. ?> 
  343. <div class="wrap"> 
  344. <h1><?php esc_attr_e( 'Tools', 'buddypress' ) ?></h1> 
  345.  
  346. <?php 
  347.  
  348. /** 
  349. * Fires inside the markup used to display the Available Tools page. 
  350. * 
  351. * @since 2.0.0 
  352. */ 
  353. do_action( 'bp_network_tool_box' ); ?> 
  354.  
  355. </div> 
  356. <?php 
  357.  
  358. /** 
  359. * Render an introduction of BuddyPress tools on Available Tools page. 
  360. * 
  361. * @since 2.0.0 
  362. */ 
  363. function bp_core_admin_available_tools_intro() { 
  364. $query_arg = array( 
  365. 'page' => 'bp-tools' 
  366. ); 
  367.  
  368. $page = bp_core_do_network_admin() ? 'admin.php' : 'tools.php' ; 
  369. $url = add_query_arg( $query_arg, bp_get_admin_url( $page ) ); 
  370. ?> 
  371. <div class="card tool-box"> 
  372. <h2><?php esc_html_e( 'BuddyPress Tools', 'buddypress' ) ?></h2> 
  373. <p> 
  374. <?php esc_html_e( 'BuddyPress keeps track of various relationships between users, groups, and activity items. Occasionally these relationships become out of sync, most often after an import, update, or migration.', 'buddypress' ); ?> 
  375. <?php printf( esc_html_x( 'Use the %s to repair these relationships.', 'buddypress tools intro', 'buddypress' ), '<a href="' . esc_url( $url ) . '">' . esc_html__( 'BuddyPress Tools', 'buddypress' ) . '</a>' ); ?> 
  376. </p> 
  377. </div> 
  378. <?php 
  379.  
  380. /** 
  381. * Delete emails and restore from defaults. 
  382. * 
  383. * @since 2.5.0 
  384. * 
  385. * @return array 
  386. */ 
  387. function bp_admin_reinstall_emails() { 
  388. $switched = false; 
  389.  
  390. // Switch to the root blog, where the email posts live. 
  391. if ( ! bp_is_root_blog() ) { 
  392. switch_to_blog( bp_get_root_blog_id() ); 
  393. bp_register_taxonomies(); 
  394.  
  395. $switched = true; 
  396.  
  397. $emails = get_posts( array( 
  398. 'fields' => 'ids',  
  399. 'post_status' => 'publish',  
  400. 'post_type' => bp_get_email_post_type(),  
  401. 'posts_per_page' => 200,  
  402. 'suppress_filters' => false,  
  403. ) ); 
  404.  
  405. if ( $emails ) { 
  406. foreach ( $emails as $email_id ) { 
  407. wp_trash_post( $email_id ); 
  408.  
  409. // Make sure we have no orphaned email type terms. 
  410. $email_types = get_terms( bp_get_email_tax_type(), array( 
  411. 'fields' => 'ids',  
  412. 'hide_empty' => false,  
  413. 'update_term_meta_cache' => false,  
  414. ) ); 
  415.  
  416. if ( $email_types ) { 
  417. foreach ( $email_types as $term_id ) { 
  418. wp_delete_term( (int) $term_id, bp_get_email_tax_type() ); 
  419.  
  420. require_once( buddypress()->plugin_dir . '/bp-core/admin/bp-core-admin-schema.php' ); 
  421. bp_core_install_emails(); 
  422.  
  423. if ( $switched ) { 
  424. restore_current_blog(); 
  425.  
  426. return array( 0, __( 'Emails have been successfully reinstalled.', 'buddypress' ) ); 
  427.  
  428. /** 
  429. * Add notice on the "Tools > BuddyPress" page if more sites need recording. 
  430. * 
  431. * This notice only shows up in the network admin dashboard. 
  432. * 
  433. * @since 2.6.0 
  434. */ 
  435. function bp_core_admin_notice_repopulate_blogs_resume() { 
  436. $screen = get_current_screen(); 
  437. if ( 'tools_page_bp-tools-network' !== $screen->id ) { 
  438. return; 
  439.  
  440. if ( '' === bp_get_option( '_bp_record_blogs_offset' ) ) { 
  441. return; 
  442.  
  443. echo '<div class="error"><p>' . __( 'It looks like you have more sites to record. Resume recording by checking the "Repopulate site tracking records" option.', 'buddypress' ) . '</p></div>'; 
  444. add_action( 'network_admin_notices', 'bp_core_admin_notice_repopulate_blogs_resume' ); 
.