PluginProbe
bbPress / 2.6.6
bbPress v2.6.6
trunk 2.0 2.0-beta-1 2.0-beta-2b 2.0-beta-3 2.0-beta-3b 2.0-rc-2 2.0-rc-3 2.0-rc-4 2.0-rc-5 2.0.1 2.0.2 2.0.3 2.1 2.1-beta-1 2.1-rc1 2.1-rc2 2.1-rc3 2.1-rc4 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.2.2 All 71 releases
bbpress / includes / admin / tools / repair.php

repair.php in bbPress 2.6.6, at includes/admin/tools/repair.php

1,307 lines 45.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * bbPress Admin Repair Page
5 *
6 * @package bbPress
7 * @subpackage Administration
8 */
9
10 // Exit if accessed directly
11 defined( 'ABSPATH' ) || exit;
12
13 /**
14 * Admin repair page
15 *
16 * @since 2.0.0 bbPress (r2613) Converted from bbPress 1.2
17 * @since 2.6.0 bbPress (r5885) Upgraded to list-table UI
18 *
19 * @todo Use a real list table
20 *
21 */
22 function bbp_admin_repair_page() {
23
24 // Get the registered repair tools
25 $tools = bbp_admin_repair_list();
26
27 // Orderby
28 $orderby = ! empty( $_GET['orderby'] )
29 ? sanitize_key( $_GET['orderby'] )
30 : 'priority';
31
32 // Order
33 $order = ! empty( $_GET['order'] ) && in_array( strtolower( $_GET['order'] ), array( 'asc', 'desc' ), true )
34 ? strtolower( $_GET['order'] )
35 : 'asc';
36
37 // New order
38 $new_order = ( 'desc' === $order )
39 ? 'asc'
40 : 'desc'; ?>
41
42 <div class="wrap">
43 <h1 class="wp-heading-inline"><?php esc_html_e( 'Forum Tools', 'bbpress' ); ?></h1>
44 <hr class="wp-header-end">
45 <h2 class="nav-tab-wrapper"><?php bbp_tools_admin_tabs( 'bbp-repair' ); ?></h2>
46
47 <p><?php esc_html_e( 'bbPress keeps track of relationships between forums, topics, replies, topic-tags, favorites, subscriptions, 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>
48 <p class="description"><?php esc_html_e( 'Some of these tools create substantial database overhead. Use caution when running more than 1 repair at a time.', 'bbpress' ); ?></p>
49
50 <?php bbp_admin_repair_tool_status_filters(); ?>
51
52 <form class="settings" method="get" action="">
53
54 <?php bbp_admin_repair_list_search_form(); ?>
55
56 <input type="hidden" name="page" value="bbp-repair" />
57 <?php wp_nonce_field( 'bbpress-do-counts' ); ?>
58
59 <div class="tablenav top">
60 <div class="alignleft actions bulkactions">
61 <label for="bulk-action-selector-top" class="screen-reader-text"><?php esc_html_e( 'Select bulk action', 'bbpress' ); ?></label>
62 <select name="action" id="bulk-action-selector-top">
63 <option value="" selected="selected"><?php esc_html_e( 'Bulk Actions', 'bbpress' ); ?></option>
64 <option value="run" class="hide-if-no-js"><?php esc_html_e( 'Run', 'bbpress' ); ?></option>
65 </select>
66 <input type="submit" id="doaction" class="button action" value="<?php esc_attr_e( 'Apply', 'bbpress' ); ?>">
67 </div>
68 <div class="alignleft actions">
69
70 <?php bbp_admin_repair_list_components_filter(); ?>
71
72 <input type="submit" name="filter_action" id="components-submit" class="button" value="<?php esc_html_e( 'Filter', 'bbpress' ); ?>">
73 </div>
74 <br class="clear">
75 </div>
76 <table class="wp-list-table widefat striped posts">
77 <thead>
78 <tr>
79 <td id="cb" class="manage-column column-cb check-column">
80 <label class="screen-reader-text" for="cb-select-all-1">
81 <?php esc_html_e( 'Select All', 'bbpress' ); ?>
82 </label>
83 <input id="cb-select-all-1" type="checkbox">
84 </td>
85 <th scope="col" id="description" class="manage-column column-primary column-description sortable <?php echo ( 'priority' === $orderby ) ? esc_attr( $order ) : 'asc'; ?>">
86 <a href="<?php echo esc_url( bbp_get_admin_repair_tool_page_url( array(
87 'orderby' => 'priority',
88 'order' => $new_order
89 ) ) ); ?>"><span><?php esc_html_e( 'Description', 'bbpress' ); ?></span><span class="sorting-indicator"></span>
90 </a>
91 </th>
92 <th scope="col" id="components" class="manage-column column-components">
93 <span><?php esc_html_e( 'Components', 'bbpress' ); ?></span>
94 </th>
95 <th scope="col" id="overhead" class="manage-column column-overhead sortable <?php echo ( 'overhead' === $orderby ) ? esc_attr( $order ) : 'asc'; ?>">
96 <a href="<?php echo esc_url( bbp_get_admin_repair_tool_page_url( array(
97 'orderby' => 'overhead',
98 'order' => $new_order
99 ) ) ); ?>"><span><?php esc_html_e( 'Overhead', 'bbpress' ); ?></span><span class="sorting-indicator"></span>
100 </a>
101 </th>
102 </tr>
103 </thead>
104
105 <tbody id="the-list">
106
107 <?php if ( ! empty( $tools ) ) : ?>
108
109 <?php foreach ( $tools as $item ) : ?>
110
111 <tr id="bbp-repair-tools" class="inactive">
112 <th scope="row" class="check-column">
113 <label class="screen-reader-text" for="<?php echo esc_attr( str_replace( '_', '-', $item['id'] ) ); ?>"></label>
114 <input type="checkbox" name="checked[]" value="<?php echo esc_attr( $item['id'] ); ?>" id="<?php echo esc_attr( str_replace( '_', '-', $item['id'] ) ); ?>">
115 </th>
116 <td class="bbp-tool-title column-primary column-description" data-colname="<?php esc_html_e( 'Description', 'bbpress' ); ?>">
117 <strong><?php echo esc_html( $item['title'] ); ?></strong><?php
118
119 // Optional description
120 if ( ! empty( $item['description'] ) ) :
121 echo '<p class="description">' . esc_html( $item['description'] ) . '</p>';
122 endif;
123
124 ?><div class="row-actions hide-if-no-js">
125 <span class="run">
126 <a href="<?php bbp_admin_repair_tool_run_url( $item ); ?>" aria-label="<?php printf( esc_html__( 'Run %s', 'bbpress' ), $item['title'] ); ?>" id="<?php echo esc_attr( $item['id'] ); ?>" ><?php esc_html_e( 'Run', 'bbpress' ); ?></a>
127 </span>
128 </div>
129 <button type="button" class="toggle-row">
130 <span class="screen-reader-text"><?php esc_html_e( 'Show more details', 'bbpress' ); ?></span>
131 </button>
132 </td>
133 <td class="column-components desc" data-colname="<?php esc_html_e( 'Components', 'bbpress' ); ?>">
134 <div class="bbp-tool-overhead">
135
136 <?php echo implode( ', ', bbp_get_admin_repair_tool_components( $item ) ); ?>
137
138 </div>
139 </td>
140 <td class="column-overhead desc" data-colname="<?php esc_html_e( 'Overhead', 'bbpress' ); ?>">
141 <div class="bbp-tool-overhead">
142
143 <?php echo implode( ', ', bbp_get_admin_repair_tool_overhead( $item ) ); ?>
144
145 </div>
146 </td>
147 </tr>
148
149 <?php endforeach; ?>
150
151 <?php else : ?>
152
153 <tr>
154 <td colspan="4">
155 <?php esc_html_e( 'No repair tools match this criteria.', 'bbpress' ); ?>
156 </td>
157 </tr>
158
159 <?php endif; ?>
160
161 </tbody>
162 <tfoot>
163 <tr>
164 <td class="manage-column column-cb check-column">
165 <label class="screen-reader-text" for="cb-select-all-2">
166 <?php esc_html_e( 'Select All', 'bbpress' ); ?>
167 </label>
168 <input id="cb-select-all-2" type="checkbox">
169 </td>
170 <th scope="col" class="manage-column column-primary column-description"><?php esc_html_e( 'Description', 'bbpress' ); ?></th>
171 <th scope="col" class="manage-column column-components"><?php esc_html_e( 'Components', 'bbpress' ); ?></th>
172 <th scope="col" class="manage-column column-overhead"><?php esc_html_e( 'Overhead', 'bbpress' ); ?></th>
173 </tr>
174 </tfoot>
175 </table>
176 <div class="tablenav bottom">
177 <div class="alignleft actions bulkactions">
178 <label for="bulk-action-selector-bottom" class="screen-reader-text"><?php esc_html_e( 'Select bulk action', 'bbpress' ); ?></label>
179 <select name="action2" id="bulk-action-selector-bottom">
180 <option value="" selected="selected"><?php esc_html_e( 'Bulk Actions', 'bbpress' ); ?></option>
181 <option value="run" class="hide-if-no-js"><?php esc_html_e( 'Run', 'bbpress' ); ?></option>
182 </select>
183 <input type="submit" id="doaction2" class="button action" value="<?php esc_attr_e( 'Apply', 'bbpress' ); ?>">
184 </div>
185 </div>
186 </form>
187 </div>
188
189 <?php
190 }
191
192 /**
193 * Recount topic replies
194 *
195 * @since 2.0.0 bbPress (r2613)
196 *
197 * @return array An array of the status code and the message
198 */
199 function bbp_admin_repair_topic_reply_count() {
200
201 // Define variables
202 $bbp_db = bbp_db();
203 $statement = esc_html__( 'Counting the number of replies in each topic&hellip; %s', 'bbpress' );
204 $result = esc_html__( 'Failed!', 'bbpress' );
205
206 // Post types and status
207 $tpt = bbp_get_topic_post_type();
208 $rpt = bbp_get_reply_post_type();
209 $pps = bbp_get_public_status_id();
210 $cps = bbp_get_closed_status_id();
211
212 // Delete the meta key _bbp_reply_count for each topic
213 $sql_delete = "DELETE `postmeta` FROM `{$bbp_db->postmeta}` AS `postmeta`
214 LEFT JOIN `{$bbp_db->posts}` AS `posts` ON `posts`.`ID` = `postmeta`.`post_id`
215 WHERE `posts`.`post_type` = '{$tpt}'
216 AND `postmeta`.`meta_key` = '_bbp_reply_count'";
217
218 if ( is_wp_error( $bbp_db->query( $sql_delete ) ) ) {
219 return array( 1, sprintf( $statement, $result ) );
220 }
221
222 // Recalculate the meta key _bbp_reply_count for each topic
223 $sql = "INSERT INTO `{$bbp_db->postmeta}` (`post_id`, `meta_key`, `meta_value`) (
224 SELECT `topics`.`ID` AS `post_id`, '_bbp_reply_count' AS `meta_key`, COUNT(`replies`.`ID`) As `meta_value`
225 FROM `{$bbp_db->posts}` AS `topics`
226 LEFT JOIN `{$bbp_db->posts}` as `replies`
227 ON `replies`.`post_parent` = `topics`.`ID`
228 AND `replies`.`post_status` = '{$pps}'
229 AND `replies`.`post_type` = '{$rpt}'
230 WHERE `topics`.`post_type` = '{$tpt}'
231 AND `topics`.`post_status` IN ( '{$pps}', '{$cps}' )
232 GROUP BY `topics`.`ID`)";
233
234 if ( is_wp_error( $bbp_db->query( $sql ) ) ) {
235 return array( 2, sprintf( $statement, $result ) );
236 }
237
238 return array( 0, sprintf( $statement, esc_html__( 'Complete!', 'bbpress' ) ) );
239 }
240
241 /**
242 * Recount topic voices
243 *
244 * @since 2.0.0 bbPress (r2613)
245 *
246 * @return array An array of the status code and the message
247 */
248 function bbp_admin_repair_topic_voice_count() {
249
250 // Define variables
251 $bbp_db = bbp_db();
252 $statement = esc_html__( 'Counting the number of voices in each topic&hellip; %s', 'bbpress' );
253 $result = esc_html__( 'Failed!', 'bbpress' );
254
255 $sql_delete = "DELETE FROM {$bbp_db->postmeta} WHERE meta_key IN ('_bbp_voice_count', '_bbp_engagement')";
256 if ( is_wp_error( $bbp_db->query( $sql_delete ) ) ) {
257 return array( 1, sprintf( $statement, $result ) );
258 }
259
260 // Post types and status
261 $tpt = bbp_get_topic_post_type();
262 $rpt = bbp_get_reply_post_type();
263 $pps = bbp_get_public_status_id();
264 $cps = bbp_get_closed_status_id();
265
266 $engagements_sql = $bbp_db->prepare( "INSERT INTO {$bbp_db->postmeta} (post_id, meta_key, meta_value) (
267 SELECT postmeta.meta_value, '_bbp_engagement', posts.post_author
268 FROM {$bbp_db->posts} AS posts
269 LEFT JOIN {$bbp_db->postmeta} AS postmeta
270 ON posts.ID = postmeta.post_id
271 AND postmeta.meta_key = '_bbp_topic_id'
272 WHERE posts.post_type IN (%s, %s)
273 AND posts.post_status IN (%s, %s)
274 GROUP BY postmeta.meta_value, posts.post_author)", $tpt, $rpt, $pps, $cps );
275
276 if ( is_wp_error( $bbp_db->query( $engagements_sql ) ) ) {
277 return array( 2, sprintf( $statement, $result ) );
278 }
279
280 $voice_count_sql = "INSERT INTO {$bbp_db->postmeta} (post_id, meta_key, meta_value) (
281 SELECT post_id, '_bbp_voice_count', COUNT(DISTINCT meta_value)
282 FROM {$bbp_db->postmeta}
283 WHERE meta_key = '_bbp_engagement'
284 GROUP BY post_id)";
285
286 if ( is_wp_error( $bbp_db->query( $voice_count_sql ) ) ) {
287 return array( 3, sprintf( $statement, $result ) );
288 }
289
290 return array( 0, sprintf( $statement, esc_html__( 'Complete!', 'bbpress' ) ) );
291 }
292
293 /**
294 * Recount non-public replies per topic (pending/spammed/trashed)
295 *
296 * @since 2.0.0 bbPress (r2747)
297 *
298 * @return array An array of the status code and the message
299 */
300 function bbp_admin_repair_topic_hidden_reply_count() {
301
302 // Define variables
303 $bbp_db = bbp_db();
304 $statement = esc_html__( 'Counting the number of pending, spammed, and trashed replies in each topic&hellip; %s', 'bbpress' );
305 $result = esc_html__( 'Failed!', 'bbpress' );
306
307 $sql_delete = "DELETE FROM `{$bbp_db->postmeta}` WHERE `meta_key` = '_bbp_reply_count_hidden'";
308 if ( is_wp_error( $bbp_db->query( $sql_delete ) ) ) {
309 return array( 1, sprintf( $statement, $result ) );
310 }
311
312 // Post types and status
313 $rpt = bbp_get_reply_post_type();
314 $sta = bbp_get_non_public_topic_statuses();
315
316 // SQL
317 $sql_status = "'" . implode( "','", $sta ) . "'";
318
319 $sql = "INSERT INTO `{$bbp_db->postmeta}` (`post_id`, `meta_key`, `meta_value`) (SELECT `post_parent`, '_bbp_reply_count_hidden', COUNT(`post_status`) as `meta_value` FROM `{$bbp_db->posts}` WHERE `post_type` = '{$rpt}' AND `post_status` IN ({$sql_status}) GROUP BY `post_parent`)";
320 if ( is_wp_error( $bbp_db->query( $sql ) ) ) {
321 return array( 2, sprintf( $statement, $result ) );
322 }
323
324 return array( 0, sprintf( $statement, esc_html__( 'Complete!', 'bbpress' ) ) );
325 }
326
327 /**
328 * Recount forum topics
329 *
330 * @since 2.0.0 bbPress (r2613)
331 *
332 * @return array An array of the status code and the message
333 */
334 function bbp_admin_repair_forum_topic_count() {
335
336 // Define variables
337 $bbp_db = bbp_db();
338 $statement = esc_html__( 'Counting the number of topics in each forum&hellip; %s', 'bbpress' );
339 $result = esc_html__( 'Failed!', 'bbpress' );
340
341 $sql_delete = "DELETE FROM {$bbp_db->postmeta} WHERE meta_key IN ( '_bbp_topic_count', '_bbp_total_topic_count', '_bbp_topic_count_hidden' )";
342 if ( is_wp_error( $bbp_db->query( $sql_delete ) ) ) {
343 return array( 1, sprintf( $statement, $result ) );
344 }
345
346 $forums = get_posts( array( 'post_type' => bbp_get_forum_post_type(), 'numberposts' => -1 ) );
347 if ( ! empty( $forums ) ) {
348 foreach ( $forums as $forum ) {
349 bbp_update_forum_topic_count( $forum->ID );
350 bbp_update_forum_topic_count_hidden( $forum->ID );
351 }
352 } else {
353 return array( 2, sprintf( $statement, $result ) );
354 }
355
356 return array( 0, sprintf( $statement, esc_html__( 'Complete!', 'bbpress' ) ) );
357 }
358
359 /**
360 * Recount topic in each topic-tag
361 *
362 * @since 2.6.0 bbPress (r6256)
363 *
364 * @return array An array of the status code and the message
365 */
366 function bbp_admin_repair_topic_tag_count() {
367
368 // Define variables
369 $statement = esc_html__( 'Counting the number of topics in each topic-tag&hellip; %s', 'bbpress' );
370 $result = esc_html__( 'Failed!', 'bbpress' );
371 $tax_id = bbp_get_topic_tag_tax_id();
372 $terms = get_terms( $tax_id, array( 'hide_empty' => false ) );
373 $tt_ids = wp_list_pluck( $terms, 'term_taxonomy_id' );
374 $ints = array_map( 'intval', $tt_ids );
375 $taxonomy = get_taxonomy( $tax_id );
376
377 // Bail if taxonomy does not exist
378 if ( empty( $taxonomy ) ) {
379 return array( 1, sprintf( $statement, $result ) );
380 }
381
382 // Custom callback
383 if ( ! empty( $taxonomy->update_count_callback ) ) {
384
385 // Bail if callback is not callable
386 if ( ! is_callable( $taxonomy->update_count_callback ) ) {
387 return array( 1, sprintf( $statement, $result ) );
388 }
389
390 call_user_func( $taxonomy->update_count_callback, $ints, $taxonomy );
391
392 // Generic callback fallback
393 } else {
394 _update_post_term_count( $ints, $taxonomy );
395 }
396
397 // Bust the cache
398 clean_term_cache( $ints, '', false );
399
400 return array( 0, sprintf( $statement, esc_html__( 'Complete!', 'bbpress' ) ) );
401 }
402
403 /**
404 * Recount forum replies
405 *
406 * @since 2.0.0 bbPress (r2613)
407 *
408 * @return array An array of the status code and the message
409 */
410 function bbp_admin_repair_forum_reply_count() {
411
412 // Define variables
413 $bbp_db = bbp_db();
414 $statement = esc_html__( 'Counting the number of replies in each forum&hellip; %s', 'bbpress' );
415 $result = esc_html__( 'Failed!', 'bbpress' );
416
417 // Post type
418 $fpt = bbp_get_forum_post_type();
419
420 // Delete the meta keys _bbp_reply_count and _bbp_total_reply_count for each forum
421 $sql_delete = "DELETE `postmeta` FROM `{$bbp_db->postmeta}` AS `postmeta`
422 LEFT JOIN `{$bbp_db->posts}` AS `posts` ON `posts`.`ID` = `postmeta`.`post_id`
423 WHERE `posts`.`post_type` = '{$fpt}'
424 AND `postmeta`.`meta_key` = '_bbp_reply_count'
425 OR `postmeta`.`meta_key` = '_bbp_total_reply_count'";
426
427 if ( is_wp_error( $bbp_db->query( $sql_delete ) ) ) {
428 return array( 1, sprintf( $statement, $result ) );
429 }
430
431 // Recalculate the metas key _bbp_reply_count and _bbp_total_reply_count for each forum
432 $forums = get_posts( array( 'post_type' => bbp_get_forum_post_type(), 'numberposts' => -1 ) );
433 if ( ! empty( $forums ) ) {
434 foreach ( $forums as $forum ) {
435 bbp_update_forum_reply_count( $forum->ID );
436 }
437 } else {
438 return array( 2, sprintf( $statement, $result ) );
439 }
440
441 return array( 0, sprintf( $statement, esc_html__( 'Complete!', 'bbpress' ) ) );
442 }
443
444 /**
445 * Recount non-public forum replies
446 *
447 * @since 2.6.0 bbPress (r6922)
448 * @since 2.6.0 bbPress (r6932) Rename to match the topic reply recount function
449 *
450 * @return array An array of the status code and the message
451 */
452 function bbp_admin_repair_forum_hidden_reply_count() {
453
454 // Define variables
455 $bbp_db = bbp_db();
456 $statement = esc_html__( 'Counting the number of pending, spammed, and trashed replies in each forum&hellip; %s', 'bbpress' );
457 $result = esc_html__( 'Failed!', 'bbpress' );
458
459 // Post type
460 $fpt = bbp_get_forum_post_type();
461
462 // Delete the meta keys _bbp_reply_count and _bbp_total_reply_count for each forum
463 $sql_delete = "DELETE `postmeta` FROM `{$bbp_db->postmeta}` AS `postmeta`
464 LEFT JOIN `{$bbp_db->posts}` AS `posts` ON `posts`.`ID` = `postmeta`.`post_id`
465 WHERE `posts`.`post_type` = '{$fpt}'
466 AND `postmeta`.`meta_key` = '_bbp_reply_count_hidden'
467 OR `postmeta`.`meta_key` = '_bbp_total_reply_count_hidden'";
468
469 if ( is_wp_error( $bbp_db->query( $sql_delete ) ) ) {
470 return array( 1, sprintf( $statement, $result ) );
471 }
472
473 // Recalculate the metas key _bbp_reply_count and _bbp_total_reply_count for each forum
474 $forums = get_posts( array( 'post_type' => bbp_get_forum_post_type(), 'numberposts' => -1 ) );
475 if ( ! empty( $forums ) ) {
476 foreach ( $forums as $forum ) {
477 bbp_update_forum_reply_count_hidden( $forum->ID );
478 }
479 } else {
480 return array( 2, sprintf( $statement, $result ) );
481 }
482
483 return array( 0, sprintf( $statement, esc_html__( 'Complete!', 'bbpress' ) ) );
484 }
485
486 /**
487 * Recount topics by the users
488 *
489 * @since 2.1.0 bbPress (r3889)
490 *
491 * @return array An array of the status code and the message
492 */
493 function bbp_admin_repair_user_topic_count() {
494
495 // Define variables
496 $bbp_db = bbp_db();
497 $statement = esc_html__( 'Counting the number of topics each user has created&hellip; %s', 'bbpress' );
498 $result = esc_html__( 'Failed!', 'bbpress' );
499
500 $sql_select = "SELECT `post_author`, COUNT(DISTINCT `ID`) as `_count` FROM `{$bbp_db->posts}` WHERE `post_type` = '" . bbp_get_topic_post_type() . "' AND `post_status` = '" . bbp_get_public_status_id() . "' GROUP BY `post_author`";
501 $insert_rows = $bbp_db->get_results( $sql_select );
502
503 if ( is_wp_error( $insert_rows ) ) {
504 return array( 1, sprintf( $statement, $result ) );
505 }
506
507 $key = $bbp_db->prefix . '_bbp_topic_count';
508 $insert_values = array();
509 foreach ( $insert_rows as $insert_row ) {
510 $insert_values[] = "('{$insert_row->post_author}', '{$key}', '{$insert_row->_count}')";
511 }
512
513 if ( !count( $insert_values ) ) {
514 return array( 2, sprintf( $statement, $result ) );
515 }
516
517 $sql_delete = "DELETE FROM `{$bbp_db->usermeta}` WHERE `meta_key` = '{$key}'";
518 if ( is_wp_error( $bbp_db->query( $sql_delete ) ) ) {
519 return array( 3, sprintf( $statement, $result ) );
520 }
521
522 foreach ( array_chunk( $insert_values, 10000 ) as $chunk ) {
523 $chunk = "\n" . implode( ",\n", $chunk );
524 $sql_insert = "INSERT INTO `{$bbp_db->usermeta}` (`user_id`, `meta_key`, `meta_value`) VALUES {$chunk}";
525
526 if ( is_wp_error( $bbp_db->query( $sql_insert ) ) ) {
527 return array( 4, sprintf( $statement, $result ) );
528 }
529 }
530
531 return array( 0, sprintf( $statement, esc_html__( 'Complete!', 'bbpress' ) ) );
532 }
533
534 /**
535 * Recount topic replied by the users
536 *
537 * @since 2.0.0 bbPress (r2613)
538 *
539 * @return array An array of the status code and the message
540 */
541 function bbp_admin_repair_user_reply_count() {
542
543 // Define variables
544 $bbp_db = bbp_db();
545 $statement = esc_html__( 'Counting the number of topics to which each user has replied&hellip; %s', 'bbpress' );
546 $result = esc_html__( 'Failed!', 'bbpress' );
547
548 $sql_select = "SELECT `post_author`, COUNT(DISTINCT `ID`) as `_count` FROM `{$bbp_db->posts}` WHERE `post_type` = '" . bbp_get_reply_post_type() . "' AND `post_status` = '" . bbp_get_public_status_id() . "' GROUP BY `post_author`";
549 $insert_rows = $bbp_db->get_results( $sql_select );
550
551 if ( is_wp_error( $insert_rows ) ) {
552 return array( 1, sprintf( $statement, $result ) );
553 }
554
555 $key = $bbp_db->prefix . '_bbp_reply_count';
556 $insert_values = array();
557 foreach ( $insert_rows as $insert_row ) {
558 $insert_values[] = "('{$insert_row->post_author}', '{$key}', '{$insert_row->_count}')";
559 }
560
561 if ( !count( $insert_values ) ) {
562 return array( 2, sprintf( $statement, $result ) );
563 }
564
565 $sql_delete = "DELETE FROM `{$bbp_db->usermeta}` WHERE `meta_key` = '{$key}'";
566 if ( is_wp_error( $bbp_db->query( $sql_delete ) ) ) {
567 return array( 3, sprintf( $statement, $result ) );
568 }
569
570 foreach ( array_chunk( $insert_values, 10000 ) as $chunk ) {
571 $chunk = "\n" . implode( ",\n", $chunk );
572 $sql_insert = "INSERT INTO `{$bbp_db->usermeta}` (`user_id`, `meta_key`, `meta_value`) VALUES {$chunk}";
573
574 if ( is_wp_error( $bbp_db->query( $sql_insert ) ) ) {
575 return array( 4, sprintf( $statement, $result ) );
576 }
577 }
578
579 return array( 0, sprintf( $statement, esc_html__( 'Complete!', 'bbpress' ) ) );
580 }
581
582 /**
583 * Repair user favorites
584 *
585 * @since 2.0.0 bbPress (r2613)
586 *
587 * @return array An array of the status code and the message
588 */
589 function bbp_admin_repair_user_favorites() {
590
591 // Define variables
592 $bbp_db = bbp_db();
593 $statement = esc_html__( 'Removing unpublished topics from user favorites&hellip; %s', 'bbpress' );
594 $result = esc_html__( 'Failed!', 'bbpress' );
595
596 // Query for users with favorites
597 $key = $bbp_db->prefix . '_bbp_favorites';
598 $users = $bbp_db->get_results( "SELECT `user_id`, `meta_value` AS `favorites` FROM `{$bbp_db->usermeta}` WHERE `meta_key` = '{$key}'" );
599
600 if ( is_wp_error( $users ) ) {
601 return array( 1, sprintf( $statement, $result ) );
602 }
603
604 $topics = $bbp_db->get_col( "SELECT `ID` FROM `{$bbp_db->posts}` WHERE `post_type` = '" . bbp_get_topic_post_type() . "' AND `post_status` = '" . bbp_get_public_status_id() . "'" );
605
606 if ( is_wp_error( $topics ) ) {
607 return array( 2, sprintf( $statement, $result ) );
608 }
609
610 $values = array();
611 foreach ( $users as $user ) {
612 if ( empty( $user->favorites ) || ! is_string( $user->favorites ) ) {
613 continue;
614 }
615
616 $favorites = array_intersect( $topics, explode( ',', $user->favorites ) );
617 if ( empty( $favorites ) || ! is_array( $favorites ) ) {
618 continue;
619 }
620
621 $favorites_joined = implode( ',', $favorites );
622 $values[] = "('{$user->user_id}', '{$key}', '{$favorites_joined}')";
623
624 // Cleanup
625 unset( $favorites, $favorites_joined );
626 }
627
628 if ( !count( $values ) ) {
629 $result = esc_html__( 'Nothing to remove!', 'bbpress' );
630 return array( 0, sprintf( $statement, $result ) );
631 }
632
633 $sql_delete = "DELETE FROM `{$bbp_db->usermeta}` WHERE `meta_key` = '{$key}'";
634 if ( is_wp_error( $bbp_db->query( $sql_delete ) ) ) {
635 return array( 4, sprintf( $statement, $result ) );
636 }
637
638 foreach ( array_chunk( $values, 10000 ) as $chunk ) {
639 $chunk = "\n" . implode( ",\n", $chunk );
640 $sql_insert = "INSERT INTO `{$bbp_db->usermeta}` (`user_id`, `meta_key`, `meta_value`) VALUES {$chunk}";
641 if ( is_wp_error( $bbp_db->query( $sql_insert ) ) ) {
642 return array( 5, sprintf( $statement, $result ) );
643 }
644 }
645
646 return array( 0, sprintf( $statement, esc_html__( 'Complete!', 'bbpress' ) ) );
647 }
648
649 /**
650 * Clean the user topic subscriptions
651 *
652 * @since 2.0.0 bbPress (r2668)
653 *
654 * @return array An array of the status code and the message
655 */
656 function bbp_admin_repair_user_topic_subscriptions() {
657
658 // Define variables
659 $bbp_db = bbp_db();
660 $statement = esc_html__( 'Removing trashed topics from user subscriptions&hellip; %s', 'bbpress' );
661 $result = esc_html__( 'Failed!', 'bbpress' );
662
663 $key = $bbp_db->prefix . '_bbp_subscriptions';
664 $users = $bbp_db->get_results( "SELECT `user_id`, `meta_value` AS `subscriptions` FROM `{$bbp_db->usermeta}` WHERE `meta_key` = '{$key}'" );
665
666 if ( is_wp_error( $users ) ) {
667 return array( 1, sprintf( $statement, $result ) );
668 }
669
670 $topics = $bbp_db->get_col( "SELECT `ID` FROM `{$bbp_db->posts}` WHERE `post_type` = '" . bbp_get_topic_post_type() . "' AND `post_status` = '" . bbp_get_public_status_id() . "'" );
671 if ( is_wp_error( $topics ) ) {
672 return array( 2, sprintf( $statement, $result ) );
673 }
674
675 $values = array();
676 foreach ( $users as $user ) {
677 if ( empty( $user->subscriptions ) || ! is_string( $user->subscriptions ) ) {
678 continue;
679 }
680
681 $subscriptions = array_intersect( $topics, explode( ',', $user->subscriptions ) );
682 if ( empty( $subscriptions ) || ! is_array( $subscriptions ) ) {
683 continue;
684 }
685
686 $subscriptions_joined = implode( ',', $subscriptions );
687 $values[] = "('{$user->user_id}', '{$key}', '{$subscriptions_joined}')";
688
689 // Cleanup
690 unset( $subscriptions, $subscriptions_joined );
691 }
692
693 if ( !count( $values ) ) {
694 $result = esc_html__( 'Nothing to remove!', 'bbpress' );
695 return array( 0, sprintf( $statement, $result ) );
696 }
697
698 $sql_delete = "DELETE FROM `{$bbp_db->usermeta}` WHERE `meta_key` = '{$key}'";
699 if ( is_wp_error( $bbp_db->query( $sql_delete ) ) ) {
700 return array( 4, sprintf( $statement, $result ) );
701 }
702
703 foreach ( array_chunk( $values, 10000 ) as $chunk ) {
704 $chunk = "\n" . implode( ",\n", $chunk );
705 $sql_insert = "INSERT INTO `{$bbp_db->usermeta}` (`user_id`, `meta_key`, `meta_value`) VALUES {$chunk}";
706 if ( is_wp_error( $bbp_db->query( $sql_insert ) ) ) {
707 return array( 5, sprintf( $statement, $result ) );
708 }
709 }
710
711 return array( 0, sprintf( $statement, esc_html__( 'Complete!', 'bbpress' ) ) );
712 }
713
714 /**
715 * Clean the user forum subscriptions
716 *
717 * @since 2.5.0 bbPress (r5155)
718 *
719 * @return array An array of the status code and the message
720 */
721 function bbp_admin_repair_user_forum_subscriptions() {
722
723 // Define variables
724 $bbp_db = bbp_db();
725 $statement = esc_html__( 'Removing trashed forums from user subscriptions&hellip; %s', 'bbpress' );
726 $result = esc_html__( 'Failed!', 'bbpress' );
727
728 $key = $bbp_db->prefix . '_bbp_forum_subscriptions';
729 $users = $bbp_db->get_results( "SELECT `user_id`, `meta_value` AS `subscriptions` FROM `{$bbp_db->usermeta}` WHERE `meta_key` = '{$key}'" );
730
731 if ( is_wp_error( $users ) ) {
732 return array( 1, sprintf( $statement, $result ) );
733 }
734
735 $forums = $bbp_db->get_col( "SELECT `ID` FROM `{$bbp_db->posts}` WHERE `post_type` = '" . bbp_get_forum_post_type() . "' AND `post_status` = '" . bbp_get_public_status_id() . "'" );
736 if ( is_wp_error( $forums ) ) {
737 return array( 2, sprintf( $statement, $result ) );
738 }
739
740 $values = array();
741 foreach ( $users as $user ) {
742 if ( empty( $user->subscriptions ) || ! is_string( $user->subscriptions ) ) {
743 continue;
744 }
745
746 $subscriptions = array_intersect( $forums, explode( ',', $user->subscriptions ) );
747 if ( empty( $subscriptions ) || ! is_array( $subscriptions ) ) {
748 continue;
749 }
750
751 $subscriptions_joined = implode( ',', $subscriptions );
752 $values[] = "('{$user->user_id}', '{$key}', '{$subscriptions_joined}')";
753
754 // Cleanup
755 unset( $subscriptions, $subscriptions_joined );
756 }
757
758 if ( !count( $values ) ) {
759 $result = esc_html__( 'Nothing to remove!', 'bbpress' );
760 return array( 0, sprintf( $statement, $result ) );
761 }
762
763 $sql_delete = "DELETE FROM `{$bbp_db->usermeta}` WHERE `meta_key` = '{$key}'";
764 if ( is_wp_error( $bbp_db->query( $sql_delete ) ) ) {
765 return array( 4, sprintf( $statement, $result ) );
766 }
767
768 foreach ( array_chunk( $values, 10000 ) as $chunk ) {
769 $chunk = "\n" . implode( ",\n", $chunk );
770 $sql_insert = "INSERT INTO `{$bbp_db->usermeta}` (`user_id`, `meta_key`, `meta_value`) VALUES {$chunk}";
771 if ( is_wp_error( $bbp_db->query( $sql_insert ) ) ) {
772 return array( 5, sprintf( $statement, $result ) );
773 }
774 }
775
776 return array( 0, sprintf( $statement, esc_html__( 'Complete!', 'bbpress' ) ) );
777 }
778
779 /**
780 * This repair tool will map each user of the current site to their respective
781 * forums role. By default, Admins will be Key Masters, and every other role
782 * will be the default role defined in Settings > Forums (Participant).
783 *
784 * @since 2.2.0 bbPress (r4340)
785 */
786 function bbp_admin_repair_user_roles() {
787
788 $statement = esc_html__( 'Remapping forum role for each user on this site&hellip; %s', 'bbpress' );
789 $changed = 0;
790 $role_map = bbp_get_user_role_map();
791 $default_role = bbp_get_default_role();
792
793 // Bail if no role map exists
794 if ( empty( $role_map ) ) {
795 return array( 1, sprintf( $statement, esc_html__( 'Failed!', 'bbpress' ) ) );
796 }
797
798 // Get non-forum roles
799 $blog_roles = array_keys( bbp_get_blog_roles() );
800
801 // Iterate through each role...
802 foreach ( $blog_roles as $role ) {
803
804 // Reset the offset
805 $offset = 0;
806
807 // If no role map exists, give the default forum role (bbp-participant)
808 $new_role = isset( $role_map[ $role ] )
809 ? $role_map[ $role ]
810 : $default_role;
811
812 // Get users of this site, limited to 1000
813 while ( $users = get_users( array(
814 'role' => $role,
815 'fields' => 'ID',
816 'number' => 1000,
817 'offset' => $offset
818 ) ) ) {
819
820 // Iterate through each user of $role and try to set it
821 foreach ( (array) $users as $user_id ) {
822 if ( bbp_set_user_role( $user_id, $new_role ) ) {
823 ++$changed; // Keep a count to display at the end
824 }
825 }
826
827 // Bump the offset for the next query iteration
828 $offset = $offset + 1000;
829 }
830 }
831
832 // Reset the offset
833 $offset = 0;
834 $bbp_db = bbp_db();
835 $cap_key = $bbp_db->get_blog_prefix() . 'capabilities';
836
837 // Users without roles should be granted the default role, but not on multi-
838 // site installations where not all users get a role by default.
839 if ( ! is_multisite() ) {
840
841 // Get users with missing capabilities on this site, limited to 1000
842 while ( $users = get_users( array(
843 'meta_key' => $cap_key,
844 'meta_compare' => 'NOT EXISTS',
845 'fields' => 'ID',
846 'number' => 1000,
847 'offset' => $offset
848 ) ) ) {
849
850 // Iterate through each user of $role and try to set it
851 foreach ( (array) $users as $user_id ) {
852 if ( bbp_set_user_role( $user_id, $default_role ) ) {
853 ++$changed; // Keep a count to display at the end
854 }
855 }
856
857 // Bump the offset for the next query iteration
858 $offset = $offset + 1000;
859 }
860
861 // On multisite, we'll look for users with an empty capabilities array.
862 // These are users who basically have malformed caps, and we can fix that.
863 } else {
864
865 // Get users with empty capabilities on this site, limited to 1000
866 while ( $users = get_users( array(
867 'meta_key' => $cap_key,
868 'meta_value' => 'a:0:{}',
869 'fields' => 'ID',
870 'number' => 1000,
871 'offset' => $offset
872 ) ) ) {
873
874 // Iterate through each user of $role and try to set it
875 foreach ( (array) $users as $user_id ) {
876 if ( bbp_set_user_role( $user_id, $default_role ) ) {
877 ++$changed; // Keep a count to display at the end
878 }
879 }
880
881 // Bump the offset for the next query iteration
882 $offset = $offset + 1000;
883 }
884 }
885
886 $result = sprintf( esc_html__( 'Complete! %s users updated.', 'bbpress' ), bbp_number_format( $changed ) );
887
888 return array( 0, sprintf( $statement, $result ) );
889 }
890
891 /**
892 * Repair the last post in every topic and forum
893 *
894 * @since 2.0.0 bbPress (r3040)
895 *
896 * @return array An array of the status code and the message
897 */
898 function bbp_admin_repair_freshness() {
899
900 // Define variables
901 $bbp_db = bbp_db();
902 $statement = esc_html__( 'Recomputing latest post in every topic and forum&hellip; %s', 'bbpress' );
903 $result = esc_html__( 'Failed!', 'bbpress' );
904
905 // First, delete everything.
906 if ( is_wp_error( $bbp_db->query( "DELETE FROM `{$bbp_db->postmeta}` WHERE `meta_key` IN ( '_bbp_last_reply_id', '_bbp_last_topic_id', '_bbp_last_active_id', '_bbp_last_active_time' )" ) ) ) {
907 return array( 1, sprintf( $statement, $result ) );
908 }
909
910 // Post types and status
911 $fpt = bbp_get_forum_post_type();
912 $tpt = bbp_get_topic_post_type();
913 $rpt = bbp_get_reply_post_type();
914 $pps = bbp_get_public_status_id();
915
916 // Next, give all the topics with replies the ID their last reply.
917 if ( is_wp_error( $bbp_db->query( "INSERT INTO `{$bbp_db->postmeta}` (`post_id`, `meta_key`, `meta_value`)
918 ( SELECT `topic`.`ID`, '_bbp_last_reply_id', MAX( `reply`.`ID` )
919 FROM `{$bbp_db->posts}` AS `topic` INNER JOIN `{$bbp_db->posts}` AS `reply` ON `topic`.`ID` = `reply`.`post_parent`
920 WHERE `reply`.`post_status` = '{$pps}' AND `topic`.`post_type` = '{$tpt}' AND `reply`.`post_type` = '{$rpt}'
921 GROUP BY `topic`.`ID` )" ) ) ) {
922 return array( 2, sprintf( $statement, $result ) );
923 }
924
925 // For any remaining topics, give a reply ID of 0.
926 if ( is_wp_error( $bbp_db->query( "INSERT INTO `{$bbp_db->postmeta}` (`post_id`, `meta_key`, `meta_value`)
927 ( SELECT `ID`, '_bbp_last_reply_id', 0
928 FROM `{$bbp_db->posts}` AS `topic` LEFT JOIN `{$bbp_db->postmeta}` AS `reply`
929 ON `topic`.`ID` = `reply`.`post_id` AND `reply`.`meta_key` = '_bbp_last_reply_id'
930 WHERE `reply`.`meta_id` IS NULL AND `topic`.`post_type` = '{$tpt}' )" ) ) ) {
931 return array( 3, sprintf( $statement, $result ) );
932 }
933
934 // Now we give all the forums with topics the ID their last topic.
935 if ( is_wp_error( $bbp_db->query( "INSERT INTO `{$bbp_db->postmeta}` (`post_id`, `meta_key`, `meta_value`)
936 ( SELECT `forum`.`ID`, '_bbp_last_topic_id', MAX( `topic`.`ID` )
937 FROM `{$bbp_db->posts}` AS `forum` INNER JOIN `{$bbp_db->posts}` AS `topic` ON `forum`.`ID` = `topic`.`post_parent`
938 WHERE `topic`.`post_status` = '{$pps}' AND `forum`.`post_type` = '{$fpt}' AND `topic`.`post_type` = '{$tpt}'
939 GROUP BY `forum`.`ID` )" ) ) ) {
940 return array( 4, sprintf( $statement, $result ) );
941 }
942
943 // For any remaining forums, give a topic ID of 0.
944 if ( is_wp_error( $bbp_db->query( "INSERT INTO `{$bbp_db->postmeta}` (`post_id`, `meta_key`, `meta_value`)
945 ( SELECT `ID`, '_bbp_last_topic_id', 0
946 FROM `{$bbp_db->posts}` AS `forum` LEFT JOIN `{$bbp_db->postmeta}` AS `topic`
947 ON `forum`.`ID` = `topic`.`post_id` AND `topic`.`meta_key` = '_bbp_last_topic_id'
948 WHERE `topic`.`meta_id` IS NULL AND `forum`.`post_type` = '{$fpt}' )" ) ) ) {
949 return array( 5, sprintf( $statement, $result ) );
950 }
951
952 // After that, we give all the topics with replies the ID their last reply (again, this time for a different reason).
953 if ( is_wp_error( $bbp_db->query( "INSERT INTO `{$bbp_db->postmeta}` (`post_id`, `meta_key`, `meta_value`)
954 ( SELECT `topic`.`ID`, '_bbp_last_active_id', MAX( `reply`.`ID` )
955 FROM `{$bbp_db->posts}` AS `topic` INNER JOIN `{$bbp_db->posts}` AS `reply` ON `topic`.`ID` = `reply`.`post_parent`
956 WHERE `reply`.`post_status` = '{$pps}' AND `topic`.`post_type` = '{$tpt}' AND `reply`.`post_type` = '{$rpt}'
957 GROUP BY `topic`.`ID` )" ) ) ) {
958 return array( 6, sprintf( $statement, $result ) );
959 }
960
961 // For any remaining topics, give a reply ID of themself.
962 if ( is_wp_error( $bbp_db->query( "INSERT INTO `{$bbp_db->postmeta}` (`post_id`, `meta_key`, `meta_value`)
963 ( SELECT `ID`, '_bbp_last_active_id', `ID`
964 FROM `{$bbp_db->posts}` AS `topic` LEFT JOIN `{$bbp_db->postmeta}` AS `reply`
965 ON `topic`.`ID` = `reply`.`post_id` AND `reply`.`meta_key` = '_bbp_last_active_id'
966 WHERE `reply`.`meta_id` IS NULL AND `topic`.`post_type` = '{$tpt}' )" ) ) ) {
967 return array( 7, sprintf( $statement, $result ) );
968 }
969
970 // Give topics with replies their last update time.
971 if ( is_wp_error( $bbp_db->query( "INSERT INTO `{$bbp_db->postmeta}` (`post_id`, `meta_key`, `meta_value`)
972 ( SELECT `topic`.`ID`, '_bbp_last_active_time', MAX( `reply`.`post_date` )
973 FROM `{$bbp_db->posts}` AS `topic` INNER JOIN `{$bbp_db->posts}` AS `reply` ON `topic`.`ID` = `reply`.`post_parent`
974 WHERE `reply`.`post_status` = '{$pps}' AND `topic`.`post_type` = '{$tpt}' AND `reply`.`post_type` = '{$rpt}'
975 GROUP BY `topic`.`ID` )" ) ) ) {
976 return array( 8, sprintf( $statement, $result ) );
977 }
978
979 // Give topics without replies their last update time.
980 if ( is_wp_error( $bbp_db->query( "INSERT INTO `{$bbp_db->postmeta}` (`post_id`, `meta_key`, `meta_value`)
981 ( SELECT `ID`, '_bbp_last_active_time', `post_date`
982 FROM `{$bbp_db->posts}` AS `topic` LEFT JOIN `{$bbp_db->postmeta}` AS `reply`
983 ON `topic`.`ID` = `reply`.`post_id` AND `reply`.`meta_key` = '_bbp_last_active_time'
984 WHERE `reply`.`meta_id` IS NULL AND `topic`.`post_type` = '{$tpt}' )" ) ) ) {
985 return array( 9, sprintf( $statement, $result ) );
986 }
987
988 // Forums need to know what their last active item is as well. Now it gets a bit more complex to do in the database.
989 $forums = $bbp_db->get_col( "SELECT `ID` FROM `{$bbp_db->posts}` WHERE `post_type` = '{$fpt}' and `post_status` != 'auto-draft'" );
990 if ( is_wp_error( $forums ) ) {
991 return array( 10, sprintf( $statement, $result ) );
992 }
993
994 // Loop through forums
995 foreach ( $forums as $forum_id ) {
996 if ( ! bbp_is_forum_category( $forum_id ) ) {
997 bbp_update_forum( array( 'forum_id' => $forum_id ) );
998 }
999 }
1000
1001 // Loop through categories when forums are done
1002 foreach ( $forums as $forum_id ) {
1003 if ( bbp_is_forum_category( $forum_id ) ) {
1004 bbp_update_forum( array( 'forum_id' => $forum_id ) );
1005 }
1006 }
1007
1008 // Complete results
1009 return array( 0, sprintf( $statement, esc_html__( 'Complete!', 'bbpress' ) ) );
1010 }
1011
1012 /**
1013 * Repair the relationship of sticky topics to the actual parent forum
1014 *
1015 * @since 2.3.0 bbPress (r4695)
1016 *
1017 * @return array An array of the status code and the message
1018 */
1019 function bbp_admin_repair_sticky() {
1020
1021 // Define variables
1022 $bbp_db = bbp_db();
1023 $statement = esc_html__( 'Repairing the sticky topic to the parent forum relationships&hellip; %s', 'bbpress' );
1024 $result = esc_html__( 'Failed!', 'bbpress' );
1025
1026 $forums = $bbp_db->get_col( "SELECT ID FROM `{$bbp_db->posts}` WHERE `post_type` = '" . bbp_get_forum_post_type() . "'" );
1027
1028 // Bail if no forums found
1029 if ( empty( $forums ) || is_wp_error( $forums ) ) {
1030 return array( 1, sprintf( $statement, $result ) );
1031 }
1032
1033 // Loop through forums and get their sticky topics
1034 foreach ( $forums as $forum ) {
1035 $forum_stickies[ $forum ] = get_post_meta( $forum, '_bbp_sticky_topics', true );
1036 }
1037
1038 // Cleanup
1039 unset( $forums, $forum );
1040
1041 // Loop through each forum with sticky topics
1042 foreach ( $forum_stickies as $forum_id => $stickies ) {
1043
1044 // Skip if no stickies
1045 if ( empty( $stickies ) ) {
1046 continue;
1047 }
1048
1049 // Loop through each sticky topic
1050 foreach ( $stickies as $id => $topic_id ) {
1051
1052 // If the topic is not a super sticky, and the forum ID does not
1053 // match the topic's forum ID, unset the forum's sticky meta.
1054 if ( ! bbp_is_topic_super_sticky( $topic_id ) && ( $forum_id !== bbp_get_topic_forum_id( $topic_id ) ) ) {
1055 unset( $forum_stickies[ $forum_id ][ $id ] );
1056 }
1057 }
1058
1059 // Get sticky topic ID's, or use empty string
1060 $stickers = ! empty( $forum_stickies[ $forum_id ] )
1061 ? array_values( $forum_stickies[ $forum_id ] )
1062 : '';
1063
1064 // Update the forum's sticky topics meta
1065 update_post_meta( $forum_id, '_bbp_sticky_topics', $stickers );
1066 }
1067
1068 // Complete results
1069 return array( 0, sprintf( $statement, esc_html__( 'Complete!', 'bbpress' ) ) );
1070 }
1071
1072 /**
1073 * Repair closed topics
1074 *
1075 * Closed topics that are missing the post-meta "_bbp_status" key value "publish"
1076 * result in unexpected behavior, primarily this would have only occurred if you
1077 * had imported forums from another forum package previous to bbPress v2.6,
1078 * https://bbpress.trac.wordpress.org/ticket/2577
1079 *
1080 * @since 2.6.0 bbPress (r5668)
1081 *
1082 * @return array An array of the status code and the message
1083 */
1084 function bbp_admin_repair_closed_topics() {
1085
1086 // Define variables
1087 $bbp_db = bbp_db();
1088 $statement = esc_html__( 'Repairing closed topics&hellip; %s', 'bbpress' );
1089 $result = esc_html__( 'No closed topics to repair.', 'bbpress' );
1090 $changed = 0;
1091
1092 // Results
1093 $query = "SELECT ID FROM `{$bbp_db->posts}` WHERE `post_type` = %s AND `post_status` = %s";
1094 $prepare = $bbp_db->prepare( $query, bbp_get_topic_post_type(), bbp_get_closed_status_id() );
1095 $closed_topics = $bbp_db->get_col( $prepare );
1096
1097 // Bail if no closed topics found
1098 if ( empty( $closed_topics ) || is_wp_error( $closed_topics ) ) {
1099 return array( 1, sprintf( $statement, $result ) );
1100 }
1101
1102 // Loop through each closed topic
1103 foreach ( $closed_topics as $closed_topic ) {
1104
1105 // Check if the closed topic already has a postmeta _bbp_status value
1106 $topic_status = get_post_meta( $closed_topic, '_bbp_status', true );
1107
1108 // If we don't have a postmeta _bbp_status value
1109 if ( empty( $topic_status ) ) {
1110 update_post_meta( $closed_topic, '_bbp_status', 'publish' );
1111 ++$changed; // Keep a count to display at the end
1112 }
1113 }
1114
1115 // Cleanup
1116 unset( $closed_topics, $closed_topic, $topic_status );
1117
1118 // Complete results
1119 $result = sprintf( _n( 'Complete! %d closed topic repaired.', 'Complete! %d closed topics repaired.', $changed, 'bbpress' ), $changed );
1120
1121 return array( 0, sprintf( $statement, $result ) );
1122 }
1123
1124 /**
1125 * Repair the private and hidden forums
1126 *
1127 * @since 2.2.0 bbPress (r4104)
1128 *
1129 * @return array An array of the status code and the message
1130 */
1131 function bbp_admin_repair_forum_visibility() {
1132 $statement = esc_html__( 'Recalculating forum visibility&hellip; %s', 'bbpress' );
1133
1134 // Bail if queries returned errors
1135 if ( ! bbp_repair_forum_visibility() ) {
1136 return array( 2, sprintf( $statement, esc_html__( 'Failed!', 'bbpress' ) ) );
1137
1138 // Complete results
1139 } else {
1140 return array( 0, sprintf( $statement, esc_html__( 'Complete!', 'bbpress' ) ) );
1141 }
1142 }
1143
1144 /**
1145 * Repair the parent forum meta for each topic and reply
1146 *
1147 * @since 2.1.0 bbPress (r3876)
1148 *
1149 * @return array An array of the status code and the message
1150 */
1151 function bbp_admin_repair_forum_meta() {
1152
1153 // Define variables
1154 $bbp_db = bbp_db();
1155 $statement = esc_html__( 'Recalculating the forum for each post&hellip; %s', 'bbpress' );
1156 $result = esc_html__( 'Failed!', 'bbpress' );
1157
1158 // First, delete everything.
1159 if ( is_wp_error( $bbp_db->query( "DELETE FROM `{$bbp_db->postmeta}` WHERE `meta_key` = '_bbp_forum_id'" ) ) ) {
1160 return array( 1, sprintf( $statement, $result ) );
1161 }
1162
1163 // Post types and status
1164 $tpt = bbp_get_topic_post_type();
1165 $rpt = bbp_get_reply_post_type();
1166 $fmt = bbp_get_forum_post_type();
1167
1168 // Next, give all the topics their parent forum id.
1169 if ( is_wp_error( $bbp_db->query( "INSERT INTO `{$bbp_db->postmeta}` (`post_id`, `meta_key`, `meta_value`)
1170 ( SELECT `topic`.`ID`, '_bbp_forum_id', `topic`.`post_parent`
1171 FROM `$bbp_db->posts`
1172 AS `topic`
1173 WHERE `topic`.`post_type` = '{$tpt}'
1174 GROUP BY `topic`.`ID` )" ) ) ) {
1175 return array( 2, sprintf( $statement, $result ) );
1176 }
1177
1178 // Next, give all the forums their parent forum id.
1179 if ( is_wp_error( $bbp_db->query( "INSERT INTO `{$bbp_db->postmeta}` (`post_id`, `meta_key`, `meta_value`)
1180 ( SELECT `forum`.`ID`, '_bbp_forum_id', `forum`.`post_parent`
1181 FROM `$bbp_db->posts`
1182 AS `forum`
1183 WHERE `forum`.`post_type` = '{$fmt}'
1184 GROUP BY `forum`.`ID` )" ) ) ) {
1185 return array( 2, sprintf( $statement, $result ) );
1186 }
1187
1188 // Next, give all the replies their parent forum id.
1189 if ( is_wp_error( $bbp_db->query( "INSERT INTO `{$bbp_db->postmeta}` (`post_id`, `meta_key`, `meta_value`)
1190 ( SELECT `reply`.`ID`, '_bbp_forum_id', `topic`.`post_parent`
1191 FROM `$bbp_db->posts`
1192 AS `reply`
1193 INNER JOIN `$bbp_db->posts`
1194 AS `topic`
1195 ON `reply`.`post_parent` = `topic`.`ID`
1196 WHERE `topic`.`post_type` = '{$tpt}'
1197 AND `reply`.`post_type` = '{$rpt}'
1198 GROUP BY `reply`.`ID` )" ) ) ) {
1199 return array( 3, sprintf( $statement, $result ) );
1200 }
1201
1202 // Complete results
1203 return array( 0, sprintf( $statement, esc_html__( 'Complete!', 'bbpress' ) ) );
1204 }
1205
1206 /**
1207 * Repair the topic for each post
1208 *
1209 * @since 2.1.0 bbPress (r3876)
1210 *
1211 * @return array An array of the status code and the message
1212 */
1213 function bbp_admin_repair_topic_meta() {
1214
1215 // Define variables
1216 $bbp_db = bbp_db();
1217 $statement = esc_html__( 'Recalculating the topic for each post&hellip; %s', 'bbpress' );
1218 $result = esc_html__( 'Failed!', 'bbpress' );
1219
1220 // First, delete everything.
1221 if ( is_wp_error( $bbp_db->query( "DELETE FROM `{$bbp_db->postmeta}` WHERE `meta_key` = '_bbp_topic_id'" ) ) ) {
1222 return array( 1, sprintf( $statement, $result ) );
1223 }
1224
1225 // Post types and status
1226 $tpt = bbp_get_topic_post_type();
1227 $rpt = bbp_get_reply_post_type();
1228
1229 // Next, give all the topics with replies the ID their last reply.
1230 if ( is_wp_error( $bbp_db->query( "INSERT INTO `{$bbp_db->postmeta}` (`post_id`, `meta_key`, `meta_value`)
1231 ( SELECT `topic`.`ID`, '_bbp_topic_id', `topic`.`ID`
1232 FROM `$bbp_db->posts`
1233 AS `topic`
1234 WHERE `topic`.`post_type` = '{$tpt}'
1235 GROUP BY `topic`.`ID` )" ) ) ) {
1236 return array( 3, sprintf( $statement, $result ) );
1237 }
1238
1239 // Next, give all the topics with replies the ID their last reply.
1240 if ( is_wp_error( $bbp_db->query( "INSERT INTO `{$bbp_db->postmeta}` (`post_id`, `meta_key`, `meta_value`)
1241 ( SELECT `reply`.`ID`, '_bbp_topic_id', `topic`.`ID`
1242 FROM `$bbp_db->posts`
1243 AS `reply`
1244 INNER JOIN `$bbp_db->posts`
1245 AS `topic`
1246 ON `reply`.`post_parent` = `topic`.`ID`
1247 WHERE `topic`.`post_type` = '{$tpt}'
1248 AND `reply`.`post_type` = '{$rpt}'
1249 GROUP BY `reply`.`ID` )" ) ) ) {
1250 return array( 4, sprintf( $statement, $result ) );
1251 }
1252
1253 // Complete results
1254 return array( 0, sprintf( $statement, esc_html__( 'Complete!', 'bbpress' ) ) );
1255 }
1256
1257 /**
1258 * Recalculate reply menu order
1259 *
1260 * @since 2.5.4 bbPress (r5367)
1261 *
1262 * @return array An array of the status code and the message
1263 */
1264 function bbp_admin_repair_reply_menu_order() {
1265
1266 // Define variables
1267 $bbp_db = bbp_db();
1268 $statement = esc_html__( 'Recalculating reply menu order&hellip; %s', 'bbpress' );
1269 $result = esc_html__( 'No reply positions to recalculate.', 'bbpress' );
1270
1271 // Delete cases where `_bbp_reply_to` was accidentally set to itself
1272 if ( is_wp_error( $bbp_db->query( "DELETE FROM `{$bbp_db->postmeta}` WHERE `meta_key` = '_bbp_reply_to' AND `post_id` = `meta_value`" ) ) ) {
1273 return array( 1, sprintf( $statement, $result ) );
1274 }
1275
1276 // Post type
1277 $rpt = bbp_get_reply_post_type();
1278
1279 // Get an array of reply id's to update the menu oder for each reply
1280 $replies = $bbp_db->get_results( "SELECT `a`.`ID` FROM `{$bbp_db->posts}` AS `a`
1281 INNER JOIN (
1282 SELECT `menu_order`, `post_parent`
1283 FROM `{$bbp_db->posts}`
1284 GROUP BY `menu_order`, `post_parent`
1285 HAVING COUNT( * ) >1
1286 )`b`
1287 ON `a`.`menu_order` = `b`.`menu_order`
1288 AND `a`.`post_parent` = `b`.`post_parent`
1289 WHERE `post_type` = '{$rpt}'", OBJECT_K );
1290
1291 // Bail if no replies returned
1292 if ( empty( $replies ) ) {
1293 return array( 1, sprintf( $statement, $result ) );
1294 }
1295
1296 // Recalculate the menu order position for each reply
1297 foreach ( $replies as $reply ) {
1298 bbp_update_reply_position( $reply->ID );
1299 }
1300
1301 // Cleanup
1302 unset( $replies, $reply );
1303
1304 // Complete results
1305 return array( 0, sprintf( $statement, esc_html__( 'Complete!', 'bbpress' ) ) );
1306 }
1307