/includes/admin/tools.php

  1. <?php 
  2.  
  3. /** 
  4. * bbPress Admin Tools Page 
  5. * 
  6. * @package bbPress 
  7. * @subpackage Administration 
  8. */ 
  9.  
  10. // Exit if accessed directly 
  11. if ( !defined( 'ABSPATH' ) ) exit; 
  12.  
  13. /** Repair ********************************************************************/ 
  14.  
  15. /** 
  16. * Admin repair page 
  17. * 
  18. * @since bbPress (r2613) 
  19. * 
  20. * @uses bbp_admin_repair_list() To get the recount list 
  21. * @uses check_admin_referer() To verify the nonce and the referer 
  22. * @uses wp_cache_flush() To flush the cache 
  23. * @uses do_action() Calls 'admin_notices' to display the notices 
  24. * @uses screen_icon() To display the screen icon 
  25. * @uses wp_nonce_field() To add a hidden nonce field 
  26. */ 
  27. function bbp_admin_repair() { 
  28. ?> 
  29.  
  30. <div class="wrap"> 
  31.  
  32. <?php screen_icon( 'tools' ); ?> 
  33.  
  34. <h2 class="nav-tab-wrapper"><?php bbp_tools_admin_tabs( __( 'Repair Forums', 'bbpress' ) ); ?></h2> 
  35.  
  36. <p><?php esc_html_e( 'bbPress keeps track of relationships between forums, topics, replies, and topic tags, and users. Occasionally these relationships become out of sync, most often after an import or migration. Use the tools below to manually recalculate these relationships.', 'bbpress' ); ?></p> 
  37. <p class="description"><?php esc_html_e( 'Some of these tools create substantial database overhead. Avoid running more than 1 repair job at a time.', 'bbpress' ); ?></p> 
  38.  
  39. <form class="settings" method="post" action=""> 
  40. <table class="form-table"> 
  41. <tbody> 
  42. <tr valign="top"> 
  43. <th scope="row"><?php esc_html_e( 'Relationships to Repair:', 'bbpress' ) ?></th> 
  44. <td> 
  45. <fieldset> 
  46. <legend class="screen-reader-text"><span><?php esc_html_e( 'Repair', 'bbpress' ) ?></span></legend> 
  47.  
  48. <?php foreach ( bbp_admin_repair_list() as $item ) : ?> 
  49.  
  50. <label><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><br /> 
  51.  
  52. <?php endforeach; ?> 
  53.  
  54. </fieldset> 
  55. </td> 
  56. </tr> 
  57. </tbody> 
  58. </table> 
  59.  
  60. <fieldset class="submit"> 
  61. <input class="button-primary" type="submit" name="submit" value="<?php esc_attr_e( 'Repair Items', 'bbpress' ); ?>" /> 
  62. <?php wp_nonce_field( 'bbpress-do-counts' ); ?> 
  63. </fieldset> 
  64. </form> 
  65. </div> 
  66.  
  67. <?php 
  68.  
  69. /** 
  70. * Handle the processing and feedback of the admin tools page 
  71. * 
  72. * @since bbPress (r2613) 
  73. * 
  74. * @uses bbp_admin_repair_list() To get the recount list 
  75. * @uses check_admin_referer() To verify the nonce and the referer 
  76. * @uses wp_cache_flush() To flush the cache 
  77. * @uses do_action() Calls 'admin_notices' to display the notices 
  78. */ 
  79. function bbp_admin_repair_handler() { 
  80.  
  81. if ( ! bbp_is_post_request() ) 
  82. return; 
  83.  
  84. check_admin_referer( 'bbpress-do-counts' ); 
  85.  
  86. // Stores messages 
  87. $messages = array(); 
  88.  
  89. wp_cache_flush(); 
  90.  
  91. foreach ( (array) bbp_admin_repair_list() as $item ) { 
  92. if ( isset( $item[2] ) && isset( $_POST[$item[0]] ) && 1 === absint( $_POST[$item[0]] ) && is_callable( $item[2] ) ) { 
  93. $messages[] = call_user_func( $item[2] ); 
  94.  
  95. if ( count( $messages ) ) { 
  96. foreach ( $messages as $message ) { 
  97. bbp_admin_tools_feedback( $message[1] ); 
  98.  
  99. /** 
  100. * Assemble the admin notices 
  101. * 
  102. * @since bbPress (r2613) 
  103. * 
  104. * @param string|WP_Error $message A message to be displayed or {@link WP_Error} 
  105. * @param string $class Optional. A class to be added to the message div 
  106. * @uses WP_Error::get_error_messages() To get the error messages of $message 
  107. * @uses add_action() Adds the admin notice action with the message HTML 
  108. * @return string The message HTML 
  109. */ 
  110. function bbp_admin_tools_feedback( $message, $class = false ) { 
  111. if ( is_string( $message ) ) { 
  112. $message = '<p>' . $message . '</p>'; 
  113. $class = $class ? $class : 'updated'; 
  114. } elseif ( is_wp_error( $message ) ) { 
  115. $errors = $message->get_error_messages(); 
  116.  
  117. switch ( count( $errors ) ) { 
  118. case 0: 
  119. return false; 
  120. break; 
  121.  
  122. case 1: 
  123. $message = '<p>' . $errors[0] . '</p>'; 
  124. break; 
  125.  
  126. default: 
  127. $message = '<ul>' . "\n\t" . '<li>' . implode( '</li>' . "\n\t" . '<li>', $errors ) . '</li>' . "\n" . '</ul>'; 
  128. break; 
  129.  
  130. $class = $class ? $class : 'error'; 
  131. } else { 
  132. return false; 
  133.  
  134. $message = '<div id="message" class="' . esc_attr( $class ) . '">' . $message . '</div>'; 
  135. $message = str_replace( "'", "\'", $message ); 
  136. $lambda = create_function( '', "echo '$message';" ); 
  137.  
  138. add_action( 'admin_notices', $lambda ); 
  139.  
  140. return $lambda; 
  141.  
  142. /** 
  143. * Get the array of the repair list 
  144. * 
  145. * @since bbPress (r2613) 
  146. * 
  147. * @uses apply_filters() Calls 'bbp_repair_list' with the list array 
  148. * @return array Repair list of options 
  149. */ 
  150. function bbp_admin_repair_list() { 
  151. $repair_list = array( 
  152. 0 => array( 'bbp-sync-topic-meta', __( 'Recalculate the parent topic for each post', 'bbpress' ), 'bbp_admin_repair_topic_meta' ),  
  153. 5 => array( 'bbp-sync-forum-meta', __( 'Recalculate the parent forum for each post', 'bbpress' ), 'bbp_admin_repair_forum_meta' ),  
  154. 10 => array( 'bbp-sync-forum-visibility', __( 'Recalculate private and hidden forums', 'bbpress' ), 'bbp_admin_repair_forum_visibility' ),  
  155. 15 => array( 'bbp-sync-all-topics-forums', __( 'Recalculate last activity in each topic and forum', 'bbpress' ), 'bbp_admin_repair_freshness' ),  
  156. 20 => array( 'bbp-sync-all-topics-sticky', __( 'Recalculate the sticky relationship of each topic', 'bbpress' ), 'bbp_admin_repair_sticky' ),  
  157. 25 => array( 'bbp-sync-all-reply-positions', __( 'Recalculate the position of each reply', 'bbpress' ), 'bbp_admin_repair_reply_menu_order' ),  
  158. 30 => array( 'bbp-group-forums', __( 'Repair BuddyPress Group Forum relationships', 'bbpress' ), 'bbp_admin_repair_group_forum_relationship' ),  
  159. 35 => array( 'bbp-forum-topics', __( 'Count topics in each forum', 'bbpress' ), 'bbp_admin_repair_forum_topic_count' ),  
  160. 40 => array( 'bbp-forum-replies', __( 'Count replies in each forum', 'bbpress' ), 'bbp_admin_repair_forum_reply_count' ),  
  161. 45 => array( 'bbp-topic-replies', __( 'Count replies in each topic', 'bbpress' ), 'bbp_admin_repair_topic_reply_count' ),  
  162. 50 => array( 'bbp-topic-voices', __( 'Count voices in each topic', 'bbpress' ), 'bbp_admin_repair_topic_voice_count' ),  
  163. 55 => array( 'bbp-topic-hidden-replies', __( 'Count spammed & trashed replies in each topic', 'bbpress' ), 'bbp_admin_repair_topic_hidden_reply_count' ),  
  164. 60 => array( 'bbp-user-topics', __( 'Count topics for each user', 'bbpress' ), 'bbp_admin_repair_user_topic_count' ),  
  165. 65 => array( 'bbp-user-replies', __( 'Count replies for each user', 'bbpress' ), 'bbp_admin_repair_user_reply_count' ),  
  166. 70 => array( 'bbp-user-favorites', __( 'Remove trashed topics from user favorites', 'bbpress' ), 'bbp_admin_repair_user_favorites' ),  
  167. 75 => array( 'bbp-user-topic-subscriptions', __( 'Remove trashed topics from user subscriptions', 'bbpress' ), 'bbp_admin_repair_user_topic_subscriptions' ),  
  168. 80 => array( 'bbp-user-forum-subscriptions', __( 'Remove trashed forums from user subscriptions', 'bbpress' ), 'bbp_admin_repair_user_forum_subscriptions' ),  
  169. 85 => array( 'bbp-user-role-map', __( 'Remap existing users to default forum roles', 'bbpress' ), 'bbp_admin_repair_user_roles' ) 
  170. ); 
  171. ksort( $repair_list ); 
  172.  
  173. return (array) apply_filters( 'bbp_repair_list', $repair_list ); 
  174.  
  175. /** 
  176. * Recount topic replies 
  177. * 
  178. * @since bbPress (r2613) 
  179. * 
  180. * @uses bbp_get_reply_post_type() To get the reply post type 
  181. * @uses wpdb::query() To run our recount sql queries 
  182. * @uses is_wp_error() To check if the executed query returned {@link WP_Error} 
  183. * @return array An array of the status code and the message 
  184. */ 
  185. function bbp_admin_repair_topic_reply_count() { 
  186. global $wpdb; 
  187.  
  188. $statement = __( 'Counting the number of replies in each topic… %s', 'bbpress' ); 
  189. $result = __( 'Failed!', 'bbpress' ); 
  190.  
  191. // Post types and status 
  192. $tpt = bbp_get_topic_post_type(); 
  193. $rpt = bbp_get_reply_post_type(); 
  194. $pps = bbp_get_public_status_id(); 
  195. $cps = bbp_get_closed_status_id(); 
  196.  
  197. // Delete the meta key _bbp_reply_count for each topic 
  198. $sql_delete = "DELETE `postmeta` FROM `{$wpdb->postmeta}` AS `postmeta` 
  199. LEFT JOIN `{$wpdb->posts}` AS `posts` ON `posts`.`ID` = `postmeta`.`post_id` 
  200. WHERE `posts`.`post_type` = '{$tpt}' 
  201. AND `postmeta`.`meta_key` = '_bbp_reply_count'"; 
  202.  
  203. if ( is_wp_error( $wpdb->query( $sql_delete ) ) ) { 
  204. return array( 1, sprintf( $statement, $result ) ); 
  205.  
  206. // Recalculate the meta key _bbp_reply_count for each topic 
  207. $sql = "INSERT INTO `{$wpdb->postmeta}` (`post_id`, `meta_key`, `meta_value`) ( 
  208. SELECT `topics`.`ID` AS `post_id`, '_bbp_reply_count' AS `meta_key`, COUNT(`replies`.`ID`) As `meta_value` 
  209. FROM `{$wpdb->posts}` AS `topics` 
  210. LEFT JOIN `{$wpdb->posts}` as `replies` 
  211. ON `replies`.`post_parent` = `topics`.`ID` 
  212. AND `replies`.`post_status` = '{$pps}' 
  213. AND `replies`.`post_type` = '{$rpt}' 
  214. WHERE `topics`.`post_type` = '{$tpt}' 
  215. AND `topics`.`post_status` IN ( '{$pps}', '{$cps}' ) 
  216. GROUP BY `topics`.`ID`);"; 
  217.  
  218. if ( is_wp_error( $wpdb->query( $sql ) ) ) { 
  219. return array( 2, sprintf( $statement, $result ) ); 
  220.  
  221. return array( 0, sprintf( $statement, __( 'Complete!', 'bbpress' ) ) ); 
  222.  
  223. /** 
  224. * Recount topic voices 
  225. * 
  226. * @since bbPress (r2613) 
  227. * 
  228. * @uses bbp_get_reply_post_type() To get the reply post type 
  229. * @uses wpdb::query() To run our recount sql queries 
  230. * @uses is_wp_error() To check if the executed query returned {@link WP_Error} 
  231. * @return array An array of the status code and the message 
  232. */ 
  233. function bbp_admin_repair_topic_voice_count() { 
  234. global $wpdb; 
  235.  
  236. $statement = __( 'Counting the number of voices in each topic… %s', 'bbpress' ); 
  237. $result = __( 'Failed!', 'bbpress' ); 
  238.  
  239. $sql_delete = "DELETE FROM `{$wpdb->postmeta}` WHERE `meta_key` = '_bbp_voice_count';"; 
  240. if ( is_wp_error( $wpdb->query( $sql_delete ) ) ) 
  241. return array( 1, sprintf( $statement, $result ) ); 
  242.  
  243. // Post types and status 
  244. $tpt = bbp_get_topic_post_type(); 
  245. $rpt = bbp_get_reply_post_type(); 
  246. $pps = bbp_get_public_status_id(); 
  247. $cps = bbp_get_closed_status_id(); 
  248.  
  249. $sql = "INSERT INTO `{$wpdb->postmeta}` (`post_id`, `meta_key`, `meta_value`) ( 
  250. SELECT `postmeta`.`meta_value`, '_bbp_voice_count', COUNT(DISTINCT `post_author`) as `meta_value` 
  251. FROM `{$wpdb->posts}` AS `posts` 
  252. LEFT JOIN `{$wpdb->postmeta}` AS `postmeta` 
  253. ON `posts`.`ID` = `postmeta`.`post_id` 
  254. AND `postmeta`.`meta_key` = '_bbp_topic_id' 
  255. WHERE `posts`.`post_type` IN ( '{$tpt}', '{$rpt}' ) 
  256. AND `posts`.`post_status` IN ( '{$pps}', '{$cps}' ) 
  257. AND `posts`.`post_author` != '0' 
  258. GROUP BY `postmeta`.`meta_value`);"; 
  259.  
  260. if ( is_wp_error( $wpdb->query( $sql ) ) ) 
  261. return array( 2, sprintf( $statement, $result ) ); 
  262.  
  263. return array( 0, sprintf( $statement, __( 'Complete!', 'bbpress' ) ) ); 
  264.  
  265. /** 
  266. * Recount topic hidden replies (spammed/trashed) 
  267. * 
  268. * @since bbPress (r2747) 
  269. * 
  270. * @uses wpdb::query() To run our recount sql queries 
  271. * @uses is_wp_error() To check if the executed query returned {@link WP_Error} 
  272. * @return array An array of the status code and the message 
  273. */ 
  274. function bbp_admin_repair_topic_hidden_reply_count() { 
  275. global $wpdb; 
  276.  
  277. $statement = __( 'Counting the number of spammed and trashed replies in each topic… %s', 'bbpress' ); 
  278. $result = __( 'Failed!', 'bbpress' ); 
  279.  
  280. $sql_delete = "DELETE FROM `{$wpdb->postmeta}` WHERE `meta_key` = '_bbp_reply_count_hidden';"; 
  281. if ( is_wp_error( $wpdb->query( $sql_delete ) ) ) 
  282. return array( 1, sprintf( $statement, $result ) ); 
  283.  
  284. $sql = "INSERT INTO `{$wpdb->postmeta}` (`post_id`, `meta_key`, `meta_value`) (SELECT `post_parent`, '_bbp_reply_count_hidden', COUNT(`post_status`) as `meta_value` FROM `{$wpdb->posts}` WHERE `post_type` = '" . bbp_get_reply_post_type() . "' AND `post_status` IN ( '" . implode( "', '", array( bbp_get_trash_status_id(), bbp_get_spam_status_id() ) ) . "') GROUP BY `post_parent`);"; 
  285. if ( is_wp_error( $wpdb->query( $sql ) ) ) 
  286. return array( 2, sprintf( $statement, $result ) ); 
  287.  
  288. return array( 0, sprintf( $statement, __( 'Complete!', 'bbpress' ) ) ); 
  289.  
  290. /** 
  291. * Repair group forum ID mappings after a bbPress 1.1 to bbPress 2.2 conversion 
  292. * 
  293. * @since bbPress (r4395) 
  294. * 
  295. * @global WPDB $wpdb 
  296. * @return If a wp_error() occurs and no converted forums are found 
  297. */ 
  298. function bbp_admin_repair_group_forum_relationship() { 
  299. global $wpdb; 
  300.  
  301. $statement = __( 'Repairing BuddyPress group-forum relationships… %s', 'bbpress' ); 
  302. $g_count = 0; 
  303. $f_count = 0; 
  304. $s_count = 0; 
  305.  
  306. // Copy the BuddyPress filter here, incase BuddyPress is not active 
  307. $prefix = apply_filters( 'bp_core_get_table_prefix', $wpdb->base_prefix ); 
  308. $groups_table = $prefix . 'bp_groups'; 
  309. $groups_meta_table = $prefix . 'bp_groups_groupmeta'; 
  310.  
  311. // Get the converted forum IDs 
  312. $forum_ids = $wpdb->query( "SELECT `forum`.`ID`, `forummeta`.`meta_value` 
  313. FROM `{$wpdb->posts}` AS `forum` 
  314. LEFT JOIN `{$wpdb->postmeta}` AS `forummeta` 
  315. ON `forum`.`ID` = `forummeta`.`post_id` 
  316. AND `forummeta`.`meta_key` = '_bbp_old_forum_id' 
  317. WHERE `forum`.`post_type` = 'forum' 
  318. GROUP BY `forum`.`ID`;" ); 
  319.  
  320. // Bail if forum IDs returned an error 
  321. if ( is_wp_error( $forum_ids ) || empty( $wpdb->last_result ) ) 
  322. return array( 2, sprintf( $statement, __( 'Failed!', 'bbpress' ) ) ); 
  323.  
  324. // Stash the last results 
  325. $results = $wpdb->last_result; 
  326.  
  327. // Update each group forum 
  328. foreach ( $results as $group_forums ) { 
  329.  
  330. // Only update if is a converted forum 
  331. if ( ! isset( $group_forums->meta_value ) ) 
  332. continue; 
  333.  
  334. // Attempt to update group meta 
  335. $updated = $wpdb->query( "UPDATE `{$groups_meta_table}` SET `meta_value` = '{$group_forums->ID}' WHERE `meta_key` = 'forum_id' AND `meta_value` = '{$group_forums->meta_value}';" ); 
  336.  
  337. // Bump the count 
  338. if ( !empty( $updated ) && ! is_wp_error( $updated ) ) { 
  339. ++$g_count; 
  340.  
  341. // Update group to forum relationship data 
  342. $group_id = (int) $wpdb->get_var( "SELECT `group_id` FROM `{$groups_meta_table}` WHERE `meta_key` = 'forum_id' AND `meta_value` = '{$group_forums->ID}';" ); 
  343. if ( !empty( $group_id ) ) { 
  344.  
  345. // Update the group to forum meta connection in forums 
  346. update_post_meta( $group_forums->ID, '_bbp_group_ids', array( $group_id ) ); 
  347.  
  348. // Get the group status 
  349. $group_status = $wpdb->get_var( "SELECT `status` FROM `{$groups_table}` WHERE `id` = '{$group_id}';" ); 
  350.  
  351. // Sync up forum visibility based on group status 
  352. switch ( $group_status ) { 
  353.  
  354. // Public groups have public forums 
  355. case 'public' : 
  356. bbp_publicize_forum( $group_forums->ID ); 
  357.  
  358. // Bump the count for output later 
  359. ++$s_count; 
  360. break; 
  361.  
  362. // Private/hidden groups have hidden forums 
  363. case 'private' : 
  364. case 'hidden' : 
  365. bbp_hide_forum( $group_forums->ID ); 
  366.  
  367. // Bump the count for output later 
  368. ++$s_count; 
  369. break; 
  370.  
  371. // Bump the count for output later 
  372. ++$f_count; 
  373.  
  374. // Make some logical guesses at the old group root forum 
  375. if ( function_exists( 'bp_forums_parent_forum_id' ) ) { 
  376. $old_default_forum_id = bp_forums_parent_forum_id(); 
  377. } elseif ( defined( 'BP_FORUMS_PARENT_FORUM_ID' ) ) { 
  378. $old_default_forum_id = (int) BP_FORUMS_PARENT_FORUM_ID; 
  379. } else { 
  380. $old_default_forum_id = 1; 
  381.  
  382. // Try to get the group root forum 
  383. $posts = get_posts( array( 
  384. 'post_type' => bbp_get_forum_post_type(),  
  385. 'meta_key' => '_bbp_old_forum_id',  
  386. 'meta_value' => $old_default_forum_id,  
  387. 'numberposts' => 1 
  388. ) ); 
  389.  
  390. // Found the group root forum 
  391. if ( ! empty( $posts ) ) { 
  392.  
  393. // Rename 'Default Forum' since it's now visible in sitewide forums 
  394. if ( 'Default Forum' === $posts[0]->post_title ) { 
  395. wp_update_post( array( 
  396. 'ID' => $posts[0]->ID,  
  397. 'post_title' => __( 'Group Forums', 'bbpress' ),  
  398. 'post_name' => __( 'group-forums', 'bbpress' ),  
  399. ) ); 
  400.  
  401. // Update the group forums root metadata 
  402. update_option( '_bbp_group_forums_root_id', $posts[0]->ID ); 
  403.  
  404. // Remove old bbPress 1.1 roles (BuddyPress) 
  405. remove_role( 'member' ); 
  406. remove_role( 'inactive' ); 
  407. remove_role( 'blocked' ); 
  408. remove_role( 'moderator' ); 
  409. remove_role( 'keymaster' ); 
  410.  
  411. // Complete results 
  412. $result = sprintf( __( 'Complete! %s groups updated; %s forums updated; %s forum statuses synced.', 'bbpress' ), bbp_number_format( $g_count ), bbp_number_format( $f_count ), bbp_number_format( $s_count ) ); 
  413. return array( 0, sprintf( $statement, $result ) ); 
  414.  
  415. /** 
  416. * Recount forum topics 
  417. * 
  418. * @since bbPress (r2613) 
  419. * 
  420. * @uses wpdb::query() To run our recount sql queries 
  421. * @uses is_wp_error() To check if the executed query returned {@link WP_Error} 
  422. * @uses bbp_get_forum_post_type() To get the forum post type 
  423. * @uses get_posts() To get the forums 
  424. * @uses bbp_update_forum_topic_count() To update the forum topic count 
  425. * @return array An array of the status code and the message 
  426. */ 
  427. function bbp_admin_repair_forum_topic_count() { 
  428. global $wpdb; 
  429.  
  430. $statement = __( 'Counting the number of topics in each forum… %s', 'bbpress' ); 
  431. $result = __( 'Failed!', 'bbpress' ); 
  432.  
  433. $sql_delete = "DELETE FROM {$wpdb->postmeta} WHERE meta_key IN ( '_bbp_topic_count', '_bbp_total_topic_count' );"; 
  434. if ( is_wp_error( $wpdb->query( $sql_delete ) ) ) 
  435. return array( 1, sprintf( $statement, $result ) ); 
  436.  
  437. $forums = get_posts( array( 'post_type' => bbp_get_forum_post_type(), 'numberposts' => -1 ) ); 
  438. if ( !empty( $forums ) ) { 
  439. foreach ( $forums as $forum ) { 
  440. bbp_update_forum_topic_count( $forum->ID ); 
  441. } else { 
  442. return array( 2, sprintf( $statement, $result ) ); 
  443.  
  444. return array( 0, sprintf( $statement, __( 'Complete!', 'bbpress' ) ) ); 
  445.  
  446. /** 
  447. * Recount forum replies 
  448. * 
  449. * @since bbPress (r2613) 
  450. * 
  451. * @uses wpdb::query() To run our recount sql queries 
  452. * @uses is_wp_error() To check if the executed query returned {@link WP_Error} 
  453. * @uses bbp_get_forum_post_type() To get the forum post type 
  454. * @uses get_posts() To get the forums 
  455. * @uses bbp_update_forum_reply_count() To update the forum reply count 
  456. * @return array An array of the status code and the message 
  457. */ 
  458. function bbp_admin_repair_forum_reply_count() { 
  459. global $wpdb; 
  460.  
  461. $statement = __( 'Counting the number of replies in each forum… %s', 'bbpress' ); 
  462. $result = __( 'Failed!', 'bbpress' ); 
  463.  
  464. // Post type 
  465. $fpt = bbp_get_forum_post_type(); 
  466.  
  467. // Delete the meta keys _bbp_reply_count and _bbp_total_reply_count for each forum 
  468. $sql_delete = "DELETE `postmeta` FROM `{$wpdb->postmeta}` AS `postmeta` 
  469. LEFT JOIN `{$wpdb->posts}` AS `posts` ON `posts`.`ID` = `postmeta`.`post_id` 
  470. WHERE `posts`.`post_type` = '{$fpt}' 
  471. AND `postmeta`.`meta_key` = '_bbp_reply_count' 
  472. OR `postmeta`.`meta_key` = '_bbp_total_reply_count'"; 
  473.  
  474. if ( is_wp_error( $wpdb->query( $sql_delete ) ) ) { 
  475. return array( 1, sprintf( $statement, $result ) ); 
  476.  
  477. // Recalculate the metas key _bbp_reply_count and _bbp_total_reply_count for each forum 
  478. $forums = get_posts( array( 'post_type' => bbp_get_forum_post_type(), 'numberposts' => -1 ) ); 
  479. if ( !empty( $forums ) ) { 
  480. foreach ( $forums as $forum ) { 
  481. bbp_update_forum_reply_count( $forum->ID ); 
  482. } else { 
  483. return array( 2, sprintf( $statement, $result ) ); 
  484.  
  485. return array( 0, sprintf( $statement, __( 'Complete!', 'bbpress' ) ) ); 
  486.  
  487. /** 
  488. * Recount topics by the users 
  489. * 
  490. * @since bbPress (r3889) 
  491. * 
  492. * @uses bbp_get_reply_post_type() To get the reply post type 
  493. * @uses wpdb::query() To run our recount sql queries 
  494. * @uses is_wp_error() To check if the executed query returned {@link WP_Error} 
  495. * @return array An array of the status code and the message 
  496. */ 
  497. function bbp_admin_repair_user_topic_count() { 
  498. global $wpdb; 
  499.  
  500. $statement = __( 'Counting the number of topics each user has created… %s', 'bbpress' ); 
  501. $result = __( 'Failed!', 'bbpress' ); 
  502. $sql_select = "SELECT `post_author`, COUNT(DISTINCT `ID`) as `_count` FROM `{$wpdb->posts}` WHERE `post_type` = '" . bbp_get_topic_post_type() . "' AND `post_status` = '" . bbp_get_public_status_id() . "' GROUP BY `post_author`;"; 
  503. $insert_rows = $wpdb->get_results( $sql_select ); 
  504.  
  505. if ( is_wp_error( $insert_rows ) ) 
  506. return array( 1, sprintf( $statement, $result ) ); 
  507.  
  508. $key = $wpdb->prefix . '_bbp_topic_count'; 
  509. $insert_values = array(); 
  510. foreach ( $insert_rows as $insert_row ) 
  511. $insert_values[] = "('{$insert_row->post_author}', '{$key}', '{$insert_row->_count}')"; 
  512.  
  513. if ( !count( $insert_values ) ) 
  514. return array( 2, sprintf( $statement, $result ) ); 
  515.  
  516. $sql_delete = "DELETE FROM `{$wpdb->usermeta}` WHERE `meta_key` = '{$key}';"; 
  517. if ( is_wp_error( $wpdb->query( $sql_delete ) ) ) 
  518. return array( 3, sprintf( $statement, $result ) ); 
  519.  
  520. foreach ( array_chunk( $insert_values, 10000 ) as $chunk ) { 
  521. $chunk = "\n" . implode( ", \n", $chunk ); 
  522. $sql_insert = "INSERT INTO `{$wpdb->usermeta}` (`user_id`, `meta_key`, `meta_value`) VALUES $chunk;"; 
  523.  
  524. if ( is_wp_error( $wpdb->query( $sql_insert ) ) ) { 
  525. return array( 4, sprintf( $statement, $result ) ); 
  526.  
  527. return array( 0, sprintf( $statement, __( 'Complete!', 'bbpress' ) ) ); 
  528.  
  529. /** 
  530. * Recount topic replied by the users 
  531. * 
  532. * @since bbPress (r2613) 
  533. * 
  534. * @uses bbp_get_reply_post_type() To get the reply post type 
  535. * @uses wpdb::query() To run our recount sql queries 
  536. * @uses is_wp_error() To check if the executed query returned {@link WP_Error} 
  537. * @return array An array of the status code and the message 
  538. */ 
  539. function bbp_admin_repair_user_reply_count() { 
  540. global $wpdb; 
  541.  
  542. $statement = __( 'Counting the number of topics to which each user has replied… %s', 'bbpress' ); 
  543. $result = __( 'Failed!', 'bbpress' ); 
  544. $sql_select = "SELECT `post_author`, COUNT(DISTINCT `ID`) as `_count` FROM `{$wpdb->posts}` WHERE `post_type` = '" . bbp_get_reply_post_type() . "' AND `post_status` = '" . bbp_get_public_status_id() . "' GROUP BY `post_author`;"; 
  545. $insert_rows = $wpdb->get_results( $sql_select ); 
  546.  
  547. if ( is_wp_error( $insert_rows ) ) 
  548. return array( 1, sprintf( $statement, $result ) ); 
  549.  
  550. $key = $wpdb->prefix . '_bbp_reply_count'; 
  551. $insert_values = array(); 
  552. foreach ( $insert_rows as $insert_row ) 
  553. $insert_values[] = "('{$insert_row->post_author}', '{$key}', '{$insert_row->_count}')"; 
  554.  
  555. if ( !count( $insert_values ) ) 
  556. return array( 2, sprintf( $statement, $result ) ); 
  557.  
  558. $sql_delete = "DELETE FROM `{$wpdb->usermeta}` WHERE `meta_key` = '{$key}';"; 
  559. if ( is_wp_error( $wpdb->query( $sql_delete ) ) ) 
  560. return array( 3, sprintf( $statement, $result ) ); 
  561.  
  562. foreach ( array_chunk( $insert_values, 10000 ) as $chunk ) { 
  563. $chunk = "\n" . implode( ", \n", $chunk ); 
  564. $sql_insert = "INSERT INTO `{$wpdb->usermeta}` (`user_id`, `meta_key`, `meta_value`) VALUES $chunk;"; 
  565.  
  566. if ( is_wp_error( $wpdb->query( $sql_insert ) ) ) { 
  567. return array( 4, sprintf( $statement, $result ) ); 
  568.  
  569. return array( 0, sprintf( $statement, __( 'Complete!', 'bbpress' ) ) ); 
  570.  
  571. /** 
  572. * Clean the users' favorites 
  573. * 
  574. * @since bbPress (r2613) 
  575. * 
  576. * @uses bbp_get_topic_post_type() To get the topic post type 
  577. * @uses wpdb::query() To run our recount sql queries 
  578. * @uses is_wp_error() To check if the executed query returned {@link WP_Error} 
  579. * @return array An array of the status code and the message 
  580. */ 
  581. function bbp_admin_repair_user_favorites() { 
  582. global $wpdb; 
  583.  
  584. $statement = __( 'Removing trashed topics from user favorites… %s', 'bbpress' ); 
  585. $result = __( 'Failed!', 'bbpress' ); 
  586. $key = $wpdb->prefix . '_bbp_favorites'; 
  587. $users = $wpdb->get_results( "SELECT `user_id`, `meta_value` AS `favorites` FROM `{$wpdb->usermeta}` WHERE `meta_key` = '{$key}';" ); 
  588.  
  589. if ( is_wp_error( $users ) ) 
  590. return array( 1, sprintf( $statement, $result ) ); 
  591.  
  592. $topics = $wpdb->get_col( "SELECT `ID` FROM `{$wpdb->posts}` WHERE `post_type` = '" . bbp_get_topic_post_type() . "' AND `post_status` = '" . bbp_get_public_status_id() . "';" ); 
  593.  
  594. if ( is_wp_error( $topics ) ) 
  595. return array( 2, sprintf( $statement, $result ) ); 
  596.  
  597. $values = array(); 
  598. foreach ( $users as $user ) { 
  599. if ( empty( $user->favorites ) || !is_string( $user->favorites ) ) 
  600. continue; 
  601.  
  602. $favorites = array_intersect( $topics, explode( ', ', $user->favorites ) ); 
  603. if ( empty( $favorites ) || !is_array( $favorites ) ) 
  604. continue; 
  605.  
  606. $favorites_joined = implode( ', ', $favorites ); 
  607. $values[] = "('{$user->user_id}', '{$key}, '{$favorites_joined}')"; 
  608.  
  609. // Cleanup 
  610. unset( $favorites, $favorites_joined ); 
  611.  
  612. if ( !count( $values ) ) { 
  613. $result = __( 'Nothing to remove!', 'bbpress' ); 
  614. return array( 0, sprintf( $statement, $result ) ); 
  615.  
  616. $sql_delete = "DELETE FROM `{$wpdb->usermeta}` WHERE `meta_key` = '{$key}';"; 
  617. if ( is_wp_error( $wpdb->query( $sql_delete ) ) ) 
  618. return array( 4, sprintf( $statement, $result ) ); 
  619.  
  620. foreach ( array_chunk( $values, 10000 ) as $chunk ) { 
  621. $chunk = "\n" . implode( ", \n", $chunk ); 
  622. $sql_insert = "INSERT INTO `$wpdb->usermeta` (`user_id`, `meta_key`, `meta_value`) VALUES $chunk;"; 
  623. if ( is_wp_error( $wpdb->query( $sql_insert ) ) ) { 
  624. return array( 5, sprintf( $statement, $result ) ); 
  625.  
  626. return array( 0, sprintf( $statement, __( 'Complete!', 'bbpress' ) ) ); 
  627.  
  628. /** 
  629. * Clean the users' topic subscriptions 
  630. * 
  631. * @since bbPress (r2668) 
  632. * 
  633. * @uses bbp_get_topic_post_type() To get the topic post type 
  634. * @uses wpdb::query() To run our recount sql queries 
  635. * @uses is_wp_error() To check if the executed query returned {@link WP_Error} 
  636. * @return array An array of the status code and the message 
  637. */ 
  638. function bbp_admin_repair_user_topic_subscriptions() { 
  639. global $wpdb; 
  640.  
  641. $statement = __( 'Removing trashed topics from user subscriptions… %s', 'bbpress' ); 
  642. $result = __( 'Failed!', 'bbpress' ); 
  643. $key = $wpdb->prefix . '_bbp_subscriptions'; 
  644. $users = $wpdb->get_results( "SELECT `user_id`, `meta_value` AS `subscriptions` FROM `{$wpdb->usermeta}` WHERE `meta_key` = '{$key}';" ); 
  645.  
  646. if ( is_wp_error( $users ) ) 
  647. return array( 1, sprintf( $statement, $result ) ); 
  648.  
  649. $topics = $wpdb->get_col( "SELECT `ID` FROM `{$wpdb->posts}` WHERE `post_type` = '" . bbp_get_topic_post_type() . "' AND `post_status` = '" . bbp_get_public_status_id() . "';" ); 
  650. if ( is_wp_error( $topics ) ) 
  651. return array( 2, sprintf( $statement, $result ) ); 
  652.  
  653. $values = array(); 
  654. foreach ( $users as $user ) { 
  655. if ( empty( $user->subscriptions ) || !is_string( $user->subscriptions ) ) 
  656. continue; 
  657.  
  658. $subscriptions = array_intersect( $topics, explode( ', ', $user->subscriptions ) ); 
  659. if ( empty( $subscriptions ) || !is_array( $subscriptions ) ) 
  660. continue; 
  661.  
  662. $subscriptions_joined = implode( ', ', $subscriptions ); 
  663. $values[] = "('{$user->user_id}', '{$key}', '{$subscriptions_joined}')"; 
  664.  
  665. // Cleanup 
  666. unset( $subscriptions, $subscriptions_joined ); 
  667.  
  668. if ( !count( $values ) ) { 
  669. $result = __( 'Nothing to remove!', 'bbpress' ); 
  670. return array( 0, sprintf( $statement, $result ) ); 
  671.  
  672. $sql_delete = "DELETE FROM `{$wpdb->usermeta}` WHERE `meta_key` = '{$key}';"; 
  673. if ( is_wp_error( $wpdb->query( $sql_delete ) ) ) 
  674. return array( 4, sprintf( $statement, $result ) ); 
  675.  
  676. foreach ( array_chunk( $values, 10000 ) as $chunk ) { 
  677. $chunk = "\n" . implode( ", \n", $chunk ); 
  678. $sql_insert = "INSERT INTO `{$wpdb->usermeta}` (`user_id`, `meta_key`, `meta_value`) VALUES $chunk;"; 
  679. if ( is_wp_error( $wpdb->query( $sql_insert ) ) ) { 
  680. return array( 5, sprintf( $statement, $result ) ); 
  681.  
  682. return array( 0, sprintf( $statement, __( 'Complete!', 'bbpress' ) ) ); 
  683.  
  684. /** 
  685. * Clean the users' forum subscriptions 
  686. * 
  687. * @since bbPress (r5155) 
  688. * 
  689. * @uses bbp_get_forum_post_type() To get the topic post type 
  690. * @uses wpdb::query() To run our recount sql queries 
  691. * @uses is_wp_error() To check if the executed query returned {@link WP_Error} 
  692. * @return array An array of the status code and the message 
  693. */ 
  694. function bbp_admin_repair_user_forum_subscriptions() { 
  695. global $wpdb; 
  696.  
  697. $statement = __( 'Removing trashed forums from user subscriptions… %s', 'bbpress' ); 
  698. $result = __( 'Failed!', 'bbpress' ); 
  699. $key = $wpdb->prefix . '_bbp_forum_subscriptions'; 
  700. $users = $wpdb->get_results( "SELECT `user_id`, `meta_value` AS `subscriptions` FROM `{$wpdb->usermeta}` WHERE `meta_key` = '{$key}';" ); 
  701.  
  702. if ( is_wp_error( $users ) ) 
  703. return array( 1, sprintf( $statement, $result ) ); 
  704.  
  705. $forums = $wpdb->get_col( "SELECT `ID` FROM `{$wpdb->posts}` WHERE `post_type` = '" . bbp_get_forum_post_type() . "' AND `post_status` = '" . bbp_get_public_status_id() . "';" ); 
  706. if ( is_wp_error( $forums ) ) 
  707. return array( 2, sprintf( $statement, $result ) ); 
  708.  
  709. $values = array(); 
  710. foreach ( $users as $user ) { 
  711. if ( empty( $user->subscriptions ) || !is_string( $user->subscriptions ) ) 
  712. continue; 
  713.  
  714. $subscriptions = array_intersect( $forums, explode( ', ', $user->subscriptions ) ); 
  715. if ( empty( $subscriptions ) || !is_array( $subscriptions ) ) 
  716. continue; 
  717.  
  718. $subscriptions_joined = implode( ', ', $subscriptions ); 
  719. $values[] = "('{$user->user_id}', '{$key}', '{$subscriptions_joined}')"; 
  720.  
  721. // Cleanup 
  722. unset( $subscriptions, $subscriptions_joined ); 
  723.  
  724. if ( !count( $values ) ) { 
  725. $result = __( 'Nothing to remove!', 'bbpress' ); 
  726. return array( 0, sprintf( $statement, $result ) ); 
  727.  
  728. $sql_delete = "DELETE FROM `{$wpdb->usermeta}` WHERE `meta_key` = '{$key}';"; 
  729. if ( is_wp_error( $wpdb->query( $sql_delete ) ) ) 
  730. return array( 4, sprintf( $statement, $result ) ); 
  731.  
  732. foreach ( array_chunk( $values, 10000 ) as $chunk ) { 
  733. $chunk = "\n" . implode( ", \n", $chunk ); 
  734. $sql_insert = "INSERT INTO `{$wpdb->usermeta}` (`user_id`, `meta_key`, `meta_value`) VALUES $chunk;"; 
  735. if ( is_wp_error( $wpdb->query( $sql_insert ) ) ) { 
  736. return array( 5, sprintf( $statement, $result ) ); 
  737.  
  738. return array( 0, sprintf( $statement, __( 'Complete!', 'bbpress' ) ) ); 
  739.  
  740. /** 
  741. * This repair tool will map each user of the current site to their respective 
  742. * forums role. By default, Admins will be Key Masters, and every other role 
  743. * will be the default role defined in Settings > Forums (Participant). 
  744. * 
  745. * @since bbPress (r4340) 
  746. * 
  747. * @uses bbp_get_user_role_map() To get the map of user roles 
  748. * @uses get_editable_roles() To get the current WordPress roles 
  749. * @uses get_users() To get the users of each role (limited to ID field) 
  750. * @uses bbp_set_user_role() To set each user's forums role 
  751. */ 
  752. function bbp_admin_repair_user_roles() { 
  753.  
  754. $statement = __( 'Remapping forum role for each user on this site… %s', 'bbpress' ); 
  755. $changed = 0; 
  756. $role_map = bbp_get_user_role_map(); 
  757. $default_role = bbp_get_default_role(); 
  758.  
  759. // Bail if no role map exists 
  760. if ( empty( $role_map ) ) 
  761. return array( 1, sprintf( $statement, __( 'Failed!', 'bbpress' ) ) ); 
  762.  
  763. // Iterate through each role... 
  764. foreach ( array_keys( bbp_get_blog_roles() ) as $role ) { 
  765.  
  766. // Reset the offset 
  767. $offset = 0; 
  768.  
  769. // If no role map exists, give the default forum role (bbp-participant) 
  770. $new_role = isset( $role_map[$role] ) ? $role_map[$role] : $default_role; 
  771.  
  772. // Get users of this site, limited to 1000 
  773. while ( $users = get_users( array( 
  774. 'role' => $role,  
  775. 'fields' => 'ID',  
  776. 'number' => 1000,  
  777. 'offset' => $offset 
  778. ) ) ) { 
  779.  
  780. // Iterate through each user of $role and try to set it 
  781. foreach ( (array) $users as $user_id ) { 
  782. if ( bbp_set_user_role( $user_id, $new_role ) ) { 
  783. ++$changed; // Keep a count to display at the end 
  784.  
  785. // Bump the offset for the next query iteration 
  786. $offset = $offset + 1000; 
  787.  
  788. $result = sprintf( __( 'Complete! %s users updated.', 'bbpress' ), bbp_number_format( $changed ) ); 
  789. return array( 0, sprintf( $statement, $result ) ); 
  790.  
  791. /** 
  792. * Recaches the last post in every topic and forum 
  793. * 
  794. * @since bbPress (r3040) 
  795. * 
  796. * @uses wpdb::query() To run our recount sql queries 
  797. * @uses is_wp_error() To check if the executed query returned {@link WP_Error} 
  798. * @return array An array of the status code and the message 
  799. */ 
  800. function bbp_admin_repair_freshness() { 
  801. global $wpdb; 
  802.  
  803. $statement = __( 'Recomputing latest post in every topic and forum… %s', 'bbpress' ); 
  804. $result = __( 'Failed!', 'bbpress' ); 
  805.  
  806. // First, delete everything. 
  807. if ( is_wp_error( $wpdb->query( "DELETE FROM `$wpdb->postmeta` WHERE `meta_key` IN ( '_bbp_last_reply_id', '_bbp_last_topic_id', '_bbp_last_active_id', '_bbp_last_active_time' );" ) ) ) 
  808. return array( 1, sprintf( $statement, $result ) ); 
  809.  
  810. // Next, give all the topics with replies the ID their last reply. 
  811. if ( is_wp_error( $wpdb->query( "INSERT INTO `$wpdb->postmeta` (`post_id`, `meta_key`, `meta_value`) 
  812. ( SELECT `topic`.`ID`, '_bbp_last_reply_id', MAX( `reply`.`ID` ) 
  813. FROM `$wpdb->posts` AS `topic` INNER JOIN `$wpdb->posts` AS `reply` ON `topic`.`ID` = `reply`.`post_parent` 
  814. WHERE `reply`.`post_status` IN ( '" . bbp_get_public_status_id() . "' ) AND `topic`.`post_type` = 'topic' AND `reply`.`post_type` = 'reply' 
  815. GROUP BY `topic`.`ID` );" ) ) ) 
  816. return array( 2, sprintf( $statement, $result ) ); 
  817.  
  818. // For any remaining topics, give a reply ID of 0. 
  819. if ( is_wp_error( $wpdb->query( "INSERT INTO `$wpdb->postmeta` (`post_id`, `meta_key`, `meta_value`) 
  820. ( SELECT `ID`, '_bbp_last_reply_id', 0 
  821. FROM `$wpdb->posts` AS `topic` LEFT JOIN `$wpdb->postmeta` AS `reply` 
  822. ON `topic`.`ID` = `reply`.`post_id` AND `reply`.`meta_key` = '_bbp_last_reply_id' 
  823. WHERE `reply`.`meta_id` IS NULL AND `topic`.`post_type` = 'topic' );" ) ) ) 
  824. return array( 3, sprintf( $statement, $result ) ); 
  825.  
  826. // Now we give all the forums with topics the ID their last topic. 
  827. if ( is_wp_error( $wpdb->query( "INSERT INTO `$wpdb->postmeta` (`post_id`, `meta_key`, `meta_value`) 
  828. ( SELECT `forum`.`ID`, '_bbp_last_topic_id', `topic`.`ID` 
  829. FROM `$wpdb->posts` AS `forum` INNER JOIN `$wpdb->posts` AS `topic` ON `forum`.`ID` = `topic`.`post_parent` 
  830. WHERE `topic`.`post_status` IN ( '" . bbp_get_public_status_id() . "' ) AND `forum`.`post_type` = 'forum' AND `topic`.`post_type` = 'topic' 
  831. GROUP BY `forum`.`ID` );" ) ) ) 
  832. return array( 4, sprintf( $statement, $result ) ); 
  833.  
  834. // For any remaining forums, give a topic ID of 0. 
  835. if ( is_wp_error( $wpdb->query( "INSERT INTO `$wpdb->postmeta` (`post_id`, `meta_key`, `meta_value`) 
  836. ( SELECT `ID`, '_bbp_last_topic_id', 0 
  837. FROM `$wpdb->posts` AS `forum` LEFT JOIN `$wpdb->postmeta` AS `topic` 
  838. ON `forum`.`ID` = `topic`.`post_id` AND `topic`.`meta_key` = '_bbp_last_topic_id' 
  839. WHERE `topic`.`meta_id` IS NULL AND `forum`.`post_type` = 'forum' );" ) ) ) 
  840. return array( 5, sprintf( $statement, $result ) ); 
  841.  
  842. // After that, we give all the topics with replies the ID their last reply (again, this time for a different reason). 
  843. if ( is_wp_error( $wpdb->query( "INSERT INTO `$wpdb->postmeta` (`post_id`, `meta_key`, `meta_value`) 
  844. ( SELECT `topic`.`ID`, '_bbp_last_active_id', MAX( `reply`.`ID` ) 
  845. FROM `$wpdb->posts` AS `topic` INNER JOIN `$wpdb->posts` AS `reply` ON `topic`.`ID` = `reply`.`post_parent` 
  846. WHERE `reply`.`post_status` IN ( '" . bbp_get_public_status_id() . "' ) AND `topic`.`post_type` = 'topic' AND `reply`.`post_type` = 'reply' 
  847. GROUP BY `topic`.`ID` );" ) ) ) 
  848. return array( 6, sprintf( $statement, $result ) ); 
  849.  
  850. // For any remaining topics, give a reply ID of themself. 
  851. if ( is_wp_error( $wpdb->query( "INSERT INTO `$wpdb->postmeta` (`post_id`, `meta_key`, `meta_value`) 
  852. ( SELECT `ID`, '_bbp_last_active_id', `ID` 
  853. FROM `$wpdb->posts` AS `topic` LEFT JOIN `$wpdb->postmeta` AS `reply` 
  854. ON `topic`.`ID` = `reply`.`post_id` AND `reply`.`meta_key` = '_bbp_last_active_id' 
  855. WHERE `reply`.`meta_id` IS NULL AND `topic`.`post_type` = 'topic' );" ) ) ) 
  856. return array( 7, sprintf( $statement, $result ) ); 
  857.  
  858. // Give topics with replies their last update time. 
  859. if ( is_wp_error( $wpdb->query( "INSERT INTO `$wpdb->postmeta` (`post_id`, `meta_key`, `meta_value`) 
  860. ( SELECT `topic`.`ID`, '_bbp_last_active_time', MAX( `reply`.`post_date` ) 
  861. FROM `$wpdb->posts` AS `topic` INNER JOIN `$wpdb->posts` AS `reply` ON `topic`.`ID` = `reply`.`post_parent` 
  862. WHERE `reply`.`post_status` IN ( '" . bbp_get_public_status_id() . "' ) AND `topic`.`post_type` = 'topic' AND `reply`.`post_type` = 'reply' 
  863. GROUP BY `topic`.`ID` );" ) ) ) 
  864. return array( 8, sprintf( $statement, $result ) ); 
  865.  
  866. // Give topics without replies their last update time. 
  867. if ( is_wp_error( $wpdb->query( "INSERT INTO `$wpdb->postmeta` (`post_id`, `meta_key`, `meta_value`) 
  868. ( SELECT `ID`, '_bbp_last_active_time', `post_date` 
  869. FROM `$wpdb->posts` AS `topic` LEFT JOIN `$wpdb->postmeta` AS `reply` 
  870. ON `topic`.`ID` = `reply`.`post_id` AND `reply`.`meta_key` = '_bbp_last_active_time' 
  871. WHERE `reply`.`meta_id` IS NULL AND `topic`.`post_type` = 'topic' );" ) ) ) 
  872. return array( 9, sprintf( $statement, $result ) ); 
  873.  
  874. // Forums need to know what their last active item is as well. Now it gets a bit more complex to do in the database. 
  875. $forums = $wpdb->get_col( "SELECT `ID` FROM `$wpdb->posts` WHERE `post_type` = 'forum' and `post_status` != 'auto-draft';" ); 
  876. if ( is_wp_error( $forums ) ) 
  877. return array( 10, sprintf( $statement, $result ) ); 
  878.  
  879. // Loop through forums 
  880. foreach ( $forums as $forum_id ) { 
  881. if ( !bbp_is_forum_category( $forum_id ) ) { 
  882. bbp_update_forum( array( 'forum_id' => $forum_id ) ); 
  883.  
  884. // Loop through categories when forums are done 
  885. foreach ( $forums as $forum_id ) { 
  886. if ( bbp_is_forum_category( $forum_id ) ) { 
  887. bbp_update_forum( array( 'forum_id' => $forum_id ) ); 
  888.  
  889. // Complete results 
  890. return array( 0, sprintf( $statement, __( 'Complete!', 'bbpress' ) ) ); 
  891.  
  892. /** 
  893. * Repairs the relationship of sticky topics to the actual parent forum 
  894. * 
  895. * @since bbPress (r4695) 
  896. * 
  897. * @uses wpdb::get_col() To run our recount sql queries 
  898. * @uses is_wp_error() To check if the executed query returned {@link WP_Error} 
  899. * @return array An array of the status code and the message 
  900. */ 
  901. function bbp_admin_repair_sticky() { 
  902. global $wpdb; 
  903.  
  904. $statement = __( 'Repairing the sticky topic to the parent forum relationships… %s', 'bbpress' ); 
  905. $result = __( 'Failed!', 'bbpress' ); 
  906. $forums = $wpdb->get_col( "SELECT ID FROM `{$wpdb->posts}` WHERE `post_type` = 'forum';" ); 
  907.  
  908. // Bail if no forums found 
  909. if ( empty( $forums ) || is_wp_error( $forums ) ) 
  910. return array( 1, sprintf( $statement, $result ) ); 
  911.  
  912. // Loop through forums and get their sticky topics 
  913. foreach ( $forums as $forum ) { 
  914. $forum_stickies[$forum] = get_post_meta( $forum, '_bbp_sticky_topics', true ); 
  915.  
  916. // Cleanup 
  917. unset( $forums, $forum ); 
  918.  
  919. // Loop through each forum with sticky topics 
  920. foreach ( $forum_stickies as $forum_id => $stickies ) { 
  921.  
  922. // Skip if no stickies 
  923. if ( empty( $stickies ) ) { 
  924. continue; 
  925.  
  926. // Loop through each sticky topic 
  927. foreach ( $stickies as $id => $topic_id ) { 
  928.  
  929. // If the topic is not a super sticky, and the forum ID does not 
  930. // match the topic's forum ID, unset the forum's sticky meta. 
  931. if ( ! bbp_is_topic_super_sticky( $topic_id ) && $forum_id !== bbp_get_topic_forum_id( $topic_id ) ) { 
  932. unset( $forum_stickies[$forum_id][$id] ); 
  933.  
  934. // Get sticky topic ID's, or use empty string 
  935. $stickers = empty( $forum_stickies[$forum_id] ) ? '' : array_values( $forum_stickies[$forum_id] ); 
  936.  
  937. // Update the forum's sticky topics meta 
  938. update_post_meta( $forum_id, '_bbp_sticky_topics', $stickers ); 
  939.  
  940. // Complete results 
  941. return array( 0, sprintf( $statement, __( 'Complete!', 'bbpress' ) ) ); 
  942.  
  943. /** 
  944. * Recaches the private and hidden forums 
  945. * 
  946. * @since bbPress (r4104) 
  947. * 
  948. * @uses delete_option() to delete private and hidden forum pointers 
  949. * @uses WP_Query() To query post IDs 
  950. * @uses is_wp_error() To return if error occurred 
  951. * @uses update_option() To update the private and hidden post ID pointers 
  952. * @return array An array of the status code and the message 
  953. */ 
  954. function bbp_admin_repair_forum_visibility() { 
  955. $statement = __( 'Recalculating forum visibility … %s', 'bbpress' ); 
  956.  
  957. // Bail if queries returned errors 
  958. if ( ! bbp_repair_forum_visibility() ) { 
  959. return array( 2, sprintf( $statement, __( 'Failed!', 'bbpress' ) ) ); 
  960.  
  961. // Complete results 
  962. } else { 
  963. return array( 0, sprintf( $statement, __( 'Complete!', 'bbpress' ) ) ); 
  964.  
  965. /** 
  966. * Recaches the forum for each post 
  967. * 
  968. * @since bbPress (r3876) 
  969. * 
  970. * @uses wpdb::query() To run our recount sql queries 
  971. * @uses is_wp_error() To check if the executed query returned {@link WP_Error} 
  972. * @return array An array of the status code and the message 
  973. */ 
  974. function bbp_admin_repair_forum_meta() { 
  975. global $wpdb; 
  976.  
  977. $statement = __( 'Recalculating the forum for each post … %s', 'bbpress' ); 
  978. $result = __( 'Failed!', 'bbpress' ); 
  979.  
  980. // First, delete everything. 
  981. if ( is_wp_error( $wpdb->query( "DELETE FROM `$wpdb->postmeta` WHERE `meta_key` = '_bbp_forum_id';" ) ) ) 
  982. return array( 1, sprintf( $statement, $result ) ); 
  983.  
  984. // Next, give all the topics with replies the ID their last reply. 
  985. if ( is_wp_error( $wpdb->query( "INSERT INTO `$wpdb->postmeta` (`post_id`, `meta_key`, `meta_value`) 
  986. ( SELECT `forum`.`ID`, '_bbp_forum_id', `forum`.`post_parent` 
  987. FROM `$wpdb->posts` 
  988. AS `forum` 
  989. WHERE `forum`.`post_type` = 'forum' 
  990. GROUP BY `forum`.`ID` );" ) ) ) 
  991. return array( 2, sprintf( $statement, $result ) ); 
  992.  
  993. // Next, give all the topics with replies the ID their last reply. 
  994. if ( is_wp_error( $wpdb->query( "INSERT INTO `$wpdb->postmeta` (`post_id`, `meta_key`, `meta_value`) 
  995. ( SELECT `topic`.`ID`, '_bbp_forum_id', `topic`.`post_parent` 
  996. FROM `$wpdb->posts` 
  997. AS `topic` 
  998. WHERE `topic`.`post_type` = 'topic' 
  999. GROUP BY `topic`.`ID` );" ) ) ) 
  1000. return array( 3, sprintf( $statement, $result ) ); 
  1001.  
  1002. // Next, give all the topics with replies the ID their last reply. 
  1003. if ( is_wp_error( $wpdb->query( "INSERT INTO `$wpdb->postmeta` (`post_id`, `meta_key`, `meta_value`) 
  1004. ( SELECT `reply`.`ID`, '_bbp_forum_id', `topic`.`post_parent` 
  1005. FROM `$wpdb->posts` 
  1006. AS `reply` 
  1007. INNER JOIN `$wpdb->posts` 
  1008. AS `topic` 
  1009. ON `reply`.`post_parent` = `topic`.`ID` 
  1010. WHERE `topic`.`post_type` = 'topic' 
  1011. AND `reply`.`post_type` = 'reply' 
  1012. GROUP BY `reply`.`ID` );" ) ) ) 
  1013. return array( 4, sprintf( $statement, $result ) ); 
  1014.  
  1015. // Complete results 
  1016. return array( 0, sprintf( $statement, __( 'Complete!', 'bbpress' ) ) ); 
  1017.  
  1018. /** 
  1019. * Recaches the topic for each post 
  1020. * 
  1021. * @since bbPress (r3876) 
  1022. * 
  1023. * @uses wpdb::query() To run our recount sql queries 
  1024. * @uses is_wp_error() To check if the executed query returned {@link WP_Error} 
  1025. * @return array An array of the status code and the message 
  1026. */ 
  1027. function bbp_admin_repair_topic_meta() { 
  1028. global $wpdb; 
  1029.  
  1030. $statement = __( 'Recalculating the topic for each post … %s', 'bbpress' ); 
  1031. $result = __( 'Failed!', 'bbpress' ); 
  1032.  
  1033. // First, delete everything. 
  1034. if ( is_wp_error( $wpdb->query( "DELETE FROM `$wpdb->postmeta` WHERE `meta_key` = '_bbp_topic_id';" ) ) ) 
  1035. return array( 1, sprintf( $statement, $result ) ); 
  1036.  
  1037. // Next, give all the topics with replies the ID their last reply. 
  1038. if ( is_wp_error( $wpdb->query( "INSERT INTO `$wpdb->postmeta` (`post_id`, `meta_key`, `meta_value`) 
  1039. ( SELECT `topic`.`ID`, '_bbp_topic_id', `topic`.`ID` 
  1040. FROM `$wpdb->posts` 
  1041. AS `topic` 
  1042. WHERE `topic`.`post_type` = 'topic' 
  1043. GROUP BY `topic`.`ID` );" ) ) ) 
  1044. return array( 3, sprintf( $statement, $result ) ); 
  1045.  
  1046. // Next, give all the topics with replies the ID their last reply. 
  1047. if ( is_wp_error( $wpdb->query( "INSERT INTO `$wpdb->postmeta` (`post_id`, `meta_key`, `meta_value`) 
  1048. ( SELECT `reply`.`ID`, '_bbp_topic_id', `topic`.`ID` 
  1049. FROM `$wpdb->posts` 
  1050. AS `reply` 
  1051. INNER JOIN `$wpdb->posts` 
  1052. AS `topic` 
  1053. ON `reply`.`post_parent` = `topic`.`ID` 
  1054. WHERE `topic`.`post_type` = 'topic' 
  1055. AND `reply`.`post_type` = 'reply' 
  1056. GROUP BY `reply`.`ID` );" ) ) ) 
  1057. return array( 4, sprintf( $statement, $result ) ); 
  1058.  
  1059. // Complete results 
  1060. return array( 0, sprintf( $statement, __( 'Complete!', 'bbpress' ) ) ); 
  1061.  
  1062. /** 
  1063. * Recalculate reply menu order 
  1064. * 
  1065. * @since bbPress (r5367) 
  1066. * 
  1067. * @uses wpdb::query() To run our recount sql queries 
  1068. * @uses is_wp_error() To check if the executed query returned {@link WP_Error} 
  1069. * @uses bbp_get_reply_post_type() To get the reply post type 
  1070. * @uses bbp_update_reply_position() To update the reply position 
  1071. * @return array An array of the status code and the message 
  1072. */ 
  1073. function bbp_admin_repair_reply_menu_order() { 
  1074. global $wpdb; 
  1075.  
  1076. $statement = __( 'Recalculating reply menu order … %s', 'bbpress' ); 
  1077. $result = __( 'No reply positions to recalculate!', 'bbpress' ); 
  1078.  
  1079. // Delete cases where `_bbp_reply_to` was accidentally set to itself 
  1080. if ( is_wp_error( $wpdb->query( "DELETE FROM `{$wpdb->postmeta}` WHERE `meta_key` = '_bbp_reply_to' AND `post_id` = `meta_value`;" ) ) ) { 
  1081. return array( 1, sprintf( $statement, $result ) );  
  1082.  
  1083. // Post type 
  1084. $rpt = bbp_get_reply_post_type(); 
  1085.  
  1086. // Get an array of reply id's to update the menu oder for each reply 
  1087. $replies = $wpdb->get_results( "SELECT `a`.`ID` FROM `{$wpdb->posts}` AS `a` 
  1088. INNER JOIN ( 
  1089. SELECT `menu_order`, `post_parent` 
  1090. FROM `{$wpdb->posts}` 
  1091. GROUP BY `menu_order`, `post_parent` 
  1092. HAVING COUNT( * ) >1 
  1093. )`b` 
  1094. ON `a`.`menu_order` = `b`.`menu_order` 
  1095. AND `a`.`post_parent` = `b`.`post_parent` 
  1096. WHERE `post_type` = '{$rpt}';", OBJECT_K ); 
  1097.  
  1098. // Bail if no replies returned 
  1099. if ( empty( $replies ) ) { 
  1100. return array( 1, sprintf( $statement, $result ) ); 
  1101.  
  1102. // Recalculate the menu order position for each reply 
  1103. foreach ( $replies as $reply ) { 
  1104. bbp_update_reply_position( $reply->ID ); 
  1105.  
  1106. // Cleanup 
  1107. unset( $replies, $reply ); 
  1108.  
  1109. // Flush the cache; things are about to get ugly. 
  1110. wp_cache_flush(); 
  1111.  
  1112. return array( 0, sprintf( $statement, __( 'Complete!', 'bbpress' ) ) ); 
  1113.  
  1114. /** Reset ********************************************************************/ 
  1115.  
  1116. /** 
  1117. * Admin reset page 
  1118. * 
  1119. * @since bbPress (r2613) 
  1120. * 
  1121. * @uses check_admin_referer() To verify the nonce and the referer 
  1122. * @uses do_action() Calls 'admin_notices' to display the notices 
  1123. * @uses screen_icon() To display the screen icon 
  1124. * @uses wp_nonce_field() To add a hidden nonce field 
  1125. */ 
  1126. function bbp_admin_reset() { 
  1127. ?> 
  1128.  
  1129. <div class="wrap"> 
  1130.  
  1131. <?php screen_icon( 'tools' ); ?> 
  1132.  
  1133. <h2 class="nav-tab-wrapper"><?php bbp_tools_admin_tabs( __( 'Reset Forums', 'bbpress' ) ); ?></h2> 
  1134. <p><?php esc_html_e( 'Revert your forums back to a brand new installation. This process cannot be undone.', 'bbpress' ); ?></p> 
  1135. <p><strong><?php esc_html_e( 'Backup your database before proceeding.', 'bbpress' ); ?></strong></p> 
  1136.  
  1137. <form class="settings" method="post" action=""> 
  1138. <table class="form-table"> 
  1139. <tbody> 
  1140. <tr valign="top"> 
  1141. <th scope="row"><?php esc_html_e( 'The following data will be removed:', 'bbpress' ) ?></th> 
  1142. <td> 
  1143. <?php esc_html_e( 'All Forums', 'bbpress' ); ?><br /> 
  1144. <?php esc_html_e( 'All Topics', 'bbpress' ); ?><br /> 
  1145. <?php esc_html_e( 'All Replies', 'bbpress' ); ?><br /> 
  1146. <?php esc_html_e( 'All Topic Tags', 'bbpress' ); ?><br /> 
  1147. <?php esc_html_e( 'Related Meta Data', 'bbpress' ); ?><br /> 
  1148. <?php esc_html_e( 'Forum Settings', 'bbpress' ); ?><br /> 
  1149. <?php esc_html_e( 'Forum Activity', 'bbpress' ); ?><br /> 
  1150. <?php esc_html_e( 'Forum User Roles', 'bbpress' ); ?><br /> 
  1151. <?php esc_html_e( 'Importer Helper Data', 'bbpress' ); ?><br /> 
  1152. </td> 
  1153. </tr> 
  1154. <tr valign="top"> 
  1155. <th scope="row"><?php esc_html_e( 'Delete imported users?', 'bbpress' ); ?></th> 
  1156. <td> 
  1157. <fieldset> 
  1158. <legend class="screen-reader-text"><span><?php esc_html_e( "Say it ain't so!", 'bbpress' ); ?></span></legend> 
  1159. <label><input type="checkbox" class="checkbox" name="bbpress-delete-imported-users" id="bbpress-delete-imported-users" value="1" /> <?php esc_html_e( 'This option will delete all previously imported users, and cannot be undone.', 'bbpress' ); ?></label> 
  1160. <p class="description"><?php esc_html_e( 'Note: Resetting without this checked will delete the meta-data necessary to delete these users.', 'bbpress' ); ?></p> 
  1161. </fieldset> 
  1162. </td> 
  1163. </tr> 
  1164. <tr valign="top"> 
  1165. <th scope="row"><?php esc_html_e( 'Are you sure you want to do this?', 'bbpress' ); ?></th> 
  1166. <td> 
  1167. <fieldset> 
  1168. <legend class="screen-reader-text"><span><?php esc_html_e( "Say it ain't so!", 'bbpress' ); ?></span></legend> 
  1169. <label><input type="checkbox" class="checkbox" name="bbpress-are-you-sure" id="bbpress-are-you-sure" value="1" /> <?php esc_html_e( 'This process cannot be undone.', 'bbpress' ); ?></label> 
  1170. <p class="description"><?php esc_html_e( 'Human sacrifice, dogs and cats living together... mass hysteria!', 'bbpress' ); ?></p> 
  1171. </fieldset> 
  1172. </td> 
  1173. </tr> 
  1174. </tbody> 
  1175. </table> 
  1176.  
  1177. <fieldset class="submit"> 
  1178. <input class="button-primary" type="submit" name="submit" value="<?php esc_attr_e( 'Reset bbPress', 'bbpress' ); ?>" /> 
  1179. <?php wp_nonce_field( 'bbpress-reset' ); ?> 
  1180. </fieldset> 
  1181. </form> 
  1182. </div> 
  1183.  
  1184. <?php 
  1185.  
  1186. /** 
  1187. * Handle the processing and feedback of the admin tools page 
  1188. * 
  1189. * @since bbPress (r2613) 
  1190. * 
  1191. * @uses check_admin_referer() To verify the nonce and the referer 
  1192. * @uses wp_cache_flush() To flush the cache 
  1193. */ 
  1194. function bbp_admin_reset_handler() { 
  1195.  
  1196. // Bail if not resetting 
  1197. if ( ! bbp_is_post_request() || empty( $_POST['bbpress-are-you-sure'] ) ) 
  1198. return; 
  1199.  
  1200. // Only keymasters can proceed 
  1201. if ( ! bbp_is_user_keymaster() ) 
  1202. return; 
  1203.  
  1204. check_admin_referer( 'bbpress-reset' ); 
  1205.  
  1206. global $wpdb; 
  1207.  
  1208. // Stores messages 
  1209. $messages = array(); 
  1210. $failed = __( 'Failed', 'bbpress' ); 
  1211. $success = __( 'Success!', 'bbpress' ); 
  1212.  
  1213. // Flush the cache; things are about to get ugly. 
  1214. wp_cache_flush(); 
  1215.  
  1216. /** Posts *****************************************************************/ 
  1217.  
  1218. $statement = __( 'Deleting Posts… %s', 'bbpress' ); 
  1219. $sql_posts = $wpdb->get_results( "SELECT `ID` FROM `{$wpdb->posts}` WHERE `post_type` IN ('forum', 'topic', 'reply')", OBJECT_K ); 
  1220. $sql_delete = "DELETE FROM `{$wpdb->posts}` WHERE `post_type` IN ('forum', 'topic', 'reply')"; 
  1221. $result = is_wp_error( $wpdb->query( $sql_delete ) ) ? $failed : $success; 
  1222. $messages[] = sprintf( $statement, $result ); 
  1223.  
  1224. /** Post Meta *************************************************************/ 
  1225.  
  1226. if ( !empty( $sql_posts ) ) { 
  1227. $sql_meta = array(); 
  1228. foreach ( $sql_posts as $key => $value ) { 
  1229. $sql_meta[] = $key; 
  1230. $statement = __( 'Deleting Post Meta… %s', 'bbpress' ); 
  1231. $sql_meta = implode( "', '", $sql_meta ); 
  1232. $sql_delete = "DELETE FROM `{$wpdb->postmeta}` WHERE `post_id` IN ('{$sql_meta}');"; 
  1233. $result = is_wp_error( $wpdb->query( $sql_delete ) ) ? $failed : $success; 
  1234. $messages[] = sprintf( $statement, $result ); 
  1235.  
  1236. /** Topic Tags ************************************************************/ 
  1237.  
  1238. $statement = __( 'Deleting Topic Tags… %s', 'bbpress' ); 
  1239. $sql_delete = "DELETE a, b, c FROM `{$wpdb->terms}` AS a LEFT JOIN `{$wpdb->term_taxonomy}` AS c ON a.term_id = c.term_id LEFT JOIN `{$wpdb->term_relationships}` AS b ON b.term_taxonomy_id = c.term_taxonomy_id WHERE c.taxonomy = 'topic-tag';"; 
  1240. $result = is_wp_error( $wpdb->query( $sql_delete ) ) ? $failed : $success; 
  1241. $messages[] = sprintf( $statement, $result ); 
  1242.  
  1243. /** User ******************************************************************/ 
  1244.  
  1245. // Delete users 
  1246. if ( !empty( $_POST['bbpress-delete-imported-users'] ) ) { 
  1247. $sql_users = $wpdb->get_results( "SELECT `user_id` FROM `{$wpdb->usermeta}` WHERE `meta_key` = '_bbp_user_id'", OBJECT_K ); 
  1248. if ( !empty( $sql_users ) ) { 
  1249. $sql_meta = array(); 
  1250. foreach ( $sql_users as $key => $value ) { 
  1251. $sql_meta[] = $key; 
  1252. $statement = __( 'Deleting User… %s', 'bbpress' ); 
  1253. $sql_meta = implode( "', '", $sql_meta ); 
  1254. $sql_delete = "DELETE FROM `{$wpdb->users}` WHERE `ID` IN ('{$sql_meta}');"; 
  1255. $result = is_wp_error( $wpdb->query( $sql_delete ) ) ? $failed : $success; 
  1256. $messages[] = sprintf( $statement, $result ); 
  1257. $statement = __( 'Deleting User Meta… %s', 'bbpress' ); 
  1258. $sql_delete = "DELETE FROM `{$wpdb->usermeta}` WHERE `user_id` IN ('{$sql_meta}');"; 
  1259. $result = is_wp_error( $wpdb->query( $sql_delete ) ) ? $failed : $success; 
  1260. $messages[] = sprintf( $statement, $result ); 
  1261.  
  1262. // Delete imported user metadata 
  1263. } else { 
  1264. $statement = __( 'Deleting User Meta… %s', 'bbpress' ); 
  1265. $sql_delete = "DELETE FROM `{$wpdb->usermeta}` WHERE `meta_key` LIKE '%%_bbp_%%';"; 
  1266. $result = is_wp_error( $wpdb->query( $sql_delete ) ) ? $failed : $success; 
  1267. $messages[] = sprintf( $statement, $result ); 
  1268.  
  1269. /** Converter *************************************************************/ 
  1270.  
  1271. $statement = __( 'Deleting Conversion Table… %s', 'bbpress' ); 
  1272. $table_name = $wpdb->prefix . 'bbp_converter_translator'; 
  1273. if ( $wpdb->get_var( "SHOW TABLES LIKE '{$table_name}'" ) === $table_name ) { 
  1274. $wpdb->query( "DROP TABLE {$table_name}" ); 
  1275. $result = $success; 
  1276. } else { 
  1277. $result = $failed; 
  1278. $messages[] = sprintf( $statement, $result ); 
  1279.  
  1280. /** Options ***************************************************************/ 
  1281.  
  1282. $statement = __( 'Deleting Settings… %s', 'bbpress' ); 
  1283. bbp_delete_options(); 
  1284. $messages[] = sprintf( $statement, $success ); 
  1285.  
  1286. /** Roles *****************************************************************/ 
  1287.  
  1288. $statement = __( 'Deleting Roles and Capabilities… %s', 'bbpress' ); 
  1289. remove_role( bbp_get_moderator_role() ); 
  1290. remove_role( bbp_get_participant_role() ); 
  1291. bbp_remove_caps(); 
  1292. $messages[] = sprintf( $statement, $success ); 
  1293.  
  1294. /** Output ****************************************************************/ 
  1295.  
  1296. if ( count( $messages ) ) { 
  1297. foreach ( $messages as $message ) { 
  1298. bbp_admin_tools_feedback( $message ); 
.