Index: src/includes/admin/admin.php
===================================================================
--- src/includes/admin/admin.php	(revision 5886)
+++ src/includes/admin/admin.php	(working copy)
@@ -588,6 +588,10 @@
 					break;
 			}
 		}
+
+		if( 'tools_page_bbp-repair' === get_current_screen()->id ) {
+			wp_enqueue_script( 'bbp-tools-js', $this->js_url . 'tools.js', array( 'jquery' ), $version );
+		}
 	}
 
 	/**
Index: src/includes/admin/js/tools.js
===================================================================
--- src/includes/admin/js/tools.js	(revision 0)
+++ src/includes/admin/js/tools.js	(working copy)
@@ -0,0 +1,38 @@
+jQuery( document ).ready( function( ) {
+
+	// add in something here to check if the recalculate menu order box is ticked.
+	// If it is, it is the *only* one that can be ticked, maybe grey out the others?
+
+	function bbp_repair_reply_call(){
+		var input = {};
+		input = {
+			'action' : 'bbp_admin_repair_reply_menu_order'
+		};
+
+		jQuery.ajax({
+        		type: 'POST',
+        		url : ajaxurl, // this is already there, let's use it
+           		data : input,
+        		dataType : 'json',
+        		success : function( reply ) {
+        			if( reply.result === 'keep-going') {
+        	   			alert( reply.notice );
+        	   			jQuery( '#the-list a#bbp-sync-all-reply-positions' ).click();
+        			}
+        			else{
+        	  		// be super sad
+        	  		//	jQuery( '.nav-tab-wrapper' ).after( "<h1>" + reply.notice + "</h1>" );
+        	  			alert( reply.notice );
+        	  			location.reload();
+        			}
+        		}
+        	});
+	}
+
+	jQuery( '#the-list a#bbp-sync-all-reply-positions' ).on( 'click', function(e){
+		e.preventDefault();
+       	bbp_repair_reply_call();
+	});
+
+
+});

Property changes on: src/includes/admin/js/tools.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: src/includes/admin/tools.php
===================================================================
--- src/includes/admin/tools.php	(revision 5886)
+++ src/includes/admin/tools.php	(working copy)
@@ -10,6 +10,9 @@
 // Exit if accessed directly
 defined( 'ABSPATH' ) || exit;
 
+// The reply menu order repair tool uses ajax
+add_action( 'wp_ajax_bbp_admin_repair_reply_menu_order', 'bbp_admin_repair_reply_menu_order_callback' );
+
 /** Repair ********************************************************************/
 
 /**
@@ -93,7 +96,7 @@
 									<strong><?php echo esc_html( $item['description'] ); ?></strong>
 									<div class="row-actions hide-if-no-js">
 										<span class="run">
-											<a href="<?php bbp_admin_repair_tool_run_url( $item['id'] ); ?>" aria-label="<?php printf( esc_html__( 'Run %s', 'bbpress' ), '' ); ?>"><?php esc_html_e( 'Run', 'bbpress' ); ?></a>
+											<a href="<?php bbp_admin_repair_tool_run_url( $item['id'] ); ?>" aria-label="<?php printf( esc_html__( 'Run %s', 'bbpress' ), '' ); ?>" id="<?php printf( esc_attr( '%s' ), $item['id'] ); ?>" ><?php esc_html_e( 'Run', 'bbpress' ); ?></a>
 										</span>
 									</div>
 									<button type="button" class="toggle-row">
@@ -2074,7 +2077,8 @@
  * @uses bbp_update_reply_position() To update the reply position
  * @return array An array of the status code and the message
  */
