diff --git a/.gitignore b/.gitignore
index 6c0c33d689..d3b77831d4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,3 +16,6 @@ provision.sh
 *.komodoproject
 
 feeds/recent.rss
+
+# winston?
+error.log
diff --git a/app.js b/app.js
index 8defae9ab2..e1201f1bd1 100644
--- a/app.js
+++ b/app.js
@@ -25,6 +25,7 @@
 
 	var fs = require('fs'),
 		async = require('async'),
+		semver = require('semver'),
 		winston = require('winston'),
 		pkg = require('./package.json'),
 		path = require('path'),
@@ -48,6 +49,12 @@
 		winston.error(err.stack);
 	};
 
+	require('child_process').exec('/usr/bin/which convert', function(err, stdout, stderr) {
+		if(err || !stdout) {
+			winston.warn('Couldn\'t find convert. Did you install imagemagick?');
+		}
+	});
+
 	// Log GNU copyright info along with server info
 	winston.info('NodeBB v' + pkg.version + ' Copyright (C) 2013 DesignCreatePlay Inc.');
 	winston.info('This program comes with ABSOLUTELY NO WARRANTY.');
@@ -73,6 +80,10 @@
 			winston.info('Base Configuration OK.');
 		}
 
+		if (semver.gt(pkg.dependencies['nodebb-theme-cerulean'], require('./node_modules/nodebb-theme-cerulean/package.json').version)) {
+			winston.error('nodebb-theme-cerulean is out of date - please run npm install.')
+		}
+
 		require('./src/database').init(function(err) {
 			meta.configs.init(function () {
 
diff --git a/package.json b/package.json
index c7ba9b9586..84aa875645 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
   "dependencies": {
     "socket.io": "~0.9.16",
     "redis": "0.8.3",
-    "mongodb": "1.3.20",
+    "mongodb": "~1.3.19",
     "express": "3.2.0",
     "express-namespace": "~0.1.1",
     "emailjs": "0.3.4",
@@ -43,10 +43,11 @@
     "uglify-js": "~2.4.0",
     "validator": "~1.5.1",
     "nodebb-plugin-mentions": "~0.1.15",
-    "nodebb-plugin-markdown": "~0.2.0",
+    "nodebb-plugin-markdown": "~0.2.1",
     "nodebb-theme-vanilla": "~0.0.9",
     "nodebb-theme-cerulean": "0.0.10",
-    "cron": "~1.0.1"
+    "cron": "~1.0.1",
+    "semver": "~2.2.1"
   },
   "optionalDependencies": {
     "hiredis": "~0.1.15"
diff --git a/public/language/de/topic.json b/public/language/de/topic.json
index e882660101..198fb414d9 100644
--- a/public/language/de/topic.json
+++ b/public/language/de/topic.json
@@ -36,5 +36,8 @@
 	"loading": "Lade",
 	"more_posts": "Mehr Posts",
 	"move_topic": "Thema verschieben",
-	"topic_will_be_moved_to": "Dieses Thema wird verschoben nach"
+	"topic_will_be_moved_to": "Dieses Thema wird verschoben nach",
+
+	"reputation": "Reputation",
+	"posts": "Posts"
 }
diff --git a/public/language/de/user.json b/public/language/de/user.json
index 492307f50d..9ca07d5433 100644
--- a/public/language/de/user.json
+++ b/public/language/de/user.json
@@ -7,7 +7,7 @@
 	"location": "Wohnort",
 	"age": "Alter",
 	"joined": "Beigetreten",
-	"profil_views": "Profilaufrufe",
+	"profile_views": "Profilaufrufe",
 	"reputation": "Reputation",
 	"posts": "Posts",
 	"followers": "Follower",
@@ -18,7 +18,7 @@
 
 	"change_picture": "Ändere Profilbild",
 	"edit": "Ändern",
-	"uploaded_pictures": "Hochgeladene Bilder",
+	"uploaded_picture": "Hochgeladene Bilder",
 	"upload_new_picture": "Neues Bild hochladen",
 	"change_password": "Ändere Passwort",
 	"confirm_password": "Passwort wiederholen",
@@ -29,7 +29,7 @@
 	"image_spec": "Du solltest nur Dateien die PNG, JPG, oder GIF kleiner als 256kb hochladen.",
 
 	"settings": "Einstellungen",
-	"show_my_email": "Zeige meine E-Mail Adresse an.",
+	"show_email": "Zeige meine E-Mail Adresse an.",
 
 	"has_no_follower": "Dieser User hat noch keine Follower.",
 	"follows_no_one": "Dieser User folgt noch niemanden."
diff --git a/public/language/en/topic.json b/public/language/en/topic.json
index 2584436a22..9df5829bea 100644
--- a/public/language/en/topic.json
+++ b/public/language/en/topic.json
@@ -32,8 +32,11 @@
 	"favourites.has_no_favourites": "You don't have any favourites, favourite some posts to see them here!",
 
 	"posted_by": "posted by",
-	"loading": "Lade",
+	"loading": "Loading",
 	"more_posts": "More Posts",
 	"move_topic": "Move Topic",
-	"topic_will_be_moved_to": "This topic will be moved to the category"
+	"topic_will_be_moved_to": "This topic will be moved to the category",
+
+	"reputation": "Reputation",
+	"posts": "Posts"
 }
diff --git a/public/language/en/user.json b/public/language/en/user.json
index 512fad96c3..1b58f198de 100644
--- a/public/language/en/user.json
+++ b/public/language/en/user.json
@@ -1,35 +1,35 @@
 {
 	"banned": "Banned",
-	"offline": "offline",
-	"email": "email",
-	"fullname": "full name",
-	"website": "website",
-	"location": "location",
-	"age": "age",
-	"joined": "joined",
-	"profil_views": "profil views",
-	"reputation": "reputation",
-	"posts": "posts",
-	"followers": "followers",
-	"following": "following",
-	"signature": "signature",
-	"gravatar": "gravatar",
-	"birthday": "birthday",
+	"offline": "Offline",
+	"email": "Email",
+	"fullname": "Full Name",
+	"website": "Website",
+	"location": "Location",
+	"age": "Age",
+	"joined": "Joined",
+	"profile_views": "Profile views",
+	"reputation": "Reputation",
+	"posts": "Posts",
+	"followers": "Followers",
+	"following": "Following",
+	"signature": "Signature",
+	"gravatar": "Gravatar",
+	"birthday": "Birthday",
 
-	"change_picture": "change picture",
-	"edit": "edit",
-	"uploaded_pictures": "uploaded pictures",
-	"upload_new_picture": "upload new picture",
-	"change_password": "change password",
-	"confirm_password": "confirm password",
-	"password": "password",
+	"change_picture": "Change Picture",
+	"edit": "Edit",
+	"uploaded_picture": "Uploaded Picture",
+	"upload_new_picture": "Upload New Picture",
+	"change_password": "Change Password",
+	"confirm_password": "Confirm Password",
+	"password": "Password",
 
 	"upload_picture": "Upload picture",
 	"upload_a_picture": "Upload a picture",
 	"image_spec": "You may only upload PNG, JPG, or GIF files under 256kb.",
 
 	"settings": "settings",
-	"show_my_email": "show my email",
+	"show_email": "Show My Email",
 
 	"has_no_follower": "This user doesn't have any followers :(",
 	"follows_no_one": "This user isn't following anyone :("
diff --git a/public/language/es/topic.json b/public/language/es/topic.json
index 865c80a59b..97f5291258 100644
--- a/public/language/es/topic.json
+++ b/public/language/es/topic.json
@@ -35,5 +35,8 @@
 	"loading": "Cargando",
 	"more_posts": "Más posts",
 	"move_topic": "Mover Tema",
-	"topic_will_be_moved_to": "Este tema sera movido a la categoría"
+	"topic_will_be_moved_to": "Este tema sera movido a la categoría",
+
+	"reputation": "Reputación",
+	"posts": "Posts"
 }
\ No newline at end of file
diff --git a/public/language/es/user.json b/public/language/es/user.json
index f1bfdf4ed0..ab0783255c 100644
--- a/public/language/es/user.json
+++ b/public/language/es/user.json
@@ -1,35 +1,35 @@
 {
 	"banned": "Banneado",
-	"offline": "desconectado",
-	"email": "email",
-	"fullname": "nombre completo",
-	"website": "website",
-	"location": "ubicación",
-	"age": "edad",
-	"joined": "registro",
-	"profil_views": "visitas en su perfil",
-	"reputation": "reputación",
-	"posts": "posts",
-	"followers": "seguidores",
-	"following": "siguiendo",
-	"signature": "firma",
-	"gravatar": "gravatar",
-	"birthday": "cumpleaños",
+	"offline": "Desconectado",
+	"email": "Email",
+	"fullname": "Nombre Completo",
+	"website": "Website",
+	"location": "Ubicación",
+	"age": "Edad",
+	"joined": "Registro",
+	"profile_views": "Visitas en su perfil",
+	"reputation": "Reputación",
+	"posts": "Posts",
+	"followers": "Seguidores",
+	"following": "Siguiendo",
+	"signature": "Firma",
+	"gravatar": "Gravatar",
+	"birthday": "Cumpleaños",
 
-	"change_picture": "cambiar foto",
-	"edit": "editar",
-	"uploaded_pictures": "fotos cargadas",
-	"upload_new_picture": "cargar nueva foto",
-	"change_password": "cambiar contraseña",
-	"confirm_password": "confirmar contraseña",
-	"password": "contraseña",
+	"change_picture": "Cambiar Foto",
+	"edit": "Editar",
+	"uploaded_picture": "Fotos Cargadas",
+	"upload_new_picture": "Cargar Nueva Foto",
+	"change_password": "Cambiar Contraseña",
+	"confirm_password": "Confirmar Contraseña",
+	"password": "Contraseña",
 
 	"upload_picture": "Cargar foto",
 	"upload_a_picture": "Cargar una foto",
 	"image_spec": "Solo puedes usar PNG, JPG, o GIF hasta 256kb.",
 
-	"settings": "opciones",
-	"show_my_email": "mostrar mi email",
+	"settings": "Opciones",
+	"show_email": "Mostrar mi Email",
 
 	"has_no_follower": "Este miembro no tiene seguidores :(",
 	"follows_no_one": "Este miembro no sigue a nadie, que pena :("
diff --git a/public/language/fr/category.json b/public/language/fr/category.json
new file mode 100644
index 0000000000..29c8c492b6
--- /dev/null
+++ b/public/language/fr/category.json
@@ -0,0 +1,14 @@
+{
+	"new_topic_button": "Nouveau Sujet",
+	"no_topics": "<strong>Il n'y a aucun topic dans cette catégorie.</strong><br />Pourquoi ne pas en créer un?",
+	"sidebar.recent_replies": "Réponses Récentes",
+	"sidebar.active_participants": "Participants Actifs",
+	"sidebar.moderators": "Modérateurs",
+	"posts": "messages",
+	"views": "vues",
+	"posted": "posté",
+	"browsing": "naviguer",
+	"no_replies": "Personne n'a répondu",
+	"replied": "répondu",
+	"last_edited_by": "dernière édition par"
+}
diff --git a/public/language/fr/footer.json b/public/language/fr/footer.json
new file mode 100644
index 0000000000..27a4d0e1a9
--- /dev/null
+++ b/public/language/fr/footer.json
@@ -0,0 +1,10 @@
+{
+	"chat.chatting_with": "Chat avec <span id=\"chat-with-name\"></span>",
+	"chat.placeholder": "taper le message ici, presser entrer pour envoyer",
+	"chat.send": "Envoyer",
+	"stats.online": "Online",
+	"stats.users": "Utilisateurs",
+	"stats.topics": "Sujets",
+	"stats.posts": "Message",
+	"success": "succès"
+}
diff --git a/public/language/fr/global.json b/public/language/fr/global.json
new file mode 100644
index 0000000000..e454103779
--- /dev/null
+++ b/public/language/fr/global.json
@@ -0,0 +1,31 @@
+{
+	"home": "Accueil",
+	"search": "Recherche",
+	"buttons.close": "Fermer",
+	"403.title": "Accès Refusé",
+	"403.message": "Il semble que vous vous soyez retrouvé sur une page dont vous n'avez pas accès. Peut-être devriez vous <a href='/login'>essayez de vous connecter</a>?",
+	"404.title": "Introuvable",
+	"404.message": "Il semble que vous vous soyez retrouvé sur une page qui n'existe pas. Retourner à <a href='/'>l'accueil</a>.",
+	"500.title": "Erreur Interne.",
+	"500.message": "Oops! Il semblerait que quelque chose se soit mal passé!",
+
+	"register": "S'inscrire",
+	"login": "Connecter",
+
+	"logout": "Déconnection",
+	"logout.title": "Vous êtes maintenant déconnecté.",
+	"logout.message": "Vous vous êtes déconnecté de NodeBB avec succès",
+
+	"save_changes": "Enregistrer les changements",
+	"close": "Fermer",
+
+	"header.admin": "Admin",
+	"header.recent": "Récent",
+	"header.unread": "Non Lu",
+	"header.users": "Utilisateurs",
+	"header.search": "Recherche",
+	"header.profile": "Profile",
+
+	"notifications.loading": "Chargement des Notifications",
+	"chats.loading": "Chargement des Chats"
+}
diff --git a/public/language/fr/login.json b/public/language/fr/login.json
new file mode 100644
index 0000000000..d892409f69
--- /dev/null
+++ b/public/language/fr/login.json
@@ -0,0 +1,10 @@
+{
+	"login": "Connexion",
+	"username": "Identifiant",
+	"password": "Mot de passe",
+	"remember_me": "Se souvenir de moi?",
+	"forgot_password": "Mot de passe oublié?",
+	"alternative_logins": "Connexion Alternative",
+	"failed_login_attempt": "Echèc d'authentification, veuillez réessayer.",
+	"login_successful": "Vous êtes maintenant connecté!"
+}
diff --git a/public/language/fr/notifications.json b/public/language/fr/notifications.json
new file mode 100644
index 0000000000..7a4ef3e3c3
--- /dev/null
+++ b/public/language/fr/notifications.json
@@ -0,0 +1,9 @@
+{
+	"title": "Notifications",
+	"back_to_home": "retour à NodeBB",
+	"mark_all_as_read": "Tout marquer comme lu",
+	"outgoing_link": "Lien Sortant",
+	"outgoing_link_message": "Vous quitter NodeBB",
+	"continue_to": "Continuer vers",
+	"return_to": "Retour vers"
+}
diff --git a/public/language/fr/recent.json b/public/language/fr/recent.json
new file mode 100644
index 0000000000..4f7d7c96cc
--- /dev/null
+++ b/public/language/fr/recent.json
@@ -0,0 +1,5 @@
+{
+	"day": "Jour",
+	"week": "Semaine",
+	"month": "Mois"
+}
diff --git a/public/language/fr/register.json b/public/language/fr/register.json
new file mode 100644
index 0000000000..bfb58f76e9
--- /dev/null
+++ b/public/language/fr/register.json
@@ -0,0 +1,16 @@
+{
+	"register": "S'inscrire",
+	"help.email": "Par défault, votre email est masqué du public.",
+	"help.username_restrictions": "Un identifiant unique entre %1 et %2 charactères. Les autres utilisateurs peuvent vous citer avec @<span id='yourUsername'>username</span>.",
+	"help.minimum_password_length": "Votre mot de passe doit avoir au moins %1 charactères.",
+	"email_address": "Adresse Email",
+	"email_address_placeholder": "Entrer l'addresse Email",
+	"username": "Nom d'utilisateur",
+	"username_placeholder": "Entré le Nom d'utilisateur",
+	"password": "Mot de passe",
+	"password_placeholder": "Entrer le Mot de passe",
+	"confirm_password": "Confirmer le Mot de passe",
+	"confirm_password_placeholder": "Confirmer le Mot de passe",
+	"register_now_button": "S'enregistrer maintenant",
+	"alternative_registration": "Enregistrement Alternatif"
+}
diff --git a/public/language/fr/reset_password.json b/public/language/fr/reset_password.json
new file mode 100644
index 0000000000..76b5724528
--- /dev/null
+++ b/public/language/fr/reset_password.json
@@ -0,0 +1,13 @@
+{
+	"reset_password": "Réinitialiser le Mot de passe",
+	"update_password": "Mettre à jour le Mot de passe",
+	"password_changed.title": "Mot de passe modifié",
+	"password_changed.message": "<p>Mot de passe réinitialisé avec succès, veuillez vous <a href=\"/login\">reconnecter</a>.",
+	"wrong_reset_code.title": "Code de Réinisialisation Incorrect",
+	"wrong_reset_code.message": "Le Code de Réinisialisation est Incorrect. Veillez réessayer, ou <a href=\"/reset\">demander un nouveau Code de Réinisialisation</a>.",
+	"new_password": "Nouveau Mot de passe",
+	"repeat_password": "Confirmer le Mot de passe",
+	"enter_email": "Veuillez entrer votre <strong>adresse email</strong> et vous recevrez un email avec les instruction pour réinitialiser votre compte.",
+	"password_reset_sent": "Réinitialisation de Mot de Passe Envoyée",
+	"invalid_email": "Email Invalide / L'Email n'existe pas!"
+}
diff --git a/public/language/fr/topic.json b/public/language/fr/topic.json
new file mode 100644
index 0000000000..d874927c33
--- /dev/null
+++ b/public/language/fr/topic.json
@@ -0,0 +1,42 @@
+{
+	"topic": "Sujet",
+	"topics": "Sujets",
+
+	"no_topics_found": "Aucun sujet trouvé!",
+
+	"profile": "Profile",
+	"posted_by": "Envoyé by",
+	"chat": "Chat",
+	"notify_me": "Être notifié des réponses dans ce sujet",
+	"quote": "Citer",
+	"reply": "Répondre",
+	"edit": "Editer",
+	"delete": "Supprimer",
+	"banned": "bannir",
+	"link": "Lien",
+
+	"thread_tools.title": "Outils du Fil",
+	"thread_tools.pin": "Epingler le fil",
+	"thread_tools.lock": "Verrouiller le fil",
+	"thread_tools.move": "Déplacer le fil",
+	"thread_tools.delete": "Supprimer le fil",
+
+	"load_categories": "Chargement des Categories",
+	"disabled_categories_note": "Les Catégories Désactivées sont grisées",
+	"confirm_move": "Déplacer",
+
+	"favourite": "Favoris",
+	"favourites": "Favoris",
+	"favourites.not_logged_in.title": "Non Connecté",
+	"favourites.not_logged_in.message": "Veuillez vous connecter avant de mettre ce message en Favoris",
+	"favourites.has_no_favourites": "Vous n'avez aucun Favoris, mettre en favoris des messages pour les voir apparaître ici!",
+
+	"posted_by": "posté par",
+	"loading": "Chargement",
+	"more_posts": "d'autres Messages",
+	"move_topic": "Déplacer le Sujet",
+	"topic_will_be_moved_to": "Ce sujet sera déplacé vers la catégorie",
+
+	"reputation": "réputation",
+	"posts": "messages"
+}
diff --git a/public/language/fr/unread.json b/public/language/fr/unread.json
new file mode 100644
index 0000000000..78f51ce820
--- /dev/null
+++ b/public/language/fr/unread.json
@@ -0,0 +1,5 @@
+{
+	"no_unread_topics": "Aucun sujet non lu.",
+	"mark_all_read": "Marquer tout comme lu",
+	"load_more": "Charger la suite"
+}
diff --git a/public/language/fr/user.json b/public/language/fr/user.json
new file mode 100644
index 0000000000..faa9883d47
--- /dev/null
+++ b/public/language/fr/user.json
@@ -0,0 +1,36 @@
+{
+	"banned": "Banni",
+	"offline": "Hors-ligne",
+	"email": "email",
+	"fullname": "Nom",
+	"website": "Site Web",
+	"location": "Emplacement",
+	"age": "age",
+	"joined": "adhésion",
+	"profil_views": "vues du profil",
+	"reputation": "réputation",
+	"posts": "messages",
+	"followers": "suiveurs",
+	"following": "suivis",
+	"signature": "signature",
+	"gravatar": "gravatar",
+	"birthday": "anniversaire",
+
+	"change_picture": "changer d'image",
+	"edit": "editer",
+	"uploaded_picture": "images uploadées",
+	"upload_new_picture": "uploader une nouvelle image",
+	"change_password": "chnger le mot de passe",
+	"confirm_password": "confirmer le mot de passe",
+	"password": "mot de passe",
+
+	"upload_picture": "Uploader un image",
+	"upload_a_picture": "Uploader un image",
+	"image_spec": "Vous pouvez uploader seulement des fichiers de types PNG, JPG, ou GIF en dessous de 256kb.",
+
+	"settings": "paramètres",
+	"show_my_email": "montrer mon email",
+
+	"has_no_follower": "Cet utilisateur n'a aucun suiver :(",
+	"follows_no_one": "Cet utilisateur ne suit personne :("
+}
diff --git a/public/language/fr/users.json b/public/language/fr/users.json
new file mode 100644
index 0000000000..8cab0e5298
--- /dev/null
+++ b/public/language/fr/users.json
@@ -0,0 +1,9 @@
+{
+	"latest_users": "Derniers Utilisateurs",
+	"top_posters": "Meilleurs Publieur",
+	"most_reputation": "Meilleur Réputation",
+	"online": "En Ligne",
+	"search": "Rechercher",
+	"enter_username": "Entrer un nom d'utilisateur pour rechercher",
+	"load_more": "Charger la suite"
+}
diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js
index bba039ea66..e0c69687f6 100644
--- a/public/src/ajaxify.js
+++ b/public/src/ajaxify.js
@@ -31,6 +31,8 @@ var ajaxify = {};
 
 	var pagination, paginator_bar;
 
+	ajaxify.currentPage = null;
+
 	ajaxify.go = function (url, callback, template, quiet) {
 		// start: the following should be set like so: ajaxify.onchange(function(){}); where the code actually belongs
 		$(window).off('scroll');
@@ -69,6 +71,8 @@ var ajaxify = {};
 		}
 
 		if (templates.is_available(tpl_url) && !templates.force_refresh(tpl_url)) {
+			ajaxify.currentPage = tpl_url;
+
 			if (window.history && window.history.pushState) {
 				window.history[!quiet ? 'pushState' : 'replaceState']({
 					url: url
@@ -90,7 +94,7 @@ var ajaxify = {};
 
 			translator.load(tpl_url);
 
-			jQuery('#footer, #content').addClass('ajaxifying');
+			jQuery('#footer, #content').removeClass('hide').addClass('ajaxifying');
 
 			templates.flush();
 			templates.load_template(function () {
@@ -129,6 +133,10 @@ var ajaxify = {};
 		return false;
 	};
 
+	ajaxify.refresh = function() {
+		ajaxify.go(ajaxify.currentPage);
+	};
+
 	$('document').ready(function () {
 		if (!window.history || !window.history.pushState) {
 			return; // no ajaxification for old browsers
@@ -154,7 +162,7 @@ var ajaxify = {};
 				return;
 			}
 
-			if (!e.ctrlKey && e.which === 1) {
+			if ((!e.ctrlKey && !e.shiftKey) && e.which === 1) {
 				if (this.host === window.location.host) {
 					// Internal link
 					var url = this.href.replace(rootUrl + '/', '');
diff --git a/public/src/app.js b/public/src/app.js
index c7fe7835e7..537d0103bd 100644
--- a/public/src/app.js
+++ b/public/src/app.js
@@ -1,16 +1,16 @@
 var socket,
 	config,
 	app = {
-		'username': null,
-		'uid': null
+		"username": null,
+		"uid": null,
+		"isFocused": true,
+		"currentRoom": null
 	};
 
 (function () {
 	var showWelcomeMessage = false;
 
-
 	app.loadConfig = function() {
-
 		$.ajax({
 			url: RELATIVE_PATH + '/api/config',
 			success: function (data) {
@@ -135,7 +135,7 @@ var socket,
 			},
 			async: false
 		});
-	}
+	};
 
 	app.logout = function() {
 		$.post(RELATIVE_PATH + '/logout', {
@@ -143,12 +143,12 @@ var socket,
 		}, function() {
 			window.location.href = RELATIVE_PATH + '/';
 		});
-	}
+	};
 
 	// takes a string like 1000 and returns 1,000
 	app.addCommas = function (text) {
 		return text.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,");
-	}
+	};
 
 	// Willingly stolen from: http://phpjs.org/functions/strip_tags/
 	app.strip_tags = function (input, allowed) {
@@ -159,7 +159,7 @@ var socket,
 		return input.replace(commentsAndPhpTags, '').replace(tags, function ($0, $1) {
 			return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : '';
 		});
-	}
+	};
 
 	// use unique alert_id to have multiple alerts visible at a time, use the same alert_id to fade out the current instance
 	// type : error, success, info, warning/notify
@@ -222,7 +222,7 @@ var socket,
 				});
 			}
 		}
-	}
+	};
 
 	app.alertSuccess = function (message, timeout) {
 		if (!timeout)
@@ -234,7 +234,7 @@ var socket,
 			type: 'success',
 			timeout: timeout
 		});
-	}
+	};
 
 	app.alertError = function (message, timeout) {
 		if (!timeout)
@@ -246,9 +246,8 @@ var socket,
 			type: 'danger',
 			timeout: timeout
 		});
-	}
+	};
 
