[Xfce4-commits] <forum:master> Update to fluxbb 1.4.4.

Nick Schermer noreply at xfce.org
Thu Feb 3 21:08:01 CET 2011


Updating branch refs/heads/master
         to 54c43bab13f0bcde143109e391b5eda6201338b2 (commit)
       from c2e97e44b135e0bc70a02aa63e6953d36ec8b3be (commit)

commit 54c43bab13f0bcde143109e391b5eda6201338b2
Author: Nick Schermer <nick at xfce.org>
Date:   Thu Feb 3 21:07:18 2011 +0100

    Update to fluxbb 1.4.4.

 admin_bans.php                                 |    8 +-
 admin_categories.php                           |    2 +-
 admin_censoring.php                            |   28 ++++-
 admin_forums.php                               |    3 +-
 admin_groups.php                               |    2 +-
 admin_index.php                                |    2 +-
 admin_loader.php                               |    2 +-
 admin_maintenance.php                          |    8 +-
 admin_options.php                              |    2 +-
 admin_permissions.php                          |    2 +-
 admin_prune.php                                |    2 +-
 admin_ranks.php                                |    2 +-
 admin_reports.php                              |   10 ++-
 admin_users.php                                |    2 +-
 db_update.php                                  |   44 ++------
 delete.php                                     |    2 +-
 edit.php                                       |    2 +-
 extern.php                                     |    4 +-
 footer.php                                     |    2 +-
 header.php                                     |   31 +++---
 help.php                                       |    2 +-
 include/cache.php                              |   78 +++++++++++++-
 include/common.php                             |    8 +-
 include/common_admin.php                       |    2 +-
 include/dblayer/common_db.php                  |    2 +-
 include/dblayer/mysql.php                      |    2 +-
 include/dblayer/mysql_innodb.php               |    2 +-
 include/dblayer/mysqli.php                     |    2 +-
 include/dblayer/mysqli_innodb.php              |    2 +-
 include/dblayer/pgsql.php                      |    2 +-
 include/dblayer/sqlite.php                     |    2 +-
 include/email.php                              |    2 +-
 include/functions.php                          |   86 +++++++++++----
 include/parser.php                             |    8 +-
 include/search_idx.php                         |   29 +++---
 index.php                                      |   21 ++++-
 lang/English/admin_censoring.php               |    5 +-
 lang/English/admin_forums.php                  |    1 +
 lang/English/admin_maintenance.php             |    1 +
 lang/English/admin_reports.php                 |   10 +-
 lang/English/common.php                        |   14 ++-
 lang/English/mail_templates/new_reply.tpl      |    4 +-
 lang/English/mail_templates/new_reply_full.tpl |    4 +-
 lang/English/mail_templates/new_topic.tpl      |    4 +-
 lang/English/mail_templates/new_topic_full.tpl |    4 +-
 lang/English/search.php                        |   92 +++++++++-------
 lang/English/topic.php                         |    1 +
 login.php                                      |   12 ++-
 misc.php                                       |    2 +-
 moderate.php                                   |   22 ++++-
 post.php                                       |   28 +++---
 profile.php                                    |   18 +++-
 register.php                                   |    2 +-
 search.php                                     |  138 +++++++++++++++++++++---
 style/Air.css                                  |    8 ++
 style/Cobalt.css                               |    3 +
 style/Earth.css                                |    8 ++
 style/Fire.css                                 |    8 ++
 style/Lithium.css                              |    3 +
 style/Mercury.css                              |    3 +
 style/Oxygen.css                               |    3 +
 style/Radium.css                               |    3 +
 style/Sulfur.css                               |    3 +
 style/Technetium.css                           |    3 +
 userlist.php                                   |    2 +-
 viewforum.php                                  |   49 +++++++--
 viewtopic.php                                  |   31 +++++-
 67 files changed, 647 insertions(+), 252 deletions(-)

diff --git a/admin_bans.php b/admin_bans.php
index 726ce0a..a344d9f 100644
--- a/admin_bans.php
+++ b/admin_bans.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
@@ -304,7 +304,7 @@ else if (isset($_GET['find_ban']))
 
 	$expire_after = isset($_GET['expire_after']) ? trim($_GET['expire_after']) : '';
 	$expire_before = isset($_GET['expire_before']) ? trim($_GET['expire_before']) : '';
-	$order_by = isset($_GET['order_by']) && in_array($_GET['order_by'], array('username', 'ip', 'email', 'expire')) ? $_GET['order_by'] : 'username';
+	$order_by = isset($_GET['order_by']) && in_array($_GET['order_by'], array('username', 'ip', 'email', 'expire')) ? 'b.'.$_GET['order_by'] : 'b.username';
 	$direction = isset($_GET['direction']) && $_GET['direction'] == 'DESC' ? 'DESC' : 'ASC';
 
 	$query_str[] = 'order_by='.$order_by;
@@ -319,7 +319,7 @@ else if (isset($_GET['find_ban']))
 		if ($expire_after === false || $expire_after == -1)
 			message($lang_admin_bans['Invalid date message']);
 
-		$conditions[] = 'expire>'.$expire_after;
+		$conditions[] = 'b.expire>'.$expire_after;
 	}
 	if ($expire_before != '')
 	{
@@ -329,7 +329,7 @@ else if (isset($_GET['find_ban']))
 		if ($expire_before === false || $expire_before == -1)
 			message($lang_admin_bans['Invalid date message']);
 
-		$conditions[] = 'expire<'.$expire_before;
+		$conditions[] = 'b.expire<'.$expire_before;
 	}
 
 	$like_command = ($db_type == 'pgsql') ? 'ILIKE' : 'LIKE';
diff --git a/admin_categories.php b/admin_categories.php
index a0f6a1e..90c2935 100644
--- a/admin_categories.php
+++ b/admin_categories.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/admin_censoring.php b/admin_censoring.php
index b63cd58..8d18f0b 100644
--- a/admin_censoring.php
+++ b/admin_censoring.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
@@ -28,11 +28,17 @@ if (isset($_POST['add_word']))
 	$search_for = pun_trim($_POST['new_search_for']);
 	$replace_with = pun_trim($_POST['new_replace_with']);
 
-	if ($search_for == '' || $replace_with == '')
-		message($lang_admin_censoring['Must enter both message']);
+	if ($search_for == '')
+		message($lang_admin_censoring['Must enter word message']);
 
 	$db->query('INSERT INTO '.$db->prefix.'censoring (search_for, replace_with) VALUES (\''.$db->escape($search_for).'\', \''.$db->escape($replace_with).'\')') or error('Unable to add censor word', __FILE__, __LINE__, $db->error());
 
+	// Regenerate the censoring cache
+	if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+		require PUN_ROOT.'include/cache.php';
+
+	generate_censoring_cache();
+
 	redirect('admin_censoring.php', $lang_admin_censoring['Word added redirect']);
 }
 
@@ -46,11 +52,17 @@ else if (isset($_POST['update']))
 	$search_for = pun_trim($_POST['search_for'][$id]);
 	$replace_with = pun_trim($_POST['replace_with'][$id]);
 
-	if ($search_for == '' || $replace_with == '')
-		message($lang_admin_censoring['Must search both message']);
+	if ($search_for == '')
+		message($lang_admin_censoring['Must enter word message']);
 
 	$db->query('UPDATE '.$db->prefix.'censoring SET search_for=\''.$db->escape($search_for).'\', replace_with=\''.$db->escape($replace_with).'\' WHERE id='.$id) or error('Unable to update censor word', __FILE__, __LINE__, $db->error());
 
+	// Regenerate the censoring cache
+	if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+		require PUN_ROOT.'include/cache.php';
+
+	generate_censoring_cache();
+
 	redirect('admin_censoring.php', $lang_admin_censoring['Word updated redirect']);
 }
 
@@ -63,6 +75,12 @@ else if (isset($_POST['remove']))
 
 	$db->query('DELETE FROM '.$db->prefix.'censoring WHERE id='.$id) or error('Unable to delete censor word', __FILE__, __LINE__, $db->error());
 
+	// Regenerate the censoring cache
+	if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+		require PUN_ROOT.'include/cache.php';
+
+	generate_censoring_cache();
+
 	redirect('admin_censoring.php',  $lang_admin_censoring['Word removed redirect']);
 }
 
diff --git a/admin_forums.php b/admin_forums.php
index c361c3d..9fe2adc 100644
--- a/admin_forums.php
+++ b/admin_forums.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
@@ -277,6 +277,7 @@ else if (isset($_GET['edit_forum']))
 										<select name="sort_by" tabindex="4">
 											<option value="0"<?php if ($cur_forum['sort_by'] == '0') echo ' selected="selected"' ?>><?php echo $lang_admin_forums['Last post'] ?></option>
 											<option value="1"<?php if ($cur_forum['sort_by'] == '1') echo ' selected="selected"' ?>><?php echo $lang_admin_forums['Topic start'] ?></option>
+											<option value="2"<?php if ($cur_forum['sort_by'] == '2') echo ' selected="selected"' ?>><?php echo $lang_admin_forums['Subject'] ?></option>
 										</select>
 									</td>
 								</tr>
diff --git a/admin_groups.php b/admin_groups.php
index 6785e13..0867ad5 100644
--- a/admin_groups.php
+++ b/admin_groups.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/admin_index.php b/admin_index.php
index 6406a6b..0064f6a 100644
--- a/admin_index.php
+++ b/admin_index.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/admin_loader.php b/admin_loader.php
index c89b745..ceaf51c 100644
--- a/admin_loader.php
+++ b/admin_loader.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/admin_maintenance.php b/admin_maintenance.php
index 9ccd8b1..81096eb 100644
--- a/admin_maintenance.php
+++ b/admin_maintenance.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
@@ -26,8 +26,10 @@ if (isset($_GET['i_per_page']) && isset($_GET['i_start_at']))
 {
 	$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']);
+
+	// Check per page is > 0
+	if ($per_page < 1)
+		message($lang_admin_maintenance['Must be integer message']);
 
 	@set_time_limit(0);
 
diff --git a/admin_options.php b/admin_options.php
index abb92ee..fc0a933 100644
--- a/admin_options.php
+++ b/admin_options.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/admin_permissions.php b/admin_permissions.php
index a73a3b3..c92e01d 100644
--- a/admin_permissions.php
+++ b/admin_permissions.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/admin_prune.php b/admin_prune.php
index 8b53d69..8d80c87 100644
--- a/admin_prune.php
+++ b/admin_prune.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/admin_ranks.php b/admin_ranks.php
index 13b5bad..2ea5892 100644
--- a/admin_ranks.php
+++ b/admin_ranks.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/admin_reports.php b/admin_reports.php
index 3675f8c..c2d99de 100644
--- a/admin_reports.php
+++ b/admin_reports.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
@@ -32,6 +32,14 @@ if (isset($_POST['zap_id']))
 
 	if ($zapped == '')
 		$db->query('UPDATE '.$db->prefix.'reports SET zapped='.time().', zapped_by='.$pun_user['id'].' WHERE id='.$zap_id) or error('Unable to zap report', __FILE__, __LINE__, $db->error());
+	
+	// Delete old reports (which cannot be viewed anyway)
+	$result = $db->query('SELECT zapped FROM '.$db->prefix.'reports WHERE zapped IS NOT NULL ORDER BY zapped DESC LIMIT 10,1') or error('Unable to fetch read reports to delete', __FILE__, __LINE__, $db->error());
+	if ($db->num_rows($result) > 0)
+	{
+		$zapped_threshold = $db->result($result);
+		$db->query('DELETE FROM '.$db->prefix.'reports WHERE zapped <= '.$zapped_threshold) or error('Unable to delete old read reports', __FILE__, __LINE__, $db->error());
+	}
 
 	redirect('admin_reports.php', $lang_admin_reports['Report zapped redirect']);
 }
diff --git a/admin_users.php b/admin_users.php
index 89d5b00..f1266f2 100644
--- a/admin_users.php
+++ b/admin_users.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/db_update.php b/db_update.php
index 0856f2a..ff299a5 100644
--- a/db_update.php
+++ b/db_update.php
@@ -1,19 +1,19 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
 
 // The FluxBB version this script updates to
-define('UPDATE_TO', '1.4.3');
+define('UPDATE_TO', '1.4.4');
 
 define('UPDATE_TO_DB_REVISION', 10);
-define('UPDATE_TO_SI_REVISION', 1);
-define('UPDATE_TO_PARSER_REVISION', 1);
+define('UPDATE_TO_SI_REVISION', 2);
+define('UPDATE_TO_PARSER_REVISION', 2);
 
-define('MIN_PHP_VERSION', '4.3.0');
+define('MIN_PHP_VERSION', '4.4.0');
 define('MIN_MYSQL_VERSION', '4.1.2');
 define('MIN_PGSQL_VERSION', '7.0.0');
 define('PUN_SEARCH_MIN_WORD', 3);
