From 00a9ec0f1293a2da05d5bf36ccc88226b7ec05d3 Mon Sep 17 00:00:00 2001
From: Julian Lam <julian@designcreateplay.com>
Date: Fri, 24 May 2013 13:14:41 -0400
Subject: [PATCH 1/4] interim commit

---
 app.js                              |  2 +-
 public/templates/config.json        |  2 ++
 public/templates/install/basic.tpl  | 28 ++++++++++++++++++
 public/templates/install/footer.tpl |  3 ++
 public/templates/install/header.tpl | 45 +++++++++++++++++++++++++++++
 src/routes/install.js               | 44 ++++++++++++++++++++++++++++
 src/webserver.js                    |  2 ++
 7 files changed, 125 insertions(+), 1 deletion(-)
 create mode 100644 public/templates/install/basic.tpl
 create mode 100644 public/templates/install/footer.tpl
 create mode 100644 public/templates/install/header.tpl
 create mode 100644 src/routes/install.js

diff --git a/app.js b/app.js
index 818968e0fb..56b95a1cee 100644
--- a/app.js
+++ b/app.js
@@ -18,7 +18,7 @@ global.templates = {};
     templates.init([
         'header', 'footer', 'logout', 'admin/header', 'admin/footer', 'admin/index',
         'emails/reset', 'emails/reset_plaintext', 'emails/email_confirm', 'emails/email_confirm_plaintext',
-        'emails/header.tpl', 'emails/footer.tpl'
+        'emails/header', 'emails/footer', 'install/header', 'install/footer', 'install/basic'
     ]);
     
     templates.ready(function() {
diff --git a/public/templates/config.json b/public/templates/config.json
index 8a35b78e0b..04aa514a26 100644
--- a/public/templates/config.json
+++ b/public/templates/config.json
@@ -10,6 +10,8 @@
         "admin/twitter[^]*": "admin/twitter",
         "admin/facebook[^]*": "admin/facebook",
         "admin/gplus[^]*": "admin/gplus",
+        "install/?$": "install/basic",
+        "install/basic/?$": "install/basic",
     	"users[^]*edit": "accountedit",
         "users[^]*friends": "friends",
         "users/[^]*": "account",
diff --git a/public/templates/install/basic.tpl b/public/templates/install/basic.tpl
new file mode 100644
index 0000000000..9ab273509e
--- /dev/null
+++ b/public/templates/install/basic.tpl
@@ -0,0 +1,28 @@
+
+<h1>Step 1 &ndash; Basic Information</h1>
+
+<p class="lead">
+	Thanks for choosing to install NodeBB! We&apos;ll need some information to set up your installation
+	configuration...
+</p>
+
+<h3>Path Information</h3>
+<p>
+	Please enter the web-accessible url that will be used to point to the NodeBB installation. If you are using a port number in the address,
+	<strong>include it in the field below, not here</strong>
+	<input type="text" class="input-large" data-field="base_url" placeholder="http://www.example.org" />
+</p>
+
+<p>
+	<label class="checkbox"><input type="checkbox" data-field="use_port" /> Use port</label>
+</p>
+
+<form class="form-inline">
+	<label>Port</label> <input type="text" data-field="port" />
+</form>
+
+<h3>NodeBB Secret</h3>
+<p>
+	This "secret" is used to encode user sessions, so they are not stored in plaintext. Enter a bunch of random characters below:
+</p>
+<input type="text" class="input-xxlarge" data-field="secret" placeholder="n239he#dh9j9$jc4h%y4yuhnx9y(&#y9ryn9c3" />
\ No newline at end of file
diff --git a/public/templates/install/footer.tpl b/public/templates/install/footer.tpl
new file mode 100644
index 0000000000..9c25607790
--- /dev/null
+++ b/public/templates/install/footer.tpl
@@ -0,0 +1,3 @@
+
+	</div>
+</body>
\ No newline at end of file
diff --git a/public/templates/install/header.tpl b/public/templates/install/header.tpl
new file mode 100644
index 0000000000..535efe3092
--- /dev/null
+++ b/public/templates/install/header.tpl
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<title>NodeBB</title>
+	<meta name="viewport" content="width=device-width, initial-scale=1.0">
+	<meta name="title" CONTENT="NodeBB">
+	<meta name="description" content="Node.js/Redis/Socket.io powered forums for a new generation">
+	<link href="/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen">
+	<link href="/vendor/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet" media="screen">
+	<link rel="stylesheet" href="/vendor/fontawesome/css/font-awesome.min.css">
+	<script type="text/javascript" src="http://code.jquery.com/jquery.js"></script>
+	<script type="text/javascript" src="/vendor/jquery/js/jquery-ui-1.10.3.custom.min.js"></script>
+	<script type="text/javascript" src="/vendor/bootstrap/js/bootstrap.min.js"></script>
+	<script type="text/javascript" src="/socket.io/socket.io.js"></script>
+	<script type="text/javascript" src="/src/app.js"></script>
+	<script type="text/javascript" src="/src/templates.js"></script>
+	<script type="text/javascript" src="/src/ajaxify.js"></script>
+	<script type="text/javascript" src="/src/jquery.form.js"></script>
+	<script type="text/javascript" src="/src/utils.js"></script>
+
+	<link rel="stylesheet" type="text/css" href="/css/style.css" />
+</head>
+
+<body>
+	<div class="navbar navbar-inverse navbar-fixed-top">
+		<div class="navbar-inner">
+			<div class="container">
+				<a class="brand" href="/">NodeBB Installation</a>
+				<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
+					<span class="icon-bar"></span>
+					<span class="icon-bar"></span>
+					<span class="icon-bar"></span>
+				</button>
+				<div class="nav-collapse collapse">
+					<ul class="nav nodebb-inline-block">
+						<li>
+							<a data-tab="basic" href="#"><i class="icon-cog"></i> Basic</a>
+						</li>
+					</ul>
+				</div>
+			</div>
+		</div>
+	</div>
+
+	<div class="container" id="content">
\ No newline at end of file
diff --git a/src/routes/install.js b/src/routes/install.js
new file mode 100644
index 0000000000..e882aa954a
--- /dev/null
+++ b/src/routes/install.js
@@ -0,0 +1,44 @@
+
+var RDB = require('../redis.js');
+
+(function(Install) {
+	Install.create_routes = function(app) {
+
+		(function() {
+			var routes = ['basic'];
+
+			for (var i=0, ii=routes.length; i<ii; i++) {
+				(function(route) {
+					app.get('/install/' + route, function(req, res) {
+						res.send(templates['install/header'] + app.create_route('install/' + route) + templates['install/footer']);
+					});
+				}(routes[i]));
+			}
+		}());
+
+		//todo consolidate.
+		app.get('/install', function(req, res) {
+			res.send(templates['install/header'] + app.create_route('install/basic') + templates['install/footer']);
+		});
+		app.get('/install/index', function(req, res) {
+			res.send(templates['install/header'] + app.create_route('install/index') + templates['install/footer']);
+		});
+
+
+		function api_method(req, res) {
+			switch(req.params.method) {
+				case 'basic' :
+					res.send('{}');
+					break;
+
+				default :
+					res.send('{}');
+			}
+		}
+
+		app.get('/api/install/:method/:tab?*', api_method);
+		app.get('/api/install/:method*', api_method);
+	};
+
+
+}(exports));
\ No newline at end of file
diff --git a/src/webserver.js b/src/webserver.js
index f7b8028b4b..111b50b40e 100644
--- a/src/webserver.js
+++ b/src/webserver.js
@@ -17,6 +17,7 @@ var express = require('express'),
 	notifications = require('./notifications.js'),
 	admin = require('./routes/admin.js'),
 	userRoute = require('./routes/user.js'),
+	installRoute = require('./routes/install.js'),
 	auth = require('./routes/authentication.js');
 
 (function(app) {
@@ -62,6 +63,7 @@ var express = require('express'),
 	auth.create_routes(app);
 	admin.create_routes(app);
 	userRoute.create_routes(app);
+	installRoute.create_routes(app);
 
 
 	app.create_route = function(url, tpl) { // to remove

From 65b6010d74a0a45e6de255984657316d2f7948a5 Mon Sep 17 00:00:00 2001
From: Julian Lam <julian.lam@gmail.com>
Date: Sat, 25 May 2013 20:32:22 -0400
Subject: [PATCH 2/4] installation wizard under /install

---
 app.js                                  |   2 +-
 public/templates/config.json            |  50 +++++-----
 public/templates/install/basic.tpl      |  59 +++++++----
 public/templates/install/footer.tpl     | 127 ++++++++++++++++++++++++
 public/templates/install/header.tpl     |  17 +++-
 public/templates/install/mail.tpl       |  36 +++++++
 public/templates/install/privileges.tpl |  40 ++++++++
 public/templates/install/redis.tpl      |  81 +++++++++++++++
 public/templates/install/social.tpl     |  47 +++++++++
 src/meta.js                             |  59 +++++++++++
 src/routes/install.js                   |   4 +-
 src/webserver.js                        |  12 +--
 src/websockets.js                       |  33 +++++-
 13 files changed, 512 insertions(+), 55 deletions(-)
 create mode 100644 public/templates/install/mail.tpl
 create mode 100644 public/templates/install/privileges.tpl
 create mode 100644 public/templates/install/redis.tpl
 create mode 100644 public/templates/install/social.tpl
 create mode 100644 src/meta.js

diff --git a/app.js b/app.js
index 56b95a1cee..4005af1aa1 100644
--- a/app.js
+++ b/app.js
@@ -18,7 +18,7 @@ global.templates = {};
     templates.init([
         'header', 'footer', 'logout', 'admin/header', 'admin/footer', 'admin/index',
         'emails/reset', 'emails/reset_plaintext', 'emails/email_confirm', 'emails/email_confirm_plaintext',
-        'emails/header', 'emails/footer', 'install/header', 'install/footer', 'install/basic'
+        'emails/header', 'emails/footer', 'install/header', 'install/footer', 'install/redis'
     ]);
     
     templates.ready(function() {
diff --git a/public/templates/config.json b/public/templates/config.json
index 04aa514a26..5af89957fb 100644
--- a/public/templates/config.json
+++ b/public/templates/config.json
@@ -1,25 +1,29 @@
 {
-    "custom_mapping": {
-    	"admin/topics[^]*": "admin/topics",
-    	"admin/categories[^]*": "admin/categories",
-    	"admin/users[^]*": "admin/users",
-        "admin/redis[^]*": "admin/redis",
-        "admin/index[^]*": "admin/index",
-        "admin/themes[^]*": "admin/themes",
-        "admin/settings[^]*": "admin/settings",
-        "admin/twitter[^]*": "admin/twitter",
-        "admin/facebook[^]*": "admin/facebook",
-        "admin/gplus[^]*": "admin/gplus",
-        "install/?$": "install/basic",
-        "install/basic/?$": "install/basic",
-    	"users[^]*edit": "accountedit",
-        "users[^]*friends": "friends",
-        "users/[^]*": "account",
-        "latest": "category",
-        "popular": "category",
-        "active": "category"
-    },
-    "force_refresh": {
-        "logout": true
-    }
+	"custom_mapping": {
+		"admin/topics[^]*": "admin/topics",
+		"admin/categories[^]*": "admin/categories",
+		"admin/users[^]*": "admin/users",
+		"admin/redis[^]*": "admin/redis",
+		"admin/index[^]*": "admin/index",
+		"admin/themes[^]*": "admin/themes",
+		"admin/settings[^]*": "admin/settings",
+		"admin/twitter[^]*": "admin/twitter",
+		"admin/facebook[^]*": "admin/facebook",
+		"admin/gplus[^]*": "admin/gplus",
+		"install/?$": "install/redis",
+		"install/basic/?": "install/basic",
+		"install/redis/?": "install/redis",
+		"install/mail/?": "install/mail",
+		"install/social/?": "install/social",
+		"install/privileges/?": "install/privileges",
+		"users[^]*edit": "accountedit",
+		"users[^]*friends": "friends",
+		"users/[^]*": "account",
+		"latest": "category",
+		"popular": "category",
+		"active": "category"
+	},
+	"force_refresh": {
+		"logout": true
+	}
 }
\ No newline at end of file
diff --git a/public/templates/install/basic.tpl b/public/templates/install/basic.tpl
index 9ab273509e..5a656fc21d 100644
--- a/public/templates/install/basic.tpl
+++ b/public/templates/install/basic.tpl
@@ -1,28 +1,51 @@
 
-<h1>Step 1 &ndash; Basic Information</h1>
+<h1>Step 2 &ndash; Basic Information</h1>
 
-<p class="lead">
-	Thanks for choosing to install NodeBB! We&apos;ll need some information to set up your installation
-	configuration...
-</p>
-
-<h3>Path Information</h3>
-<p>
-	Please enter the web-accessible url that will be used to point to the NodeBB installation. If you are using a port number in the address,
-	<strong>include it in the field below, not here</strong>
-	<input type="text" class="input-large" data-field="base_url" placeholder="http://www.example.org" />
-</p>
+<form class="form-inline">
+	<h3>Path Information</h3>
+	<p>
+		Please enter the web-accessible url that will be used to point to the NodeBB installation. If you are using a port number in the address,
+		<strong>include it in the field below, not here</strong>. Do not include a trailing slash.<br />
+		<input type="text" class="input-large" data-field="base_url" placeholder="http://www.example.org" />
+	</p>
 
-<p>
-	<label class="checkbox"><input type="checkbox" data-field="use_port" /> Use port</label>
-</p>
+	<p>
+		<label class="checkbox"><input type="checkbox" data-field="use_port" checked /> Use port</label>
+	</p>
+	<p>
+		<label>Port</label> <input type="number" class="input-mini" data-field="port" value="4567" placeholder="4567" />
+	</p>
 
-<form class="form-inline">
-	<label>Port</label> <input type="text" data-field="port" />
+	<p>
+		Path to uploads folder (relative to the root of the NodeBB install)<br />
+		<input type="text" class="input-large" data-field="upload_url" value="/public/uploads" placeholder="/public/uploads" />
+	</p>
 </form>
 
 <h3>NodeBB Secret</h3>
 <p>
 	This "secret" is used to encode user sessions, so they are not stored in plaintext. Enter a bunch of random characters below:
 </p>
-<input type="text" class="input-xxlarge" data-field="secret" placeholder="n239he#dh9j9$jc4h%y4yuhnx9y(&#y9ryn9c3" />
\ No newline at end of file
+<input type="text" class="input-xxlarge" data-field="secret" placeholder="n239he#dh9j9$jc4h%y4yuhnx9y(&#y9ryn9c3" />
+
+<hr />
+<div class="pull-right">
+	<button data-path="mail" class="btn btn-primary btn-large">Next &ndash; <i class="icon-envelope"></i> Mail</button>
+</div>
+<div>
+	<button data-path="redis" class="btn btn-primary btn-large">Previous &ndash; <i class="icon-hdd"></i> Redis</button>
+</div>
+
+<script>
+	(function() {
+		nodebb_setup.prepare();
+
+		var	portToggle = document.querySelector('input[data-field="use_port"]'),
+			portEl = document.querySelector('input[data-field="port"]');
+
+		portToggle.addEventListener('change', function(e) {
+			if (e.target.checked) portEl.disabled = false;
+			else portEl.disabled = true;
+		});
+	})();
+</script>
\ No newline at end of file
diff --git a/public/templates/install/footer.tpl b/public/templates/install/footer.tpl
index 9c25607790..9823d43371 100644
--- a/public/templates/install/footer.tpl
+++ b/public/templates/install/footer.tpl
@@ -1,3 +1,130 @@
 
 	</div>
+	<script>
+		var nodebb_setup = {
+			config: undefined,
+			prepare: function() {
+				// Bounce if config is not ready
+				if (nodebb_setup.config === undefined) {
+					ajaxify.go('install/redis');
+					app.alert({
+						alert_id: 'config-ready',
+						type: 'error',
+						timeout: 10000,
+						title: 'NodeBB Configuration Not Ready!',
+						message:	'NodeBB cannot proceed with setup at this time as Redis database information ' +
+									'was not found. Please enter the information below.'
+					});
+
+					return;
+				}
+
+				// Populate the fields on the page from the config
+				var fields = document.querySelectorAll('#content [data-field]'),
+					numFields = fields.length,
+					x, key, inputType;
+				for(x=0;x<numFields;x++) {
+					key = fields[x].getAttribute('data-field');
+					inputType = fields[x].getAttribute('type');
+					if (nodebb_setup.config[key]) {
+						switch(inputType) {
+							case 'text':
+							case 'textarea':
+							case 'number':
+								fields[x].value = nodebb_setup.config[key];
+							break;
+
+							case 'checkbox':
+								fields[x].checked = nodebb_setup.config[key] ? true : false;
+							break;
+						}
+					} else {
+						// Save defaults, if they're not found in the config
+						var	defaultFields = [
+								'use_port', 'port', 'upload_url', 'mailer:host',
+								'mailer:port', 'privileges:manage_content',
+								'privileges:manage_topic'
+							],
+							defaultVal;
+						if (defaultFields.indexOf(key) !== -1) {
+							console.log('saving default value: ', key);
+							switch(inputType) {
+								case 'text':
+								case 'textarea':
+								case 'number':
+									defaultVal = fields[x].value;
+								break;
+
+								case 'checkbox':
+									defaultVal = fields[x].checked ? '1' : '0';
+								break;
+							}
+							socket.emit('api:config.set', {
+								key: key,
+								value: defaultVal
+							});
+							nodebb_setup.config[key] = defaultVal;
+						}
+					}
+				}
+			}
+		};
+
+		(function() {
+			// Listen for field changes and auto-save on change
+			var contentEl = document.getElementById('content');
+
+			contentEl.addEventListener('change', function(e) {
+				if (e.target.hasAttribute('data-field')) {
+					var key = e.target.getAttribute('data-field'),
+						value;
+
+					switch(e.target.getAttribute('type')) {
+						case 'text':
+						case 'textarea':
+						case 'number':
+							value = e.target.value;
+						break;
+						case 'checkbox':
+							value = e.target.checked ? 1 : 0;
+						break;
+
+						default:
+							return false;
+						break;
+					}
+
+					socket.emit('api:config.set', { key: key, value: value });
+					nodebb_setup.config[key] = value;
+				}
+			}, false);
+			contentEl.addEventListener('click', function(e) {
+				if (e.target.hasAttribute('data-path')) {
+					var	href = 'install/' + e.target.getAttribute('data-path');
+					console.log(href);
+					if (!e.target.disabled) ajaxify.go(href);
+				}
+			}, false);
+
+			socket.on('api:config.set', function(data) {
+				if (data.status === 'ok') {
+					app.alert({
+						alert_id: 'config_status',
+						timeout: 2500,
+						title: 'Configuration Saved',
+						message: 'Your changes to the NodeBB configuration have been saved',
+						type: 'success'
+					});
+				} else {
+					app.alert({
+						alert_id: 'config_status',
+						timeout: 2500,
+						title: 'Configuration Not Saved',
+						message: 'NodeBB encountered a problem saving your changes',
+						type: 'error'
+					});
+				}
+			});
+		})();
+	</script>
 </body>
\ No newline at end of file
diff --git a/public/templates/install/header.tpl b/public/templates/install/header.tpl
index 535efe3092..d598b0bb06 100644
--- a/public/templates/install/header.tpl
+++ b/public/templates/install/header.tpl
@@ -25,7 +25,7 @@
 	<div class="navbar navbar-inverse navbar-fixed-top">
 		<div class="navbar-inner">
 			<div class="container">
-				<a class="brand" href="/">NodeBB Installation</a>
+				<a class="brand" href="/install">NodeBB Installation</a>
 				<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
 					<span class="icon-bar"></span>
 					<span class="icon-bar"></span>
@@ -34,12 +34,25 @@
 				<div class="nav-collapse collapse">
 					<ul class="nav nodebb-inline-block">
 						<li>
-							<a data-tab="basic" href="#"><i class="icon-cog"></i> Basic</a>
+							<a data-tab="redis" href="/install/redis"><i class="icon-hdd"></i> Redis</a>
+						</li>
+						<li>
+							<a data-tab="basic" href="/install/basic"><i class="icon-cog"></i> Basic</a>
+						</li>
+						<li>
+							<a data-tab="email" href="/install/email"><i class="icon-envelope"></i> Mail</a>
+						</li>
+						<li>
+							<a data-tab="social" href="/install/social"><i class="icon-facebook"></i> Social</a>
+						</li>
+						<li>
+							<a data-tab="privileges" href="/install/privileges"><i class="icon-legal"></i> Privileges</a>
 						</li>
 					</ul>
 				</div>
 			</div>
 		</div>
 	</div>
+	<div id="alert_window"></div>
 
 	<div class="container" id="content">
\ No newline at end of file
diff --git a/public/templates/install/mail.tpl b/public/templates/install/mail.tpl
new file mode 100644
index 0000000000..42ba612a18
--- /dev/null
+++ b/public/templates/install/mail.tpl
@@ -0,0 +1,36 @@
+
+<h1>Step 3 &ndash; Mailer Information</h1>
+
+<form class="form-inline">
+	<p>
+		The mailer information is used when sending out registration confirmation emails to new users.
+		It is also used to send password reset emails to users who have forgotten their password.
+	</p>
+	<p>
+		The defaults here correspond to a local <code>sendmail</code> server, although any third-party
+		mail server can be used.
+	</p>
+	<p>
+		<label>Hostname</label> <input type="text" class="input-medium" data-field="mailer:host" value="127.0.0.1" placeholder="127.0.0.1" />
+	</p>
+	<p>
+		<label>Port</label> <input type="number" class="input-mini" data-field="mailer:port" value="25" placeholder="25" />
+	</p>
+	<p>
+		<label>From</label> <input type="text" class="input-large" data-field="mailer:from" placeholder="John Smith <jsmith@example.org>" />
+	</p>
+</form>
+
+<hr />
+<div class="pull-right">
+	<button data-path="social" class="btn btn-primary btn-large">Next &ndash; <i class="icon-facebook"></i> Social</button>
+</div>
+<div>
+	<button data-path="basic" class="btn btn-primary btn-large">Previous &ndash; <i class="icon-cog"></i> Basic</button>
+</div>
+
+<script>
+	(function() {
+		nodebb_setup.prepare();
+	})();
+</script>
\ No newline at end of file
diff --git a/public/templates/install/privileges.tpl b/public/templates/install/privileges.tpl
new file mode 100644
index 0000000000..466febc9c5
--- /dev/null
+++ b/public/templates/install/privileges.tpl
@@ -0,0 +1,40 @@
+
+<h1>Step 5 &ndash; User Privilege Thresholds</h1>
+
+<form class="form-inline">
+	<p>
+		Privilege thresholds grants a community membership the ability to moderate itself.
+		These numbers denote the minimum amount of user reputation required before the
+		corresponding privilege is unlocked.
+	</p>
+	<p>
+		Reputation is gained when other users favourite posts that you have made.
+	</p>
+
+	<p>
+		<label>Manage Content</label> <input type="number" class="input-mini" value="1000" placeholder="1000" data-field="privileges:manage_content" />
+	</p>
+	<p>
+		Users with reach the "Manage Content" threshold are able to edit/delete other users' posts.
+	</p>
+	<p>
+		<label>Manage Topics</label> <input type="number" class="input-mini" value="2000" placeholder="2000" data-field="privileges:manage_topic" />
+	</p>
+	<p>
+		Users with reach the "Manage Topics" threshold are able to edit, lock, pin, close, and delete topics.
+	</p>
+</form>
+
+<hr />
+<div class="pull-right">
+	<a href="/" class="btn btn-success btn-large"><i class="icon-thumbs-up"></i> Start using NodeBB!</a>
+</div>
+<div>
+	<button data-path="social" class="btn btn-primary btn-large">Previous &ndash; <i class="icon-facebook"></i> Social</button>
+</div>
+
+<script>
+	(function() {
+		nodebb_setup.prepare();
+	})();
+</script>
\ No newline at end of file
diff --git a/public/templates/install/redis.tpl b/public/templates/install/redis.tpl
new file mode 100644
index 0000000000..e60954ee89
--- /dev/null
+++ b/public/templates/install/redis.tpl
@@ -0,0 +1,81 @@
+
+<h1>Step 1 &ndash; Establish Redis Connection</h1>
+
+<p class="lead">
+	Thanks for choosing to install NodeBB! We&apos;ll need some information to set up your installation
+	configuration...
+</p>
+<p>
+	Please enter the details of your Redis server here. If redis is hosted on the same
+	server as NodeBB, you can leave the default values as-is.
+</p>
+
+<form class="form-horizontal">
+	<div class="control-group">
+		<label class="control-label" for="redis-hostname">Hostname</label>
+		<div class="controls">
+			<input type="text" id="redis-hostname" class="input-medium" placeholder="127.0.0.1" value="127.0.0.1" />
+		</div>
+	</div>
+	<div class="control-group">
+		<label class="control-label" for="redis-port">Port</label>
+		<div class="controls">
+			<input type="number" id="redis-port" class="input-mini" placeholder="6379" value="6379" />
+		</div>
+	</div>
+	<div class="control-group">
+		<div class="controls">
+			<button class="btn" id="test-redis">Test</button>
+		</div>
+	</div>
+</form>
+
+<hr />
+<div class="pull-right">
+	<button data-path="basic" class="btn btn-primary btn-large" disabled>Next &ndash; <i class="icon-cog"></i> Basic</button>
+</div>
+
+<script>
+	(function() {
+		var	testRedisEl = document.getElementById('test-redis');
+		testRedisEl.addEventListener('click', function(e) {
+			e.preventDefault();
+
+			if (e.target.className.indexOf('testing') === -1) {
+				e.target.className += ' testing';
+				e.target.innerHTML = '<i class="icon-spinner icon-spin"></i> Testing';
+				socket.once('api:config.redisTest', function(data) {
+					if (data && data.status === 'ok') {
+						e.target.className = 'btn btn-success testing';
+						e.target.innerHTML = 'Redis Connection Successful!';
+
+						app.alert({
+							type: 'success',
+							timeout: 10000,
+							alert_id: 'config-ready',
+							title: 'Setup Ready!',
+							message:	'NodeBB is ready to continue with the setup process. ' +
+										'Any changes you make now will be saved automatically'
+						});
+
+						// Grab configs from the db and enable the 'next' button
+						socket.emit('api:config.setup', {
+							'redis/host': document.getElementById('redis-hostname').value,
+							'redis/port': document.getElementById('redis-port').value
+						});
+					} else {
+						e.target.className = 'btn btn-danger';
+						e.target.innerHTML = 'Could not connect to Redis, click here to test again';
+					}
+				});
+				socket.emit('api:config.redisTest');
+			}
+		});
+
+		var nextBtn = document.querySelector('button[data-path="basic"]');
+		socket.once('api:config.setup', function(data) {
+			nodebb_setup.config = data;
+			nextBtn.disabled = false;
+		});
+	})();
+</script>
diff --git a/public/templates/install/social.tpl b/public/templates/install/social.tpl
new file mode 100644
index 0000000000..046021dbf5
--- /dev/null
+++ b/public/templates/install/social.tpl
@@ -0,0 +1,47 @@
+
+<h1>Step 4 &ndash; Social Media Logins (Optional)</h1>
+
+<form class="form-inline">
+	<p>
+		You may opt to allow users to register and login in via a social media account, if that
+		social network supports doing so.
+	</p>
+
+	<h3>Facebook</h3>
+	<p>
+		<label>Application ID</label> <input type="text" class="input-medium" data-field="social/facebook/app_id" />
+	</p>
+	<p>
+		<label>Application Secret</label> <input type="text" class="input-large" data-field="social/facebook/secret" />
+	</p>
+
+	<h3>Twitter</h3>
+	<p>
+		<label>Application Key</label> <input type="text" class="input-medium" data-field="social/twitter/key" />
+	</p>
+	<p>
+		<label>Application Secret</label> <input type="text" class="input-large" data-field="social/twitter/secret" />
+	</p>
+
+	<h3>Google</h3>
+	<p>
+		<label>Application Key</label> <input type="text" class="input-xxlarge" data-field="social/google/key" />
+	</p>
+	<p>
+		<label>Application Secret</label> <input type="text" class="input-large" data-field="social/google/secret" />
+	</p>
+</form>
+
+<hr />
+<div class="pull-right">
+	<button data-path="privileges" class="btn btn-primary btn-large">Next &ndash; <i class="icon-legal"></i> Privileges</button>
+</div>
+<div>
+	<button data-path="mail" class="btn btn-primary btn-large">Previous &ndash; <i class="icon-envelope"></i> Mail</button>
+</div>
+
+<script>
+	(function() {
+		nodebb_setup.prepare();
+	})();
+</script>
\ No newline at end of file
diff --git a/src/meta.js b/src/meta.js
new file mode 100644
index 0000000000..f4fd57edc2
--- /dev/null
+++ b/src/meta.js
@@ -0,0 +1,59 @@
+var config = require('../config.js'),
+	utils = require('./../public/src/utils.js'),
+	RDB = require('./redis.js'),
+	async = require('async');
+
+(function(Meta) {
+	Meta.testRedis = function(callback) {
+		RDB.set('nodebb-redis-test', 'foobar', function(err, res) {
+			if (!err) {
+				RDB.get('nodebb-redis-test', function(err, res) {
+					if (!err && res === 'foobar') {
+						callback(true);
+					} else {
+						callback(false);
+					}
+				});
+			} else {
+				callback(false);
+			}
+		});
+	}
+
+	Meta.config = {
+		get: function(callback) {
+			var config = {};
+
+			async.waterfall([
+				function(next) {
+					RDB.hkeys('config', function(err, keys) {
+						next(err, keys);
+					});
+				},
+				function(keys, next) {
+					async.each(keys, function(key, next) {
+						RDB.hget('config', key, function(err, value) {
+							if (!err) {
+								config[key] = value;
+							}
+
+							next(err);
+						});
+					}, next);
+				}
+			], function(err) {
+				if (!err) {
+					config.status = 'ok';
+					callback(config);
+				} else callback({
+					status: 'error'
+				});
+			});
+		},
+		set: function(field, value, callback) {
+			RDB.hset('config', field, value, function(err, res) {
+				callback(err);
+			});
+		}
+	}
+}(exports));
\ No newline at end of file
diff --git a/src/routes/install.js b/src/routes/install.js
index e882aa954a..5b352fcd71 100644
--- a/src/routes/install.js
+++ b/src/routes/install.js
@@ -5,7 +5,7 @@ var RDB = require('../redis.js');
 	Install.create_routes = function(app) {
 
 		(function() {
-			var routes = ['basic'];
+			var routes = ['basic', 'redis', 'mail', 'social', 'privileges'];
 
 			for (var i=0, ii=routes.length; i<ii; i++) {
 				(function(route) {
@@ -21,7 +21,7 @@ var RDB = require('../redis.js');
 			res.send(templates['install/header'] + app.create_route('install/basic') + templates['install/footer']);
 		});
 		app.get('/install/index', function(req, res) {
-			res.send(templates['install/header'] + app.create_route('install/index') + templates['install/footer']);
+			res.send(templates['install/header'] + app.create_route('install/basic') + templates['install/footer']);
 		});
 
 
diff --git a/src/webserver.js b/src/webserver.js
index 111b50b40e..48c3b9fca2 100644
--- a/src/webserver.js
+++ b/src/webserver.js
@@ -18,7 +18,8 @@ var express = require('express'),
 	admin = require('./routes/admin.js'),
 	userRoute = require('./routes/user.js'),
 	installRoute = require('./routes/install.js'),
-	auth = require('./routes/authentication.js');
+	auth = require('./routes/authentication.js'),
+	meta = require('./meta.js');
 
 (function(app) {
 	var templates = null;
@@ -222,13 +223,8 @@ var express = require('express'),
 	app.get('/api/:method/:id*', api_method);
 
 	app.get('/test', function(req, res) {
-		// notifications.remove_by_uniqueId('foobar', 1, function(success) {
-		// 	res.send('remove: ' + success);
-		// });
-		notifications.create('a bunch more text', 5, '/category/2/general-discussion', 'foobar', function(nid) {
-			notifications.push(nid, 1, function() {
-				res.send('nid: ' + nid)
-			});
+		meta.config.get(function(config) {
+			res.send(JSON.stringify(config, null, 4));
 		});
 	});
 
diff --git a/src/websockets.js b/src/websockets.js
index 0a3c15d826..a40f5216b6 100644
--- a/src/websockets.js
+++ b/src/websockets.js
@@ -11,7 +11,9 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
 	categories = require('./categories.js'),
 	notifications = require('./notifications.js'),
 	threadTools = require('./threadTools.js'),
-	postTools = require('./postTools.js');
+	postTools = require('./postTools.js'),
+	meta = require('./meta.js'),
+	async = require('async');
 	
 (function(io) {
 	var	users = {},
@@ -292,6 +294,35 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
 				});
 			}
 		});
+
+		socket.on('api:config.redisTest', function() {
+			meta.testRedis(function(success) {
+				socket.emit('api:config.redisTest', {
+					status: success ? 'ok' : 'error'
+				});
+			});
+		});
+
+		socket.on('api:config.setup', function(data) {
+			async.parallel([
+				function(next) {
+					meta.config.set('redis/host', data['redis/host'], next);
+				},
+				function(next) {
+					meta.config.set('redis/port', data['redis/port'], next);
+				}
+			], function(err) {
+				meta.config.get(function(config) {
+					socket.emit('api:config.setup', config);
+				});
+			});
+		});
+
+		socket.on('api:config.set', function(data) {
+			meta.config.set(data.key, data.value, function(err) {
+				if (!err) socket.emit('api:config.set', { status: 'ok' });
+			});
+		});
 	});
 	
 }(SocketIO));

From 069cf5c6cbfd5ba135cdf5ab6f1a54628a65957f Mon Sep 17 00:00:00 2001
From: Julian Lam <julian.lam@gmail.com>
Date: Sat, 25 May 2013 20:42:27 -0400
Subject: [PATCH 3/4] minor tweak to 'start' button at end of install wizard

---
 public/templates/install/privileges.tpl | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/public/templates/install/privileges.tpl b/public/templates/install/privileges.tpl
index 466febc9c5..a35a1cfe7a 100644
--- a/public/templates/install/privileges.tpl
+++ b/public/templates/install/privileges.tpl
@@ -27,7 +27,7 @@
 
 <hr />
 <div class="pull-right">
-	<a href="/" class="btn btn-success btn-large"><i class="icon-thumbs-up"></i> Start using NodeBB!</a>
+	<button id="start-nodebb" class="btn btn-success btn-large"><i class="icon-thumbs-up"></i> Start using NodeBB!</button>
 </div>
 <div>
 	<button data-path="social" class="btn btn-primary btn-large">Previous &ndash; <i class="icon-facebook"></i> Social</button>
@@ -36,5 +36,11 @@
 <script>
 	(function() {
 		nodebb_setup.prepare();
+
+		var startEl = document.getElementById('start-nodebb');
+		startEl.addEventListener('click', function(e) {
+			e.preventDefault();
+			document.location.href = '/';
+		});
 	})();
 </script>
\ No newline at end of file

From 88bf9425db8d63b6b04e49390e9853e35fc8433e Mon Sep 17 00:00:00 2001
From: Julian Lam <julian.lam@gmail.com>
Date: Mon, 27 May 2013 14:02:57 -0400
Subject: [PATCH 4/4] install script + patching up integration with install
 wizard

---
 app.js                                  | 185 +++++++++++++++++-------
 public/templates/config.json            |   4 +-
 public/templates/install/footer.tpl     |  34 +++--
 public/templates/install/header.tpl     |   6 -
 public/templates/install/mail.tpl       |   5 +-
 public/templates/install/privileges.tpl |   2 +-
 public/templates/install/social.tpl     |  14 +-
 src/meta.js                             |   3 +-
 src/notifications.js                    |   3 +-
 src/postTools.js                        |   5 +-
 src/posts.js                            |   1 -
 src/redis.js                            |   4 +-
 src/routes/authentication.js            |  25 ++--
 src/routes/user.js                      |   3 -
 src/threadTools.js                      |   3 +-
 src/topics.js                           |   1 -
 src/user.js                             |   3 +-
 src/webserver.js                        |   5 +-
 src/websockets.js                       |  18 +--
 19 files changed, 188 insertions(+), 136 deletions(-)

diff --git a/app.js b/app.js
index 4005af1aa1..38bcdfd222 100644
--- a/app.js
+++ b/app.js
@@ -1,54 +1,131 @@
-var categories = require('./src/categories.js'),
-    templates = require('./public/src/templates.js'),
-    webserver = require('./src/webserver.js'),
-    websockets = require('./src/websockets.js'),
-    admin = {
-        'categories': require('./src/admin/categories.js')
-    },
-    fs = require('fs');
-
-DEVELOPMENT = true;
-
-global.configuration = {};
-global.templates = {};
-
-(function(config) {
-    config['ROOT_DIRECTORY'] = __dirname;
-
-    templates.init([
-        'header', 'footer', 'logout', 'admin/header', 'admin/footer', 'admin/index',
-        'emails/reset', 'emails/reset_plaintext', 'emails/email_confirm', 'emails/email_confirm_plaintext',
-        'emails/header', 'emails/footer', 'install/header', 'install/footer', 'install/redis'
-    ]);
-    
-    templates.ready(function() {
-        webserver.init();
-    });
-
-    //setup scripts to be moved outside of the app in future.
-    function setup_categories() {
-        console.log('Checking categories...');
-        categories.getAllCategories(function(data) {
-            if (data.categories.length === 0) {
-                console.log('Setting up default categories...');
-
-                fs.readFile(config.ROOT_DIRECTORY + '/install/data/categories.json', function(err, default_categories) {
-                    default_categories = JSON.parse(default_categories);
-                    
-                    for (var category in default_categories) {
-                        admin.categories.create(default_categories[category]);
-                    }
-                });
-
-            } else {
-                console.log('Good.');
-            }
-        });
-    }
-
-
-    setup_categories();
-
-    
-
-}(global.configuration));
\ No newline at end of file
+// Read config.js to grab redis info
+var fs = require('fs'),
+	path = require('path'),
+	utils = require('./public/src/utils.js');
+
+console.log('Info: Checking for valid base configuration file');
+fs.readFile(path.join(__dirname, 'config.json'), function(err, data) {
+	if (!err) {
+		global.config = JSON.parse(data);
+		global.config.url = global.config.base_url + (global.config.use_port ? ':' + global.config.port : '') + '/';
+		global.config.upload_url = global.config.url + 'uploads/';
+		console.log('Info: Base Configuration OK.');
+
+		var	meta = require('./src/meta.js');
+		meta.config.get(function(config) {
+			for(c in config) {
+				if (config.hasOwnProperty(c)) {
+					global.config[c] = config[c];
+				}
+			}
+
+			var categories = require('./src/categories.js'),
+				templates = require('./public/src/templates.js'),
+				webserver = require('./src/webserver.js'),
+				websockets = require('./src/websockets.js'),
+				admin = {
+					'categories': require('./src/admin/categories.js')
+				};
+
+			DEVELOPMENT = true;
+
+			global.configuration = {};
+			global.templates = {};
+
+			(function(config) {
+				config['ROOT_DIRECTORY'] = __dirname;
+
+				templates.init([
+					'header', 'footer', 'logout', 'admin/header', 'admin/footer', 'admin/index',
+					'emails/reset', 'emails/reset_plaintext', 'emails/email_confirm', 'emails/email_confirm_plaintext',
+					'emails/header', 'emails/footer', 'install/header', 'install/footer', 'install/redis'
+				]);
+
+				templates.ready(function() {
+					webserver.init();
+				});
+
+				//setup scripts to be moved outside of the app in future.
+				function setup_categories() {
+					console.log('Info: Checking categories...');
+					categories.getAllCategories(function(data) {
+						if (data.categories.length === 0) {
+							console.log('Info: Setting up default categories...');
+
+							fs.readFile(config.ROOT_DIRECTORY + '/install/data/categories.json', function(err, default_categories) {
+								default_categories = JSON.parse(default_categories);
+
+								for (var category in default_categories) {
+									admin.categories.create(default_categories[category]);
+								}
+							});
+
+						} else {
+							console.log('Info: Good.');
+						}
+					});
+				}
+				setup_categories();
+			}(global.configuration));
+		});
+	} else {
+		// New install, ask setup questions
+		console.log('Info: Configuration not found, starting NodeBB setup');
+		var	ask = function(question, callback) {
+				process.stdin.resume();
+				process.stdout.write(question + ': ');
+
+				process.stdin.once('data', function(data) {
+					callback(data.toString().trim());
+				});
+			}
+
+		process.stdout.write(
+			"\nWelcome to NodeBB!\nThis looks like a new installation, so you'll have to answer a " +
+			"few questions about your environment before we can proceed with the setup.\n\n\nWhat is...\n\n"
+			);
+
+		ask('... the publically accessible URL of this installation? (http://localhost)', function(base_url) {
+			ask('... the port number of your install? (4567)', function(port) {
+				ask('Will you be using a port number to access NodeBB? (y)', function(use_port) {
+					ask('... the host IP or address of your Redis instance? (127.0.0.1)', function(redis_host) {
+						ask('... the host port of your Redis instance? (6379)', function(redis_port) {
+							ask('... your NodeBB secret? (keyboard mash for a bit here)', function(secret) {
+								if (!base_url) base_url = 'http://localhost';
+								if (!port) port = 4567;
+								if (!use_port) use_port = true; else use_port = (use_port === 'y' ? true : false);
+								if (!redis_host) redis_host = '127.0.0.1';
+								if (!redis_port) redis_port = 6379;
+								if (!secret) secret = utils.generateUUID();
+
+								var	fs = require('fs'),
+									path = require('path'),
+									config = {
+										secret: secret,
+										base_url: base_url,
+										port: port,
+										use_port: use_port,
+										redis: {
+											host: redis_host,
+											port: redis_port
+										}
+									}
+
+								fs.writeFile(path.join(__dirname, 'config.json'), JSON.stringify(config, null, 4), function(err) {
+									if (err) throw err;
+									else {
+										process.stdout.write(
+											"\n\nConfiguration Saved OK\n\nPlease start NodeBB again and navigate to " +
+											base_url + (use_port ? ':' + port : '') + "/install to continue setup.\n\n"
+										);
+										process.exit();
+									}
+								});
+							});
+						});
+					});
+				});
+			});
+		});
+	}
+});
\ No newline at end of file
diff --git a/public/templates/config.json b/public/templates/config.json
index 5af89957fb..551606ba59 100644
--- a/public/templates/config.json
+++ b/public/templates/config.json
@@ -10,9 +10,7 @@
 		"admin/twitter[^]*": "admin/twitter",
 		"admin/facebook[^]*": "admin/facebook",
 		"admin/gplus[^]*": "admin/gplus",
-		"install/?$": "install/redis",
-		"install/basic/?": "install/basic",
-		"install/redis/?": "install/redis",
+		"install/?$": "install/mail",
 		"install/mail/?": "install/mail",
 		"install/social/?": "install/social",
 		"install/privileges/?": "install/privileges",
diff --git a/public/templates/install/footer.tpl b/public/templates/install/footer.tpl
index 9823d43371..e76153ecb1 100644
--- a/public/templates/install/footer.tpl
+++ b/public/templates/install/footer.tpl
@@ -5,17 +5,26 @@
 			config: undefined,
 			prepare: function() {
 				// Bounce if config is not ready
-				if (nodebb_setup.config === undefined) {
-					ajaxify.go('install/redis');
-					app.alert({
-						alert_id: 'config-ready',
-						type: 'error',
-						timeout: 10000,
-						title: 'NodeBB Configuration Not Ready!',
-						message:	'NodeBB cannot proceed with setup at this time as Redis database information ' +
-									'was not found. Please enter the information below.'
-					});
+				// if (nodebb_setup.config === undefined) {
+				// 	ajaxify.go('install/redis');
+				// 	app.alert({
+				// 		alert_id: 'config-ready',
+				// 		type: 'error',
+				// 		timeout: 10000,
+				// 		title: 'NodeBB Configuration Not Ready!',
+				// 		message:	'NodeBB cannot proceed with setup at this time as Redis database information ' +
+				// 					'was not found. Please enter the information below.'
+				// 	});
+
+				// 	return;
+				// }
 
+				// Come back in 500ms if the config isn't ready yet
+				if (nodebb_setup.config === undefined) {
+					console.log('Config not ready...');
+					setTimeout(function() {
+						nodebb_setup.prepare();
+					}, 500);
 					return;
 				}
 
@@ -106,6 +115,11 @@
 				}
 			}, false);
 
+			socket.emit('api:config.get');
+			socket.on('api:config.get', function(data) {
+				nodebb_setup.config = data;
+			});
+
 			socket.on('api:config.set', function(data) {
 				if (data.status === 'ok') {
 					app.alert({
diff --git a/public/templates/install/header.tpl b/public/templates/install/header.tpl
index d598b0bb06..d9f66909cb 100644
--- a/public/templates/install/header.tpl
+++ b/public/templates/install/header.tpl
@@ -33,12 +33,6 @@
 				</button>
 				<div class="nav-collapse collapse">
 					<ul class="nav nodebb-inline-block">
-						<li>
-							<a data-tab="redis" href="/install/redis"><i class="icon-hdd"></i> Redis</a>
-						</li>
-						<li>
-							<a data-tab="basic" href="/install/basic"><i class="icon-cog"></i> Basic</a>
-						</li>
 						<li>
 							<a data-tab="email" href="/install/email"><i class="icon-envelope"></i> Mail</a>
 						</li>
diff --git a/public/templates/install/mail.tpl b/public/templates/install/mail.tpl
index 42ba612a18..f8dcb261f2 100644
--- a/public/templates/install/mail.tpl
+++ b/public/templates/install/mail.tpl
@@ -1,5 +1,5 @@
 
-<h1>Step 3 &ndash; Mailer Information</h1>
+<h1>Mailer Information</h1>
 
 <form class="form-inline">
 	<p>
@@ -25,9 +25,6 @@
 <div class="pull-right">
 	<button data-path="social" class="btn btn-primary btn-large">Next &ndash; <i class="icon-facebook"></i> Social</button>
 </div>
-<div>
-	<button data-path="basic" class="btn btn-primary btn-large">Previous &ndash; <i class="icon-cog"></i> Basic</button>
-</div>
 
 <script>
 	(function() {
diff --git a/public/templates/install/privileges.tpl b/public/templates/install/privileges.tpl
index a35a1cfe7a..5a3659e5e1 100644
--- a/public/templates/install/privileges.tpl
+++ b/public/templates/install/privileges.tpl
@@ -1,5 +1,5 @@
 
-<h1>Step 5 &ndash; User Privilege Thresholds</h1>
+<h1>User Privilege Thresholds</h1>
 
 <form class="form-inline">
 	<p>
diff --git a/public/templates/install/social.tpl b/public/templates/install/social.tpl
index 046021dbf5..16654f8f93 100644
--- a/public/templates/install/social.tpl
+++ b/public/templates/install/social.tpl
@@ -1,5 +1,5 @@
 
-<h1>Step 4 &ndash; Social Media Logins (Optional)</h1>
+<h1>Social Media Logins</h1>
 
 <form class="form-inline">
 	<p>
@@ -9,26 +9,26 @@
 
 	<h3>Facebook</h3>
 	<p>
-		<label>Application ID</label> <input type="text" class="input-medium" data-field="social/facebook/app_id" />
+		<label>Application ID</label> <input type="text" class="input-medium" data-field="social:facebook:app_id" />
 	</p>
 	<p>
-		<label>Application Secret</label> <input type="text" class="input-large" data-field="social/facebook/secret" />
+		<label>Application Secret</label> <input type="text" class="input-large" data-field="social:facebook:secret" />
 	</p>
 
 	<h3>Twitter</h3>
 	<p>
-		<label>Application Key</label> <input type="text" class="input-medium" data-field="social/twitter/key" />
+		<label>Application Key</label> <input type="text" class="input-medium" data-field="social:twitter:key" />
 	</p>
 	<p>
-		<label>Application Secret</label> <input type="text" class="input-large" data-field="social/twitter/secret" />
+		<label>Application Secret</label> <input type="text" class="input-large" data-field="social:twitter:secret" />
 	</p>
 
 	<h3>Google</h3>
 	<p>
-		<label>Application Key</label> <input type="text" class="input-xxlarge" data-field="social/google/key" />
+		<label>Application ID</label> <input type="text" class="input-xxlarge" data-field="social:google:id" />
 	</p>
 	<p>
-		<label>Application Secret</label> <input type="text" class="input-large" data-field="social/google/secret" />
+		<label>Application Secret</label> <input type="text" class="input-large" data-field="social:google:secret" />
 	</p>
 </form>
 
diff --git a/src/meta.js b/src/meta.js
index f4fd57edc2..44d31f2abc 100644
--- a/src/meta.js
+++ b/src/meta.js
@@ -1,5 +1,4 @@
-var config = require('../config.js'),
-	utils = require('./../public/src/utils.js'),
+var utils = require('./../public/src/utils.js'),
 	RDB = require('./redis.js'),
 	async = require('async');
 
diff --git a/src/notifications.js b/src/notifications.js
index 8ea7dc9f24..3e63628068 100644
--- a/src/notifications.js
+++ b/src/notifications.js
@@ -1,5 +1,4 @@
-var	config = require('../config.js'),
-	RDB = require('./redis.js'),
+var	RDB = require('./redis.js'),
 	async = require('async'),
 	utils = require('../public/src/utils.js');
 
diff --git a/src/postTools.js b/src/postTools.js
index 682ae1d51d..74cce37873 100644
--- a/src/postTools.js
+++ b/src/postTools.js
@@ -2,7 +2,6 @@ var	RDB = require('./redis.js'),
 	posts = require('./posts.js'),
 	threadTools = require('./threadTools.js'),
 	user = require('./user.js'),
-	config = require('../config.js'),
 	async = require('async'),
 	marked = require('marked');
 
@@ -35,8 +34,8 @@ marked.setOptions({
 			// DRY fail in threadTools.
 
 			user.getUserField(uid, 'reputation', function(reputation) {
-					next(null, reputation >= config.privilege_thresholds.manage_content);
-				});
+				next(null, reputation >= global.config['privileges:manage_content']);
+			});
 		}
 
 		async.parallel([getThreadPrivileges, isOwnPost, hasEnoughRep], function(err, results) {
diff --git a/src/posts.js b/src/posts.js
index 58bdce5d36..3d737c70d2 100644
--- a/src/posts.js
+++ b/src/posts.js
@@ -4,7 +4,6 @@ var	RDB = require('./redis.js'),
 	user = require('./user.js'),
 	topics = require('./topics.js'),
 	favourites = require('./favourites.js'),
-	config = require('../config.js'),
 	threadTools = require('./threadTools.js'),
 	async = require('async');
 
diff --git a/src/redis.js b/src/redis.js
index 935e8afe06..5ac950f057 100644
--- a/src/redis.js
+++ b/src/redis.js
@@ -3,11 +3,9 @@
 		ERROR_LOGS = true,
 
 		redis = require('redis'),
-		config = require('../config.js'),
 		utils = require('./../public/src/utils.js');
 	
-
-	RedisDB.exports = redis.createClient(config.redis.port, config.redis.host, config.redis.options);
+	RedisDB.exports = redis.createClient(global.config.redis.port, global.config.redis.host);
 
 	RedisDB.exports.handle = function(error) {
 		if (error !== null) {
diff --git a/src/routes/authentication.js b/src/routes/authentication.js
index e9c17d29ea..3777e2f478 100644
--- a/src/routes/authentication.js
+++ b/src/routes/authentication.js
@@ -1,5 +1,4 @@
 (function(Auth) {
-
 	var passport = require('passport'),
 		passportLocal = require('passport-local').Strategy,
 		passportTwitter = require('passport-twitter').Strategy,
@@ -7,11 +6,7 @@
 		passportFacebook = require('passport-facebook').Strategy,
 		login_strategies = [],
 
-		user_module = require('./../user.js'),
-		config = require('./../../config.js');
-
-
-
+		user_module = require('./../user.js');
 
 	passport.use(new passportLocal(function(user, password, next) {
 		user_module.loginViaLocal(user, password, function(login) {
@@ -20,10 +15,10 @@
 		});
 	}));
 
-	if (config.twitter && config.twitter.key && config.twitter.key.length > 0 && config.twitter.secret.length > 0) {
+	if (global.config['social:twitter:key'] && global.config['social:twitter:secret']) {
 		passport.use(new passportTwitter({
-			consumerKey: config.twitter.key,
-			consumerSecret: config.twitter.secret,
+			consumerKey: global.config['social:twitter:key'],
+			consumerSecret: global.config['social:twitter:secret'],
 			callbackURL: config.url + 'auth/twitter/callback'
 		}, function(token, tokenSecret, profile, done) {
 			user_module.loginViaTwitter(profile.id, profile.username, function(err, user) {
@@ -35,10 +30,10 @@
 		login_strategies.push('twitter');
 	}
 
-	if (config.google && config.google.id.length > 0 && config.google.secret.length > 0) {
+	if (global.config['social:google:id'] && global.config['social:google:secret']) {
 		passport.use(new passportGoogle({
-			clientID: config.google.id,
-			clientSecret: config.google.secret,
+			clientID: global.config['social:google:id'],
+			clientSecret: global.config['social:google:secret'],
 			callbackURL: config.url + 'auth/google/callback'
 		}, function(accessToken, refreshToken, profile, done) {
 			user_module.loginViaGoogle(profile.id, profile.displayName, profile.emails[0].value, function(err, user) {
@@ -50,10 +45,10 @@
 		login_strategies.push('google');
 	}
 
-	if (config.facebook && config.facebook.app_id.length > 0 && config.facebook.secret.length > 0) {
+	if (global.config['social:facebook:app_id'] && global.config['social:facebook:secret']) {
 		passport.use(new passportFacebook({
-			clientID: config.facebook.app_id,
-			clientSecret: config.facebook.secret,
+			clientID: global.config['social:facebook:app_id'],
+			clientSecret: global.config['social:facebook:secret'],
 			callbackURL: config.url + 'auth/facebook/callback'
 		}, function(accessToken, refreshToken, profile, done) {
 			user_module.loginViaFacebook(profile.id, profile.displayName, profile.emails[0].value, function(err, user) {
diff --git a/src/routes/user.js b/src/routes/user.js
index 213f44275b..6ec3a91103 100644
--- a/src/routes/user.js
+++ b/src/routes/user.js
@@ -1,12 +1,9 @@
 
-
 var user = require('./../user.js'),
 	fs = require('fs'),
 	utils = require('./../../public/src/utils.js'),
-	config = require('../../config.js'),
 	marked = require('marked');
 
-
 (function(User) {
 	User.create_routes = function(app) {
 
diff --git a/src/threadTools.js b/src/threadTools.js
index 82acdca15c..391aba631b 100644
--- a/src/threadTools.js
+++ b/src/threadTools.js
@@ -2,7 +2,6 @@ var	RDB = require('./redis.js'),
 	topics = require('./topics.js'),
 	categories = require('./categories.js'),
 	user = require('./user.js'),
-	config = require('../config.js'),
 	async = require('async');
 
 
@@ -23,7 +22,7 @@ var	RDB = require('./redis.js'),
 			// DRY fail in postTools
 
 			user.getUserField(uid, 'reputation', function(reputation) {
-				next(null, reputation >= config.privilege_thresholds.manage_thread);
+				next(null, reputation >= global.config['privileges:manage_topic']);
 			});
 		}
 		
diff --git a/src/topics.js b/src/topics.js
index 82f353cc3e..82e14d0bf8 100644
--- a/src/topics.js
+++ b/src/topics.js
@@ -3,7 +3,6 @@ var	RDB = require('./redis.js')
 	posts = require('./posts.js'),
 	utils = require('./../public/src/utils.js'),
 	user = require('./user.js'),
-	config = require('../config.js'),
 	categories = require('./categories.js'),
 	posts = require('./posts.js'),
 	marked = require('marked'),
diff --git a/src/user.js b/src/user.js
index aa59326810..008e93838c 100644
--- a/src/user.js
+++ b/src/user.js
@@ -1,5 +1,4 @@
-var config = require('../config.js'),
-	utils = require('./../public/src/utils.js'),
+var utils = require('./../public/src/utils.js'),
 	RDB = require('./redis.js'),
 	crypto = require('crypto'),
 	emailjs = require('emailjs'),
diff --git a/src/webserver.js b/src/webserver.js
index 831da86b20..4f3a25f606 100644
--- a/src/webserver.js
+++ b/src/webserver.js
@@ -3,9 +3,8 @@ var express = require('express'),
 	server = require('http').createServer(WebServer),
 	RedisStore = require('connect-redis')(express),
 	path = require('path'),
-    config = require('../config.js'),
     redis = require('redis'),
-	redisServer = redis.createClient(config.redis.port, config.redis.host, config.redis.options),
+	redisServer = redis.createClient(global.config.redis.port, global.config.redis.host),
 	marked = require('marked'),
 	utils = require('../public/src/utils.js'),
 	fs = require('fs'),
@@ -36,7 +35,7 @@ var express = require('express'),
 			client: redisServer,
 			ttl: 60*60*24*14
 		}),
-		secret: config.secret,
+		secret: global.config.secret,
 		key: 'express.sid'
 	}));
 
diff --git a/src/websockets.js b/src/websockets.js
index a94566e10d..05bdfb0a2d 100644
--- a/src/websockets.js
+++ b/src/websockets.js
@@ -2,7 +2,6 @@
 var SocketIO = require('socket.io').listen(global.server, { log:false }),
 	cookie = require('cookie'),
 	connect = require('connect'),
-	config = require('../config.js'),
 	user = require('./user.js'),
 	posts = require('./posts.js'),
 	favourites = require('./favourites.js'),
@@ -26,7 +25,7 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
 	io.set('authorization', function(handshakeData, accept) {
 		if (handshakeData.headers.cookie) {
 			handshakeData.cookie = cookie.parse(handshakeData.headers.cookie);
-			handshakeData.sessionID = connect.utils.parseSignedCookie(handshakeData.cookie['express.sid'], config.secret);
+			handshakeData.sessionID = connect.utils.parseSignedCookie(handshakeData.cookie['express.sid'], global.config.secret);
 
 			if (handshakeData.cookie['express.sid'] == handshakeData.sessionID) {
 				return accept('Cookie is invalid.', false);
@@ -302,18 +301,9 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
 			});
 		});
 
-		socket.on('api:config.setup', function(data) {
-			async.parallel([
-				function(next) {
-					meta.config.set('redis/host', data['redis/host'], next);
-				},
-				function(next) {
-					meta.config.set('redis/port', data['redis/port'], next);
-				}
-			], function(err) {
-				meta.config.get(function(config) {
-					socket.emit('api:config.setup', config);
-				});
+		socket.on('api:config.get', function(data) {
+			meta.config.get(function(config) {
+				socket.emit('api:config.get', config);
 			});
 		});