PluginProbe
bbPress / 2.6.17
bbPress v2.6.17
2.6.17 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 All 72 releases
bbpress / includes / admin / tools / repair.php

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

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