@@ -471,34 +471,6 @@ if (empty($stage))
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <title><?php echo $lang_update['Update'] ?></title>
 <link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
-<script type="text/javascript">
-/* <![CDATA[ */
-function process_form(the_form)
-{
-	var element_names = {
-		"req_db_pass": "<?php echo $lang_update['Database password'] ?>",
-		"req_old_charset": "<?php echo $lang_update['Current character set label'] ?>"
-	};
-	if (document.all || document.getElementById)
-	{
-		for (var i = 0; i < the_form.length; ++i)
-		{
-			var elem = the_form.elements[i];
-			if (elem.name && (/^req_/.test(elem.name)))
-			{
-				if (!elem.value && elem.type && (/^(?:text(?:area)?|password|file)$/i.test(elem.type)))
-				{
-					alert('"' + element_names[elem.name] + '" <?php echo $lang_update['Required field'] ?>');
-					elem.focus();
-					return false;
-				}
-			}
-		}
-	}
-	return true;
-}
-/* ]]> */
-</script>
 </head>
 <body onload="document.getElementById('install').req_db_type.focus();document.getElementById('install').start.disabled=false;">
 
@@ -519,7 +491,7 @@ function process_form(the_form)
 <div class="blockform">
 	<h2><span><?php echo $lang_update['Update'] ?></span></h2>
 	<div class="box">
-		<form method="post" action="db_update.php" onsubmit="this.start.disabled=true;if(process_form(this)){return true;}else{this.start.disabled=false;return false;}">
+		<form method="post" action="db_update.php">
 			<input type="hidden" name="stage" value="start" />
 			<div class="inform">
 				<fieldset>
@@ -1114,7 +1086,7 @@ switch ($stage)
 			$db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.$db->escape($default_style).'\' WHERE conf_name = \'o_default_style\'') or error('Unable to update default style config', __FILE__, __LINE__, $db->error());
 
 		// Should we do charset conversion or not?
-		if (strpos($cur_version, '1.2') === 0 && isset($_GET['convert_charset']))
+		if (strpos($cur_version, '1.2') === 0 && isset($_POST['convert_charset']))
 			$query_str = '?stage=conv_bans&req_old_charset='.$old_charset;
 
 		break;
@@ -1489,7 +1461,7 @@ switch ($stage)
 				else if (preg_match('/(?:\[\/?(?:b|u|s|ins|del|em|i|h|colou?r|quote|code|img|url|email|list|\*)\]|\[(?:img|url|quote|list)=)/i', $username))
 					$errors[$id][] = $lang_update['Username BBCode error'];
 
-				$result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE (UPPER(username)=UPPER(\''.$db->escape($username).'\') OR UPPER(username)=UPPER(\''.$db->escape(preg_replace('/[^\w]/', '', $username)).'\')) AND id>1') or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+				$result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE (UPPER(username)=UPPER(\''.$db->escape($username).'\') OR UPPER(username)=UPPER(\''.$db->escape(preg_replace('/[^\p{L}\p{N}]/u', '', $username)).'\')) AND id>1') or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
 
 				if ($db->num_rows($result))
 				{
diff --git a/delete.php b/delete.php
index bdd1270..7844c4b 100644
--- a/delete.php
+++ b/delete.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/edit.php b/edit.php
index 3c5f4d4..9d29456 100644
--- a/edit.php
+++ b/edit.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/extern.php b/extern.php
index 7c52fda..e921889 100644
--- a/extern.php
+++ b/extern.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
@@ -139,7 +139,7 @@ function output_rss($feed)
 		echo "\t\t\t".'<description><![CDATA['.escape_cdata($item['description']).']]></description>'."\n";
 		echo "\t\t\t".'<author><![CDATA['.(isset($item['author']['email']) ? escape_cdata($item['author']['email']) : 'dummy at example.com').' ('.escape_cdata($item['author']['name']).')]]></author>'."\n";
 		echo "\t\t\t".'<pubDate>'.gmdate('r', $item['pubdate']).'</pubDate>'."\n";
-		echo "\t\t\t".'<guid>'.$item['link'].'</guid>'."\n";
+		echo "\t\t\t".'<guid>'.pun_htmlspecialchars($item['link']).'</guid>'."\n";
 
 		echo "\t\t".'</item>'."\n";
 	}
diff --git a/footer.php b/footer.php
index 24d7d8e..e6eafb0 100644
--- a/footer.php
+++ b/footer.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/header.php b/header.php
index 70a4e66..ed7b0e4 100644
--- a/header.php
+++ b/header.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
@@ -142,8 +142,12 @@ function process_form(the_form)
 // JavaScript tricks for IE6 and older
 echo '<!--[if lte IE 6]><script type="text/javascript" src="style/imports/minmax.js"></script><![endif]-->'."\n";
 
-if (isset($page_head))
-	echo implode("\n", $page_head)."\n";
+if (!isset($page_head))
+	$page_head = array();
+
+$page_head['top'] = '<link rel="top" href="index.php" title="'.$lang_common['Forum index'].'" />';
+
+echo implode("\n", $page_head)."\n";
 
 $tpl_temp = trim(ob_get_contents());
 $tpl_main = str_replace('<pun_head>', $tpl_temp, $tpl_main);
@@ -181,7 +185,7 @@ $tpl_main = str_replace('<pun_navlinks>','<div id="brdmenu" class="inbox">'."\n\
 
 
 // START SUBST - <pun_status>
-$page_statusinfo = $page_quicklinks = array();
+$page_statusinfo = $page_topicsearches = array();
 
 if ($pun_user['is_guest'])
 	$page_statusinfo = '<p>'.$lang_common['Not logged in'].'</p>';
@@ -204,19 +208,18 @@ else
 			$page_statusinfo[] = '<li class="maintenancelink"><span><strong><a href="admin_options.php#maintenance">'.$lang_common['Maintenance mode enabled'].'</a></strong></span></li>';
 	}
 
-	$script_name = basename($_SERVER['PHP_SELF']);
-	if ($script_name == 'index.php')
-		$page_quicklinks[] = '<a href="misc.php?action=markread">'.$lang_common['Mark all as read'].'</a>';
-
 	if ($pun_user['g_read_board'] == '1' && $pun_user['g_search'] == '1')
-		$page_quicklinks[] = '<a href="search.php?action=show_new" title="'.$lang_common['Show new posts'].'">'.$lang_common['New posts'].'</a>';
+	{
+		$page_topicsearches[] = '<a href="search.php?action=show_replies" title="'.$lang_common['Show posted topics'].'">'.$lang_common['Posted topics'].'</a>';
+		$page_topicsearches[] = '<a href="search.php?action=show_new" title="'.$lang_common['Show new posts'].'">'.$lang_common['New posts header'].'</a>';
+	}
 }
 
 // Quick searches
 if ($pun_user['g_read_board'] == '1' && $pun_user['g_search'] == '1')
 {
-	$page_quicklinks[] = '<a href="search.php?action=show_recent" title="'.$lang_common['Show active topics'].'">'.$lang_common['Active topics'].'</a>';
-	$page_quicklinks[] = '<a href="search.php?action=show_unanswered" title="'.$lang_common['Show unanswered topics'].'">'.$lang_common['Unanswered topics'].'</a>';
+	$page_topicsearches[] = '<a href="search.php?action=show_recent" title="'.$lang_common['Show active topics'].'">'.$lang_common['Active topics'].'</a>';
+	$page_topicsearches[] = '<a href="search.php?action=show_unanswered" title="'.$lang_common['Show unanswered topics'].'">'.$lang_common['Unanswered topics'].'</a>';
 }
 
 
@@ -234,10 +237,10 @@ else
 	$tpl_temp .= "\n\t\t\t".$page_statusinfo;
 
 // Generate quicklinks
-if (count($page_quicklinks))
+if (count($page_topicsearches))
 {
 	$tpl_temp .= "\n\t\t\t".'<ul class="conr">';
-	$tpl_temp .= "\n\t\t\t\t".'<li><span>'.implode('</span></li>'."\n\t\t\t\t".'<li><span>', $page_quicklinks).'</span></li>';
+	$tpl_temp .= "\n\t\t\t\t".'<li><span>'.$lang_common['Topic searches'].' '.implode(' | ', $page_topicsearches).'</span></li>';
 	$tpl_temp .= "\n\t\t\t".'</ul>'."\n\t\t\t".'<div class="clearer"></div>';
 }
 
@@ -248,7 +251,7 @@ $tpl_main = str_replace('<pun_status>', $tpl_temp, $tpl_main);
 
 
 // START SUBST - <pun_announcement>
-if ($pun_config['o_announcement'] == '1')
+if ($pun_user['g_read_board'] == '1' && $pun_config['o_announcement'] == '1')
 {
 	ob_start();
 
diff --git a/help.php b/help.php
index e046273..c1e94ae 100644
--- a/help.php
+++ b/help.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/include/cache.php b/include/cache.php
index 7db17ff..89f6b73 100644
--- a/include/cache.php
+++ b/include/cache.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
@@ -26,7 +26,7 @@ function generate_config_cache()
 	// Output config as PHP code
 	$fh = @fopen(FORUM_CACHE_DIR.'cache_config.php', 'wb');
 	if (!$fh)
-		error('Unable to write configuration cache file to cache directory. Please make sure PHP has write access to the directory \'cache\'', __FILE__, __LINE__);
+		error('Unable to write configuration cache file to cache directory. Please make sure PHP has write access to the directory \''.pun_htmlspecialchars(FORUM_CACHE_DIR).'\'', __FILE__, __LINE__);
 
 	fwrite($fh, '<?php'."\n\n".'define(\'PUN_CONFIG_LOADED\', 1);'."\n\n".'$pun_config = '.var_export($output, true).';'."\n\n".'?>');
 
@@ -54,7 +54,7 @@ function generate_bans_cache()
 	// Output ban list as PHP code
 	$fh = @fopen(FORUM_CACHE_DIR.'cache_bans.php', 'wb');
 	if (!$fh)
-		error('Unable to write bans cache file to cache directory. Please make sure PHP has write access to the directory \'cache\'', __FILE__, __LINE__);
+		error('Unable to write bans cache file to cache directory. Please make sure PHP has write access to the directory \''.pun_htmlspecialchars(FORUM_CACHE_DIR).'\'', __FILE__, __LINE__);
 
 	fwrite($fh, '<?php'."\n\n".'define(\'PUN_BANS_LOADED\', 1);'."\n\n".'$pun_bans = '.var_export($output, true).';'."\n\n".'?>');
 
@@ -82,7 +82,7 @@ function generate_ranks_cache()
 	// Output ranks list as PHP code
 	$fh = @fopen(FORUM_CACHE_DIR.'cache_ranks.php', 'wb');
 	if (!$fh)
-		error('Unable to write ranks cache file to cache directory. Please make sure PHP has write access to the directory \'cache\'', __FILE__, __LINE__);
+		error('Unable to write ranks cache file to cache directory. Please make sure PHP has write access to the directory \''.pun_htmlspecialchars(FORUM_CACHE_DIR).'\'', __FILE__, __LINE__);
 
 	fwrite($fh, '<?php'."\n\n".'define(\'PUN_RANKS_LOADED\', 1);'."\n\n".'$pun_ranks = '.var_export($output, true).';'."\n\n".'?>');
 
@@ -127,7 +127,7 @@ function generate_quickjump_cache($group_id = false)
 		// Output quick jump as PHP code
 		$fh = @fopen(FORUM_CACHE_DIR.'cache_quickjump_'.$group_id.'.php', 'wb');
 		if (!$fh)
-			error('Unable to write quick jump cache file to cache directory. Please make sure PHP has write access to the directory \'cache\'', __FILE__, __LINE__);
+			error('Unable to write quick jump cache file to cache directory. Please make sure PHP has write access to the directory \''.pun_htmlspecialchars(FORUM_CACHE_DIR).'\'', __FILE__, __LINE__);
 
 		$output = '<?php'."\n\n".'if (!defined(\'PUN\')) exit;'."\n".'define(\'PUN_QJ_LOADED\', 1);'."\n".'$forum_id = isset($forum_id) ? $forum_id : 0;'."\n\n".'?>';
 
@@ -168,4 +168,72 @@ function generate_quickjump_cache($group_id = false)
 	}
 }
 
