diff --git a/public/src/forum/admin/users.js b/public/src/forum/admin/users.js
index d3ade3d363..9c2af40ce5 100644
--- a/public/src/forum/admin/users.js
+++ b/public/src/forum/admin/users.js
@@ -25,15 +25,9 @@ define(function() {
 			elements.each(function(index, element) {
 				var banBtn = $(element);
 				var uid = getUID(banBtn);
-				if (isUserAdmin(banBtn) || uid === yourid) {
-					banBtn.addClass('disabled');
-				} else if (isUserBanned(banBtn)) {
-					banBtn.addClass('btn-warning');
-				} else if (!isUserAdmin(banBtn)) {
-					banBtn.removeClass('disabled');
-				} else {
-					banBtn.removeClass('btn-warning');
-				}
+
+				banBtn.toggleClass('disabled', isUserAdmin(banBtn) || uid === yourid);
+				banBtn.toggleClass('btn-warning', isUserBanned(banBtn));
 			});
 		}
 
@@ -41,18 +35,9 @@ define(function() {
 			elements.each(function(index, element) {
 				var adminBtn = $(element);
 				var uid = getUID(adminBtn);
-				if (isUserAdmin(adminBtn)) {
-					adminBtn.attr('value', 'UnMake Admin').html('Remove Admin');
-					if (uid === yourid) {
-						adminBtn.addClass('disabled');
-					}
-				} else if (isUserBanned(adminBtn)) {
-					adminBtn.addClass('disabled');
-				} else if (!isUserBanned(adminBtn)) {
-					adminBtn.removeClass('disabled');
-				} else {
-					adminBtn.removeClass('btn-warning');
-				}
+
+				adminBtn.toggleClass('disabled', (isUserAdmin(adminBtn) && uid === yourid) || isUserBanned(adminBtn));
+				adminBtn.toggleClass('btn-success', isUserAdmin(adminBtn));
 			});
 		}
 
@@ -101,23 +86,39 @@ define(function() {
 				});
 			} else if (!isUserAdmin(adminBtn)) {
 				socket.emit('admin.user.makeAdmin', uid);
-				adminBtn.attr('value', 'UnMake Admin').html('Remove Admin');
 				parent.attr('data-admin', 1);
 				updateUserBanButtons($('.ban-btn'));
-
+				updateUserAdminButtons($('.admin-btn'));
 			} else if(uid !== yourid) {
 				bootbox.confirm('Do you really want to remove this user as admin "' + parent.attr('data-username') + '"?', function(confirm) {
 					if (confirm) {
 						socket.emit('admin.user.removeAdmin', uid);
-						adminBtn.attr('value', 'Make Admin').html('Make Admin');
 						parent.attr('data-admin', 0);
 						updateUserBanButtons($('.ban-btn'));
+						updateUserAdminButtons($('.admin-btn'));
 					}
 				});
 			}
 			return false;
 		});
 
