Index: branches/rsr.v5.1.dev/web/punbb/admin_maintenance.php
===================================================================
--- branches/rsr.v5.1.dev/web/punbb/admin_maintenance.php	(revision 1)
+++ branches/rsr.v5.1.dev/web/punbb/admin_maintenance.php	(revision 3)
@@ -1,27 +1,9 @@
 <?php
 
-/***********************************************************************
-
-  Copyright (C) 2002-2005  Rickard Andersson (rickard@punbb.org)
-
-  This file is part of PunBB.
-
-  PunBB is free software; you can redistribute it and/or modify it
-  under the terms of the GNU General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-
-  PunBB is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with this program; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
-  MA  02111-1307  USA
-
-************************************************************************/
-
+/**
+ * Copyright (C) 2008-2011 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
 
 // Tell header.php to use the admin template
@@ -30,18 +12,25 @@
 define('PUN_DISABLE_BUFFERING', 1);
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 require PUN_ROOT.'include/common_admin.php';
 
-if ($pun_user['g_id'] > PUN_ADMIN)
+
+if ($pun_user['g_id'] != PUN_ADMIN)
 	message($lang_common['No permission']);
 
-if (isset($_GET['i_per_page']) && isset($_GET['i_start_at']))
+// Load the admin_maintenance.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_maintenance.php';
+
+$action = isset($_REQUEST['action']) ? trim($_REQUEST['action']) : '';
+
+if ($action == 'rebuild')
 {
-
-	$per_page = intval($_GET['i_per_page']);
-	$start_at = intval($_GET['i_start_at']);
-	if ($per_page < 1 || $start_at < 1)
-		message($lang_common['Bad request']);
+	$per_page = isset($_GET['i_per_page']) ? intval($_GET['i_per_page']) : 0;
+	$start_at = isset($_GET['i_start_at']) ? intval($_GET['i_start_at']) : 0;
+
+	// Check per page is > 0
+	if ($per_page < 1)
+		message($lang_admin_maintenance['Posts must be integer message']);
 
 	@set_time_limit(0);
@@ -53,7 +42,6 @@
 		confirm_referrer('admin_maintenance.php');
 
-		$truncate_sql = ($db_type != 'sqlite') ? 'TRUNCATE TABLE ' : 'DELETE FROM ';
-		$db->query($truncate_sql.$db->prefix.'search_matches') or error('Impossible de vider la table search index match', __FILE__, __LINE__, $db->error());
-		$db->query($truncate_sql.$db->prefix.'search_words') or error('Impossible de vider la table search index words', __FILE__, __LINE__, $db->error());
+		$db->truncate_table('search_matches') or error('Unable to empty search index match table', __FILE__, __LINE__, $db->error());
+		$db->truncate_table('search_words') or error('Unable to empty search index words table', __FILE__, __LINE__, $db->error());
 
 		// Reset the sequence for the search words (not needed for SQLite)
@@ -62,13 +50,15 @@
 			case 'mysql':
 			case 'mysqli':
-				$result = $db->query('ALTER TABLE '.$db->prefix.'search_words auto_increment=1') or error('Impossible de modifier l\'auto-incrÃ©mentation', __FILE__, __LINE__, $db->error());
+			case 'mysql_innodb':
+			case 'mysqli_innodb':
+				$result = $db->query('ALTER TABLE '.$db->prefix.'search_words auto_increment=1') or error('Unable to update table auto_increment', __FILE__, __LINE__, $db->error());
 				break;
 
 			case 'pgsql';
-				$result = $db->query('SELECT setval(\'search_words_id_seq\', 1, false)') or error('Impossible de modifier la sÃ©quence', __FILE__, __LINE__, $db->error());
-		}
-	}
-
-	$end_at = $start_at + $per_page;
+				$result = $db->query('SELECT setval(\''.$db->prefix.'search_words_id_seq\', 1, false)') or error('Unable to update sequence', __FILE__, __LINE__, $db->error());
+		}
+	}
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_maintenance['Rebuilding search index']);
 
 ?>
@@ -77,11 +67,16 @@
 <html>
 <head>
-<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
-<title><?php echo pun_htmlspecialchars($pun_config['o_board_title']) ?> / Reconstruction des index de recherches ...</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title><?php echo generate_page_title($page_title) ?></title>
 <style type="text/css">
 body {
-	font: 10px Verdana, Arial, Helvetica, sans-serif;
+	font: 12px Verdana, Arial, Helvetica, sans-serif;
 	color: #333333;
 	background-color: #FFFFFF
+}
+
+h1 {
+	font-size: 16px;
+	font-weight: normal;
 }
 </style>
@@ -89,51 +84,168 @@
 <body>
 
-Reconstruction des index ... C'est peut Ãªtre le bon moment pour aller prendre un cafÃ© :-)<br /><br />
-
-<?php
+<h1><?php echo $lang_admin_maintenance['Rebuilding index info'] ?></h1>
+<hr />
+
+<?php
+
+	$query_str = '';
 
 	require PUN_ROOT.'include/search_idx.php';
 
-	// Fetch posts to process
-	$result = $db->query('SELECT DISTINCT t.id, p.id, p.message FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'posts AS p ON t.id=p.topic_id WHERE t.id>='.$start_at.' AND t.id<'.$end_at.' ORDER BY t.id') or error('Impossible de retrouver les informations des discussions et des messages', __FILE__, __LINE__, $db->error());
-
-	$cur_topic = 0;
-	while ($cur_post = $db->fetch_row($result))
-	{
-		if ($cur_post[0] <> $cur_topic)
-		{
-			// Fetch subject and ID of first post in topic
-			$result2 = $db->query('SELECT p.id, t.subject, MIN(p.posted) AS first FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id WHERE t.id='.$cur_post[0].' GROUP BY p.id, t.subject ORDER BY first LIMIT 1') or error('Impossible de retrouver les informations des discussions', __FILE__, __LINE__, $db->error());
-			list($first_post, $subject) = $db->fetch_row($result2);
-
-			$cur_topic = $cur_post[0];
-		}
-
-		echo 'Traitement du message <strong>'.$cur_post[1].'</strong> de la discussion <strong>'.$cur_post[0].'</strong><br />'."\n";
-
-		if ($cur_post[1] == $first_post)	// This is the "topic post" so we have to index the subject as well
-			update_search_index('post', $cur_post[1], $cur_post[2], $subject);
+	// Fetch posts to process this cycle
+	$result = $db->query('SELECT p.id, p.message, t.subject, t.first_post_id FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id WHERE p.id >= '.$start_at.' ORDER BY p.id ASC LIMIT '.$per_page) or error('Unable to fetch posts', __FILE__, __LINE__, $db->error());
+
+	$end_at = 0;
+	while ($cur_item = $db->fetch_assoc($result))
+	{
+		echo '<p><span>'.sprintf($lang_admin_maintenance['Processing post'], $cur_item['id']).'</span></p>'."\n";
+
+		if ($cur_item['id'] == $cur_item['first_post_id'])
+			update_search_index('post', $cur_item['id'], $cur_item['message'], $cur_item['subject']);
 		else
-			update_search_index('post', $cur_post[1], $cur_post[2]);
+			update_search_index('post', $cur_item['id'], $cur_item['message']);
+
+		$end_at = $cur_item['id'];
 	}
 
 	// Check if there is more work to do
-	$result = $db->query('SELECT id FROM '.$db->prefix.'topics WHERE id>'.$end_at) or error('Impossible de retrouver les informations de discussions', __FILE__, __LINE__, $db->error());
-
-	$query_str = ($db->num_rows($result)) ? '?i_per_page='.$per_page.'&i_start_at='.$end_at : '';
+	if ($end_at > 0)
+	{
+		$result = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE id > '.$end_at.' ORDER BY id ASC LIMIT 1') or error('Unable to fetch next ID', __FILE__, __LINE__, $db->error());
+
+		if ($db->num_rows($result) > 0)
+			$query_str = '?action=rebuild&i_per_page='.$per_page.'&i_start_at='.$db->result($result);
+	}
 
 	$db->end_transaction();
 	$db->close();
 
-	exit('<script type="text/javascript">window.location="admin_maintenance.php'.$query_str.'"</script><br />La redirection javaScript a Ã©chouÃ©e. <a href="admin_maintenance.php'.$query_str.'">Cliquez ici</a> pour continuer.');
+	exit('<script type="text/javascript">window.location="admin_maintenance.php'.$query_str.'"</script><hr /><p>'.sprintf($lang_admin_maintenance['Javascript redirect failed'], '<a href="admin_maintenance.php'.$query_str.'">'.$lang_admin_maintenance['Click here'].'</a>').'</p>');
 }
 
+if ($action == 'prune')
+{
+	$prune_from = trim($_POST['prune_from']);
+	$prune_sticky = intval($_POST['prune_sticky']);
+
+	if (isset($_POST['prune_comply']))
+	{
+		confirm_referrer('admin_maintenance.php');
+
+		$prune_days = intval($_POST['prune_days']);
+		$prune_date = ($prune_days) ? time() - ($prune_days * 86400) : -1;
+
+		@set_time_limit(0);
+
+		if ($prune_from == 'all')
+		{
+			$result = $db->query('SELECT id FROM '.$db->prefix.'forums') or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
+			$num_forums = $db->num_rows($result);
+
+			for ($i = 0; $i < $num_forums; ++$i)
+			{
+				$fid = $db->result($result, $i);
+
+				prune($fid, $prune_sticky, $prune_date);
+				update_forum($fid);
+			}
+		}
+		else
+		{
+			$prune_from = intval($prune_from);
+			prune($prune_from, $prune_sticky, $prune_date);
+			update_forum($prune_from);
+		}
+
+		// Locate any "orphaned redirect topics" and delete them
+		$result = $db->query('SELECT t1.id FROM '.$db->prefix.'topics AS t1 LEFT JOIN '.$db->prefix.'topics AS t2 ON t1.moved_to=t2.id WHERE t2.id IS NULL AND t1.moved_to IS NOT NULL') or error('Unable to fetch redirect topics', __FILE__, __LINE__, $db->error());
+		$num_orphans = $db->num_rows($result);
+
+		if ($num_orphans)
+		{
+			for ($i = 0; $i < $num_orphans; ++$i)
+				$orphans[] = $db->result($result, $i);
+
+			$db->query('DELETE FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $orphans).')') or error('Unable to delete redirect topics', __FILE__, __LINE__, $db->error());
+		}
+
+		redirect('admin_maintenance.php', $lang_admin_maintenance['Posts pruned redirect']);
+	}
+
+	$prune_days = trim($_POST['req_prune_days']);
+	if ($prune_days == '' || preg_match('%[^0-9]%', $prune_days))
+		message($lang_admin_maintenance['Days must be integer message']);
+
+	$prune_date = time() - ($prune_days * 86400);
+
+	// Concatenate together the query for counting number of topics to prune
+	$sql = 'SELECT COUNT(id) FROM '.$db->prefix.'topics WHERE last_post<'.$prune_date.' AND moved_to IS NULL';
+
+	if ($prune_sticky == '0')
+		$sql .= ' AND sticky=0';
+
+	if ($prune_from != 'all')
+	{
+		$prune_from = intval($prune_from);
+		$sql .= ' AND forum_id='.$prune_from;
+
+		// Fetch the forum name (just for cosmetic reasons)
+		$result = $db->query('SELECT forum_name FROM '.$db->prefix.'forums WHERE id='.$prune_from) or error('Unable to fetch forum name', __FILE__, __LINE__, $db->error());
+		$forum = '"'.pun_htmlspecialchars($db->result($result)).'"';
+	}
+	else
+		$forum = $lang_admin_maintenance['All forums'];
+
+	$result = $db->query($sql) or error('Unable to fetch topic prune count', __FILE__, __LINE__, $db->error());
+	$num_topics = $db->result($result);
+
+	if (!$num_topics)
+		message(sprintf($lang_admin_maintenance['No old topics message'], $prune_days));
+
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Prune']);
+	define('PUN_ACTIVE_PAGE', 'admin');
+	require PUN_ROOT.'header.php';
+
+	generate_admin_menu('maintenance');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo $lang_admin_maintenance['Prune head'] ?></span></h2>
+		<div class="box">
+			<form method="post" action="admin_maintenance.php">
+				<div class="inform">
+					<input type="hidden" name="action" value="prune" />
+					<input type="hidden" name="prune_days" value="<?php echo $prune_days ?>" />
+					<input type="hidden" name="prune_sticky" value="<?php echo $prune_sticky ?>" />
+					<input type="hidden" name="prune_from" value="<?php echo $prune_from ?>" />
+					<fieldset>
+						<legend><?php echo $lang_admin_maintenance['Confirm prune subhead'] ?></legend>
+						<div class="infldset">
+							<p><?php printf($lang_admin_maintenance['Confirm prune info'], $prune_days, $forum, forum_number_format($num_topics)) ?></p>
+							<p class="warntext"><?php echo $lang_admin_maintenance['Confirm prune warn'] ?></p>
+						</div>
+					</fieldset>
+				</div>
+				<p class="buttons"><input type="submit" name="prune_comply" value="<?php echo $lang_admin_common['Prune'] ?>" /><a href="javascript:history.go(-1)"><?php echo $lang_admin_common['Go back'] ?></a></p>
+			</form>
+		</div>
+	</div>
+	<div class="clearer"></div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+	exit;
+}
+
 
 // Get the first post ID from the db
-$result = $db->query('SELECT id FROM '.$db->prefix.'topics ORDER BY id LIMIT 1') or error('Impossible de retrouver les informations de discussions', __FILE__, __LINE__, $db->error());
+$result = $db->query('SELECT id FROM '.$db->prefix.'posts ORDER BY id ASC LIMIT 1') or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
 if ($db->num_rows($result))
 	$first_id = $db->result($result);
 
-$page_title = pun_htmlspecialchars($pun_config['o_board_title']).' / Admin / Maintenance';
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Maintenance']);
+define('PUN_ACTIVE_PAGE', 'admin');
 require PUN_ROOT.'header.php';
 
@@ -142,36 +254,96 @@
 ?>
 	<div class="blockform">
-		<h2><span>Maintenance des Forums</span></h2>
+		<h2><span><?php echo $lang_admin_maintenance['Maintenance head'] ?></span></h2>
 		<div class="box">
 			<form method="get" action="admin_maintenance.php">
 				<div class="inform">
+					<input type="hidden" name="action" value="rebuild" />
 					<fieldset>
-						<legend>Reconstruction des index de recherches</legend>
+						<legend><?php echo $lang_admin_maintenance['Rebuild index subhead'] ?></legend>
 						<div class="infldset">
-							<p>Si vous avez ajoutÃ©, modifiÃ© ou supprimÃ© manuellement des messages dans la base de donnÃ©es ou si vous avez des problÃšmes avec la  recherche vous devriez reconstruire les index de recherche (supprime les mots inutiles). Pour de meilleures performances, pendant la reconstruction des index, vous devriez mettre vos forums en mode maintenance. <strong>La reconstruction des index de recherches peut prendre beaucoup de temps et augmenter considÃ©rablement la charge serveur au cours du processus de reconstruction&#160;!</strong></p>
+							<p><?php printf($lang_admin_maintenance['Rebuild index info'], '<a href="admin_options.php#maintenance">'.$lang_admin_common['Maintenance mode'].'</a>') ?></p>
 							<table class="aligntop" cellspacing="0">
 								<tr>
-									<th scope="row">Discussions par cycle</th>
-									<td>
-										<input type="text" name="i_per_page" size="7" maxlength="7" value="100" tabindex="1" />
-										<span>Le nombre de discussions Ã  traiter par cycle. Si vous saisissez 100, une centaine de discussions sera traitÃ©e et ensuite la page sera actualisÃ©e. Cela permet d'Ã©viter que le script n'atteigne le temps limite d'exÃ©cution pendant le processus de recontruction.</span>
-									</td>
-								</tr>
-								<tr>
-									<th scope="row">ID de la discussion de dÃ©part</th>
+									<th scope="row"><?php echo $lang_admin_maintenance['Posts per cycle label'] ?></th>
+									<td>
+										<input type="text" name="i_per_page" size="7" maxlength="7" value="300" tabindex="1" />
+										<span><?php echo $lang_admin_maintenance['Posts per cycle help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_maintenance['Starting post label'] ?></th>
 									<td>
 										<input type="text" name="i_start_at" size="7" maxlength="7" value="<?php echo (isset($first_id)) ? $first_id : 0 ?>" tabindex="2" />
-										<span>L'ID de discussion de laquelle vous souhaitez lancer la reconstruction. La valeur par dÃ©faut est le premier ID diponible dans la base de donnÃ©es. Normalement vous ne devriez pas avoir Ã  changer ceci.</span>
-									</td>
-								</tr>
-								<tr>
-									<th scope="row">Vider index</th>
+										<span><?php echo $lang_admin_maintenance['Starting post help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_maintenance['Empty index label'] ?></th>
 									<td class="inputadmin">
-										<span><input type="checkbox" name="i_empty_index" value="1" tabindex="3" checked="checked" />&#160;&#160;Cochez cette option si vous souhaitez que les index de recherches soient vidÃ©s avant la reconstruction (voir ci-dessous).</span>
+										<span><input type="checkbox" name="i_empty_index" value="1" tabindex="3" checked="checked" />&#160;&#160;<?php echo $lang_admin_maintenance['Empty index help'] ?></span>
 									</td>
 								</tr>
 							</table>
-							<p class="topspace">Lorsque le processus sera terminÃ© vous serez redirigÃ© sur cette page. Il est fortement recommandÃ© que Javascript soit activÃ© sur votre navigateur pour effectuer la reconstruction (pour une redirection automatique lorsqu'un cycle est achevÃ©). Si vous Ãªtes obligÃ© d'abandonner le processus de reconstruction, notez l'ID du dernier sujet traitÃ© et saisissez cet ID+1 dans le champ "ID de la discussion de dÃ©part" quand/si vous reprennez le processus  ("Vider index" ne doit pas Ãªtre cochÃ©).</p>
-							<div class="fsetsubmit"><input type="submit" name="rebuild_index" value=" Reconstruire index " tabindex="4" /></div>
+							<p class="topspace"><?php echo $lang_admin_maintenance['Rebuild completed info'] ?></p>
+							<div class="fsetsubmit"><input type="submit" name="rebuild_index" value="<?php echo $lang_admin_maintenance['Rebuild index'] ?>" tabindex="4" /></div>
+						</div>
+					</fieldset>
+				</div>
+			</form>
+
+			<form method="post" action="admin_maintenance.php" onsubmit="return process_form(this)">
+				<div class="inform">
+					<input type="hidden" name="action" value="prune" />
+					<fieldset>
+						<legend><?php echo $lang_admin_maintenance['Prune subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_maintenance['Days old label'] ?></th>
+									<td>
+										<input type="text" name="req_prune_days" size="3" maxlength="3" tabindex="5" />
+										<span><?php echo $lang_admin_maintenance['Days old help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_maintenance['Prune sticky label'] ?></th>
+									<td>
+										<input type="radio" name="prune_sticky" value="1" tabindex="6" checked="checked" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="prune_sticky" value="0" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_maintenance['Prune sticky help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_maintenance['Prune from label'] ?></th>
+									<td>
+										<select name="prune_from" tabindex="7">
+											<option value="all"><?php echo $lang_admin_maintenance['All forums'] ?></option>
+<?php
+
+	$result = $db->query('SELECT c.id AS cid, c.cat_name, f.id AS fid, f.forum_name FROM '.$db->prefix.'categories AS c INNER JOIN '.$db->prefix.'forums AS f ON c.id=f.cat_id WHERE f.redirect_url IS NULL ORDER BY c.disp_position, c.id, f.disp_position') or error('Unable to fetch category/forum list', __FILE__, __LINE__, $db->error());
+
+	$cur_category = 0;
+	while ($forum = $db->fetch_assoc($result))
+	{
+		if ($forum['cid'] != $cur_category) // Are we still in the same category?
+		{
+			if ($cur_category)
+				echo "\t\t\t\t\t\t\t\t\t\t\t".'</optgroup>'."\n";
+
+			echo "\t\t\t\t\t\t\t\t\t\t\t".'<optgroup label="'.pun_htmlspecialchars($forum['cat_name']).'">'."\n";
+			$cur_category = $forum['cid'];
+		}
+
+		echo "\t\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$forum['fid'].'">'.pun_htmlspecialchars($forum['forum_name']).'</option>'."\n";
+	}
+
+?>
+											</optgroup>
+										</select>
+										<span><?php echo $lang_admin_maintenance['Prune from help'] ?></span>
+									</td>
+								</tr>
+							</table>
+							<p class="topspace"><?php printf($lang_admin_maintenance['Prune info'], '<a href="admin_options.php#maintenance">'.$lang_admin_common['Maintenance mode'].'</a>') ?></p>
+							<div class="fsetsubmit"><input type="submit" name="prune" value="<?php echo $lang_admin_common['Prune'] ?>" tabindex="8" /></div>
 						</div>
 					</fieldset>