+
+//
+// Generate the censoring cache PHP script
+//
+function generate_censoring_cache()
+{
+	global $db;
+
+	$result = $db->query('SELECT search_for, replace_with FROM '.$db->prefix.'censoring') or error('Unable to fetch censoring list', __FILE__, __LINE__, $db->error());
+	$num_words = $db->num_rows($result);
+
+	$search_for = $replace_with = array();
+	for ($i = 0; $i < $num_words; $i++)
+	{
+		list($search_for[$i], $replace_with[$i]) = $db->fetch_row($result);
+		$search_for[$i] = '/(?<=[^\p{L}\p{N}])('.str_replace('\*', '[\p{L}\p{N}]*?', preg_quote($search_for[$i], '/')).')(?=[^\p{L}\p{N}])/iu';
+	}
+
+	// Output censored words as PHP code
+	$fh = @fopen(FORUM_CACHE_DIR.'cache_censoring.php', 'wb');
+	if (!$fh)
+		error('Unable to write censoring cache file to cache directory. Please make sure PHP has write access to the directory \''.pun_htmlspecialchars(FORUM_CACHE_DIR).'\'', __FILE__, __LINE__);
+
+	fwrite($fh, '<?php'."\n\n".'define(\'PUN_CENSOR_LOADED\', 1);'."\n\n".'$search_for = '.var_export($search_for, true).';'."\n\n".'$replace_with = '.var_export($replace_with, true).';'."\n\n".'?>');
+
+	fclose($fh);
+
+	if (function_exists('apc_delete_file'))
+		@apc_delete_file(FORUM_CACHE_DIR.'cache_censoring.php');
+}
+
+
+//
+// Generate the stopwords cache PHP script
+//
+function generate_stopwords_cache()
+{
+	$stopwords = array();
+
+	$d = dir(PUN_ROOT.'lang');
+	while (($entry = $d->read()) !== false)
+	{
+		if ($entry{0} == '.')
+			continue;
+
+		if (is_dir(PUN_ROOT.'lang/'.$entry) && file_exists(PUN_ROOT.'lang/'.$entry.'/stopwords.txt'))
+			$stopwords = array_merge($stopwords, file(PUN_ROOT.'lang/'.$entry.'/stopwords.txt'));
+	}
+	$d->close();
+
+	// Tidy up and filter the stopwords
+	$stopwords = array_map('pun_trim', $stopwords);
+	$stopwords = array_filter($stopwords);
+
+	// Output stopwords as PHP code
+	$fh = @fopen(FORUM_CACHE_DIR.'cache_stopwords.php', 'wb');
+	if (!$fh)
+		error('Unable to write stopwords cache file to cache directory. Please make sure PHP has write access to the directory \''.pun_htmlspecialchars(FORUM_CACHE_DIR).'\'', __FILE__, __LINE__);
+
+	fwrite($fh, '<?php'."\n\n".'define(\'PUN_STOPWORDS_LOADED\', 1);'."\n\n".'$stopwords = '.var_export($stopwords, true).';'."\n\n".'?>');
+
+	fclose($fh);
+
+	if (function_exists('apc_delete_file'))
+		@apc_delete_file(FORUM_CACHE_DIR.'cache_stopwords.php');
+}
+
+
 define('FORUM_CACHE_FUNCTIONS_LOADED', true);
diff --git a/include/common.php b/include/common.php
index d5caa89..5f0fb8d 100644
--- a/include/common.php
+++ b/include/common.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
@@ -10,11 +10,11 @@ if (!defined('PUN_ROOT'))
 	exit('The constant PUN_ROOT must be defined and point to a valid FluxBB installation root directory.');
 
 // Define the version and database revision that this code was written for
-define('FORUM_VERSION', '1.4.3');
+define('FORUM_VERSION', '1.4.4');
 
 define('FORUM_DB_REVISION', 10);
-define('FORUM_SI_REVISION', 1);
-define('FORUM_PARSER_REVISION', 1);
+define('FORUM_SI_REVISION', 2);
+define('FORUM_PARSER_REVISION', 2);
 
 // Block prefetch requests
 if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch')
diff --git a/include/common_admin.php b/include/common_admin.php
index 240f9f6..98fc797 100644
--- a/include/common_admin.php
+++ b/include/common_admin.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/include/dblayer/common_db.php b/include/dblayer/common_db.php
index 121d895..a832376 100644
--- a/include/dblayer/common_db.php
+++ b/include/dblayer/common_db.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/include/dblayer/mysql.php b/include/dblayer/mysql.php
index e5a41dc..f35609f 100644
--- a/include/dblayer/mysql.php
+++ b/include/dblayer/mysql.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/include/dblayer/mysql_innodb.php b/include/dblayer/mysql_innodb.php
index 1b4d352..8071a3b 100644
--- a/include/dblayer/mysql_innodb.php
+++ b/include/dblayer/mysql_innodb.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/include/dblayer/mysqli.php b/include/dblayer/mysqli.php
index a23065a..cab2776 100644
--- a/include/dblayer/mysqli.php
+++ b/include/dblayer/mysqli.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/include/dblayer/mysqli_innodb.php b/include/dblayer/mysqli_innodb.php
index b209c82..cefbd73 100644
--- a/include/dblayer/mysqli_innodb.php
+++ b/include/dblayer/mysqli_innodb.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/include/dblayer/pgsql.php b/include/dblayer/pgsql.php
index 23f751d..9d4547e 100644
--- a/include/dblayer/pgsql.php
+++ b/include/dblayer/pgsql.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/include/dblayer/sqlite.php b/include/dblayer/sqlite.php
index 0a1820b..422177c 100644
--- a/include/dblayer/sqlite.php
+++ b/include/dblayer/sqlite.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/include/email.php b/include/email.php
index 86f0660..cd7f48d 100644
--- a/include/email.php
+++ b/include/email.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/include/functions.php b/include/functions.php
index 70e60c4..27ef686 100644
--- a/include/functions.php
+++ b/include/functions.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
@@ -25,31 +25,46 @@ function check_cookie(&$pun_user)
 
 	$now = time();
 
-	// We assume it's a guest
-	$cookie = array('user_id' => 1, 'password_hash' => 'Guest', 'expiration_time' => 0);
-
-	// If a cookie is set, we get the user_id and password hash from it
-	if (isset($_COOKIE[$cookie_name]) && preg_match('/a:3:{i:0;s:\d+:"(\d+)";i:1;s:\d+:"([0-9a-f]+)";i:2;i:(\d+);}/', $_COOKIE[$cookie_name], $matches))
-		list(, $cookie['user_id'], $cookie['password_hash'], $cookie['expiration_time']) = $matches;
+	// If the cookie is set and it matches the correct pattern, then read the values from it
+	if (isset($_COOKIE[$cookie_name]) && preg_match('/^(\d+)\|([0-9a-fA-F]+)\|(\d+)\|([0-9a-fA-F]+)$/', $_COOKIE[$cookie_name], $matches))
+	{
+		$cookie = array(
+			'user_id'			=> intval($matches[1]),
+			'password_hash' 	=> $matches[2],
+			'expiration_time'	=> intval($matches[3]),
+			'cookie_hash'		=> $matches[4],
+		);
+	}
 