+		$('#users-container').on('click', '.delete-btn', function() {
+			var deleteBtn = $(this);
+			var parent = deleteBtn.parents('.users-box');
+			var uid = getUID(deleteBtn);
+			bootbox.confirm('<b>Warning!</b><br/>Do you really want to delete this user "' + parent.attr('data-username') + '"?<br/> This action is not reversable, all user data and content will be erased!', function(confirm) {
+				if (confirm) {
+					socket.emit('admin.user.deleteUser', uid, function(err) {
+						if (err) {
+							return app.alertError(err.message);
+						}
+						parent.remove();
+						app.alertSuccess('User Deleted!');
+					});
+				}
+			});
+		});
+
 		function handleUserCreate() {
 			var errorEl = $('#create-modal-error');
 			$('#createUser').on('click', function() {
diff --git a/src/admin/user.js b/src/admin/user.js
index 36b8db4704..387988fa6b 100644
--- a/src/admin/user.js
+++ b/src/admin/user.js
@@ -1,4 +1,8 @@
-var utils = require('../../public/src/utils'),
+'use strict';
+
+
+var async = require('async'),
+	utils = require('../../public/src/utils'),
 	user = require('../user'),
 	groups = require('../groups');
 
@@ -6,23 +10,13 @@ var utils = require('../../public/src/utils'),
 
 	UserAdmin.createUser = function(uid, userData, callback) {
 		user.isAdministrator(uid, function(err, isAdmin) {
-			if(err) {
-				return callback(err);
+			if(err || !isAdmin) {
+				return callback(err || new Error('You are not an administrator'));
 			}
 
-			if (isAdmin) {
-				user.create(userData, function(err) {
-					if(err) {
-						return callback(err);
-					}
-
-					callback(null);
-				});
-			} else {
-				callback(new Error('You are not an administrator'));
-			}
+			user.create(userData, callback);
 		});
-	}
+	};
 
 	UserAdmin.makeAdmin = function(uid, theirid, socket) {
 		user.isAdministrator(uid, function(err, isAdmin) {
@@ -105,4 +99,18 @@ var utils = require('../../public/src/utils'),
 		});
 	};
 
+	UserAdmin.deleteUser = function(uid, theirid, callback) {
+		async.waterfall([
+			function(next) {
+				user.isAdministrator(uid, next);
+			},
+			function(isAdmin, next) {
+				if(!isAdmin) {
+					return next(new Error('You are not an administrator'));
+				}
+				user.delete(uid, theirid, next);
+			}
+		], callback);
+	};
+
 }(exports));
\ No newline at end of file
diff --git a/src/categories/activeusers.js b/src/categories/activeusers.js
index 059432f3e5..7fd86651aa 100644
--- a/src/categories/activeusers.js
+++ b/src/categories/activeusers.js
@@ -49,8 +49,8 @@ module.exports = function(Categories) {
 		}
 	};
 
-	Categories.removeActiveUser = function(cid, uid) {
-		db.sortedSetRemove('cid:' + cid + ':active_users', uid);
+	Categories.removeActiveUser = function(cid, uid, callback) {
+		db.sortedSetRemove('cid:' + cid + ':active_users', uid, callback);
 	};
 
 	Categories.getActiveUsers = function(cid, callback) {
diff --git a/src/controllers/topics.js b/src/controllers/topics.js
index 8edf274e15..15452aaf80 100644
--- a/src/controllers/topics.js
+++ b/src/controllers/topics.js
@@ -145,9 +145,9 @@ topicsController.get = function(req, res, next) {
 	], function (err, data) {
 		if (err) {
 			if (err.message === 'not-enough-privileges') {
-				return res.redirect('403');
+				return res.locals.isAPI ? res.json(403, err.message) : res.redirect('403');
 			} else {
-				return res.redirect('404');
+				return res.locals.isAPI ? res.json(404, 'not-found') : res.redirect('404');
 			}
 		}
 
@@ -170,7 +170,7 @@ topicsController.get = function(req, res, next) {
 
 		// Paginator for noscript
 		data.pages = [];
-		for(var x=1;x<=data.pageCount;x++) {
+		for(var x=1; x<=data.pageCount; x++) {
 			data.pages.push({
 				page: x,
 				active: x === parseInt(page, 10)
diff --git a/src/events.js b/src/events.js
index 57b61c8aeb..3890b707e7 100644
--- a/src/events.js
+++ b/src/events.js
@@ -1,6 +1,8 @@
 
+'use strict';
 
 var fs = require('fs'),
+	winston = require('winston'),
 	path = require('path'),
 	nconf = require('nconf'),
 	user = require('./user');
@@ -11,54 +13,62 @@ var fs = require('fs'),
 
 	events.logPasswordChange = function(uid) {
 		logWithUser(uid, 'changed password');
-	}
+	};
+
+	events.logAdminChangeUserPassword = function(adminUid, theirUid, callback) {
+		logAdminEvent(adminUid, theirUid, 'changed password of', callback);
+	};
+
+	events.logAdminUserDelete = function(adminUid, theirUid, callback) {
+		logAdminEvent(adminUid, theirUid, 'deleted', callback);
+	};
 
-	events.logAdminChangeUserPassword = function(adminUid, theirUid) {
+	function logAdminEvent(adminUid, theirUid, message, callback) {
 		user.getMultipleUserFields([adminUid, theirUid], ['username'], function(err, userData) {
 			if(err) {
 				return winston.error('Error logging event. ' + err.message);
 			}
 
-			var msg = userData[0].username + '(uid ' + adminUid + ') changed password of ' + userData[1].username + '(uid ' + theirUid + ')';
-			events.log(msg);
+			var msg = userData[0].username + '(uid ' + adminUid + ') ' + message + ' ' +  userData[1].username + '(uid ' + theirUid + ')';
+			events.log(msg, callback);
 		});
 	}
 
 	events.logPasswordReset = function(uid) {
 		logWithUser(uid, 'reset password');
-	}
+	};
 
 	events.logEmailChange = function(uid, oldEmail, newEmail) {
 		logWithUser(uid,'changed email from "' + oldEmail + '" to "' + newEmail +'"');
-	}
+	};
 
 	events.logUsernameChange = function(uid, oldUsername, newUsername) {
 		logWithUser(uid,'changed username from "' + oldUsername + '" to "' + newUsername +'"');
-	}
+	};
 
 	events.logAdminLogin = function(uid) {
 		logWithUser(uid, 'logged into admin panel');
-	}
+	};
 
 	events.logPostEdit = function(uid, pid) {
 		logWithUser(uid, 'edited post (pid ' + pid + ')');
-	}
+	};
 
 	events.logPostDelete = function(uid, pid) {
 		logWithUser(uid, 'deleted post (pid ' + pid + ')');
-	}
+	};
 
 	events.logPostRestore = function(uid, pid) {
 		logWithUser(uid, 'restored post (pid ' + pid + ')');
-	}
+	};
 
 	events.logTopicDelete = function(uid, tid) {
 		logWithUser(uid, 'deleted topic (tid ' + tid + ')');
-	}
+	};
 
 	events.logTopicRestore = function(uid, tid) {
 		logWithUser(uid, 'restored topic (tid ' + tid + ')');
-	}
+	};
 
 	function logWithUser(uid, string) {
 
@@ -72,7 +82,7 @@ var fs = require('fs'),
 		});
 	}
 