-	app.currentRoom = null;
 	app.enterRoom = function (room) {
 		if (socket) {
 			if (app.currentRoom === room) {
@@ -272,7 +271,7 @@ var socket,
 		});
 
 		socket.emit('api:user.get_online_users', uids);
-	}
+	};
 
 	function highlightNavigationLink() {
 		var path = window.location.pathname,
@@ -291,7 +290,7 @@ var socket,
 				}
 			});
 		}
-	}
+	};
 
 	app.createUserTooltips = function() {
 		$('img[title].teaser-pic,img[title].user-img').each(function() {
@@ -300,13 +299,13 @@ var socket,
 				title: $(this).attr('title')
 			});
 		});
-	}
+	};
 
 	app.makeNumbersHumanReadable = function(elements) {
 		elements.each(function() {
 			$(this).html(utils.makeNumberHumanReadable($(this).attr('title')));
 		});
-	}
+	};
 
 	app.processPage = function () {
 		app.populateOnlineUsers();
@@ -323,7 +322,7 @@ var socket,
 		setTimeout(function () {
 			window.scrollTo(0, 1); // rehide address bar on mobile after page load completes.
 		}, 100);
-	}
+	};
 
 	app.showLoginMessage = function () {
 		function showAlert() {
@@ -343,13 +342,13 @@ var socket,
 				showAlert();
 			}
 		}