-	if ($cookie['user_id'] > 1)
+	// If it has a non-guest user, and hasn't expired
+	if (isset($cookie) && $cookie['user_id'] > 1 && $cookie['expiration_time'] > $now)
 	{
+		// If the cookie has been tampered with
+		if (forum_hmac($cookie['user_id'].'|'.$cookie['expiration_time'], $cookie_seed.'_cookie_hash') != $cookie['cookie_hash'])
+		{
+			$expire = $now + 31536000; // The cookie expires after a year
+			pun_setcookie(1, pun_hash(uniqid(rand(), true)), $expire);
+			set_default_user();
+
+			return;
+		}
+
 		// Check if there's a user with the user ID and password hash from the cookie
 		$result = $db->query('SELECT u.*, g.*, o.logged, o.idle FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON u.group_id=g.g_id LEFT JOIN '.$db->prefix.'online AS o ON o.user_id=u.id WHERE u.id='.intval($cookie['user_id'])) or error('Unable to fetch user information', __FILE__, __LINE__, $db->error());
 		$pun_user = $db->fetch_assoc($result);
 
 		// If user authorisation failed
-		if (!isset($pun_user['id']) || md5($cookie_seed.$pun_user['password']) !== $cookie['password_hash'])
+		if (!isset($pun_user['id']) || forum_hmac($pun_user['password'], $cookie_seed.'_password_hash') !== $cookie['password_hash'])
 		{
 			$expire = $now + 31536000; // The cookie expires after a year
-			pun_setcookie(1, md5(uniqid(rand(), true)), $expire);
+			pun_setcookie(1, pun_hash(uniqid(rand(), true)), $expire);
 			set_default_user();
 
 			return;
 		}
 
 		// Send a new, updated cookie with a new expiration timestamp
-		$expire = (intval($cookie['expiration_time']) > $now + $pun_config['o_timeout_visit']) ? $now + 1209600 : $now + $pun_config['o_timeout_visit'];
+		$expire = ($cookie['expiration_time'] > $now + $pun_config['o_timeout_visit']) ? $now + 1209600 : $now + $pun_config['o_timeout_visit'];
 		pun_setcookie($pun_user['id'], $pun_user['password'], $expire);
 
 		// Set a default language if the user selected language no longer exists
@@ -246,6 +261,35 @@ function set_default_user()
 
 
 //
+// SHA1 HMAC with PHP 4 fallback
+//
+function forum_hmac($data, $key, $raw_output = false)
+{
+	if (function_exists('hash_hmac'))
+		return hash_hmac('sha1', $data, $key, $raw_output);
+
+	// If key size more than blocksize then we hash it once
+	if (strlen($key) > 64)
+		$key = sha1($key, true); // we have to use raw output here to match the standard
+
+	// Ensure we're padded to exactly one block boundary
+	$key = str_pad($key, 64, chr(0x00));
+
+	$hmac_opad = str_repeat(chr(0x5C), 64);
+	$hmac_ipad = str_repeat(chr(0x36), 64);
+
+	// Do inner and outer padding
+	for ($i = 0;$i < 64;$i++) {
+		$hmac_opad[$i] = $hmac_opad[$i] ^ $key[$i];
+		$hmac_ipad[$i] = $hmac_ipad[$i] ^ $key[$i];
+	}
+
+	// Finally, calculate the HMAC
+	return sha1($hmac_opad.sha1($hmac_ipad.$data, true), $raw_output);
+}
+
+
+//
 // Set a cookie, FluxBB style!
 // Wrapper for forum_setcookie
 //
@@ -253,7 +297,7 @@ function pun_setcookie($user_id, $password_hash, $expire)
 {
 	global $cookie_name, $cookie_seed;
 
-	forum_setcookie($cookie_name, serialize(array((string) $user_id, md5($cookie_seed.$password_hash), (int) $expire)), $expire);
+	forum_setcookie($cookie_name, $user_id.'|'.forum_hmac($password_hash, $cookie_seed.'_password_hash').'|'.$expire.'|'.forum_hmac($user_id.'|'.$expire, $cookie_seed.'_cookie_hash'), $expire);
 }
 
 
@@ -376,7 +420,7 @@ function check_username($username, $exclude_id = null)
 	// Check that the username (or a too similar username) is not already registered
 	$query = ($exclude_id) ? ' AND id!='.$exclude_id : '';
 
-	$result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE (UPPER(username)=UPPER(\''.$db->escape($username).'\') OR UPPER(username)=UPPER(\''.$db->escape(preg_replace('/[^\w]/', '', $username)).'\')) AND id>1'.$query) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+	$result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE (UPPER(username)=UPPER(\''.$db->escape($username).'\') OR UPPER(username)=UPPER(\''.$db->escape(preg_replace('/[^\p{L}\p{N}]/u', '', $username)).'\')) AND id>1'.$query) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
 
 	if ($db->num_rows($result))
 	{
@@ -471,7 +515,7 @@ function generate_navlinks()
 	}
 
 	// Are there any additional navlinks we should insert into the array before imploding it?
-	if ($pun_config['o_additional_navlinks'] != '')
+	if ($pun_user['g_read_board'] == '1' && $pun_config['o_additional_navlinks'] != '')
 	{
 		if (preg_match_all('#([0-9]+)\s*=\s*(.*?)\n#s', $pun_config['o_additional_navlinks']."\n", $extra_links))
 		{
@@ -779,14 +823,16 @@ function censor_words($text)
 	// If not already built in a previous call, build an array of censor words and their replacement text
 	if (!isset($search_for))
 	{
-		$result = $db->query('SELECT search_for, replace_with FROM '.$db->prefix.'censoring') or error('Unable to fetch censor word list', __FILE__, __LINE__, $db->error());
-		$num_words = $db->num_rows($result);
+		if (file_exists(FORUM_CACHE_DIR.'cache_censoring.php'))
+			include FORUM_CACHE_DIR.'cache_censoring.php';
 
-		$search_for = array();
-		for ($i = 0; $i < $num_words; ++$i)
+		if (!defined('PUN_CENSOR_LOADED'))
 		{
-			list($search_for[$i], $replace_with[$i]) = $db->fetch_row($result);
-			$search_for[$i] = '/(?<=\W)('.str_replace('\*', '\w*?', preg_quote($search_for[$i], '/')).')(?=\W)/i';
+			if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+				require PUN_ROOT.'include/cache.php';
+
+			generate_censoring_cache();
+			require FORUM_CACHE_DIR.'cache_censoring.php';
 		}
 	}
 
diff --git a/include/parser.php b/include/parser.php
index 5d6f805..2e2bb02 100644
--- a/include/parser.php
+++ b/include/parser.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
@@ -784,8 +784,8 @@ function do_clickable($text)
 {
 	$text = ' '.$text;
 
-	$text = preg_replace('#(?<=[\s\]\)])(<)?(\[)?(\()?([\'"]?)(https?|ftp|news){1}://([\w\-]+\.([\w\-]+\.)*\w+(:[0-9]+)?(/[^\s\[]*[^\s.,?!\[;:-])?)\4(?(3)(\)))(?(2)(\]))(?(1)(>))(?![^\s]*\[/(?:url|img)\])#ie', 'stripslashes(\'$1$2$3$4\').handle_url_tag(\'$5://$6\', \'$5://$6\', true).stripslashes(\'$4$10$11$12\')', $text);
-	$text = preg_replace('#(?<=[\s\]\)])(<)?(\[)?(\()?([\'"]?)(www|ftp)\.(([\w\-]+\.)*\w+(:[0-9]+)?(/[^\s\[]*[^\s.,?!\[;:-])?)\4(?(3)(\)))(?(2)(\]))(?(1)(>))(?![^\s]*\[/(?:url|img)\])#ie', 'stripslashes(\'$1$2$3$4\').handle_url_tag(\'$5.$6\', \'$5.$6\', true).stripslashes(\'$4$10$11$12\')', $text);
+	$text = preg_replace('#(?<=[\s\]\)])(<)?(\[)?(\()?([\'"]?)(https?|ftp|news){1}://([\p{L}\p{N}\-]+\.([\p{L}\p{N}\-]+\.)*[\p{L}\p{N}]+(:[0-9]+)?(/[^\s\[]*[^\s.,?!\[;:-])?)\4(?(3)(\)))(?(2)(\]))(?(1)(>))(?![^\s]*\[/(?:url|img)\])#uie', 'stripslashes(\'$1$2$3$4\').handle_url_tag(\'$5://$6\', \'$5://$6\', true).stripslashes(\'$4$10$11$12\')', $text);
+	$text = preg_replace('#(?<=[\s\]\)])(<)?(\[)?(\()?([\'"]?)(www|ftp)\.(([\p{L}\p{N}\-]+\.)*[\p{L}\p{N}]+(:[0-9]+)?(/[^\s\[]*[^\s.,?!\[;:-])?)\4(?(3)(\)))(?(2)(\]))(?(1)(>))(?![^\s]*\[/(?:url|img)\])#uie', 'stripslashes(\'$1$2$3$4\').handle_url_tag(\'$5.$6\', \'$5.$6\', true).stripslashes(\'$4$10$11$12\')', $text);
 
 	return substr($text, 1);
 }
@@ -803,7 +803,7 @@ function do_smilies($text)
 	foreach ($smilies as $smiley_text => $smiley_img)
 	{
 		if (strpos($text, $smiley_text) !== false)
-			$text = preg_replace("#(?<=[>\s])".preg_quote($smiley_text, '#')."(?=\W)#m", '<img src="'.pun_htmlspecialchars(get_base_url(true).'/img/smilies/'.$smiley_img).'" width="15" height="15" alt="'.substr($smiley_img, 0, strrpos($smiley_img, '.')).'" />', $text);
+			$text = preg_replace('#(?<=[>\s])'.preg_quote($smiley_text, '#').'(?=[^\p{L}\p{N}])#um', '<img src="'.pun_htmlspecialchars(get_base_url(true).'/img/smilies/'.$smiley_img).'" width="15" height="15" alt="'.substr($smiley_img, 0, strrpos($smiley_img, '.')).'" />', $text);
 	}
 
 	return substr($text, 1, -1);
diff --git a/include/search_idx.php b/include/search_idx.php
index e9292a4..eed20a2 100644
--- a/include/search_idx.php
+++ b/include/search_idx.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
@@ -51,13 +51,13 @@ function split_words($text, $idx)
 	$text = preg_replace('/\[\/?(b|u|s|ins|del|em|i|h|colou?r|quote|code|img|url|email|list)(?:\=[^\]]*)?\]/', ' ', $text);
 
 	// Remove any apostrophes or dashes which aren't part of words
-	$text = substr(preg_replace('/((?<=\W)[\'\-]|[\'\-](?=\W))/', '', ' '.$text.' '), 1, -1);
+	$text = substr(preg_replace('/((?<=[^\p{L}\p{N}])[\'\-]|[\'\-](?=[^\p{L}\p{N}]))/u', '', ' '.$text.' '), 1, -1);
 
-	// Remove symbols and multiple whitespace, allow % and * if we aren't indexing
-	$text = preg_replace('/[\^\$&\(\)<>`"„\|, at _\?~\+\[\]{}:=\/#\\\\;!\.…\s•'.($idx ? '%\*' : '').']+/u', ' ', $text);
+	// Remove punctuation and symbols (actually anything that isn't a letter or number), allow apostrophes and dashes (and % * if we aren't indexing)
+	$text = preg_replace('/(?![\'\-'.($idx ? '' : '%\*').'])[^\p{L}\p{N}]+/u', ' ', $text);
 
-	// Replace multiple dashes with just one
-	$text = preg_replace('/-{2,}/', '-', $text);
+	// Replace multiple whitespace or dashes
+	$text = preg_replace('/(\s){2,}/u', '\1', $text);
 
 	// Fill an array with all the words
 	$words = array_unique(explode(' ', $text));
@@ -79,24 +79,25 @@ function split_words($text, $idx)
 //
 function validate_search_word($word, $idx)
 {
-	global $pun_user, $pun_config;
 	static $stopwords;
 
 	// If the word is a keyword we don't want to index it, but we do want to be allowed to search it
 	if (is_keyword($word))
 		return !$idx;
 
-	$language = isset($pun_user['language']) ? $pun_user['language'] : $pun_config['o_default_lang'];
 	if (!isset($stopwords))
 	{
-		if (file_exists(PUN_ROOT.'lang/'.$language.'/stopwords.txt'))
+		if (file_exists(FORUM_CACHE_DIR.'cache_stopwords.php'))
+			include FORUM_CACHE_DIR.'cache_stopwords.php';
+
+		if (!defined('PUN_STOPWORDS_LOADED'))
 		{
-			$stopwords = file(PUN_ROOT.'lang/'.$language.'/stopwords.txt');
-			$stopwords = array_map('pun_trim', $stopwords);
-			$stopwords = array_filter($stopwords);
+			if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+				require PUN_ROOT.'include/cache.php';
+
+			generate_stopwords_cache();
+			require FORUM_CACHE_DIR.'cache_stopwords.php';
 		}
-		else
-			$stopwords = array();
 	}
 
 	// If it is a stopword it isn't valid
diff --git a/index.php b/index.php
index b9ac313..36495c9 100644
--- a/index.php
+++ b/index.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
@@ -34,6 +34,12 @@ if ($pun_config['o_feed_type'] == '1')
 else if ($pun_config['o_feed_type'] == '2')
 	$page_head = array('feed' => '<link rel="alternate" type="application/atom+xml" href="extern.php?action=feed&type=atom" title="'.$lang_common['Atom active topics feed'].'" />');
 
+$forum_actions = array();
+
+// Display a "mark all as read" link
+if (!$pun_user['is_guest'])
+	$forum_actions[] = '<a href="misc.php?action=markread">'.$lang_common['Mark all as read'].'</a>';
+
 $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']));
 define('PUN_ALLOW_INDEX', 1);
 define('PUN_ACTIVE_PAGE', 'index');
@@ -181,6 +187,19 @@ if ($pun_user['g_view_users'] == '1')
 else
 	$stats['newest_user'] = pun_htmlspecialchars($stats['last_user']['username']);
 
+if (!empty($forum_actions))
+{
+
+?>
+<div class="linksb">
+	<div class="inbox crumbsplus">
+		<p class="subscribelink clearb"><?php echo implode(' - ', $forum_actions); ?></p>
+	</div>
+</div>
+<?php
+
+}
+
 ?>
 <div id="brdstats" class="block">
 	<h2><span><?php echo $lang_index['Board info'] ?></span></h2>
diff --git a/lang/English/admin_censoring.php b/lang/English/admin_censoring.php
index 9d49495..85ad048 100644
--- a/lang/English/admin_censoring.php
+++ b/lang/English/admin_censoring.php
@@ -3,15 +3,14 @@
 // Language definitions used in admin_censoring.php
 $lang_admin_censoring = array(
 
-'Must enter both message'	=>	'You must enter both a word to censor and text to replace it with.',
-'Must search both message'	=>	'You must enter both text to search for and text to replace with.',
+'Must enter word message'	=>	'You must enter a word to censor.',
 'Word updated redirect'		=>	'Censor word updated. Redirecting …',
 'Word added redirect'		=>	'Censor word added. Redirecting …',
 'Word removed redirect'		=>	'Censor word removed. Redirecting …',
 'Censoring head'			=>	'Censoring',
 'Add word subhead'			=>	'Add word',
 'Add word info'				=>	'Enter a word that you want to censor and the replacement text for this word. Wildcards are accepted (i.e. *some* would match somewhere and lonesome). Censor words also affect usernames. New users will not be able to register with usernames containing any censored words. The search is case insensitive.',
-'Censoring enabled'		=>	'<strong>Censoring is enabled in %s.</strong>',
+'Censoring enabled'			=>	'<strong>Censoring is enabled in %s.</strong>',
 'Censoring disabled'		=>	'<strong>Censoring is disabled in %s.</strong>',
 'Censored word label'		=>	'Censored word',
 'Replacement label'			=>	'Replacement word(s)',
diff --git a/lang/English/admin_forums.php b/lang/English/admin_forums.php
index 4a0e9df..c933602 100644
--- a/lang/English/admin_forums.php
+++ b/lang/English/admin_forums.php
@@ -40,6 +40,7 @@ $lang_admin_forums = array(
 'Sort by label'				=>	'Sort topics by',
 'Last post'					=>	'Last post',
 'Topic start'				=>	'Topic start',
+'Subject'					=>	'Subject',
 'Redirect label'			=>	'Redirect URL',
 'Redirect help'				=>	'Only available in empty forums',
 'Group permissions subhead'	=>	'Edit group permissions for this forum',
diff --git a/lang/English/admin_maintenance.php b/lang/English/admin_maintenance.php
index a0e62b3..aa29f7f 100644
--- a/lang/English/admin_maintenance.php
+++ b/lang/English/admin_maintenance.php
@@ -19,5 +19,6 @@ $lang_admin_maintenance = array(
 'Processing post'				=>	'Processing post <strong>%s</strong> …',
 'Click here'					=>	'Click here',
 'Javascript redirect failed'	=>	'JavaScript redirect unsuccessful. %s to continue …',
+'Must be integer message'		=>	'Posts per cycle must be a positive integer value.',
 
 );
diff --git a/lang/English/admin_reports.php b/lang/English/admin_reports.php
index ccb9d13..21ccb13 100644
--- a/lang/English/admin_reports.php
+++ b/lang/English/admin_reports.php
@@ -3,18 +3,18 @@
 // Language definitions used in admin_reports.php
 $lang_admin_reports = array(
 
-'Report zapped redirect'	=>	'Report zapped. Redirecting …',
+'Report zapped redirect'	=>	'Report marked as read. Redirecting …',
 'New reports head'			=>	'New reports',
 'Deleted user'				=>	'Deleted user',
 'Deleted'					=>	'Deleted',
 'Report subhead'			=>	'Reported %s',
 'Reported by'				=>	'Reported by %s',
 'Reason'					=>	'Reason',
-'Zap'						=>	'Zap',
+'Zap'						=>	'Mark as read',
 'No new reports'			=>	'There are no new reports.',
-'Last 10 head'				=>	'10 last zapped reports',
+'Last 10 head'				=>	'10 last read reports',
 'NA'						=>	'N/A',
-'Zapped subhead'			=>	'Zapped %s by %s',
-'No zapped reports'			=>	'There are no zapped reports.',
+'Zapped subhead'			=>	'Marked as read %s by %s',
+'No zapped reports'			=>	'There are no read reports.',
 
 );
diff --git a/lang/English/common.php b/lang/English/common.php
index 7334b24..7a88e18 100644
--- a/lang/English/common.php
+++ b/lang/English/common.php
@@ -69,6 +69,7 @@ $lang_common = array(
 'Write message legend'				=>	'Write your message and submit',
 'Previous'							=>	'Previous',
 'Next'								=>	'Next',
+'Forum index'						=>	'Forum index',
 'Spacer'							=>	'…', // Ellipsis for paginate
 
 // Title
@@ -101,12 +102,15 @@ $lang_common = array(
 'Logged in as'						=>	'Logged in as',
 'Admin'								=>	'Administration',
 'Last visit'						=>	'Last visit: %s',
-'New posts'							=>	'New posts',
-'Active topics'						=>	'Active topics',
-'Unanswered topics'					=>	'Unanswered topics',
+'Topic searches'					=>	'Topics:',
+'New posts header'					=>	'New',
+'Active topics'						=>	'Active',
+'Unanswered topics'					=>	'Unanswered',
+'Posted topics'						=>	'Posted',
 'Show new posts'					=>	'Find topics with new posts since your last visit.',
-'Show active topics'				=>	'Find topics which contain recent posts.',
-'Show unanswered topics'			=>	'Find topics which have not been replied to.',
+'Show active topics'				=>	'Find topics with recent posts.',
+'Show unanswered topics'			=>	'Find topics with no replies.',
+'Show posted topics'				=>	'Find topics you have posted to.',
 'Mark all as read'					=>	'Mark all topics as read',
 'Mark forum read'					=>	'Mark this forum as read',
 'Title separator'					=>	' / ',
diff --git a/lang/English/mail_templates/new_reply.tpl b/lang/English/mail_templates/new_reply.tpl
index e9dab0b..b423bf5 100644
--- a/lang/English/mail_templates/new_reply.tpl
+++ b/lang/English/mail_templates/new_reply.tpl
@@ -1,6 +1,6 @@
-Subject: Reply to topic: <topic_subject>
+Subject: Reply to topic: '<topic_subject>'
 
-<replier> has replied to the topic <topic_subject> to which you are subscribed. There may be more new replies, but this is the only notification you will receive until you visit the board again.
+<replier> has replied to the topic '<topic_subject>' to which you are subscribed. There may be more new replies, but this is the only notification you will receive until you visit the board again.
 
 The post is located at <post_url>
 
diff --git a/lang/English/mail_templates/new_reply_full.tpl b/lang/English/mail_templates/new_reply_full.tpl
index 7231145..99371c2 100644
--- a/lang/English/mail_templates/new_reply_full.tpl
+++ b/lang/English/mail_templates/new_reply_full.tpl
@@ -1,6 +1,6 @@
-Subject: Reply to topic: <topic_subject>
+Subject: Reply to topic: '<topic_subject>'
 
-<replier> has replied to the topic <topic_subject> to which you are subscribed. There may be more new replies, but this is the only notification you will receive until you visit the board again.
+<replier> has replied to the topic '<topic_subject>' to which you are subscribed. There may be more new replies, but this is the only notification you will receive until you visit the board again.
 
 The message reads as follows:
 -----------------------------------------------------------------------
diff --git a/lang/English/mail_templates/new_topic.tpl b/lang/English/mail_templates/new_topic.tpl
index 7e2308c..ae5e927 100644
--- a/lang/English/mail_templates/new_topic.tpl
+++ b/lang/English/mail_templates/new_topic.tpl
@@ -1,6 +1,6 @@
-Subject: New topic in forum: <forum_name>
+Subject: New topic in forum: '<forum_name>'
 
-<poster> has posted a new topic <topic_subject> in the forum <forum_name>, to which you are subscribed.
+<poster> has posted a new topic '<topic_subject>' in the forum '<forum_name>', to which you are subscribed.
 
 The topic is located at <topic_url>
 
diff --git a/lang/English/mail_templates/new_topic_full.tpl b/lang/English/mail_templates/new_topic_full.tpl
index 3f5b408..25afd49 100644
--- a/lang/English/mail_templates/new_topic_full.tpl
+++ b/lang/English/mail_templates/new_topic_full.tpl
@@ -1,6 +1,6 @@
-Subject: New topic in forum: <forum_name>
+Subject: New topic in forum: '<forum_name>'
 
-<poster> has posted a new topic <topic_subject> in the forum <forum_name>, to which you are subscribed.
+<poster> has posted a new topic '<topic_subject>' in the forum '<forum_name>', to which you are subscribed.
 
 The message reads as follows:
 -----------------------------------------------------------------------
diff --git a/lang/English/search.php b/lang/English/search.php
index a1f28c4..171e022 100644
--- a/lang/English/search.php
+++ b/lang/English/search.php
@@ -4,47 +4,59 @@
 $lang_search = array(
 
 // The search form
-'User search'				=>	'User search',
-'No search permission'		=>	'You do not have permission to use the search feature.',
-'Search flood'				=>	'At least %s seconds have to pass between searches. Please wait a while and try searching again.',
-'Search'					=>	'Search',
-'Search criteria legend'	=>	'Enter your search criteria',
-'Search info'				=>	'To search by keyword, enter a term or terms to search for. Separate terms with spaces. Use AND, OR and NOT to refine your search. To search by author enter the username of the author whose posts you wish to search for. Use wildcard character * for partial matches.',
-'Keyword search'			=>	'Keyword search',
-'Author search'				=>	'Author search',
-'Search in legend'			=>	'Select where to search',
-'Search in info'			=>	'Choose in which forum you would like to search and if you want to search in topic subjects, message text or both.',
-'Forum search'				=>	'Forum',
-'All forums'				=>	'All forums',
-'Search in'					=>	'Search in',
-'Message and subject'		=>	'Message text and topic subject',
-'Message only'				=>	'Message text only',
-'Topic only'				=>	'Topic subject only',
-'Sort by'					=>	'Sort by',
-'Sort order'				=>	'Sort order',
-'Search results legend'		=>	'Select how to view search results',
-'Search results info'		=>	'You can choose how you wish to sort and show your results.',
-'Sort by post time'			=>	'Post time',
-'Sort by author'			=>	'Author',
-'Sort by subject'			=>	'Subject',
-'Sort by forum'				=>	'Forum',
-'Ascending'					=>	'Ascending',
-'Descending'				=>	'Descending',
-'Show as'					=>	'Show results as',
-'Show as topics'			=>	'Topics',
-'Show as posts'				=>	'Posts',
+'User search'						=>	'User search',
+'No search permission'				=>	'You do not have permission to use the search feature.',
+'Search flood'						=>	'At least %s seconds have to pass between searches. Please wait a while and try searching again.',
+'Search'							=>	'Search',
+'Search criteria legend'			=>	'Enter your search criteria',
+'Search info'						=>	'To search by keyword, enter a term or terms to search for. Separate terms with spaces. Use AND, OR and NOT to refine your search. To search by author enter the username of the author whose posts you wish to search for. Use wildcard character * for partial matches.',
+'Keyword search'					=>	'Keyword search',
+'Author search'						=>	'Author search',
+'Search in legend'					=>	'Select where to search',
+'Search in info'					=>	'Choose in which forum you would like to search and if you want to search in topic subjects, message text or both.',
+'Forum search'						=>	'Forum',
+'All forums'						=>	'All forums',
+'Search in'							=>	'Search in',
+'Message and subject'				=>	'Message text and topic subject',
+'Message only'						=>	'Message text only',
+'Topic only'						=>	'Topic subject only',
+'Sort by'							=>	'Sort by',
+'Sort order'						=>	'Sort order',
+'Search results legend'				=>	'Select how to view search results',
+'Search results info'				=>	'You can choose how you wish to sort and show your results.',
+'Sort by post time'					=>	'Post time',
+'Sort by author'					=>	'Author',
+'Sort by subject'					=>	'Subject',
+'Sort by forum'						=>	'Forum',
+'Ascending'							=>	'Ascending',
+'Descending'						=>	'Descending',
+'Show as'							=>	'Show results as',
+'Show as topics'					=>	'Topics',
+'Show as posts'						=>	'Posts',
 
 // Results
-'Search results'			=>	'Search results',
-'No terms'					=>	'You have to enter at least one keyword and/or an author to search for.',
-'No hits'					=>	'Your search returned no hits.',
-'No user posts'				=>	'There are no posts by this user in this forum.',
-'No user topics'			=>	'There are no topics by this user in this forum.',
-'No subscriptions'			=>	'This user is currently not subscribed to any topics.',
-'No new posts'				=>	'There are no topics with new posts since your last visit.',
-'No recent posts'			=>	'No new posts have been made within the last 24 hours.',
-'No unanswered'				=>	'There are no unanswered posts in this forum.',
-'Go to post'				=>	'Go to post',
-'Go to topic'				=>	'Go to topic'
+'Search results'					=>	'Search results',
+'Search topics'						=>	'Search topics',
+'Search posts'						=>	'Search posts',
+'Quick search show_new'				=>	'New',
+'Quick search show_recent'			=>	'Active',
+'Quick search show_unanswered'		=>	'Unanswered',
+'Quick search show_replies'			=>	'Yours',
+'Quick search show_user_topics'		=>	'By %s',
+'Quick search show_user_posts'		=>	'By %s',
+'Quick search show_subscriptions'	=>	'Subscribed by %s',
+'By keywords'						=>	'With keywords \'%s\'',
+'By user'							=>	'With posts by %s',
+'By both'							=>	'With keywords \'%s\' in posts by %s',
+'No terms'							=>	'You have to enter at least one keyword and/or an author to search for.',
+'No hits'							=>	'Your search returned no hits.',
+'No user posts'						=>	'There are no posts by this user in this forum.',
+'No user topics'					=>	'There are no topics by this user in this forum.',
+'No subscriptions'					=>	'This user is currently not subscribed to any topics.',
+'No new posts'						=>	'There are no topics with new posts since your last visit.',
+'No recent posts'					=>	'No new posts have been made within the last 24 hours.',
+'No unanswered'						=>	'There are no unanswered posts in this forum.',
+'Go to post'						=>	'Go to post',
+'Go to topic'						=>	'Go to topic'
 
 );
diff --git a/lang/English/topic.php b/lang/English/topic.php
index 93b0505..f964f0d 100644
--- a/lang/English/topic.php
+++ b/lang/English/topic.php
@@ -27,6 +27,7 @@ $lang_topic = array(
 'Mod controls'		=>	'Moderator controls',
 'New icon'			=>	'New post',
 'Re'				=>	'Re:',
+'Preview'			=>	'Preview',
 
 // Solved strings
 'Mark solved'		=> 	'Mark as solved',
diff --git a/login.php b/login.php
index b0c5a50..903c351 100644
--- a/login.php
+++ b/login.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
@@ -113,7 +113,7 @@ else if ($action == 'out')
 	if (isset($pun_user['logged']))
 		$db->query('UPDATE '.$db->prefix.'users SET last_visit='.$pun_user['logged'].' WHERE id='.$pun_user['id']) or error('Unable to update user visit data', __FILE__, __LINE__, $db->error());
 
-	pun_setcookie(1, md5(uniqid(rand(), true)), time() + 31536000);
+	pun_setcookie(1, pun_hash(uniqid(rand(), true)), time() + 31536000);
 
 	redirect('index.php', $lang_login['Logout redirect']);
 }
@@ -248,11 +248,19 @@ if (!empty($_SERVER['HTTP_REFERER']))
 	if (strpos($referrer['host'], 'www.') === 0)
 		$referrer['host'] = substr($referrer['host'], 4);
 
+	// Make sure the path component exists
+	if (!isset($referrer['path']))
+		$referrer['path'] = '';
+
 	$valid = parse_url(get_base_url());
 	// Remove www subdomain if it exists
 	if (strpos($valid['host'], 'www.') === 0)
 		$valid['host'] = substr($valid['host'], 4);
 
+	// Make sure the path component exists
+	if (!isset($valid['path']))
+		$valid['path'] = '';
+
 	if ($referrer['host'] == $valid['host'] && preg_match('#^'.preg_quote($valid['path']).'/(.*?)\.php#i', $referrer['path']))
 		$redirect_url = $_SERVER['HTTP_REFERER'];
 }
diff --git a/misc.php b/misc.php
index 211bc05..8468313 100644
--- a/misc.php
+++ b/misc.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/moderate.php b/moderate.php
index 14b289d..2150c22 100644
--- a/moderate.php
+++ b/moderate.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
@@ -745,6 +745,22 @@ $cur_forum = $db->fetch_assoc($result);
 if ($cur_forum['redirect_url'] != '')
 	message($lang_common['Bad request']);
 
+switch ($cur_forum['sort_by'])
+{
+	case 0:
+		$sort_by = 'last_post DESC';
+		break;
+	case 1:
+		$sort_by = 'posted DESC';
+		break;
+	case 2:
+		$sort_by = 'subject ASC';
+		break;
+	default:
+		$sort_by = 'last_post DESC';
+		break;
+}
+
 // Determine the topic offset (based on $_GET['p'])
 $num_pages = ceil($cur_forum['num_topics'] / $pun_user['disp_topics']);
 
@@ -793,7 +809,7 @@ require PUN_ROOT.'header.php';
 
 
 // Retrieve a list of topic IDs, LIMIT is (really) expensive so we only fetch the IDs here then later fetch the remaining data
-$result = $db->query('SELECT id FROM '.$db->prefix.'topics WHERE forum_id='.$fid.' ORDER BY sticky DESC, '.(($cur_forum['sort_by'] == '1') ? 'posted' : 'last_post').' DESC, id DESC LIMIT '.$start_from.', '.$pun_user['disp_topics']) or error('Unable to fetch topic IDs', __FILE__, __LINE__, $db->error());
+$result = $db->query('SELECT id FROM '.$db->prefix.'topics WHERE forum_id='.$fid.' ORDER BY sticky DESC, '.$sort_by.', id DESC LIMIT '.$start_from.', '.$pun_user['disp_topics']) or error('Unable to fetch topic IDs', __FILE__, __LINE__, $db->error());
 
 // If there are topics in this forum
 if ($db->num_rows($result))
@@ -803,7 +819,7 @@ if ($db->num_rows($result))
 		$topic_ids[] = $cur_topic_id;
 
 	// Select topics
-	$result = $db->query('SELECT id, poster, subject, posted, last_post, last_post_id, last_poster, num_views, num_replies, closed, sticky, moved_to FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $topic_ids).') ORDER BY sticky DESC, '.(($cur_forum['sort_by'] == '1') ? 'posted' : 'last_post').' DESC, id DESC') or error('Unable to fetch topic list for forum', __FILE__, __LINE__, $db->error());
+	$result = $db->query('SELECT id, poster, subject, posted, last_post, last_post_id, last_poster, num_views, num_replies, closed, sticky, moved_to FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $topic_ids).') ORDER BY sticky DESC, '.$sort_by.', id DESC') or error('Unable to fetch topic list for forum', __FILE__, __LINE__, $db->error());
 
 	$button_status = '';
 	$topic_count = 0;
diff --git a/post.php b/post.php
index ef31a51..f15f3d1 100644
--- a/post.php
+++ b/post.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
@@ -226,15 +226,15 @@ if (isset($_POST['form_sent']))
 								$mail_subject_full = trim(substr($mail_tpl_full, 8, $first_crlf-8));
 								$mail_message_full = trim(substr($mail_tpl_full, $first_crlf));
 
-								$mail_subject = str_replace('<topic_subject>', '\''.$cur_posting['subject'].'\'', $mail_subject);
-								$mail_message = str_replace('<topic_subject>', '\''.$cur_posting['subject'].'\'', $mail_message);
+								$mail_subject = str_replace('<topic_subject>', $cur_posting['subject'], $mail_subject);
+								$mail_message = str_replace('<topic_subject>', $cur_posting['subject'], $mail_message);
 								$mail_message = str_replace('<replier>', $username, $mail_message);
 								$mail_message = str_replace('<post_url>', get_base_url().'/viewtopic.php?pid='.$new_pid.'#p'.$new_pid, $mail_message);
 								$mail_message = str_replace('<unsubscribe_url>', get_base_url().'/misc.php?action=unsubscribe&tid='.$tid, $mail_message);
 								$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'].' '.$lang_common['Mailer'], $mail_message);
 
-								$mail_subject_full = str_replace('<topic_subject>', '\''.$cur_posting['subject'].'\'', $mail_subject_full);
-								$mail_message_full = str_replace('<topic_subject>', '\''.$cur_posting['subject'].'\'', $mail_message_full);
+								$mail_subject_full = str_replace('<topic_subject>', $cur_posting['subject'], $mail_subject_full);
+								$mail_message_full = str_replace('<topic_subject>', $cur_posting['subject'], $mail_message_full);
 								$mail_message_full = str_replace('<replier>', $username, $mail_message_full);
 								$mail_message_full = str_replace('<message>', $message, $mail_message_full);
 								$mail_message_full = str_replace('<post_url>', get_base_url().'/viewtopic.php?pid='.$new_pid.'#p'.$new_pid, $mail_message_full);
@@ -312,10 +312,10 @@ if (isset($_POST['form_sent']))
 						{
 							if (file_exists(PUN_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_topic.tpl'))
 							{
-								// Load the "new reply" template
+								// Load the "new topic" template
 								$mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_topic.tpl'));
 
-								// Load the "new reply full" template (with post included)
+								// Load the "new topic full" template (with post included)
 								$mail_tpl_full = trim(file_get_contents(PUN_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_topic_full.tpl'));
 
 								// The first row contains the subject (it also starts with "Subject:")
@@ -327,19 +327,17 @@ if (isset($_POST['form_sent']))
 								$mail_subject_full = trim(substr($mail_tpl_full, 8, $first_crlf-8));
 								$mail_message_full = trim(substr($mail_tpl_full, $first_crlf));
 
-								$mail_subject = str_replace('<topic_subject>', '\''.$subject.'\'', $mail_subject);
-								$mail_subject = str_replace('<forum_name>', '\''.$cur_posting['forum_name'].'\'', $mail_subject);
-								$mail_message = str_replace('<topic_subject>', '\''.$subject.'\'', $mail_message);
-								$mail_message = str_replace('<forum_name>', '\''.$cur_posting['forum_name'].'\'', $mail_message);
+								$mail_subject = str_replace('<forum_name>', $cur_posting['forum_name'], $mail_subject);
+								$mail_message = str_replace('<topic_subject>', $subject, $mail_message);
+								$mail_message = str_replace('<forum_name>', $cur_posting['forum_name'], $mail_message);
 								$mail_message = str_replace('<poster>', $username, $mail_message);
 								$mail_message = str_replace('<topic_url>', get_base_url().'/viewtopic.php?id='.$new_tid, $mail_message);
 								$mail_message = str_replace('<unsubscribe_url>', get_base_url().'/misc.php?action=unsubscribe&fid='.$cur_posting['id'], $mail_message);
 								$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'].' '.$lang_common['Mailer'], $mail_message);
 
-								$mail_subject_full = str_replace('<topic_subject>', '\''.$subject.'\'', $mail_subject_full);
-								$mail_subject_full = str_replace('<forum_name>', '\''.$cur_posting['forum_name'].'\'', $mail_subject_full);
-								$mail_message_full = str_replace('<topic_subject>', '\''.$subject.'\'', $mail_message_full);
-								$mail_message_full = str_replace('<forum_name>', '\''.$cur_posting['forum_name'].'\'', $mail_message_full);
+								$mail_subject_full = str_replace('<forum_name>', $cur_posting['forum_name'], $mail_subject_full);
+								$mail_message_full = str_replace('<topic_subject>', $subject, $mail_message_full);
+								$mail_message_full = str_replace('<forum_name>', $cur_posting['forum_name'], $mail_message_full);
 								$mail_message_full = str_replace('<poster>', $username, $mail_message_full);
 								$mail_message_full = str_replace('<message>', $message, $mail_message_full);
 								$mail_message_full = str_replace('<topic_url>', get_base_url().'/viewtopic.php?id='.$new_tid, $mail_message_full);
diff --git a/profile.php b/profile.php
index a223ca0..ce9814c 100644
--- a/profile.php
+++ b/profile.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
@@ -1039,8 +1039,20 @@ if ($pun_user['id'] != $id &&																	// If we arent the user (i.e. edit
 	$posts_field = '';
 	if ($pun_config['o_show_post_count'] == '1' || $pun_user['is_admmod'])
 		$posts_field = forum_number_format($user['num_posts']);
-	if ($pun_user['g_search'] == '1' && $user['num_posts'] > 0)
-		$posts_field .= (($posts_field != '') ? ' - ' : '').'<a href="search.php?action=show_user_topics&user_id='.$id.'">'.$lang_profile['Show topics'].'</a> - <a href="search.php?action=show_user_posts&user_id='.$id.'">'.$lang_profile['Show posts'].'</a>';
+	if ($pun_user['g_search'] == '1')
+	{
+		$quick_searches = array();
+		if ($user['num_posts'] > 0)
+		{
+			$quick_searches[] = '<a href="search.php?action=show_user_topics&user_id='.$id.'">'.$lang_profile['Show topics'].'</a>';
+			$quick_searches[] = '<a href="search.php?action=show_user_posts&user_id='.$id.'">'.$lang_profile['Show posts'].'</a>';
+		}
+		if ($pun_user['is_admmod'] && $pun_config['o_topic_subscriptions'] == '1')
+			$quick_searches[] = '<a href="search.php?action=show_subscriptions&user_id='.$id.'">'.$lang_profile['Show subscriptions'].'</a>';
+		
+		if (!empty($quick_searches))
+			$posts_field .= (($posts_field != '') ? ' - ' : '').implode(' - ', $quick_searches);
+	}
 	if ($posts_field != '')
 	{
 		$user_activity[] = '<dt>'.$lang_common['Posts'].'</dt>';
diff --git a/register.php b/register.php
index f6d638e..123836e 100644
--- a/register.php
+++ b/register.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/search.php b/search.php
index 9d8b901..141c2f5 100644
--- a/search.php
+++ b/search.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
@@ -79,6 +79,11 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 	}
 	else if ($action == 'show_recent')
 		$interval = isset($_GET['value']) ? intval($_GET['value']) : 86400;
+	else if ($action == 'show_replies')
+	{
+		if ($pun_user['is_guest'])
+			message($lang_common['Bad request']);
+	}
 	else if ($action != 'show_new' && $action != 'show_unanswered')
 		message($lang_common['Bad request']);
 
@@ -98,6 +103,7 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 			$sort_by = $temp['sort_by'];
 			$sort_dir = $temp['sort_dir'];
 			$show_as = $temp['show_as'];
+			$search_type = $temp['search_type'];
 
 			unset($temp);
 		}
@@ -275,11 +281,20 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 
 			// If we searched for both keywords and author name we want the intersection between the results
 			if ($author && $keywords)
+			{
 				$search_ids = array_intersect_assoc($keyword_results, $author_results);
+				$search_type = array('both', array($keywords, $author), $forum, isset($_GET['search_in']) ? $_GET['search_in'] : '');
+			}
 			else if ($keywords)
+			{
 				$search_ids = $keyword_results;
+				$search_type = array('keywords', $keywords, $forum, isset($_GET['search_in']) ? $_GET['search_in'] : '');
+			}
 			else
+			{
 				$search_ids = $author_results;
+				$search_type = array('author', $author, $forum, isset($_GET['search_in']) ? $_GET['search_in'] : '');
+			}
 
 			unset($keyword_results, $author_results);
 
@@ -294,8 +309,9 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 			if (!$num_hits)
 				message($lang_search['No hits']);
 		}
-		else if ($action == 'show_new' || $action == 'show_recent' || $action == 'show_user_posts' || $action == 'show_user_topics' || $action == 'show_subscriptions' || $action == 'show_unanswered')
+		else if ($action == 'show_new' || $action == 'show_recent' || $action == 'show_replies' || $action == 'show_user_posts' || $action == 'show_user_topics' || $action == 'show_subscriptions' || $action == 'show_unanswered')
 		{
+			$search_type = array('action', $action);
 			$show_as = 'topics';
 			// We want to sort things after last post
 			$sort_by = 0;
@@ -322,6 +338,15 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 				if (!$num_hits)
 					message($lang_search['No recent posts']);
 			}
+			// If it's a search for topics in which the user has posted
+			else if ($action == 'show_replies')
+			{
+				$result = $db->query('SELECT t.id FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'posts AS p ON t.id=p.topic_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND p.poster_id='.$pun_user['id'].' GROUP BY t.id'.($db_type == 'pgsql' ? ', t.last_post' : '').' ORDER BY t.last_post DESC') or error('Unable to fetch topic list', __FILE__, __LINE__, $db->error());
+				$num_hits = $db->num_rows($result);
+				
+				if (!$num_hits)
+					message($lang_search['No user posts']);
+			}
 			// If it's a search for posts by a specific user ID
 			else if ($action == 'show_user_posts')
 			{
@@ -332,6 +357,9 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 
 				if (!$num_hits)
 					message($lang_search['No user posts']);
+
+				// Pass on the user ID so that we can later know whos posts we're searching for
+				$search_type[2] = $user_id;
 			}
 			// If it's a search for topics by a specific user ID
 			else if ($action == 'show_user_topics')
@@ -341,6 +369,9 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 
 				if (!$num_hits)
 					message($lang_search['No user topics']);
+
+				// Pass on the user ID so that we can later know whos topics we're searching for
+				$search_type[2] = $user_id;
 			}
 			// If it's a search for subscribed topics
 			else if ($action == 'show_subscriptions')
@@ -353,6 +384,9 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 
 				if (!$num_hits)
 					message($lang_search['No subscriptions']);
+
+				// Pass on user ID so that we can later know whose subscriptions we're searching for
+				$search_type[2] = $user_id;
 			}
 			// If it's a search for unanswered posts
 			else
@@ -393,6 +427,7 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 			'sort_by'			=> $sort_by,
 			'sort_dir'			=> $sort_dir,
 			'show_as'			=> $show_as,
+			'search_type'		=> $search_type
 		));
 		$search_id = mt_rand(1, 2147483647);
 
@@ -400,7 +435,7 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 
 		$db->query('INSERT INTO '.$db->prefix.'search_cache (id, ident, search_data) VALUES('.$search_id.', \''.$db->escape($ident).'\', \''.$db->escape($temp).'\')') or error('Unable to insert search results', __FILE__, __LINE__, $db->error());
 
-		if ($action != 'show_new' && $action != 'show_recent')
+		if ($search_type[0] != 'action')
 		{
 			$db->end_transaction();
 			$db->close();
@@ -411,6 +446,11 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 		}
 	}
 
+	$forum_actions = array();
+
+	// If we're on the new posts search, display a "mark all as read" link
+	if (!$pun_user['is_guest'] && $search_type[0] == 'action' && $search_type[1] == 'show_new')
+		$forum_actions[] = '<a href="misc.php?action=markread">'.$lang_common['Mark all as read'].'</a>';
 
 	// Fetch results to display
 	if (!empty($search_ids))
@@ -447,15 +487,79 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 		// throw away the first $start_from of $search_ids, only keep the top $per_page of $search_ids
 		$search_ids = array_slice($search_ids, $start_from, $per_page);
 
+		// Run the query and fetch the results
+		if ($show_as == 'posts')
+			$result = $db->query('SELECT p.id AS pid, p.poster AS pposter, p.posted AS pposted, p.poster_id, p.message, p.hide_smilies, t.id AS tid, t.poster, t.subject, t.first_post_id, t.last_post, t.last_post_id, t.last_poster, t.num_replies, t.forum_id, f.forum_name FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id WHERE p.id IN('.implode(',', $search_ids).') ORDER BY '.$sort_by_sql.' '.$sort_dir) or error('Unable to fetch search results', __FILE__, __LINE__, $db->error());
+		else
+			$result = $db->query('SELECT t.id AS tid, t.poster, t.subject, t.last_post, t.last_post_id, t.last_poster, t.num_replies, t.closed, t.sticky, t.forum_id, f.forum_name FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id WHERE t.id IN('.implode(',', $search_ids).') ORDER BY '.$sort_by_sql.' '.$sort_dir) or error('Unable to fetch search results', __FILE__, __LINE__, $db->error());
+
+		$search_set = array();
+		while ($row = $db->fetch_assoc($result))
+			$search_set[] = $row;
+
+		$crumbs_text = array();
+		$crumbs_text['show_as'] = $show_as == 'topics' ? $lang_search['Search topics'] : $lang_search['Search posts'];
+
+		if ($search_type[0] == 'action')
+		{
+			if ($search_type[1] == 'show_user_topics')
+				$crumbs_text['search_type'] = '<a href="search.php?action=show_user_topics&user_id='.$search_type[2].'">'.sprintf($lang_search['Quick search show_user_topics'], pun_htmlspecialchars($search_set[0]['poster'])).'</a>';
+			else if ($search_type[1] == 'show_user_posts')
+				$crumbs_text['search_type'] = '<a href="search.php?action=show_user_posts&user_id='.$search_type[2].'">'.sprintf($lang_search['Quick search show_user_posts'], pun_htmlspecialchars($search_set[0]['pposter'])).'</a>';
+			else if ($search_type[1] == 'show_subscriptions')
+			{
+				// Fetch username of subscriber
+				$subscriber_id = $search_type[2];
+				$result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE id='.$subscriber_id) or error('Unable to fetch username of subscriber', __FILE__, __LINE__, $db->error());
+
+				if ($db->num_rows($result))
+					$subscriber_name = $db->result($result);
+				else
+					message($lang_common['Bad request']);
+
+				$crumbs_text['search_type'] = '<a href="search.php?action=show_subscriptions&user_id='.$subscriber_id.'">'.sprintf($lang_search['Quick search show_subscriptions'], pun_htmlspecialchars($subscriber_name)).'</a>';
+			}
+			else
+				$crumbs_text['search_type'] = '<a href="search.php?action='.pun_htmlspecialchars($search_type[1]).'">'.$lang_search['Quick search '.$search_type[1]].'</a>';
+		}
+		else
+		{
+			$keywords = $author = '';
+
+			if ($search_type[0] == 'both')
+			{
+				list ($keywords, $author) = $search_type[1];
+				$crumbs_text['search_type'] = sprintf($lang_search['By both'], pun_htmlspecialchars($keywords), pun_htmlspecialchars($author));
+			}
+			else if ($search_type[0] == 'keywords')
+			{
+				$keywords = $search_type[1];
+				$crumbs_text['search_type'] = sprintf($lang_search['By keywords'], pun_htmlspecialchars($keywords));
+			}
+			else if ($search_type[0] == 'author')
+			{
+				$author = $search_type[1];
+				$crumbs_text['search_type'] = sprintf($lang_search['By user'], pun_htmlspecialchars($author));
+			}
+
+			$crumbs_text['search_type'] = '<a href="search.php?action=search&keywords='.pun_htmlspecialchars($keywords).'&author='.pun_htmlspecialchars($author).'&forum='.pun_htmlspecialchars($search_type[2]).'&search_in='.pun_htmlspecialchars($search_type[3]).'&sort_by='.pun_htmlspecialchars($sort_by).'&sort_dir='.pun_htmlspecialchars($sort_dir).'&show_as='.pun_htmlspecialchars($show_as).'">'.$crumbs_text['search_type'].'</a>';
+		}
+
 		$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_search['Search results']);
 		define('PUN_ACTIVE_PAGE', 'search');
 		require PUN_ROOT.'header.php';
 
-
 ?>
 <div class="linkst">
-	<div class="inbox">
-		<p class="pagelink"><?php echo $paging_links ?></p>
+	<div class="inbox crumbsplus">
+		<ul class="crumbs">
+			<li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+			<li><span>» </span><a href="search.php"><?php echo $crumbs_text['show_as'] ?></a></li>
+			<li><span>» </span><strong><?php echo $crumbs_text['search_type'] ?></strong></li>
+		</ul>
+		<div class="pagepost">
+			<p class="pagelink"><?php echo $paging_links ?></p>
+		</div>
 		<div class="clearer"></div>
 	</div>
 </div>
@@ -497,12 +601,7 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 		if (!$pun_user['is_guest'])
 			$tracked_topics = get_tracked_topics();
 
-		if ($show_as == 'posts')
-			$result = $db->query('SELECT p.id AS pid, p.poster AS pposter, p.posted AS pposted, p.poster_id, p.message, p.hide_smilies, t.id AS tid, t.poster, t.subject, t.first_post_id, t.last_post, t.last_post_id, t.last_poster, t.num_replies, t.forum_id, f.forum_name FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id WHERE p.id IN('.implode(',', $search_ids).') ORDER BY '.$sort_by_sql.' '.$sort_dir) or error('Unable to fetch search results', __FILE__, __LINE__, $db->error());
-		else
-			$result = $db->query('SELECT t.id AS tid, t.poster, t.subject, t.last_post, t.last_post_id, t.last_poster, t.num_replies, t.closed, t.sticky, t.forum_id, f.forum_name FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id WHERE t.id IN('.implode(',', $search_ids).') ORDER BY '.$sort_by_sql.' '.$sort_dir) or error('Unable to fetch search results', __FILE__, __LINE__, $db->error());
-
-		while ($cur_search = $db->fetch_assoc($result))
+		foreach ($search_set as $cur_search)
 		{
 			$forum = '<a href="viewforum.php?id='.$cur_search['forum_id'].'">'.pun_htmlspecialchars($cur_search['forum_name']).'</a>';
 
@@ -550,7 +649,8 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 				<div class="postleft">
 					<dl>
 						<dt><?php echo $pposter ?></dt>
-						<dd><span><?php echo $lang_topic['Replies'].' '.forum_number_format($cur_search['num_replies']) ?></span></dd>
+<?php if ($cur_search['pid'] == $cur_search['first_post_id']) : ?>						<dd><span><?php echo $lang_topic['Replies'].' '.forum_number_format($cur_search['num_replies']) ?></span></dd>
+<?php endif; ?>
 						<dd><div class="<?php echo $icon_type ?>"><div class="nosize"><?php echo $icon_text ?></div></div></dd>
 					</dl>
 				</div>
@@ -649,8 +749,16 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 
 ?>
 <div class="<?php echo ($show_as == 'topics') ? 'linksb' : 'postlinksb'; ?>">
-	<div class="inbox">
-		<p class="pagelink"><?php echo $paging_links ?></p>
+	<div class="inbox crumbsplus">
+		<div class="pagepost">
+			<p class="pagelink"><?php echo $paging_links ?></p>
+		</div>
+		<ul class="crumbs">
+			<li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+			<li><span>» </span><a href="search.php"><?php echo $crumbs_text['show_as'] ?></a></li>
+			<li><span>» </span><strong><?php echo $crumbs_text['search_type'] ?></strong></li>
+		</ul>
+<?php echo (!empty($forum_actions) ? "\t\t".'<p class="subscribelink clearb">'.implode(' - ', $forum_actions).'</p>'."\n" : '') ?>
 		<div class="clearer"></div>
 	</div>
 </div>
diff --git a/style/Air.css b/style/Air.css
index c723b6c..50de49d 100644
--- a/style/Air.css
+++ b/style/Air.css
@@ -507,6 +507,14 @@ html, body, .pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
 	top: 33px;
 }
 
+#punindex .subscribelink {
+	top: 0px;
+}
+
+#punindex .linksb {
+	height: 12px;
+}
+
 /*****************************************************************
 4. MAIN TABLES
 *****************************************************************/
diff --git a/style/Cobalt.css b/style/Cobalt.css
index dfbddbd..0c52912 100644
--- a/style/Cobalt.css
+++ b/style/Cobalt.css
@@ -373,6 +373,9 @@
 	text-decoration: underline;
 	}
 
+#punindex .subscribelink {
+	margin-top: 6px;
+	}
 
 /* Board Footer
 ----------------------------------------------------------------*/
diff --git a/style/Earth.css b/style/Earth.css
index a5a918a..40427ae 100644
--- a/style/Earth.css
+++ b/style/Earth.css
@@ -507,6 +507,14 @@ html, body, .pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
 	top: 33px;
 }
 
+#punindex .subscribelink {
+	top: 0px;
+}
+
+#punindex .linksb {
+	height: 12px;
+}
+
 /*****************************************************************
 4. MAIN TABLES
 *****************************************************************/
diff --git a/style/Fire.css b/style/Fire.css
index d9e689c..0050f97 100644
--- a/style/Fire.css
+++ b/style/Fire.css
@@ -507,6 +507,14 @@ html, body, .pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
 	top: 33px;
 }
 
+#punindex .subscribelink {
+	top: 0px;
+}
+
+#punindex .linksb {
+	height: 12px;
+}
+
 /*****************************************************************
 4. MAIN TABLES
 *****************************************************************/