-function bbp_admin_repair_reply_menu_order() {
+function bbp_admin_repair_reply_menu_order_callback() {
+	global $wpdb;
 
 	// Define variables
 	$bbp_db    = bbp_db();
@@ -2088,38 +2092,138 @@
 
 	// Post type
 	$rpt = bbp_get_reply_post_type();
+ 	$pst = bbp_get_pending_status_id();
 
-	// Get an array of reply id's to update the menu oder for each reply
-	$replies = $bbp_db->get_results( "SELECT `a`.`ID` FROM `{$bbp_db->posts}` AS `a`
-										INNER JOIN (
-											SELECT `menu_order`, `post_parent`
-											FROM `{$bbp_db->posts}`
-											GROUP BY `menu_order`, `post_parent`
-											HAVING COUNT( * ) >1
-										)`b`
-										ON `a`.`menu_order` = `b`.`menu_order`
-										AND `a`.`post_parent` = `b`.`post_parent`
-										WHERE `post_type` = '{$rpt}';", OBJECT_K );
+ 	// Set the menu_order of pending replies who have a post_date_gmt of 0 (posts that have been posted but never published)
+ 	$wpdb->update('wp_posts', array('menu_order'=>'0'), array('post_type'=>$rpt, 'post_status'=>$pst, 'post_date_gmt'=>'0000-00-00 00:00:00') );
 
-	// Bail if no replies returned
-	if ( empty( $replies ) ) {
-		return array( 1, sprintf( $statement, $result ) );
-	}
+ 	// Post type
+ 	$offset = 0;
+ 	$limit = 500;
+ 	$not_done = false; // we'll find out in a moment that this is wrong. Could be better named.
+ 	$replies_count = 0;
 
-	// Recalculate the menu order position for each reply
-	foreach ( $replies as $reply ) {
-		bbp_update_reply_position( $reply->ID );
-	}
+ 	$queryTimes = array();
+ 	$updateTimes = array();
 
-	// Cleanup
-	unset( $replies, $reply );
+ 	// ready steady GO
+ 	$startTime = microtime(true);
 
-	// Flush the cache; things are about to get ugly.
-	wp_cache_flush();
+ 	// first look through the menu_order 0 replies. Are there any in topics which aren't spammed or pending? Only topics that are....
+ 	$replies_mozero = $wpdb->get_results( "SELECT `a`.`post_parent` FROM `{$wpdb->posts}` AS `a`
+  										INNER JOIN (
+  										SELECT `menu_order`, `post_parent`
+										FROM `{$wpdb->posts}` WHERE `post_type` = '{$rpt}' AND `menu_order` = 0
+  										GROUP BY `menu_order`, `post_parent`
+  										HAVING COUNT( * ) >1
+  										)`b`
+  										ON `a`.`menu_order` = `b`.`menu_order`
+  										AND `a`.`post_parent` = `b`.`post_parent`
 
-	return array( 0, sprintf( $statement, __( 'Complete!', 'bbpress' ) ) );
-}
+ 									 LIMIT {$limit}", OBJECT_K );
 
+ 	while( !$not_done ){
+ 		// Get an array of reply id's to update the menu oder for each reply
+ 		$startQueryTime = microtime(true);
+ 		$replies = $wpdb->get_results( "SELECT `a`.`ID` FROM `{$wpdb->posts}` AS `a`
+ 										INNER JOIN (
+ 										SELECT `menu_order`, `post_parent`
+ 										FROM `{$wpdb->posts}` WHERE `post_type` = '{$rpt}' AND `menu_order` != 0
+ 										GROUP BY `menu_order`, `post_parent`
+ 										HAVING COUNT( * ) >1
+ 										)`b`
+ 										ON `a`.`menu_order` = `b`.`menu_order`
+ 										AND `a`.`post_parent` = `b`.`post_parent`
+
+ 									 LIMIT {$limit}", OBJECT_K );
+
+ 		$endQueryTime = microtime(true);
+
+ 		$queryTimes[] = ( $endQueryTime - $startQueryTime );
+
+ 		// this is not handling more than 500 at a time
+
+
+ 		$replies_count += count( $replies );
+ 		$offset += $limit;
+
+ 		// Bail if no replies returned
+ 		if ( $replies_count == 0 ) {
+ 			// Flush the cache; things are about to get ugly.
+  			wp_cache_flush();
+ 			$data = array('notice' => 'all done all the way' , 'result' => 'all_done');
+ 			echo json_encode( $data );
+ 			wp_die();
+
+ 		}
+
+ 		// Recalculate the menu order position for each reply
+ 		foreach ( $replies as $reply ) {
+ 			$startUpdateTime = microtime(true);
+ 			bbp_update_reply_position( $reply->ID );
+ 			$endUpdateTime = microtime(true);
+ 			$updateTimes[] = ( $endUpdateTime - $startUpdateTime );
+
+ 		}
+
+ 		$not_done = true;
+  	}
+  	// Cleanup
+  	unset( $replies, $reply );
+ 	$midTime = microtime(true);
+
+ 	$averageQueryTime = array_sum( $queryTimes ) / count( $queryTimes );
+
+ 	$averageUpdateTime = array_sum( $updateTimes ) / count( $updateTimes );
+ 	$stdDevUpdateTime = stats_standard_deviation( $updateTimes );
+
+ 	$endTime = microtime(true);
+
+ 	$data = array('notice' => 'still more to do ' .  ($endTime - $startTime) . ' seconds.', 'result' => 'keep-going');
+ 	echo json_encode( $data );
+ 	wp_die();
+
+ 	//return array( 0, sprintf( $statement, __('Average update time: ' . $averageUpdateTime . "\n\nStandard deviation: " . $stdDevUpdateTime . "\n\nAverage query time: " . $averageQueryTime . "\n\nOffset: " . $offset . ' // Replies count: ' . $replies_count . ' // Mid time: ' . ($midTime - $startTime) . ' // End time: ' . ($endTime - $startTime), 'bbpress' ) ) );
+  }
+
+ /**
+  *  standard deviation thing from http://php.net/manual/en/function.stats-standard-deviation.php
+  */
+
+ if (!function_exists('stats_standard_deviation')) {
+     /**
+      * This user-land implementation follows the implementation quite strictly;
+      * it does not attempt to improve the code or algorithm in any way. It will
+      * raise a warning if you have fewer than 2 values in your array, just like
+      * the extension does (although as an E_USER_WARNING, not E_WARNING).
+      *
+      * @param array $a
+      * @param bool $sample [optional] Defaults to false
+      * @return float|bool The standard deviation or false on error.
+      */
+     function stats_standard_deviation(array $a, $sample = false) {
+         $n = count($a);
+         if ($n === 0) {
+             trigger_error("The array has zero elements", E_USER_WARNING);
+             return false;
+         }
+         if ($sample && $n === 1) {
+             trigger_error("The array has only 1 element", E_USER_WARNING);
+             return false;
+         }
+         $mean = array_sum($a) / $n;
+         $carry = 0.0;
+         foreach ($a as $val) {
+             $d = ((double) $val) - $mean;
+             $carry += $d * $d;
+         };
+         if ($sample) {
+            --$n;
+         }
+         return sqrt($carry / $n);
+     }
+ }
+
 /** Reset ********************************************************************/
 
 /**
Index: src/includes/common/functions.php
===================================================================
--- src/includes/common/functions.php	(revision 5886)
+++ src/includes/common/functions.php	(working copy)
@@ -1682,6 +1682,7 @@
 	// The ID of the cached query
 	$cache_id  = 'bbp_parent_all_' . $parent_id . '_type_' . $post_type . '_child_ids';
 
+	wp_cache_flush();
 	// Check for cache and set if needed
 	$child_ids = wp_cache_get( $cache_id, 'bbpress_posts' );
 	if ( false === $child_ids ) {
@@ -1715,7 +1716,7 @@
 		// Join post statuses together
 		$post_status = "'" . implode( "', '", $post_status ) . "'";
 		$bbp_db      = bbp_db();
-		$query       = $bbp_db->prepare( "SELECT ID FROM {$bbp_db->posts} WHERE post_parent = %d AND post_status IN ( {$post_status} ) AND post_type = '%s' ORDER BY ID DESC;", $parent_id, $post_type );
+		$query       = $bbp_db->prepare( "SELECT ID FROM {$bbp_db->posts} WHERE post_parent = %d AND post_status IN ( {$post_status} ) AND post_type = '%s' ORDER BY post_date DESC;", $parent_id, $post_type );
 		$child_ids   = (array) $bbp_db->get_col( $query );
 
 		wp_cache_set( $cache_id, $child_ids, 'bbpress_posts' );
