Index: src/includes/admin/admin.php
===================================================================
--- src/includes/admin/admin.php	(revision 5654)
+++ src/includes/admin/admin.php	(working copy)
@@ -585,6 +585,10 @@
 					break;
 			}
 		}
+		elseif( 'tools_page_bbp-repair' === get_current_screen()->base ){
+			// Enqueue the tools JS (NOT the minified version for now, change later)
+			wp_enqueue_script( 'bbp-admin-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,53 @@
+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? 
+	
+	$( '#bbp-sync-all-reply-positions' ).change( function(){
+		
+		if( $( this ).prop( 'checked' ) ){
+			// here, disable the rest of the form here
+		}
+	});
+
+	$( 'form.settings' ).submit( function( e ) {
+
+		if( $( '#bbp-sync-all-reply-positions' ).prop('checked') ){
+
+			e.preventDefault();
+        	bbp_repair_reply_call();
+		}
+	});
+
+	function bbp_repair_reply_call(){
+
+		var input = {};
+		
+		input = { 
+			'action' : 'bbp_admin_repair_reply_menu_order'
+		}
+
+		console.log( input );
+
+		$.ajax({
+        		type: 'POST',
+        		url : ajaxurl, // this is already there, let's use it
+           		data : input,
+        		dataType : 'json',
+        		success : function( reply ) {  
+        			if( reply.result === 'success') {
+        	   			alert( reply.notice );
+        	   			//keep going keep going keep going 
+        			}
+        			else{
+        	  		// be super sad
+        	  		//	$( '.nav-tab-wrapper' ).after( "<h1>" + reply.notice + "</h1>" );
+        	  			alert( reply.notice );
+        	  			location.reload();
+        			}
+        		}
+        	});
+	}
+	
+
+});
\ No newline at end of file
Index: src/includes/admin/tools.php
===================================================================
--- src/includes/admin/tools.php	(revision 5654)
+++ 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 ********************************************************************/
 
 /**
@@ -1366,12 +1369,18 @@
  * @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;
 
+
 	$statement = __( 'Recalculating reply menu order &hellip; %s', 'bbpress' );
 	$result    = __( 'No reply positions to recalculate!',         'bbpress' );
 
+	// if this is going to get calculated via an ajax call, it needs 
+
+
 	// Delete cases where `_bbp_reply_to` was accidentally set to itself
 	if ( is_wp_error( $wpdb->query( "DELETE FROM `{$wpdb->postmeta}` WHERE `meta_key` = '_bbp_reply_to' AND `post_id` = `meta_value`;" ) ) ) {
 		return array( 1, sprintf( $statement, $result ) );
@@ -1379,38 +1388,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 = $wpdb->get_results( "SELECT `a`.`ID` FROM `{$wpdb->posts}` AS `a`
+	// 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') );
+
+	// Post type
+	$offset = 0;
+	$limit = 500;
+	$done = false;
+	$replies_count = 0;
+	
+	$queryTimes = array();
+	$updateTimes = array();
+	
+	// ready steady GO
+	$startTime = microtime(true);
+	
+	// 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}`
+											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`
-										WHERE `post_type` = '{$rpt}';", OBJECT_K );
+	
+									 LIMIT {$limit}", OBJECT_K );
+	
+	while( !$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 );
 
-	// Bail if no replies returned
-	if ( empty( $replies ) ) {
-		return array( 1, sprintf( $statement, $result ) );
-	}
+		$endQueryTime = microtime(true);
+		
+		$queryTimes[] = ( $endQueryTime - $startQueryTime ); 
+		
+		if( $offset >= 2000 ){
+			$done = true;
+		}
 
-	// Recalculate the menu order position for each reply
-	foreach ( $replies as $reply ) {
-		bbp_update_reply_position( $reply->ID );
+		$replies_count += count( $replies );
+		$offset += $limit;
+	
+		// Bail if no replies returned
+		if ( $replies_count == 0 ) {
+
+			$data = array('notice' => 'no replies, so so sad' , 'result' => 'no_replies');
+			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 ); 
+			
+		}
 	}
-
 	// Cleanup
 	unset( $replies, $reply );
-
+	$midTime = microtime(true);
+	
+	$averageQueryTime = array_sum( $queryTimes ) / count( $queryTimes ); 
+		
+	$averageUpdateTime = array_sum( $updateTimes ) / count( $updateTimes ); 
+	$stdDevUpdateTime = stats_standard_deviation( $updateTimes );
+	
 	// Flush the cache; things are about to get ugly.
 	wp_cache_flush();
+	$endTime = microtime(true);
 
-	return array( 0, sprintf( $statement, __( 'Complete!', 'bbpress' ) ) );
+	$data = array('notice' => 'whoo hoo to you too! It took ' .  ($endTime - $startTime) . ' seconds.', 'result' => 'success');
+	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 ********************************************************************/
 
 /**