diff --git a/style/Lithium.css b/style/Lithium.css
index 3f472e2..f9d50e3 100644
--- a/style/Lithium.css
+++ b/style/Lithium.css
@@ -373,6 +373,9 @@
 	text-decoration: underline;
 	}
 
+#punindex .subscribelink {
+	margin-top: 6px;
+	}
 
 /* Board Footer
 ----------------------------------------------------------------*/
diff --git a/style/Mercury.css b/style/Mercury.css
index c58b601..93cea7e 100644
--- a/style/Mercury.css
+++ b/style/Mercury.css
@@ -373,6 +373,9 @@
 	text-decoration: underline;
 	}
 
+#punindex .subscribelink {
+	margin-top: 6px;
+	}
 
 /* Board Footer
 ----------------------------------------------------------------*/
diff --git a/style/Oxygen.css b/style/Oxygen.css
index c4807f5..1af7310 100644
--- a/style/Oxygen.css
+++ b/style/Oxygen.css
@@ -374,6 +374,9 @@
 	text-decoration: underline;
 	}
 
+#punindex .subscribelink {
+	margin-top: 6px;
+	}
 
 /* Board Footer
 ----------------------------------------------------------------*/
diff --git a/style/Radium.css b/style/Radium.css
index 49d2586..babe283 100644
--- a/style/Radium.css
+++ b/style/Radium.css
@@ -373,6 +373,9 @@
 	text-decoration: underline;
 	}
 