-	}
+	};
 
 	app.addCommasToNumbers = function () {
 		$('.formatted-number').each(function (index, element) {
 			$(element).html(app.addCommas($(element).html()));
 		});
-	}
+	};
 
 	app.openChat = function (username, touid) {
 		if (username === app.username) {
@@ -384,7 +383,7 @@ var socket,
 			chat.load(chatModal.attr('UUID'));
 			chat.center(chatModal);
 		});
-	}
+	};
 
 	app.scrollToTop = function () {
 		$('body,html').animate({
@@ -442,6 +441,14 @@ var socket,
 			input.val('');
 			return false;
 		});
+
+		$(window).blur(function(){
+			app.isFocused = false;
+		});
+
+		$(window).focus(function(){
+			app.isFocused = true;
+		});
 	});
 
 	showWelcomeMessage = location.href.indexOf('loggedin') !== -1;
diff --git a/public/src/forum/accountedit.js b/public/src/forum/accountedit.js
index 5e4eb0fcc3..f4c86f3ba4 100644
--- a/public/src/forum/accountedit.js
+++ b/public/src/forum/accountedit.js
@@ -84,7 +84,9 @@ define(['forum/accountheader', 'uploader'], function(header, uploader) {
 		$('#uploadPictureBtn').on('click', function() {
 
 			$('#change-picture-modal').modal('hide');
-			uploader.open(config.relative_path + '/user/uploadpicture', function(imageUrlOnServer) {
+			uploader.open(RELATIVE_PATH + '/user/uploadpicture', function(imageUrlOnServer) {
+				imageUrlOnServer = imageUrlOnServer + '?' + new Date().getTime();
+
 				$('#user-current-picture').attr('src', imageUrlOnServer);
 				$('#user-uploaded-picture').attr('src', imageUrlOnServer);
 
diff --git a/public/src/forum/admin/categories.js b/public/src/forum/admin/categories.js
index d94eb9434b..0874ffa878 100644
--- a/public/src/forum/admin/categories.js
+++ b/public/src/forum/admin/categories.js
@@ -1,4 +1,4 @@
-define(function() {
+define(['uploader'], function(uploader) {
 	var	Categories = {};
 
 	Categories.init = function() {
@@ -82,7 +82,8 @@ define(function() {
 				description: $('#inputDescription').val(),
 				icon: $('#new-category-modal i').val(),
 				bgColor: '#0059b2',
-				color: '#fff'
+				color: '#fff',
+				order: $('.admin-categories #entry-container').children().length + 1
 			};
 
 			socket.emit('api:admin.categories.create', category, function(err, data) {
@@ -147,7 +148,6 @@ define(function() {
 				var btn = $(this);
 				var categoryRow = btn.parents('li');
 				var cid = categoryRow.attr('data-cid');
-				console.log(this.getAttribute('data-disabled'));
 
 				var disabled = this.getAttribute('data-disabled') === '0' ? '1' : '0';
 				categoryRow.remove();
@@ -179,6 +179,31 @@ define(function() {
 				var	cid = $(this).parents('li[data-cid]').attr('data-cid');
 				Categories.launchPermissionsModal(cid);
 			});
+
+
+			$('.upload-button').on('click', function() {
+				var inputEl = this;
+				
+				uploader.open(RELATIVE_PATH + '/admin/category/uploadpicture', function(imageUrlOnServer) {
+					inputEl.value = imageUrlOnServer;
+					$(inputEl).parents('li[data-cid]').find('.preview-box').css('background', 'url(' + imageUrlOnServer + '?' + new Date().getTime() + ')');
+					modified(inputEl);
+				});
+			});
+
+			$('.admin-categories').delegate('.delete-image', 'click', function() {
+				var parent = $(this).parents('li[data-cid]'),
+					inputEl = parent.find('.upload-button'),
+					preview = parent.find('.preview-box'),
+					bgColor = parent.find('.category_bgColor').val();
+
+				inputEl.value = '';
+				modified(inputEl);
+
+				preview.css('background', bgColor);
+
+				$(this).hide();
+			});
 		});
 	};
 
diff --git a/public/src/forum/admin/settings.js b/public/src/forum/admin/settings.js
index 989e94a79c..8e8fc359df 100644
--- a/public/src/forum/admin/settings.js
+++ b/public/src/forum/admin/settings.js
@@ -76,7 +76,7 @@ define(['uploader'], function(uploader) {
 
 		$('#uploadLogoBtn').on('click', function() {
 
-			uploader.open(config.relative_path + '/admin/uploadlogo', function(image) {
+			uploader.open(RELATIVE_PATH + '/admin/uploadlogo', function(image) {
 				$('#logoUrl').val(image);
 			});
 
diff --git a/public/src/forum/category.js b/public/src/forum/category.js
index 44a11eaea9..6b25a85701 100644
--- a/public/src/forum/category.js
+++ b/public/src/forum/category.js
@@ -83,36 +83,39 @@ define(function () {
 	Category.onNewTopic = function(data) {
 		var html = templates.prepare(templates['category'].blocks['topics']).parse({
 			topics: [data]
-		}),
-			topic = $(html),
-			container = $('#topics-container'),
-			topics = $('#topics-container').children('.category-item'),
-			numTopics = topics.length;
-
-		jQuery('#topics-container, .category-sidebar').removeClass('hidden');
-		jQuery('#category-no-topics').remove();
-
-		if (numTopics > 0) {
-			for (var x = 0; x < numTopics; x++) {
-				if ($(topics[x]).find('.fa-thumb-tack').length) {
-					if(x === numTopics - 1) {
-						topic.insertAfter(topics[x]);
+		});
+		
+		translator.translate(html, function(translatedHTML) {
+			var topic = $(translatedHTML),
+				container = $('#topics-container'),
+				topics = $('#topics-container').children('.category-item'),
+				numTopics = topics.length;
+
+			jQuery('#topics-container, .category-sidebar').removeClass('hidden');
+			jQuery('#category-no-topics').remove();
+
+			if (numTopics > 0) {
+				for (var x = 0; x < numTopics; x++) {
+					if ($(topics[x]).find('.fa-thumb-tack').length) {
+						if(x === numTopics - 1) {
+							topic.insertAfter(topics[x]);
+						}
+						continue;
 					}
-					continue;
+					topic.insertBefore(topics[x]);
+					break;
 				}
-				topic.insertBefore(topics[x]);
-				break;
+			} else {
+				container.append(topic);
 			}
-		} else {
-			container.append(topic);
-		}
 
-		topic.hide().fadeIn('slow');
-		socket.emit('api:categories.getRecentReplies', templates.get('category_id'));
+			topic.hide().fadeIn('slow');
+			socket.emit('api:categories.getRecentReplies', templates.get('category_id'));
 
-		addActiveUser(data);
+			addActiveUser(data);
 
-		$('#topics-container span.timeago').timeago();
+			$('#topics-container span.timeago').timeago();
+		});
 	}
 
 	function addActiveUser(data) {
@@ -131,20 +134,22 @@ define(function () {
 	}
 
 	Category.onTopicsLoaded = function(topics) {
-
 		var html = templates.prepare(templates['category'].blocks['topics']).parse({
 			topics: topics
-		}),
-			container = $('#topics-container');
+		});
+		
+		translator.translate(html, function(translatedHTML) {
+			var container = $('#topics-container');
 
-		jQuery('#topics-container, .category-sidebar').removeClass('hidden');
-		jQuery('#category-no-topics').remove();
+			jQuery('#topics-container, .category-sidebar').removeClass('hidden');
+			jQuery('#category-no-topics').remove();
 
-		html = $(html);
-		container.append(html);
+			html = $(translatedHTML);
+			container.append(html);
 
-		$('#topics-container span.timeago').timeago();
-		app.makeNumbersHumanReadable(html.find('.human-readable-number'));
+			$('#topics-container span.timeago').timeago();
+			app.makeNumbersHumanReadable(html.find('.human-readable-number'));
+		});
 	}
 
 	Category.loadMoreTopics = function(cid) {
diff --git a/public/src/forum/footer.js b/public/src/forum/footer.js
index eb8d0f3e20..dc89b15f01 100644
--- a/public/src/forum/footer.js
+++ b/public/src/forum/footer.js
@@ -67,6 +67,7 @@
 		notifTrigger = notifContainer.querySelector('a'),
 		notifList = document.getElementById('notif-list'),
 		notifIcon = $('.notifications a');
+
 	notifTrigger.addEventListener('click', function(e) {
 		e.preventDefault();
 		if (notifContainer.className.indexOf('open') === -1) {
@@ -169,6 +170,10 @@
 		});
 		app.refreshTitle();
 
+		if (ajaxify.currentPage === 'notifications') {
+			ajaxify.refresh();
+		}
+
 		// Update the favicon + local storage
 		var	savedCount = parseInt(localStorage.getItem('notifications:count'),10) || 0;
 		localStorage.setItem('notifications:count', savedCount+1);
@@ -208,7 +213,7 @@
 		});
 	});
 
-	socket.on('chatMessage', function(data) {
+	socket.on('event:chats.receive', function(data) {
 		require(['chat'], function(chat) {
 			var modal = null;
 			if (chat.modalExists(data.fromuid)) {
@@ -219,6 +224,9 @@
 					chat.load(modal.attr('UUID'));
 				} else {
 					chat.toggleNew(modal.attr('UUID'), true);
+				}
+
+				if (!modal.is(":visible") || !app.isFocused) {
 					app.alternatingTitle(data.username + ' has messaged you');
 				}
 			} else {
diff --git a/public/src/forum/recent.js b/public/src/forum/recent.js
index 6b162db34f..c883c9c54b 100644
--- a/public/src/forum/recent.js
+++ b/public/src/forum/recent.js
@@ -81,18 +81,20 @@ define(function() {
 	}
 
 	Recent.onTopicsLoaded = function(topics) {
-
 		var html = templates.prepare(templates['recent'].blocks['topics']).parse({
 			topics: topics
-		}),
-			container = $('#topics-container');
+		});
+		
+		translator.translate(html, function(translatedHTML) {
+			var container = $('#topics-container');
 
-		$('#category-no-topics').remove();
+			$('#category-no-topics').remove();
 
-		html = $(html);
-		container.append(html);
-		$('span.timeago').timeago();
-		app.makeNumbersHumanReadable(html.find('.human-readable-number'));
+			html = $(html);
+			container.append(html);
+			$('span.timeago').timeago();
+			app.makeNumbersHumanReadable(html.find('.human-readable-number'));
+		});
 	}
 
 	Recent.loadMoreTopics = function() {
diff --git a/public/src/forum/topic.js b/public/src/forum/topic.js
index fd77d2084f..3a47ce901c 100644
--- a/public/src/forum/topic.js
+++ b/public/src/forum/topic.js
@@ -802,8 +802,8 @@ define(function() {
 
 		pagination.parentNode.style.display = 'block';
 		progressBarContainer.css('display', '');
-
-		if (scrollTop < 50 && Topic.postCount > 1) {
+		
+		if (scrollTop < jQuery('.posts > .post-row:first-child').height() && Topic.postCount > 1) {
 			localStorage.removeItem("topic:" + tid + ":bookmark");
 			pagination.innerHTML = '1 out of ' + Topic.postCount;
 			progressBar.width(0);
diff --git a/public/src/forum/unread.js b/public/src/forum/unread.js
index 6debeeba84..e0dd1dd7d7 100644
--- a/public/src/forum/unread.js
+++ b/public/src/forum/unread.js
@@ -71,18 +71,20 @@ define(function() {
 		});
 
 		function onTopicsLoaded(topics) {
-
 			var html = templates.prepare(templates['unread'].blocks['topics']).parse({
 				topics: topics
-			}),
-				container = $('#topics-container');
+			});
 
-			$('#category-no-topics').remove();
+			translator.translate(html, function(translatedHTML) {
+				var container = $('#topics-container');
 
-			html = $(html);
-			container.append(html);
-			$('span.timeago').timeago();
-			app.makeNumbersHumanReadable(html.find('.human-readable-number'));
+				$('#category-no-topics').remove();
+
+				html = $(translatedHTML);
+				container.append(html);
+				$('span.timeago').timeago();
+				app.makeNumbersHumanReadable(html.find('.human-readable-number'));
+			});
 		}
 
 		function loadMoreTopics() {
diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js
index 2aeb2f02bf..490f6e571b 100644
--- a/public/src/modules/chat.js
+++ b/public/src/modules/chat.js
@@ -103,6 +103,7 @@ define(['taskbar'], function(taskbar) {
 		module.bringModalToTop(chatModal);
 		checkOnlineStatus(chatModal);
 		taskbar.updateActive(uuid);
+		chatModal.find('#chat-message-input').focus();
 	}
 
 	module.minimize = function(uuid) {
@@ -114,7 +115,7 @@ define(['taskbar'], function(taskbar) {
 	}
 
 	function getChatMessages(chatModal, callback) {
-		socket.emit('getChatMessages', {touid:chatModal.touid}, function(messages) {
+		socket.emit('api:chats.get', {touid:chatModal.touid}, function(messages) {
 			for(var i = 0; i<messages.length; ++i) {
 				module.appendChatMessage(chatModal, messages[i].content, messages[i].timestamp);
 			}
@@ -141,7 +142,7 @@ define(['taskbar'], function(taskbar) {
 		var msg = app.strip_tags(chatModal.find('#chat-message-input').val());
 		if(msg.length) {
 			msg = msg +'\n';
-			socket.emit('sendChatMessage', { touid:chatModal.touid, message:msg});
+			socket.emit('api:chats.send', { touid:chatModal.touid, message:msg});
 			chatModal.find('#chat-message-input').val('');
 		}
 	}
diff --git a/public/src/templates.js b/public/src/templates.js
index 6772f998b7..e629ef08b6 100644
--- a/public/src/templates.js
+++ b/public/src/templates.js
@@ -300,14 +300,7 @@
 						namespace = namespace.replace(d + '.', '');
 						template = setBlock(regex, result, template);
 					} else if (data[d] instanceof Object) {
-						namespace += d + '.';
-
-						regex = makeRegex(d),
-						block = getBlock(regex, namespace, template)
-						if (block == null) continue;
-
-						block = parse(data[d], namespace, block);
-						template = setBlock(regex, block, template);
+						template = parse(data[d], d + '.', template);
 					} else {
 						function checkConditional(key, value) {
 							var conditional = makeConditionalRegex(key),
@@ -320,14 +313,16 @@
 									if (conditionalBlock[1]) {
 										// there is an else statement
 										if (!value) {
-											template = template.replace(matches[i], conditionalBlock[1]);
+											template = template.replace(matches[i], conditionalBlock[1].replace(/<!-- ((\IF\b)|(\bENDIF\b))([^@]*?)-->/gi, ''));
 										} else {
-											template = template.replace(matches[i], conditionalBlock[0]);
+											template = template.replace(matches[i], conditionalBlock[0].replace(/<!-- ((\IF\b)|(\bENDIF\b))([^@]*?)-->/gi, ''));
 										}
 									} else {
 										// regular if statement
 										if (!value) {
 											template = template.replace(matches[i], '');
+										} else {
+											template = template.replace(matches[i], matches[i].replace(/<!-- ((\IF\b)|(\bENDIF\b))([^@]*?)-->/gi, ''));
 										}
 									}
 								}
@@ -350,7 +345,11 @@
 			if (namespace) {
 				var regex = new RegExp("{" + namespace + "[\\s\\S]*?}", 'g');
 				template = template.replace(regex, '');
+				namespace = '';
 			}
+			
+			// clean up all undefined conditionals
+			template = template.replace(/<!-- IF([^@]*?)ENDIF([^@]*?)-->/gi, '');
 
 			return template;
 
diff --git a/public/templates/account.tpl b/public/templates/account.tpl
index da0014b0fe..200fd854c2 100644
--- a/public/templates/account.tpl
+++ b/public/templates/account.tpl
@@ -53,7 +53,7 @@
 					<span class="timeago" title="{joindate}"></span>
 					<br/>
 
-					<span class="account-bio-label">[[user:profil_views]]</span>
+					<span class="account-bio-label">[[user:profile_views]]</span>
 					<span class="formatted-number">{profileviews}</span>
 					<br/>
 
diff --git a/public/templates/accountedit.tpl b/public/templates/accountedit.tpl
index e9b970a1be..fa0e64d055 100644
--- a/public/templates/accountedit.tpl
+++ b/public/templates/accountedit.tpl
@@ -10,17 +10,17 @@
 				<div class="modal-body">
 					<div id="gravatar-box">
 						<img id="user-gravatar-picture" src="" class="img-thumbnail user-profile-picture">
-						<span class="user-picture-label">[[user: gravatar]]</span>
+						<span class="user-picture-label">[[user:gravatar]]</span>
 						<i class='fa fa-check fa-2x'></i>
 					</div>
 					<br/>
 					<div id="uploaded-box">
 						<img id="user-uploaded-picture" src="" class="img-thumbnail user-profile-picture">
-						<span class="user-picture-label">[[user: uploaded_picture]]</span>
+						<span class="user-picture-label">[[user:uploaded_picture]]</span>
 						<i class='fa fa-check fa-2x'></i>
 					</div>
 
-					<a id="uploadPictureBtn" href="#">[[user: upload_new_picture]]</a>
+					<a id="uploadPictureBtn" href="#">[[user:upload_new_picture]]</a>
 				</div>
 				<div class="modal-footer">
 					<button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Close</button>
diff --git a/public/templates/accountsettings.tpl b/public/templates/accountsettings.tpl
index f705621153..f3987b69cc 100644
--- a/public/templates/accountsettings.tpl
+++ b/public/templates/accountsettings.tpl
@@ -12,7 +12,7 @@
 			<h4>privacy</h4>
 			<div class="checkbox">
 				<label>
-	      			<input id="showemailCheckBox" type="checkbox" {showemail}> [[user:show_my_email]]
+	      			<input id="showemailCheckBox" type="checkbox" {showemail}> [[user:show_email]]
 	    		</label>
 	    	</div>
 		</div>
diff --git a/public/templates/admin/categories.tpl b/public/templates/admin/categories.tpl
index 4cc65d8bca..a8da120e82 100644
--- a/public/templates/admin/categories.tpl
+++ b/public/templates/admin/categories.tpl
@@ -14,12 +14,15 @@
 		<!-- BEGIN categories -->
 			<li data-cid="{categories.cid}" class="entry-row">
 				<div class="row">
-					<div class="col-sm-2 hidden-xs">
-						<div class="preview-box" style="background: {categories.bgColor}; color: {categories.color};">
+					<div class="col-sm-2 hidden-xs text-center">
+						<div class="preview-box" style="background: {categories.background}; color: {categories.color};">
 							<div class="icon">
 								<i data-name="icon" value="{categories.icon}" class="fa {categories.icon} fa-2x"></i>
 							</div>
-						</div>
+						</div><br />
+						<!-- IF categories.image -->
+						<small class="pointer delete-image"><i data-name="icon" value="fa-times" class="fa fa-times"></i> Delete Image</small>
+						<!-- ENDIF categories.image -->
 					</div>
 					<div class="col-sm-10">
 						<form class="form">
@@ -67,11 +70,8 @@
 													<hr />
 													<li data-disabled="{categories.disabled}"><a href="#"></a></li>
 												</ul>
+												<button type="button" data-name="image" data-value="{categories.image}" class="btn btn-default upload-button"><i class="fa fa-upload"></i> Image</button>
 											</div>
-											<!-- <div class="btn-group">
-												<button type="submit" class="btn btn-default disable-btn" data-disabled="{categories.disabled}">Disable</button>
-												<button type="button" class="btn btn-default permissions">Permissions</button>
-											</div> -->
 										</div>
 									</div>
 								</div>
@@ -80,17 +80,7 @@
 							<input type="hidden" data-name="order" data-value="{categories.order}"></input>
 						</form>
 					</div>
-				<!-- <form class="form-inline">
-					<div class="icon">
-						<i data-name="icon" value="{categories.icon}" class="fa {categories.icon} fa-2x"></i>
-					</div>
-					<input placeholder="Category Name" data-name="name" value="{categories.name}" class="form-control category_name"></input>
-					<input placeholder="#0059b2" data-name="bgColor" value="{categories.bgColor}" class="form-control category_bgColor" />
-					<input placeholder="#fff" data-name="color" value="{categories.color}" class="form-control category_color" />
-					<input data-name="description" placeholder="Category Description" value="{categories.description}" class="form-control category_description description"></input>
-					<input type="hidden" data-name="order" data-value="{categories.order}"></input>
-					<button type="submit" class="btn btn-default disable-btn" data-disabled="{categories.disabled}">Disable</button>
-				</form> -->
+				</div>
 			</li>
 
 		<!-- END categories -->
diff --git a/public/templates/admin/settings.tpl b/public/templates/admin/settings.tpl
index da88d2003e..a5e6192b1b 100644
--- a/public/templates/admin/settings.tpl
+++ b/public/templates/admin/settings.tpl
@@ -26,6 +26,11 @@
 		<p>Use <strong>privilege thresholds</strong> to manage how much reputation a user must gain to receive moderator access.</p><br />
 		<strong>Manage Thread</strong><br /> <input type="text" class="form-control" value="1000" data-field="privileges:manage_topic"><br />
 		<strong>Manage Content</strong><br /> <input type="text" class="form-control" value="1000" data-field="privileges:manage_content"><br />
+		<div class="checkbox">
+			<label>
+				<input type="checkbox" data-field="privileges:disabled"> <strong>Disable Privilege Threshold System</strong>
+			</label>
+		</div>
 	</div>
 </form>
 
@@ -55,6 +60,17 @@
 	</div>
 </form>
 
+<form>
+	<h3>Profile Settings</h3>
+	<div class="alert alert-warning">
+		<div class="checkbox">
+			<label>
+				<input type="checkbox" data-field="profile:convertProfileImageToPNG"> <strong>Convert profile image uploads to PNG</strong>
+			</label>
+		</div>
+	</div>
+</form>
+
 <form>
 	<h3>User Settings</h3>
 	<div class="alert alert-warning">
diff --git a/public/templates/footer.tpl b/public/templates/footer.tpl
index a9f51f2242..4434336ed8 100644
--- a/public/templates/footer.tpl
+++ b/public/templates/footer.tpl
@@ -59,7 +59,7 @@
 	<div id="alert_window"></div>
 
 
-	<footer id="footer" class="container footer">
+	<footer id="footer" class="container footer hide">
 		{footerHTML}
 		<div class="copyright">
 			Copyright &copy; 2013 <a target="_blank" href="http://www.nodebb.com">NodeBB Forums</a> | <a target="_blank" href="//github.com/designcreateplay/NodeBB/graphs/contributors">Contributors</a>
diff --git a/public/templates/home.tpl b/public/templates/home.tpl
index 2c8a92e801..13d7bd6caf 100644
--- a/public/templates/home.tpl
+++ b/public/templates/home.tpl
@@ -8,7 +8,7 @@
 		<a href="category/{categories.slug}" itemprop="url">
 			<meta itemprop="name" content="{categories.name}">
 			<h4><span class="badge {categories.badgeclass}">{categories.topic_count} </span> {categories.name}</h4>
-			<div class="icon" style="background: {categories.bgColor}; color: {categories.color};">
+			<div class="icon" style="background: {categories.background}; color: {categories.color};">
 				<div id="category-{categories.cid}" class="category-slider-{categories.post_count}">
 					<div class="category-box"><i class="fa {categories.icon} fa-4x"></i></div>
 					<div class="category-box" itemprop="description">{categories.description}</div>
diff --git a/public/templates/topic.tpl b/public/templates/topic.tpl
index 47d068dcef..7802c41524 100644
--- a/public/templates/topic.tpl
+++ b/public/templates/topic.tpl
@@ -9,198 +9,199 @@
 <input type="hidden" template-variable="facebook-share-url" value="{facebook-share-url}" />
 <input type="hidden" template-variable="google-share-url" value="{google-share-url}" />
 
-<div class="container">
-	<div class="topic row">
-		<ol class="breadcrumb">
-			<li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
-				<a href="/" itemprop="url"><span itemprop="title">[[global:home]]</span></a>
-			</li>
-			<li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
-				<a href="/category/{category_slug}" itemprop="url"><span itemprop="title">{category_name}</span></a>
-			</li>
-			<li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
-				<span itemprop="title">{topic_name} <a target="_blank" href="../{topic_id}.rss"><i class="fa fa-rss-square"></i></a></span>
-			</li>
-
-		</ol>
 
-		<ul id="post-container" class="container posts" data-tid="{topic_id}">
-			<!-- BEGIN posts -->
-				<li class="row post-row infiniteloaded" data-pid="{posts.pid}" data-uid="{posts.uid}" data-username="{posts.username}" data-index="{posts.index}" data-deleted="{posts.deleted}" itemscope itemtype="http://schema.org/Comment">
-					<a id="post_anchor_{posts.pid}" name="{posts.pid}"></a>
+<div class="topic">
+	<ol class="breadcrumb">
+		<li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
+			<a href="/" itemprop="url"><span itemprop="title">[[global:home]]</span></a>
+		</li>
+		<li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
+			<a href="/category/{category_slug}" itemprop="url"><span itemprop="title">{category_name}</span></a>
+		</li>
+		<li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
+			<span itemprop="title">{topic_name} <a target="_blank" href="../{topic_id}.rss"><i class="fa fa-rss-square"></i></a></span>
+		</li>
+
+	</ol>
+
+	<ul id="post-container" class="posts" data-tid="{topic_id}">
+		<!-- BEGIN posts -->
+			<li class="row post-row infiniteloaded" data-pid="{posts.pid}" data-uid="{posts.uid}" data-username="{posts.username}" data-index="{posts.index}" data-deleted="{posts.deleted}" itemscope itemtype="http://schema.org/Comment">
+				<a id="post_anchor_{posts.pid}" name="{posts.pid}"></a>
+
+				<meta itemprop="datePublished" content="{posts.relativeTime}">
+				<meta itemprop="dateModified" content="{posts.relativeEditTime}">
+
+				<div class="col-md-1 profile-image-block hidden-xs hidden-sm sub-post">
+					<a href="/user/{posts.userslug}">
+						<img src="{posts.picture}" align="left" class="img-thumbnail" itemprop="image" />
+						<!-- IF posts.user_banned -->
+						<span class="label label-danger">[[topic:banned]]</span>
+						<!-- ENDIF posts.user_banned -->
+					</a>
 
-					<meta itemprop="datePublished" content="{posts.relativeTime}">
-					<meta itemprop="dateModified" content="{posts.relativeEditTime}">
+				</div>
 
-					<div class="col-md-1 profile-image-block hidden-xs hidden-sm sub-post">
-						<a href="/user/{posts.userslug}">
-							<img src="{posts.picture}" align="left" class="img-thumbnail" itemprop="image" />
-							<!-- IF posts.user_banned -->
-							<span class="label label-danger">[[topic:banned]]</span>
-							<!-- ENDIF posts.user_banned -->
+				<div class="col-md-11">
+					<div class="post-block">
+						<a class="main-post avatar" href="/user/{posts.userslug}">
+							<img itemprop="image" src="{posts.picture}" align="left" class="img-thumbnail" width=150 height=150 />
 						</a>
-					</div>
+						<h3 class="main-post">
+							<p id="topic_title_{posts.pid}" class="topic-title" itemprop="name">{topic_name}</p>
+						</h3>
+
+						<div class="topic-buttons">
+							<div class="btn-group">
+								<button class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" type="button" title="[[topic:posted_by]] {posts.username}">
+									<span class="username-field" href="/user/{posts.userslug}" itemprop="author">{posts.username}&nbsp;</span>
+									<span class="caret"></span>
+								</button>
+
+							    <ul class="dropdown-menu">
+									<li><a href="/user/{posts.userslug}"><i class="fa fa-user"></i> [[topic:profile]]</a></li>
+									<li><div class="chat"><i class="fa fa-comment"></i> [[topic:chat]]</div></li>
+							    </ul>
+							</div>
 
-					<div class="col-md-11">
-						<div class="post-block">
-							<a class="main-post avatar" href="/user/{posts.userslug}">
-								<img itemprop="image" src="{posts.picture}" align="left" class="img-thumbnail" width=150 height=150 />
-							</a>
-							<h3 class="main-post">
-								<p id="topic_title_{posts.pid}" class="topic-title" itemprop="name">{topic_name}</p>
-							</h3>
-
-							<div class="topic-buttons">
-								<div class="btn-group">
-									<button class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" type="button" title="[[topic:posted_by]] {posts.username}">
-										<span class="username-field" href="/user/{posts.userslug}" itemprop="author">{posts.username}&nbsp;</span>
-										<span class="caret"></span>
-									</button>
-
-								    <ul class="dropdown-menu">
-										<li><a href="/user/{posts.userslug}"><i class="fa fa-user"></i> [[topic:profile]]</a></li>
-										<li><div class="chat"><i class="fa fa-comment"></i> [[topic:chat]]</div></li>
-								    </ul>
-								</div>
+							<div class="btn-group">
+								<!-- IF @first -->
+								<button class="btn btn-sm btn-default follow" type="button" title="Be notified of new replies in this topic"><i class="fa fa-eye"></i></button>
+								<!-- ENDIF @first -->
+								<button data-favourited="{posts.favourited}" class="favourite btn btn-sm btn-default <!-- IF posts.favourited --> btn-warning <!-- ENDIF posts.favourited -->" type="button">
+									<span class="favourite-text">[[topic:favourite]]</span>
+									<span class="post_rep_{posts.pid}">{posts.reputation} </span>
+									<!-- IF posts.favourited -->
+									<i class="fa fa-star"></i>
+									<!-- ELSE -->
+									<i class="fa fa-star-o"></i>
+									<!-- ENDIF posts.favourited -->
+								</button>
+							</div>
+							<div class="btn-group">
+								<button class="btn btn-sm btn-default quote" type="button" title="[[topic:quote]]"><i class="fa fa-quote-left"></i></button>
+								<button class="btn btn-sm btn-primary btn post_reply" type="button">[[topic:reply]] <i class="fa fa-reply"></i></button>
+							</div>
 
-								<div class="btn-group">
-									<!-- IF @first -->
-									<button class="btn btn-sm btn-default follow" type="button" title="Be notified of new replies in this topic"><i class="fa fa-eye"></i></button>
-									<!-- ENDIF @first -->
-									<button data-favourited="{posts.favourited}" class="favourite btn btn-sm btn-default <!-- IF posts.favourited --> btn-warning <!-- ENDIF posts.favourited -->" type="button">
-										<span class="favourite-text">[[topic:favourite]]</span>
-										<span class="post_rep_{posts.pid}">{posts.reputation} </span>
-										<!-- IF posts.favourited -->
-										<i class="fa fa-star"></i>
-										<!-- ELSE -->
-										<i class="fa fa-star-o"></i>
-										<!-- ENDIF posts.favourited -->
-									</button>
-								</div>
-								<div class="btn-group">
-									<button class="btn btn-sm btn-default quote" type="button" title="[[topic:quote]]"><i class="fa fa-quote-left"></i></button>
-									<button class="btn btn-sm btn-primary btn post_reply" type="button">[[topic:reply]] <i class="fa fa-reply"></i></button>
+							<div class="pull-right">
+								<div class="btn-group post-tools">
+									<button class="btn btn-sm btn-default link" type="button" title="[[topic:link]]"><i class="fa fa-link"></i></button>
+									<button class="btn btn-sm btn-default facebook-share" type="button" title=""><i class="fa fa-facebook"></i></button>
+									<button class="btn btn-sm btn-default twitter-share" type="button" title=""><i class="fa fa-twitter"></i></button>
+									<button class="btn btn-sm btn-default google-share" type="button" title=""><i class="fa fa-google-plus"></i></button>
 								</div>
 
-								<div class="pull-right">
-									<div class="btn-group post-tools">
-										<button class="btn btn-sm btn-default link" type="button" title="[[topic:link]]"><i class="fa fa-link"></i></button>
-										<button class="btn btn-sm btn-default facebook-share" type="button" title=""><i class="fa fa-facebook"></i></button>
-										<button class="btn btn-sm btn-default twitter-share" type="button" title=""><i class="fa fa-twitter"></i></button>
-										<button class="btn btn-sm btn-default google-share" type="button" title=""><i class="fa fa-google-plus"></i></button>
-									</div>
-
-									<!-- IF posts.display_moderator_tools -->
-									<div class="btn-group post-tools">
-										<button class="btn btn-sm btn-default edit" type="button" title="[[topic:edit]]"><i class="fa fa-pencil"></i></button>
-										<button class="btn btn-sm btn-default delete" type="button" title="[[topic:delete]]"><i class="fa fa-trash-o"></i></button>
-									</div>
-									<!-- ENDIF posts.display_moderator_tools -->
+								<!-- IF posts.display_moderator_tools -->
+								<div class="btn-group post-tools">
+									<button class="btn btn-sm btn-default edit" type="button" title="[[topic:edit]]"><i class="fa fa-pencil"></i></button>
+									<button class="btn btn-sm btn-default delete" type="button" title="[[topic:delete]]"><i class="fa fa-trash-o"></i></button>
 								</div>
-
-								<input id="post_{posts.pid}_link" value="" class="pull-right" style="display:none;"></input>
+								<!-- ENDIF posts.display_moderator_tools -->
 							</div>
 
-							<div id="content_{posts.pid}" class="post-content" itemprop="text">{posts.content}</div>
-							<!-- IF posts.signature -->
-							<div class="post-signature">{posts.signature}</div>
-							<!-- ENDIF posts.signature -->
-
-							<div class="post-info">
-								<span class="pull-left">
-									{posts.additional_profile_info}
-								</span>
-								<span class="pull-right">
-									[[category:posted]] <span class="relativeTimeAgo timeago" title="{posts.relativeTime}"></span>
-									<!-- IF posts.editor -->
-									<span>| [[category:last_edited_by]] <strong><a href="/user/{posts.editorslug}">{posts.editorname}</a></strong></span>
-									<span class="timeago" title="{posts.relativeEditTime}"></span>
-									<!-- ENDIF posts.editor -->
-								</span>
-								<div style="clear:both;"></div>
-							</div>
+							<input id="post_{posts.pid}_link" value="" class="pull-right" style="display:none;"></input>
 						</div>
-					</div>
-				</li>
-
-				<!-- IF @first -->
-				<li class="well post-bar">
-					<div class="inline-block">
-						<small class="topic-stats">
-							<span>[[category:posts]]</span>
-							<strong><span id="topic-post-count" class="human-readable-number" title="{postcount}">{postcount}</span></strong> |
-							<span>[[category:views]]</span>
-							<strong><span class="human-readable-number" title="{viewcount}">{viewcount}</span></strong> |
-							<span>[[category:browsing]]</span>
-						</small>
-						<div class="thread_active_users active-users inline-block"></div>
-					</div>
-					<div class="topic-main-buttons pull-right inline-block">
-						<button class="btn btn-primary post_reply" type="button">[[topic:reply]]</button>
-						<div class="btn-group thread-tools hide">
-							<button class="btn btn-default dropdown-toggle" data-toggle="dropdown" type="button">[[topic:thread_tools.title]] <span class="caret"></span></button>
-							<ul class="dropdown-menu">
-								<li><a href="#" class="pin_thread"><i class="fa fa-thumb-tack"></i> [[topic:thread_tools.pin]]</a></li>
-								<li><a href="#" class="lock_thread"><i class="fa fa-lock"></i> [[topic:thread_tools.lock]]</a></li>
-								<li class="divider"></li>
-								<li><a href="#" class="move_thread"><i class="fa fa-arrows"></i> [[topic:thread_tools.move]]</a></li>
-								<li class="divider"></li>
-								<li><a href="#" class="delete_thread"><span class="text-error"><i class="fa fa-trash-o"></i> [[topic:thread_tools.delete]]</span></a></li>
-							</ul>
+
+						<div id="content_{posts.pid}" class="post-content" itemprop="text">{posts.content}</div>
+						<!-- IF posts.signature -->
+						<div class="post-signature">{posts.signature}</div>
+						<!-- ENDIF posts.signature -->
+
+						<div class="post-info">
+							<span class="pull-left">
+								[[topic:reputation]]: <i class='fa fa-star'></i> <span class='formatted-number'>{posts.user_rep}</span>&nbsp;|&nbsp;[[topic:posts]]: <i class='fa fa-pencil'></i> <span class='formatted-number'>{posts.user_postcount}</span>
+								{posts.additional_profile_info}
+							</span>
+							<span class="pull-right">
+								[[category:posted]] <span class="relativeTimeAgo timeago" title="{posts.relativeTime}"></span>
+								<!-- IF posts.editor -->
+								<span>| [[category:last_edited_by]] <strong><a href="/user/{posts.editorslug}">{posts.editorname}</a></strong></span>
+								<span class="timeago" title="{posts.relativeEditTime}"></span>
+								<!-- ENDIF posts.editor -->
+							</span>
+							<div style="clear:both;"></div>
 						</div>
 					</div>
-					<div style="clear:both;"></div>
-				</li>
-				<!-- ENDIF @first -->
-			<!-- END posts -->
-		</ul>
-
-		<div class="well col-md-11 col-xs-12 pull-right hide">
-			<div class="topic-main-buttons pull-right inline-block hide">
-				<div class="loading-indicator" done="0" style="display:none;">
-					[[topic:loading]] <span class="hidden-xs" style="display:inline!important;">[[topic:more_posts]]</span> <i class="fa fa-refresh fa-spin"></i>
 				</div>
-				<button class="btn btn-primary post_reply" type="button">[[topic:reply]]</button>
-				<div class="btn-group thread-tools hide">
-					<button class="btn btn-default dropdown-toggle" data-toggle="dropdown" type="button">[[topic:thread_tools.title]] <span class="caret"></span></button>
-					<ul class="dropdown-menu">
-						<li><a href="#" class="pin_thread"><i class="fa fa-thumb-tack"></i> [[topic:thread_tools.pin]]</a></li>
-						<li><a href="#" class="lock_thread"><i class="fa fa-lock"></i> [[topic:thread_tools.lock]]</a></li>
-						<li class="divider"></li>
-						<li><a href="#" class="move_thread"><i class="fa fa-arrows"></i> [[topic:thread_tools.move]]</a></li>
-						<li class="divider"></li>
-						<li><a href="#" class="delete_thread"><span class="text-error"><i class="fa fa-trash-o"></i> [[topic:thread_tools.delete]]</span></a></li>
-					</ul>
+			</li>
+
+			<!-- IF @first -->
+			<li class="well post-bar">
+				<div class="inline-block">
+					<small class="topic-stats">
+						<span>[[category:posts]]</span>
+						<strong><span id="topic-post-count" class="human-readable-number" title="{postcount}">{postcount}</span></strong> |
+						<span>[[category:views]]</span>
+						<strong><span class="human-readable-number" title="{viewcount}">{viewcount}</span></strong> |
+						<span>[[category:browsing]]</span>
+					</small>
+					<div class="thread_active_users active-users inline-block"></div>
+				</div>
+				<div class="topic-main-buttons pull-right inline-block">
+					<button class="btn btn-primary post_reply" type="button">[[topic:reply]]</button>
+					<div class="btn-group thread-tools hide">
+						<button class="btn btn-default dropdown-toggle" data-toggle="dropdown" type="button">[[topic:thread_tools.title]] <span class="caret"></span></button>
+						<ul class="dropdown-menu">
+							<li><a href="#" class="pin_thread"><i class="fa fa-thumb-tack"></i> [[topic:thread_tools.pin]]</a></li>
+							<li><a href="#" class="lock_thread"><i class="fa fa-lock"></i> [[topic:thread_tools.lock]]</a></li>
+							<li class="divider"></li>
+							<li><a href="#" class="move_thread"><i class="fa fa-arrows"></i> [[topic:thread_tools.move]]</a></li>
+							<li class="divider"></li>
+							<li><a href="#" class="delete_thread"><span class="text-error"><i class="fa fa-trash-o"></i> [[topic:thread_tools.delete]]</span></a></li>
+						</ul>
+					</div>
 				</div>
+				<div style="clear:both;"></div>
+			</li>
+			<!-- ENDIF @first -->
+		<!-- END posts -->
+	</ul>
+
+	<div class="well col-md-11 col-xs-12 pull-right hide">
+		<div class="topic-main-buttons pull-right inline-block hide">
+			<div class="loading-indicator" done="0" style="display:none;">
+				[[topic:loading]] <span class="hidden-xs hidden-sm" style="display:inline!important;">[[topic:more_posts]]</span> <i class="fa fa-refresh fa-spin"></i>
+			</div>
+			<button class="btn btn-primary post_reply" type="button">[[topic:reply]]</button>
+			<div class="btn-group thread-tools hide">
+				<button class="btn btn-default dropdown-toggle" data-toggle="dropdown" type="button">[[topic:thread_tools.title]] <span class="caret"></span></button>
+				<ul class="dropdown-menu">
+					<li><a href="#" class="pin_thread"><i class="fa fa-thumb-tack"></i> [[topic:thread_tools.pin]]</a></li>
+					<li><a href="#" class="lock_thread"><i class="fa fa-lock"></i> [[topic:thread_tools.lock]]</a></li>
+					<li class="divider"></li>
+					<li><a href="#" class="move_thread"><i class="fa fa-arrows"></i> [[topic:thread_tools.move]]</a></li>
+					<li class="divider"></li>
+					<li><a href="#" class="delete_thread"><span class="text-error"><i class="fa fa-trash-o"></i> [[topic:thread_tools.delete]]</span></a></li>
+				</ul>
 			</div>
-			<div style="clear:both;"></div>
 		</div>
+		<div style="clear:both;"></div>
+	</div>
 
-		<div id="move_thread_modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="Move Topic" aria-hidden="true">
-			<div class="modal-dialog">
-				<div class="modal-content">
-					<div class="modal-header">
-						<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
-						<h3>[[topic:move_topic]]</h3>
-					</div>
-					<div class="modal-body">
-						<p id="categories-loading"><i class="fa fa-spin fa-refresh"></i> [[topic:load_categories]]</p>
-						<ul class="category-list"></ul>
-						<p>
-							[[topic:disabled_categories_note]]
-						</p>
-						<div id="move-confirm" style="display: none;">
-							<hr />
-							<div class="alert alert-info">[[topic:topic_will_be_moved_to]] <strong><span id="confirm-category-name"></span></strong></div>
-						</div>
-					</div>
-					<div class="modal-footer">
-						<button type="button" class="btn btn-default" data-dismiss="modal" id="move_thread_cancel">[[global:buttons.close]]</button>
-						<button type="button" class="btn btn-primary" id="move_thread_commit" disabled>[[topic:confirm_move]]</button>
+	<div id="move_thread_modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="Move Topic" aria-hidden="true">
+		<div class="modal-dialog">
+			<div class="modal-content">
+				<div class="modal-header">
+					<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+					<h3>[[topic:move_topic]]</h3>
+				</div>
+				<div class="modal-body">
+					<p id="categories-loading"><i class="fa fa-spin fa-refresh"></i> [[topic:load_categories]]</p>
+					<ul class="category-list"></ul>
+					<p>
+						[[topic:disabled_categories_note]]
+					</p>
+					<div id="move-confirm" style="display: none;">
+						<hr />
+						<div class="alert alert-info">[[topic:topic_will_be_moved_to]] <strong><span id="confirm-category-name"></span></strong></div>
 					</div>
 				</div>
+				<div class="modal-footer">
+					<button type="button" class="btn btn-default" data-dismiss="modal" id="move_thread_cancel">[[global:buttons.close]]</button>
+					<button type="button" class="btn btn-primary" id="move_thread_commit" disabled>[[topic:confirm_move]]</button>
+				</div>
 			</div>
 		</div>
-
 	</div>
-</div>
+
+</div>
\ No newline at end of file
diff --git a/src/categories.js b/src/categories.js
index 538bc0d5c3..2da43b1dd7 100644
--- a/src/categories.js
+++ b/src/categories.js
@@ -229,7 +229,7 @@ var db = require('./database.js'),
 				return;
 			}
 
-			posts.getPostSummaryByPids(pids, function(err, postData) {
+			posts.getPostSummaryByPids(pids, true, function(err, postData) {
 				if (postData.length > count) {
 					postData = postData.slice(0, count);
 				}
@@ -288,7 +288,10 @@ var db = require('./database.js'),
 	Categories.getCategoryData = function(cid, callback) {
 		db.exists('category:' + cid, function(err, exists) {
 			if (exists) {
-				db.getObject('category:' + cid, callback);
+				db.getObject('category:' + cid, function(err, data) {
+					data.background = data.image ? 'url(' + data.image + ')' : data.bgColor;
+					callback(err, data);
+				});
 			} else {
 				callback(new Error('No category found!'));
 			}
diff --git a/src/database/mongo.js b/src/database/mongo.js
index 9408aff75a..d6af3bc0b2 100644
--- a/src/database/mongo.js
+++ b/src/database/mongo.js
@@ -46,7 +46,7 @@
 						return;
 					}
 					if(collection) {
-						collection.ensureIndex({_key :1, setName:1}, {background:true}, function(err, name){
+						collection.ensureIndex({_key :1}, {background:true}, function(err, name){
 							if(err) {
 								winston.error("Error creating index " + err.message);
 							}
@@ -73,6 +73,39 @@
 		});
 	}
 
+	//
+	// helper functions
+	//
+	function removeHiddenFields(item) {
+		if(item) {
+			if(item._id) {
+				delete item._id;
+			}
+			if(item._key) {
+				delete item._key;
+			}
+		}
+		return item;
+	}
+
+	function findItem(data, key) {
+		if(!data) {
+			return null;
+		}
+
+		for(var i=0; i<data.length; ++i) {
+			if(data[i]._key === key) {
+				var item = data.splice(i, 1);
+				if(item && item.length) {
+					return item[0];
+				} else {
+					return null;
+				}
+			}
+		}
+		return null;
+	}
+
 
 	//
 	// Exported functions
@@ -155,8 +188,7 @@
 			stats.raw = JSON.stringify(stats, null, 4);
 
 			stats.mongo = true;
-			//remove this when andrew adds in undefined checking to templates
-			stats.redis = false;
+
 			callback(err, stats);
 
 		});
@@ -165,7 +197,7 @@
 	// key
 
 	module.exists = function(key, callback) {
-		db.collection('objects').findOne({$or:[{_key:key}, {setName:key}]}, function(err, item) {
+		db.collection('objects').findOne({_key:key}, function(err, item) {
 			callback(err, item !== undefined && item !== null);
 		});
 	}
@@ -180,16 +212,8 @@
 				}
 			}
 
-			if(result === 0) {
-				db.collection('objects').remove({setName:key}, function(err, result) {
-					if(callback) {
-						callback(err, result);
-					}
-				});
-			} else {
-				if(callback) {
-					callback(null, result);
-				}
+			if(callback) {
+				callback(null, result);
 			}
 		});
 	}
@@ -210,21 +234,6 @@
 	}
 
 	//hashes
-	function removeHiddenFields(item) {
-		if(item) {
-			if(item._id) {
-				delete item._id;
-			}
-			if(item._key) {
-				delete item._key;
-			}
-			if(item.setName) {
-				delete item.setName;
-			}
-		}
-		return item;
-	}
-
 	module.setObject = function(key, data, callback) {
 		data['_key'] = key;
 		db.collection('objects').update({_key:key}, {$set:data}, {upsert:true, w: 1}, function(err, result) {
@@ -262,30 +271,10 @@
 				return callback(err);
 			}
 
-			var returnData = [],
-				resultIndex = 0;
-
-
-			function findData(key) {
-				if(!data) {
-					return null;
-				}
-
-				for(var i=0; i<data.length; ++i) {
-					if(data[i]._key === key) {
-						var item = data.splice(i, 1);
-						if(item && item.length) {
-							return item[0];
-						} else {
-							return null;
-						}
-					}
-				}
-				return null;
-			}
+			var returnData = [];
 
 			for(var i=0; i<keys.length; ++i) {
-				returnData.push(findData(keys[i]));
+				returnData.push(findItem(data, keys[i]));
 			}
 
 			callback(err, returnData);
@@ -505,15 +494,18 @@
 			value:value
 		};
 
-		data.setName = key;
-		module.setObject(key + ':' + value, data, callback);
+		db.collection('objects').update({_key:key, value:value}, {$set:data}, {upsert:true, w: 1}, function(err, result) {
+			if(callback) {
+				callback(err, result);
+			}
+		});
 	}
 
 	module.sortedSetRemove = function(key, value, callback) {
 		if(value !== null && value !== undefined) {
 			value = value.toString();
 		}
-		db.collection('objects').remove({setName:key, value:value}, function(err, result) {
+		db.collection('objects').remove({_key:key, value:value}, function(err, result) {
 			if(callback) {
 				callback(err, result);
 			}
@@ -521,7 +513,7 @@
 	}
 
 	function getSortedSetRange(key, start, stop, sort, callback) {
-		db.collection('objects').find({setName:key}, {fields:{value:1}})
+		db.collection('objects').find({_key:key}, {fields:{value:1}})
 			.limit(stop - start + 1)
 			.skip(start)
 			.sort({score: sort})
@@ -557,7 +549,7 @@
 			stop = args[5];
 
 
-		db.collection('objects').find({setName:key, score: {$gt:min, $lt:max}}, {fields:{value:1}})
+		db.collection('objects').find({_key:key, score: {$gte:min, $lte:max}}, {fields:{value:1}})
 			.limit(stop - start + 1)
 			.skip(start)
 			.sort({score: -1})
@@ -576,7 +568,7 @@
 	}
 
 	module.sortedSetCount = function(key, min, max, callback) {
-		db.collection('objects').count({setName:key, score: {$gt:min, $lt:max}}, function(err, count) {
+		db.collection('objects').count({_key:key, score: {$gte:min, $lte:max}}, function(err, count) {
 			if(err) {
 				return callback(err);
 			}
@@ -609,7 +601,7 @@
 		if(value !== null && value !== undefined) {
 			value = value.toString();
 		}
-		db.collection('objects').findOne({setName:key, value: value}, {fields:{score:1}}, function(err, result) {
+		db.collection('objects').findOne({_key:key, value: value}, {fields:{score:1}}, function(err, result) {
 			if(err) {
 				return callback(err);
 			}
@@ -625,22 +617,17 @@
 		if(value !== null && value !== undefined) {
 			value = value.toString();
 		}
-		db.collection('objects').find({setName:{$in:keys}, value: value}).toArray(function(err, result) {
+		db.collection('objects').find({_key:{$in:keys}, value: value}).toArray(function(err, result) {
 			if(err) {
 				return callback(err);
 			}
 
 			var returnData = [],
-				resultIndex = 0;
+				item;
 
 			for(var i=0; i<keys.length; ++i) {
-
-				if(result && resultIndex < result.length && keys[i] === result[resultIndex].setName) {
-					returnData.push(result[resultIndex].score);
-					++resultIndex;
-				} else {
-					returnData.push(null);
-				}
+				item = findItem(result, keys[i]);
+				returnData.push(item ? item.score : null);
 			}
 
 			callback(null, returnData);
diff --git a/src/database/redis.js b/src/database/redis.js
index b3a27ef895..d24573384b 100644
--- a/src/database/redis.js
+++ b/src/database/redis.js
@@ -168,8 +168,6 @@
 			}
 			redisData.raw = JSON.stringify(redisData, null, 4);
 			redisData.redis = true;
-			//remove this when andrew adds in undefined checking to templates
-			redisData.mongo = false;
 
 			callback(null, redisData);
 		});
diff --git a/src/install.js b/src/install.js
index 601fdff077..ba59ac4826 100644
--- a/src/install.js
+++ b/src/install.js
@@ -104,12 +104,12 @@ var async = require('async'),
 					}
 				},
 				function (next) {
-					var	success = function(err, config) {
+					var	success = function (err, config) {
 						if (!config) {
 							return next(new Error('aborted'));
 						}
 
-						function dbQuestionsSuccess(err, databaseConfig) {
+						var dbQuestionsSuccess = function (err, databaseConfig) {
 							if (!databaseConfig) {
 								return next(new Error('aborted'));
 							}
@@ -135,7 +135,7 @@ var async = require('async'),
 
 							config.bcrypt_rounds = 12;
 							config.upload_path = '/public/uploads';
-							config.use_port = (config.use_port.slice(0, 1) === 'y') ? true : false;
+							config.use_port = config.use_port.slice(0, 1) === 'y';
 
 							var urlObject = url.parse(config.base_url),
 								relative_path = (urlObject.pathname && urlObject.pathname.length > 1) ? urlObject.pathname : '',
@@ -152,12 +152,20 @@ var async = require('async'),
 							install.save(server_conf, client_conf, function(err) {
 								require('./database').init(next);
 							});
-						}
+						};
 
 						if(config.database === 'redis') {
-							prompt.get(install.redisQuestions, dbQuestionsSuccess);
+							if (config['redis:host'] && config['redis:port']) {
+								dbQuestionsSuccess(null, config);
+							} else {
+								prompt.get(install.redisQuestions, dbQuestionsSuccess);
+							}
 						} else if(config.database === 'mongo') {
-							prompt.get(install.mongoQuestions, dbQuestionsSuccess);
+							if (config['mongo:host'] && config['mongo:port']) {
+								dbQuestionsSuccess(null, config);
+							} else {
+								prompt.get(install.mongoQuestions, dbQuestionsSuccess);
+							}
 						} else {
 							return next(new Error('unknown database : ' + config.database));
 						}
@@ -173,9 +181,9 @@ var async = require('async'),
 					} else {
 						// Use provided values, fall back to defaults
 						var	config = {},
-							question, x, numQ;
-						for(x=0,numQ=install.questions.length;x<numQ;x++) {
-							question = install.questions[x];
+							question, x, numQ, allQuestions = install.questions.concat(install.redisQuestions).concat(install.mongoQuestions);
+						for(x=0,numQ=allQuestions.length;x<numQ;x++) {
+							question = allQuestions[x];
 							config[question.name] = install.values[question.name] || question['default'] || '';
 						}
 						success(null, config);
diff --git a/src/meta.js b/src/meta.js
index fa50858515..f0573d5d64 100644
--- a/src/meta.js
+++ b/src/meta.js
@@ -144,27 +144,19 @@ var fs = require('fs'),
 	};
 
 	Meta.title = {
-		build: function (urlFragment, current_user, callback) {
-			var self = this,
-				user = require('./user');
+		build: function (urlFragment, callback) {
+			var user = require('./user');
 
-			async.parallel({
-				title: function (next) {
-					self.parseFragment(urlFragment, next);
-				},
-				notifCount: function (next) {
-					user.notifications.getUnreadCount(current_user, next);
-				}
-			}, function (err, values) {
+			Meta.title.parseFragment(urlFragment, function(err, title) {
 				var title;
 
 				if (err) {
 					title = Meta.config.title || 'NodeBB';
 				} else {
-					title = (values.title ? values.title + ' | ' : '') + (Meta.config.title || 'NodeBB');
+					title = (title ? title + ' | ' : '') + (Meta.config.title || 'NodeBB');
 				}
 
-				callback(null, title, values.notifCount);
+				callback(null, title);
 			});
 		},
 		parseFragment: function (urlFragment, callback) {
diff --git a/src/postTools.js b/src/postTools.js
index e31fa70552..506868ff95 100644
--- a/src/postTools.js
+++ b/src/postTools.js
@@ -48,10 +48,14 @@ var db = require('./database'),
 		}
 
 		function hasEnoughRep(next) {
-			user.getUserField(uid, 'reputation', function(err, reputation) {
-				if (err) return next(null, false);
-				next(null, parseInt(reputation, 10) >= parseInt(meta.config['privileges:manage_content'], 10));
-			});
+			if (parseInt(meta.config['privileges:disabled'], 10)) {
+				return next(null, false);
+			} else {
+				user.getUserField(uid, 'reputation', function(err, reputation) {
+					if (err) return next(null, false);
+					next(null, parseInt(reputation, 10) >= parseInt(meta.config['privileges:manage_content'], 10));
+				});
+			}
 		}
 
 		async.parallel([getThreadPrivileges, isOwnPost, hasEnoughRep], function(err, results) {
diff --git a/src/posts.js b/src/posts.js
index c496afdd99..e17e22f300 100644
--- a/src/posts.js
+++ b/src/posts.js
@@ -134,45 +134,30 @@ var db = require('./database'),
 					callback(new Error('reply-error'), null);
 				}
 
-				async.parallel([
-					function(next) {
-						topics.markUnRead(tid, function(err) {
-							if(err) {
-								return next(err);
-							}
-							topics.markAsRead(tid, uid);
-							next();
-						});
-					},
-					function(next) {
-						topics.pushUnreadCount(null, next);
-					},
-					function(next) {
-						Posts.getCidByPid(postData.pid, function(err, cid) {
-							if(err) {
-								return next(err);
-							}
+				Posts.getCidByPid(postData.pid, function(err, cid) {
+					if(err) {
+						return callback(err, null);
+					}
 
-							db.delete('cid:' + cid + ':read_by_uid');
-							next();
-						});
-					},
-					function(next) {
-						threadTools.notifyFollowers(tid, uid);
-						next();
-					},
-					function(next) {
-						Posts.addUserInfoToPost(postData, function(err) {
-							if(err) {
-								return next(err);
-							}
-							next();
-						});
+					db.delete('cid:' + cid + ':read_by_uid');
+				});
+
+				topics.markAsUnreadForAll(tid, function(err) {
+					if(err) {
+						return callback(err, null);
 					}
-				], function(err, results) {
+
+					topics.markAsRead(tid, uid);
+					topics.pushUnreadCount();
+				});
+
+				threadTools.notifyFollowers(tid, uid);
+				
+				Posts.addUserInfoToPost(postData, function(err) {
 					if(err) {
 						return callback(err, null);
 					}
+
 					callback(null, postData);
 				});
 			});
@@ -205,7 +190,7 @@ var db = require('./database'),
 	Posts.addUserInfoToPost = function(post, callback) {
 		user.getUserFields(post.uid, ['username', 'userslug', 'reputation', 'postcount', 'picture', 'signature', 'banned'], function(err, userData) {
 			if (err) {
-				return callback();
+				return callback(err);
 			}
 
 			postTools.parseSignature(userData.signature, function(err, signature) {
@@ -242,7 +227,7 @@ var db = require('./database'),
 		});
 	};
 
-	Posts.getPostSummaryByPids = function(pids, callback) {
+	Posts.getPostSummaryByPids = function(pids, stripTags, callback) {
 
 		var posts = [];
 
@@ -283,10 +268,17 @@ var db = require('./database'),
 				function(postData, next) {
 					if (postData.content) {
 						postTools.parse(postData.content, function(err, content) {
-							if (!err) {
+							if(err) {
+								return next(err);
+							}
+
+							if(stripTags) {
 								postData.content = utils.strip_tags(content);
+							} else {
+								postData.content = content;
 							}
-							next(err, postData);
+
+							next(null, postData);
 						});
 					} else {
 						next(null, postData);
@@ -504,7 +496,7 @@ var db = require('./database'),
 			if (err)
 				return callback(err, null);
 
-			Posts.getPostSummaryByPids(pids, function(err, posts) {
+			Posts.getPostSummaryByPids(pids, false, function(err, posts) {
 				if (err)
 					return callback(err, null);
 
diff --git a/src/routes/admin.js b/src/routes/admin.js
index 2831787434..495b341a31 100644
--- a/src/routes/admin.js
+++ b/src/routes/admin.js
@@ -10,7 +10,8 @@ var nconf = require('nconf'),
 	pkg = require('./../../package.json'),
 	categories = require('./../categories'),
 	meta = require('../meta'),
-	plugins = require('../plugins');
+	plugins = require('../plugins'),
+	utils = require('./../../public/src/utils.js');
 
 
 
@@ -97,6 +98,53 @@ var nconf = require('nconf'),
 				});
 			});
 
+			app.post('/category/uploadpicture', function(req, res) {
+				if (!req.user)
+					return res.redirect('/403');
+
+				var allowedTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif'];
+
+				if (allowedTypes.indexOf(req.files.userPhoto.type) === -1) {
+					res.send({
+						error: 'Allowed image types are png, jpg and gif!'
+					});
+					return;
+				}
+
+				var tempPath = req.files.userPhoto.path;
+				var extension = path.extname(req.files.userPhoto.name);
+
+				if (!extension) {
+					res.send({
+						error: 'Error uploading file! Error : Invalid extension!'
+					});
+					return;
+				}
+
+				var filename =  'category' + utils.generateUUID() + extension;
+				var uploadPath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), filename);
+
+				winston.info('Attempting upload to: ' + uploadPath);
+
+				var is = fs.createReadStream(tempPath);
+				var os = fs.createWriteStream(uploadPath);
+
+				is.on('end', function () {
+					fs.unlinkSync(tempPath);
+					console.log(nconf.get('upload_url') + filename);
+					res.json({
+						path: nconf.get('upload_url') + filename
+					});
+				});
+
+				os.on('error', function (err) {
+					fs.unlinkSync(tempPath);
+					winston.err(err);
+				});
+
+				is.pipe(os);
+			});
+
 			app.post('/uploadlogo', function(req, res) {
 
 				if (!req.user)
diff --git a/src/routes/api.js b/src/routes/api.js
index 0bebd9f975..0ba2af2a44 100644
--- a/src/routes/api.js
+++ b/src/routes/api.js
@@ -2,6 +2,7 @@ var path = require('path'),
 	nconf = require('nconf'),
 	async = require('async'),
 
+	db = require('../database'),
 	user = require('../user'),
 	auth = require('./authentication'),
 	topics = require('../topics'),
@@ -214,14 +215,18 @@ var path = require('path'),
 			});
 
 			app.get('/search', function (req, res) {
-				return res.json({
-					show_no_topics: 'hide',
-					show_no_posts: 'hide',
-					show_results: 'hide',
-					search_query: '',
-					posts: [],
-					topics: []
-				});
+				if (req.user && req.user.uid) {
+					return res.json({
+						show_no_topics: 'hide',
+						show_no_posts: 'hide',
+						show_results: 'hide',
+						search_query: '',
+						posts: [],
+						topics: []
+					});
+				} else {
+					res.send(403);
+				}
 			});
 
 			app.get('/search/:term', function (req, res, next) {
@@ -232,7 +237,7 @@ var path = require('path'),
 							return callback(err, null);
 						}
 
-						posts.getPostSummaryByPids(pids, function (err, posts) {
+						posts.getPostSummaryByPids(pids, false, function (err, posts) {
 							if (err){
 								return callback(err, null);
 							}
@@ -253,20 +258,24 @@ var path = require('path'),
 					});
 				}
 
-				async.parallel([searchPosts, searchTopics], function (err, results) {
-					if (err) {
-						return next();
-					}
+				if (req.user && req.user.uid) {
+					async.parallel([searchPosts, searchTopics], function (err, results) {
+						if (err) {
+							return next();
+						}
 
-					return res.json({
-						show_no_topics: results[1].length ? 'hide' : '',
-						show_no_posts: results[0].length ? 'hide' : '',
-						show_results: '',
-						search_query: req.params.term,
-						posts: results[0],
-						topics: results[1]
+						return res.json({
+							show_no_topics: results[1].length ? 'hide' : '',
+							show_no_posts: results[0].length ? 'hide' : '',
+							show_results: '',
+							search_query: req.params.term,
+							posts: results[0],
+							topics: results[1]
+						});
 					});
-				});
+				} else {
+					res.send(403);
+				}
 			});
 
 			app.get('/reset', function (req, res) {
diff --git a/src/routes/debug.js b/src/routes/debug.js
index d089a48b2c..14f87fa8a7 100644
--- a/src/routes/debug.js
+++ b/src/routes/debug.js
@@ -78,6 +78,7 @@ var	DebugRoute = function(app) {
 				});
 			});
 		});
+
 	});
 };
 
diff --git a/src/routes/user.js b/src/routes/user.js
index 3eb0acd6b8..a978e50eee 100644
--- a/src/routes/user.js
+++ b/src/routes/user.js
@@ -102,7 +102,7 @@ var fs = require('fs'),
 				if (!req.user)
 					return res.redirect('/403');
 
-				var uploadSize = meta.config.maximumProfileImageSize || 256;
+				var uploadSize = parseInt(meta.config.maximumProfileImageSize, 10) || 256;
 
 				if (req.files.userPhoto.size > uploadSize * 1024) {
 					res.send({
@@ -147,18 +147,21 @@ var fs = require('fs'),
 				return;
 			}
 
-			var filename = uid + '-profileimg' + extension;
+			var convertToPNG = parseInt(meta.config['profile:convertProfileImageToPNG'], 10);
+
+			var filename = uid + '-profileimg' + (convertToPNG ? '.png' : extension);
 			var uploadPath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), filename);
 
 			winston.info('Attempting upload to: ' + uploadPath);
 
 			var is = fs.createReadStream(tempPath);
 			var os = fs.createWriteStream(uploadPath);
+			var im = require('node-imagemagick');
 
 			is.on('end', function () {
 				fs.unlinkSync(tempPath);
 
-				require('node-imagemagick').crop({
+				im.crop({
 					srcPath: uploadPath,
 					dstPath: uploadPath,
 					width: 128,
@@ -177,6 +180,22 @@ var fs = require('fs'),
 					user.setUserField(uid, 'uploadedpicture', imageUrl);
 					user.setUserField(uid, 'picture', imageUrl);
 
+					if (convertToPNG) {
+						im.convert([uploadPath, 'png:-'], 
+							function(err, stdout){
+								if (err) {
+									winston.err(err);
+									res.send({
+										error: 'Unable to convert image to PNG.'
+									});
+									return;
+								}
+
+								fs.writeFileSync(uploadPath, stdout, 'binary');  
+							});
+					}
+					
+
 					res.json({
 						path: imageUrl
 					});
diff --git a/src/threadTools.js b/src/threadTools.js
index ed32f795be..ba4ec6fe49 100644
--- a/src/threadTools.js
+++ b/src/threadTools.js
@@ -33,10 +33,14 @@ var db = require('./database'),
 				});
 			},
 			hasEnoughRep: function(next) {
-				user.getUserField(uid, 'reputation', function(err, reputation) {
-					if (err) return next(null, false);
-					next(null, parseInt(reputation, 10) >= parseInt(meta.config['privileges:manage_topic'], 10));
-				});
+				if (parseInt(meta.config['privileges:disabled'], 10)) {
+					return next(null, false);
+				} else {
+					user.getUserField(uid, 'reputation', function(err, reputation) {
+						if (err) return next(null, false);
+						next(null, parseInt(reputation, 10) >= parseInt(meta.config['privileges:manage_topic'], 10));
+					});
+				}
 			}
 		}, function(err, results) {
 			callback(err, !results ? undefined : {
diff --git a/src/topics.js b/src/topics.js
index f5a496bc77..8e5f4a3f43 100644
--- a/src/topics.js
+++ b/src/topics.js
@@ -701,7 +701,7 @@ var async = require('async'),
 		});
 	}
 
-	Topics.markUnRead = function(tid, callback) {
+	Topics.markAsUnreadForAll = function(tid, callback) {
 		db.delete('tid:' + tid + ':read_by_uid', callback);
 	}
 
diff --git a/src/upgrade.js b/src/upgrade.js
index d3b938743e..dac9e4a015 100644
--- a/src/upgrade.js
+++ b/src/upgrade.js
@@ -237,7 +237,15 @@ Upgrade.upgradeRedis = function(callback) {
 
 			function updateKeyToHash(key, next) {
 				RDB.get(key, function(err, value) {
-					RDB.hset('global', newKeys[key], value, next);
+					if(err) {
+						return next(err);
+					}
+
+					if(value === null) {
+						RDB.hset('global', newKeys[key], initialValues[key], next);
+					} else {
+						RDB.hset('global', newKeys[key], value, next);
+					}
 				});
 			}
 
@@ -270,6 +278,19 @@ Upgrade.upgradeRedis = function(callback) {
 					'totalpostcount':'postCount'
 				};
 
+				var initialValues = {
+					'global:next_user_id': 1,
+					'next_topic_id': 0,
+					'next_gid': 1,
+					'notifications:next_nid': 0,
+					'global:next_category_id': 12,
+					'global:next_message_id': 0,
+					'global:next_post_id': 0,
+					'usercount': 1,
+					'totaltopiccount': 0,
+					'totalpostcount': 0
+				};
+
 				async.each(keys, updateKeyToHash, function(err) {
 					if(err) {
 						return next(err);
diff --git a/src/websockets.js b/src/websockets.js
index 8a76ca3402..a355ec873f 100644
--- a/src/websockets.js
+++ b/src/websockets.js
@@ -677,7 +677,7 @@ websockets.init = function(io) {
 			});
 		});
 
-		socket.on('getChatMessages', function(data, callback) {
+		socket.on('api:chats.get', function(data, callback) {
 			var touid = data.touid;
 			Messaging.getMessages(uid, touid, function(err, messages) {
 				if (err)
@@ -687,7 +687,7 @@ websockets.init = function(io) {
 			});
 		});
 
-		socket.on('sendChatMessage', function(data) {
+		socket.on('api:chats.send', function(data) {
 
 			var touid = data.touid;
 			if (touid === uid || uid === 0) {
@@ -696,9 +696,15 @@ websockets.init = function(io) {
 
 			var msg = utils.strip_tags(data.message);
 
-			user.getUserField(uid, 'username', function(err, username) {
+			user.getMultipleUserFields([uid, touid], ['username'], function(err, usersData) {
+				if(err) {
+					return;
+				}
+
 				var finalMessage = username + ' : ' + msg,
-					notifText = 'New message from <strong>' + username + '</strong>';
+					notifText = 'New message from <strong>' + username + '</strong>',
+					username = usersData[0].username,
+					toUsername = usersData[1].username;
 
 				if (!isUserOnline(touid)) {
 					notifications.create(notifText, 'javascript:app.openChat(&apos;' + username + '&apos;, ' + uid + ');', 'notification_' + uid + '_' + touid, function(nid) {
@@ -715,7 +721,7 @@ websockets.init = function(io) {
 						numSockets = userSockets[touid].length;
 
 						for (var x = 0; x < numSockets; ++x) {
-							userSockets[touid][x].emit('chatMessage', {
+							userSockets[touid][x].emit('event:chats.receive', {
 								fromuid: uid,
 								username: username,
 								message: finalMessage,
@@ -729,9 +735,9 @@ websockets.init = function(io) {
 						numSockets = userSockets[uid].length;
 
 						for (var x = 0; x < numSockets; ++x) {
-							userSockets[uid][x].emit('chatMessage', {
+							userSockets[uid][x].emit('event:chats.receive', {
 								fromuid: touid,
-								username: username,
+								username: toUsername,
 								message: 'You : ' + msg,
 								timestamp: Date.now()
 							});
@@ -1078,8 +1084,8 @@ websockets.init = function(io) {
 		});
 
 		socket.on('api:meta.buildTitle', function(text, callback) {
-			meta.title.build(text, uid, function(err, title, numNotifications) {
-				callback(title, numNotifications);
+			meta.title.build(text, function(err, title) {
+				callback(title);
 			});
 		});