-	events.log = function(msg) {
+	events.log = function(msg, callback) {
 		var logFile = path.join(nconf.get('base_dir'), logFileName);
 
 		msg = '[' + new Date().toUTCString() + '] - ' + msg;
@@ -80,10 +90,17 @@ var fs = require('fs'),
 		fs.appendFile(logFile, msg + '\n', function(err) {
 			if(err) {
 				winston.error('Error logging event. ' + err.message);
+				if (typeof callback === 'function') {
+					callback(err);
+				}
 				return;
 			}
+
+			if (typeof callback === 'function') {
+				callback();
+			}
 		});
-	}
+	};
 
 	events.getLog = function(callback) {
 		var logFile = path.join(nconf.get('base_dir'), logFileName);
@@ -95,6 +112,6 @@ var fs = require('fs'),
 				callback(null, 'No logs found');
 			}
 		});
-	}
+	};
 
 }(module.exports));
\ No newline at end of file
diff --git a/src/favourites.js b/src/favourites.js
index 8084647bdd..2c164d1f28 100644
--- a/src/favourites.js
+++ b/src/favourites.js
@@ -224,9 +224,11 @@ var async = require('async'),
 						});
 					}
 
-					socket.emit('posts.unfavourite', {
-						pid: pid
-					});
+					if (socket) {
+						socket.emit('posts.unfavourite', {
+							pid: pid
+						});
+					}
 				}
 			});
 		});
diff --git a/src/groups.js b/src/groups.js
index db6ecdb81b..4b0ba0ea4a 100644
--- a/src/groups.js
+++ b/src/groups.js
@@ -7,6 +7,10 @@
 		user = require('./user'),
 		db = require('./database');
 