+#punindex .subscribelink {
+	margin-top: 6px;
+	}
 
 /* Board Footer
 ----------------------------------------------------------------*/
diff --git a/style/Sulfur.css b/style/Sulfur.css
index 3b322f1..e9d7e5a 100644
--- a/style/Sulfur.css
+++ b/style/Sulfur.css
@@ -373,6 +373,9 @@
 	text-decoration: underline;
 	}
 
+#punindex .subscribelink {
+	margin-top: 6px;
+	}
 
 /* Board Footer
 ----------------------------------------------------------------*/
diff --git a/style/Technetium.css b/style/Technetium.css
index c0ecaad..765e11b 100644
--- a/style/Technetium.css
+++ b/style/Technetium.css
@@ -446,6 +446,9 @@ body {
 	margin-left: 6px;
 	}
 
+#punindex .subscribelink {
+	margin-top: 6px;
+	}
 
 /* Board Footer
 ----------------------------------------------------------------*/
diff --git a/userlist.php b/userlist.php
index 9e58e83..c37242a 100644
--- a/userlist.php
+++ b/userlist.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
diff --git a/viewforum.php b/viewforum.php
index b3c5033..7a464f0 100644
--- a/viewforum.php
+++ b/viewforum.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
@@ -43,18 +43,32 @@ if ($cur_forum['redirect_url'] != '')
 $mods_array = ($cur_forum['moderators'] != '') ? unserialize($cur_forum['moderators']) : array();
 $is_admmod = ($pun_user['g_id'] == PUN_ADMIN || ($pun_user['g_moderator'] == '1' && array_key_exists($pun_user['username'], $mods_array))) ? true : false;
 