+	Groups.getGroupIds = function (callback) {
+		db.getObjectValues('group:gid', callback);
+	};
+
 	Groups.list = function(options, callback) {
 		db.getObjectValues('group:gid', function (err, gids) {
 			if (gids.length > 0) {
diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js
index 316d77a347..14093734ec 100644
--- a/src/socket.io/admin.js
+++ b/src/socket.io/admin.js
@@ -112,6 +112,10 @@ SocketAdmin.user.unbanUser = function(socket, theirid) {
 	admin.user.unbanUser(socket.uid, theirid, socket);
 };
 
+SocketAdmin.user.deleteUser = function(socket, theirid, callback) {
+	admin.user.deleteUser(socket.uid, theirid, callback);
+};
+
 SocketAdmin.user.search = function(socket, username, callback) {
 	user.search(username, function(err, data) {
 		function isAdmin(userData, next) {
diff --git a/src/threadTools.js b/src/threadTools.js
index 05d5f2d09a..415e2db5bb 100644
--- a/src/threadTools.js
+++ b/src/threadTools.js
@@ -311,7 +311,7 @@ var winston = require('winston'),
 				return callback(err);
 			}
 
-			if (pids.length === 0) {
+			if (!pids.length) {
 				return callback(null, null);
 			}
 
diff --git a/src/topics.js b/src/topics.js
index 32af5a6ebe..032e3ce440 100644
--- a/src/topics.js
+++ b/src/topics.js
@@ -695,7 +695,11 @@ var async = require('async'),
 			privilegeCache = {},
 			userCache = {};
 
+
 		function loadTopicInfo(topicData, next) {
+			if (!topicData) {
+				return next(null, null);
+			}
 
 			function isTopicVisible(topicData, topicInfo) {
 				var deleted = parseInt(topicData.deleted, 10) !== 0;
@@ -736,6 +740,10 @@ var async = require('async'),
 				categoryCache[topicData.cid] = topicInfo.categoryData;
 				userCache[topicData.uid] = topicInfo.user;
 
+				if (!topicInfo.teaser) {
+					return next(null, null);
+				}
+
 				if (!isTopicVisible(topicData, topicInfo)) {
 					return next(null, null);
 				}
diff --git a/src/user.js b/src/user.js
index 0be4ae124c..ba55215512 100644
--- a/src/user.js
+++ b/src/user.js
@@ -281,7 +281,7 @@ var bcrypt = require('bcryptjs'),
 					User.isAdministrator(user.uid, next);
 				},
 				function(isAdmin, next) {
-					user.status = !user.status ? 'online' : '';
+					user.status = !user.status ? 'online' : user.status;
 					user.administrator = isAdmin ? '1':'0';
 					if (set === 'users:online') {
 						return callback(null, user);
diff --git a/src/user/admin.js b/src/user/admin.js
index ece5b56cf3..b9f3350135 100644
--- a/src/user/admin.js
+++ b/src/user/admin.js
@@ -2,7 +2,15 @@
 'use strict';
 
 var async = require('async'),
-	db = require('./../database');
+	db = require('./../database'),
+	posts = require('./../posts'),
+	user = require('./../user'),
+	topics = require('./../topics'),
+	categories = require('./../categories'),
+	plugins = require('./../plugins'),
+	events = require('./../events'),
+	groups = require('./../groups');
+
 
 module.exports = function(User) {
 
@@ -51,4 +59,320 @@ module.exports = function(User) {
 	User.unban = function(uid, callback) {
 		User.setUserField(uid, 'banned', 0, callback);
 	};
-};
\ No newline at end of file
+
+	User.delete = function(adminUid, uid, callback) {
+		async.waterfall([
+			function(next) {
+				deletePosts(uid, next);
+			},
+			function(next) {
+				deleteTopics(uid, next);
+			},
+			function(next) {
+				events.logAdminUserDelete(adminUid, uid, next);
+			}
+		], function(err) {
+			if (err) {
+				return callback(err);
+			}
+
+			deleteAccount(uid, callback);
+		});
+	};
+
+	function deletePosts(uid, callback) {
+		db.getSortedSetRange('uid:' + uid + ':posts', 0, -1, function(err, pids) {
+			if (err) {
+				return callback(err);
+			}
+
+			async.each(pids, deletePost, callback);
+		});
+	}
+
+	function deletePost(pid, callback) {
+		async.parallel([
+			function(next) {
+				deletePostFromTopic(pid, next);
+			},
+			function(next) {
+				deletePostFromCategoryRecentPosts(pid, next);
+			},
+			function(next) {
+				deletePostFromUsersFavourites(pid, next);
+			},
+			function(next) {
+				deletePostFromUsersVotes(pid, next);
+			},
+			function(next) {
+				db.sortedSetRemove('posts:pid', pid, next);
+			}
+		], function(err) {
+			if (err) {
+				return callback(err);
+			}
+
+			plugins.fireHook('action:post.delete', pid);
+			db.delete('post:' + pid, callback);
+		});
+	}
+
+	function deletePostFromTopic(pid, callback) {
+		posts.getPostFields(pid, ['tid', 'deleted'], function(err, postData) {
+			if (err) {
+				return callback(err);
+			}
+
+			db.sortedSetRemove('tid:' + postData.tid + ':posts', pid, function(err) {
+				if (err) {
+					return callback(err);
+				}
+
+				if (parseInt(postData.deleted, 10) === 0) {
+					db.decrObjectField('global', 'postCount', callback);
+				} else {
+					callback();
+				}
+			});
+		});
+	}
+
+	function deletePostFromCategoryRecentPosts(pid, callback) {
+		db.getSortedSetRange('categories:cid', 0, -1, function(err, cids) {
+			if (err) {
+				return callback(err);
+			}
+
+			async.each(cids, function(cid, next) {
+				db.sortedSetRemove('categories:recent_posts:cid:' + cid, pid, next);
+			}, callback);
+		});
+	}
+
+	function deletePostFromUsersFavourites(pid, callback) {
+		db.getSetMembers('pid:' + pid + ':users_favourited', function(err, uids) {
+			if (err) {
+				return callback(err);
+			}
+
+			async.each(uids, function(uid, next) {
+				db.sortedSetRemove('uid:' + uid + ':favourites', pid, next);
+			}, function(err) {
+				if (err) {
+					return callback(err);
+				}
+
+				db.delete('pid:' + pid + ':users_favourited', callback);
+			});
+		});
+	}
+
+	function deletePostFromUsersVotes(pid, callback) {
+		async.parallel({
+			upvoters: function(next) {
+				db.getSetMembers('pid:' + pid + ':upvote', next);
+			},
+			downvoters: function(next) {
+				db.getSetMembers('pid:' + pid + ':downvote', next);
+			}
+		}, function(err, results) {
+			if (err) {
+				return callback(err);
+			}
+
+			async.parallel([
+				function(next) {
+					async.each(results.upvoters, function(uid, next) {
+						db.sortedSetRemove('uid:' + uid + ':upvote', pid, next);
+					}, next);
+				},
+				function(next) {
+					async.each(results.downvoters, function(uid, next) {
+						db.sortedSetRemove('uid:' + uid + ':downvote', pid, next);
+					}, next);
+				}
+			], callback);
+		});
+	}
+
+	function deleteTopics(uid, callback) {
+		db.getSortedSetRange('uid:' + uid + ':topics', 0, -1, function(err, tids) {
+			if (err) {
+				return callback(err);
+			}
+			async.each(tids, deleteTopic, callback);
+		});
+	}
+
+	function deleteTopic(tid, callback) {
+
+		async.parallel([
+			function(next) {
+				db.delete('tid:' + tid + ':followers', next);
+			},
+			function(next) {
+				db.delete('tid:' + tid + ':read_by_uid', next);
+			},
+			function(next) {
+				db.sortedSetRemove('topics:tid', tid, next);
+			},
+			function(next) {
+				db.sortedSetRemove('topics:recent', tid, next);
+			},
+			function(next) {
+				db.sortedSetRemove('topics:posts', tid, next);
+			},
+			function(next) {
+				db.sortedSetRemove('topics:views', tid, next);
+			},
+			function(next) {
+				deleteTopicFromCategory(tid, next);
+			}
+		], function(err) {
+			if (err) {
+				return callback(err);
+			}
+			plugins.fireHook('action:topic.delete', tid);
+			db.delete('topic:' + tid, callback);
+		});
+	}
+
+	function deleteTopicFromCategory(tid, callback) {
+		topics.getTopicFields(tid, ['cid', 'deleted'], function(err, topicData) {
+			if (err) {
+				return callback(err);
+			}
+
+			db.sortedSetRemove('categories:' + topicData.cid + ':tid', tid, function(err) {
+				if (err) {
+					return callback(err);
+				}
+
+				db.decrObjectField('category:' + topicData.cid, 'topic_count', function(err) {
+					if (err) {
+						return callback(err);
+					}
+
+					if (parseInt(topicData.deleted) === 0) {
+						db.decrObjectField('global', 'topicCount', callback);
+					} else {
+						callback();
+					}
+				});
+			});
+		});
+	}
+
+	function deleteAccount(uid, callback) {
+		user.getUserFields(uid, ['username', 'userslug', 'email'], function(err, userData) {
+			if (err)  {
+				return callback(err);
+			}
+
+			async.parallel([
+				function(next) {
+					db.deleteObjectField('username:uid', userData.username, next);
+				},
+				function(next) {
+					db.deleteObjectField('userslug:uid', userData.userslug, next);
+				},
+				function(next) {
+					db.deleteObjectField('email:uid', userData.email, next);
+				},
+				function(next) {
+					db.delete('uid:' + uid + ':notifications:read', next);
+				},
+				function(next) {
+					db.delete('uid:' + uid + ':notifications:unread', next);
+				},
+				function(next) {
+					db.sortedSetRemove('users:joindate', uid, next);
+				},
+				function(next) {
+					db.sortedSetRemove('users:postcount', uid, next);
+				},
+				function(next) {
+					db.sortedSetRemove('users:reputation', uid, next);
+				},
+				function(next) {
+					db.delete('uid:' + uid + ':favourites', next);
+				},
+				function(next) {
+					db.delete('uid:' + uid + ':topics', next);
+				},
+				function(next) {
+					db.delete('uid:' + uid + ':posts', next);
+				},
+				function(next) {
+					db.delete('uid:' + uid + ':chats', next);
+				},
+				function(next) {
+					db.delete('uid:' + uid + ':ip', next);
+				},
+				function(next) {
+					db.delete('uid:' + uid + ':upvote', next);
+				},
+				function(next) {
+					db.delete('uid:' + uid + ':downvote', next);
+				},
+				function(next) {
+					deleteUserFromCategoryActiveUsers(uid, next);
+				},
+				function(next) {
+					deleteUserFromFollowers(uid, next);
+				},
+				function(next) {
+					deleteUserFromGroups(uid, next);
+				}
+			], function(err) {
+				if (err) {
+					return callback(err);
+				}
+
+				async.parallel([
+					function(next) {
+						db.delete('followers:' + uid, next);
+					},
+					function(next) {
+						db.delete('following:' + uid, next);
+					},
+					function(next) {
+						db.delete('user:' + uid, next);
+					}
+				], callback);
+			});
+		});
+	}
+
+	function deleteUserFromCategoryActiveUsers(uid, callback) {
+		db.getSortedSetRange('categories:cid', 0, -1, function(err, cids) {
+			if (err) {
+				return callback(err);
+			}
+
+			async.each(cids, function(cid, next) {
+				categories.removeActiveUser(cid, uid, next);
+			}, callback);
+		});
+	}
+
+	function deleteUserFromFollowers(uid, callback) {
+		db.getSetMembers('followers:' + uid, function(err, uids) {
+			if (err) {
+				return callback(err);
+			}
+
+			async.each(uids, function(theiruid, next) {
+				db.setRemove('following:' + theiruid, uid, next);
+			}, callback);
+		});
+	}
+
+	function deleteUserFromGroups(uid, callback) {
+		groups.getGroupIds(function(err, gids) {
+			async.each(gids, function(gid, next) {
+				groups.leave(gid, uid, next);
+			}, callback);
+		});
+	}
+};