+switch ($cur_forum['sort_by'])
+{
+	case 0:
+		$sort_by = 'last_post DESC';
+		break;
+	case 1:
+		$sort_by = 'posted DESC';
+		break;
+	case 2:
+		$sort_by = 'subject ASC';
+		break;
+	default:
+		$sort_by = 'last_post DESC';
+		break;
+}
+
 // Can we or can we not post new topics?
 if (($cur_forum['post_topics'] == '' && $pun_user['g_post_topics'] == '1') || $cur_forum['post_topics'] == '1' || $is_admmod)
 	$post_link = "\t\t\t".'<p class="postlink conr"><a href="post.php?fid='.$id.'">'.$lang_forum['Post topic'].'</a></p>'."\n";
 else
 	$post_link = '';
 
-
 // Get topic/forum tracking data
 if (!$pun_user['is_guest'])
 	$tracked_topics = get_tracked_topics();
 
-
 // Determine the topic offset (based on $_GET['p'])
 $num_pages = ceil($cur_forum['num_topics'] / $pun_user['disp_topics']);
 
@@ -64,10 +78,29 @@ $start_from = $pun_user['disp_topics'] * ($p - 1);
 // Generate paging links
 $paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'viewforum.php?id='.$id);
 
+
+// Add relationship meta tags
+$page_head = array();
+$page_head['up'] = '<link rel="up" href="index.php" title="'.$lang_common['Forum index'].'" />';
+
+if ($num_pages > 1)
+{
+	if ($p > 1)
+	{
+		$page_head['first'] = '<link rel="first" href="viewforum.php?id='.$id.'&p=1" title="'.sprintf($lang_common['Page'], 1).'" />';
+		$page_head['prev'] = '<link rel="prev" href="viewforum.php?id='.$id.'&p='.($p-1).'" title="'.sprintf($lang_common['Page'], $p-1).'" />';
+	}
+	if ($p < $num_pages)
+	{
+		$page_head['next'] = '<link rel="next" href="viewforum.php?id='.$id.'&p='.($p+1).'" title="'.sprintf($lang_common['Page'], $p+1).'" />';
+		$page_head['last'] = '<link rel="last" href="viewforum.php?id='.$id.'&p='.$num_pages.'" title="'.sprintf($lang_common['Page'], $num_pages).'" />';
+	}
+}
+
 if ($pun_config['o_feed_type'] == '1')
-	$page_head = array('feed' => '<link rel="alternate" type="application/rss+xml" href="extern.php?action=feed&fid='.$id.'&type=rss" title="'.$lang_common['RSS forum feed'].'" />');
+	$page_head['feed'] = '<link rel="alternate" type="application/rss+xml" href="extern.php?action=feed&fid='.$id.'&type=rss" title="'.$lang_common['RSS forum feed'].'" />';
 else if ($pun_config['o_feed_type'] == '2')
-	$page_head = array('feed' => '<link rel="alternate" type="application/atom+xml" href="extern.php?action=feed&fid='.$id.'&type=atom" title="'.$lang_common['Atom forum feed'].'" />');
+	$page_head['feed'] = '<link rel="alternate" type="application/atom+xml" href="extern.php?action=feed&fid='.$id.'&type=atom" title="'.$lang_common['Atom forum feed'].'" />';
 
 $forum_actions = array();
 
@@ -121,7 +154,7 @@ require PUN_ROOT.'header.php';
 <?php
 
 // Retrieve a list of topic IDs, LIMIT is (really) expensive so we only fetch the IDs here then later fetch the remaining data
-$result = $db->query('SELECT id FROM '.$db->prefix.'topics WHERE forum_id='.$id.' ORDER BY sticky DESC, '.(($cur_forum['sort_by'] == '1') ? 'posted' : 'last_post').' DESC, id DESC LIMIT '.$start_from.', '.$pun_user['disp_topics']) or error('Unable to fetch topic IDs', __FILE__, __LINE__, $db->error());
+$result = $db->query('SELECT id FROM '.$db->prefix.'topics WHERE forum_id='.$id.' ORDER BY sticky DESC, '.$sort_by.', id DESC LIMIT '.$start_from.', '.$pun_user['disp_topics']) or error('Unable to fetch topic IDs', __FILE__, __LINE__, $db->error());
 
 // If there are topics in this forum
 if ($db->num_rows($result))
@@ -137,12 +170,12 @@ if ($db->num_rows($result))
 	if ($pun_user['is_guest'] || $pun_config['o_show_dot'] == '0')
 	{
 		// Without "the dot"
-		$sql = 'SELECT id, poster, subject, posted, last_post, last_post_id, last_poster, num_views, num_replies, closed, sticky, moved_to FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $topic_ids).') ORDER BY sticky DESC, '.(($cur_forum['sort_by'] == '1') ? 'posted' : 'last_post').' DESC, id DESC';
+		$sql = 'SELECT id, poster, subject, posted, last_post, last_post_id, last_poster, num_views, num_replies, closed, sticky, moved_to FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $topic_ids).') ORDER BY sticky DESC, '.$sort_by.', id DESC';
 	}
 	else
 	{
 		// With "the dot"
-		$sql = 'SELECT p.poster_id AS has_posted, t.id, t.subject, t.poster, t.posted, t.last_post, t.last_post_id, t.last_poster, t.num_views, t.num_replies, t.closed, t.sticky, t.moved_to FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'posts AS p ON t.id=p.topic_id AND p.poster_id='.$pun_user['id'].' WHERE t.id IN('.implode(',', $topic_ids).') GROUP BY t.id'.($db_type == 'pgsql' ? ', t.subject, t.poster, t.posted, t.last_post, t.last_post_id, t.last_poster, t.num_views, t.num_replies, t.closed, t.sticky, t.moved_to, p.poster_id' : '').' ORDER BY t.sticky DESC, t.'.(($cur_forum['sort_by'] == '1') ? 'posted' : 'last_post').' DESC, t.id DESC';
+		$sql = 'SELECT p.poster_id AS has_posted, t.id, t.subject, t.poster, t.posted, t.last_post, t.last_post_id, t.last_poster, t.num_views, t.num_replies, t.closed, t.sticky, t.moved_to FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'posts AS p ON t.id=p.topic_id AND p.poster_id='.$pun_user['id'].' WHERE t.id IN('.implode(',', $topic_ids).') GROUP BY t.id'.($db_type == 'pgsql' ? ', t.subject, t.poster, t.posted, t.last_post, t.last_post_id, t.last_poster, t.num_views, t.num_replies, t.closed, t.sticky, t.moved_to, p.poster_id' : '').' ORDER BY t.sticky DESC, t.'.$sort_by.', t.id DESC';
 	}
 
 	$result = $db->query($sql) or error('Unable to fetch topic list', __FILE__, __LINE__, $db->error());
diff --git a/viewtopic.php b/viewtopic.php
index ee45171..9941d33 100644
--- a/viewtopic.php
+++ b/viewtopic.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2010 FluxBB
+ * 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
  */
@@ -27,14 +27,14 @@ require PUN_ROOT.'lang/'.$pun_user['language'].'/topic.php';
 // If a post ID is specified we determine topic ID and page number so we can redirect to the correct message
 if ($pid)
 {
-	$result = $db->query('SELECT topic_id, posted FROM '.$db->prefix.'posts WHERE id='.$pid) or error(__FILE__, __LINE__);
+	$result = $db->query('SELECT topic_id, posted FROM '.$db->prefix.'posts WHERE id='.$pid) or error('Unable to fetch topic ID', __FILE__, __LINE__, $db->error());
 	if (!$db->num_rows($result))
 		message($lang_common['Bad request']);
 
 	list($id, $posted) = $db->fetch_row($result);
 
 	// Determine on what page the post is located (depending on $forum_user['disp_posts'])
-	$result = $db->query('SELECT COUNT(id) FROM '.$db->prefix.'posts WHERE topic_id='.$id.' AND posted<'.$posted) or error(__FILE__, __LINE__);
+	$result = $db->query('SELECT COUNT(id) FROM '.$db->prefix.'posts WHERE topic_id='.$id.' AND posted<'.$posted) or error('Unable to count previous posts', __FILE__, __LINE__, $db->error());
 	$num_posts = $db->result($result) + 1;
 
 	$_GET['p'] = ceil($num_posts / $pun_user['disp_posts']);
@@ -200,6 +200,25 @@ $start_from = $pun_user['disp_posts'] * ($p - 1);
 $paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'viewtopic.php?id='.$id);
 
 
+// Add relationship meta tags
+$page_head = array();
+$page_head['up'] = '<link rel="up" href="viewforum.php?id='.$cur_topic['forum_id'].'" title="'.pun_htmlspecialchars($cur_topic['forum_name']).'" />';
+
+if ($num_pages > 1)
+{
+	if ($p > 1)
+	{
+		$page_head['first'] = '<link rel="first" href="viewtopic.php?id='.$id.'&p=1" title="'.sprintf($lang_common['Page'], 1).'" />';
+		$page_head['prev'] = '<link rel="prev" href="viewtopic.php?id='.$id.'&p='.($p-1).'" title="'.sprintf($lang_common['Page'], $p-1).'" />';
+	}
+	if ($p < $num_pages)
+	{
+		$page_head['next'] = '<link rel="next" href="viewtopic.php?id='.$id.'&p='.($p+1).'" title="'.sprintf($lang_common['Page'], $p+1).'" />';
+		$page_head['last'] = '<link rel="last" href="viewtopic.php?id='.$id.'&p='.$num_pages.'" title="'.sprintf($lang_common['Page'], $num_pages).'" />';
+	}
+}
+
+
 if ($pun_config['o_censoring'] == '1')
 	$cur_topic['subject'] = censor_words($cur_topic['subject']);
 
@@ -226,9 +245,9 @@ else
 	$subscraction = '';
 
 if ($pun_config['o_feed_type'] == '1')
-	$page_head = array('feed' => '<link rel="alternate" type="application/rss+xml" href="extern.php?action=feed&tid='.$id.'&type=rss" title="'.$lang_common['RSS topic feed'].'" />');
+	$page_head['feed'] = '<link rel="alternate" type="application/rss+xml" href="extern.php?action=feed&tid='.$id.'&type=rss" title="'.$lang_common['RSS topic feed'].'" />';
 else if ($pun_config['o_feed_type'] == '2')
-	$page_head = array('feed' => '<link rel="alternate" type="application/atom+xml" href="extern.php?action=feed&tid='.$id.'&type=atom" title="'.$lang_common['Atom topic feed'].'" />');
+	$page_head['feed'] = '<link rel="alternate" type="application/atom+xml" href="extern.php?action=feed&tid='.$id.'&type=atom" title="'.$lang_common['Atom topic feed'].'" />';
 
 $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), pun_htmlspecialchars($cur_topic['forum_name']), pun_htmlspecialchars($cur_topic['subject']));
 define('PUN_ALLOW_INDEX', 1);
@@ -481,7 +500,7 @@ if ($quickpost)
 					</div>
 				</fieldset>
 			</div>
-			<p class="buttons"><input type="submit" name="submit" tabindex="2" value="<?php echo $lang_common['Submit'] ?>" accesskey="s" /></p>
+			<p class="buttons"><input type="submit" name="submit" tabindex="2" value="<?php echo $lang_common['Submit'] ?>" accesskey="s" /> <input type="submit" name="preview" value="<?php echo $lang_topic['Preview'] ?>" tabindex="3" accesskey="p" /></p>
 		</form>
 	</div>
 </div>



More information about the Xfce4-commits mailing list