From d770963b69bdaaf8936105c55c09a818ef47d683 Mon Sep 17 00:00:00 2001 From: Baris Usakli Date: Mon, 11 Nov 2013 12:18:20 -0500 Subject: [PATCH 001/142] closes #490 --- src/install.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/install.js b/src/install.js index 437168061f..be2891eb6b 100644 --- a/src/install.js +++ b/src/install.js @@ -135,6 +135,9 @@ var async = require('async'), winston.info('Populating database with default configs, if not already set...'); var meta = require('./meta'), defaults = [{ + field: 'title', + value: 'NodeBB' + }, { field: 'postDelay', value: 10000 }, { From 4ce6ac5af969324689050944f46123c4d40c6b71 Mon Sep 17 00:00:00 2001 From: Baris Usakli Date: Mon, 11 Nov 2013 12:40:15 -0500 Subject: [PATCH 002/142] closes #491, added check to prevent crash in getAnonUserCount --- src/posts.js | 2 +- src/topics.js | 2 +- src/upgrade.js | 13 +++++++++++++ src/websockets.js | 4 ++-- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/posts.js b/src/posts.js index 30bd219d3b..07cf4f8a93 100644 --- a/src/posts.js +++ b/src/posts.js @@ -265,7 +265,7 @@ var RDB = require('./redis.js'), Posts.emitTooManyPostsAlert = function(socket) { socket.emit('event:alert', { title: 'Too many posts!', - message: 'You can only post every ' + meta.config.postDelay / 1000 + ' seconds.', + message: 'You can only post every ' + meta.config.postDelay + ' seconds.', type: 'danger', timeout: 2000 }); diff --git a/src/topics.js b/src/topics.js index 7e21f07d78..cc383493a9 100644 --- a/src/topics.js +++ b/src/topics.js @@ -685,7 +685,7 @@ var RDB = require('./redis.js'), user.getUserField(uid, 'lastposttime', function(err, lastposttime) { if (err) lastposttime = 0; - if (Date.now() - lastposttime < meta.config.postDelay) { + if (Date.now() - lastposttime < meta.config.postDelay * 1000) { callback(new Error('too-many-posts'), null); return; } diff --git a/src/upgrade.js b/src/upgrade.js index 18a6b3c326..f5a4789b5b 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -81,6 +81,19 @@ Upgrade.upgrade = function() { next(); } }); + }, + function(next) { + RDB.hget('config', 'postDelay', function(err, postDelay) { + if(parseInt(postDelay, 10) > 10) { + RDB.hset('config', 'postDelay', 10, function(err, success) { + winston.info('[2013/11/11] Updated postDelay to 10 seconds.'); + next(); + }); + } else { + winston.info('[2013/11/11] Update to postDelay skipped.'); + next(); + } + }); } // Add new schema updates here ], function(err) { diff --git a/src/websockets.js b/src/websockets.js index aa650d4173..cb01f26d0e 100644 --- a/src/websockets.js +++ b/src/websockets.js @@ -157,7 +157,7 @@ module.exports.init = function(io) { for (var i = 0; i < clients.length; ++i) { var hs = clients[i].handshake; - if (hs && clients[i].state.user.uid === 0) { + if (hs && clients[i].state && clients[i].state.user.uid === 0) { ++anonCount; } } @@ -407,7 +407,7 @@ module.exports.init = function(io) { return; } - if (Date.now() - lastPostTime < meta.config.postDelay) { + if (Date.now() - lastPostTime < meta.config.postDelay * 1000) { posts.emitTooManyPostsAlert(socket); return; } From 5647d55147a198c8b3bc051dd6d5fcc38c650103 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 11 Nov 2013 12:41:33 -0500 Subject: [PATCH 003/142] updated postDelay upgrade script to update if the old value is over 1000, not 10! --- src/upgrade.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/upgrade.js b/src/upgrade.js index f5a4789b5b..75c05b7257 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -84,7 +84,7 @@ Upgrade.upgrade = function() { }, function(next) { RDB.hget('config', 'postDelay', function(err, postDelay) { - if(parseInt(postDelay, 10) > 10) { + if(parseInt(postDelay, 10) > 1000) { RDB.hset('config', 'postDelay', 10, function(err, success) { winston.info('[2013/11/11] Updated postDelay to 10 seconds.'); next(); From f81c583d860fc4b67a0bfb5206a58ed217dfe57a Mon Sep 17 00:00:00 2001 From: Baris Usakli Date: Mon, 11 Nov 2013 12:44:42 -0500 Subject: [PATCH 004/142] changed postdelay default --- src/install.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/install.js b/src/install.js index be2891eb6b..5795a4d605 100644 --- a/src/install.js +++ b/src/install.js @@ -139,7 +139,7 @@ var async = require('async'), value: 'NodeBB' }, { field: 'postDelay', - value: 10000 + value: 10 }, { field: 'minimumPostLength', value: 8 From 4e39c5014416766128adb6706be0175dddff166a Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 11 Nov 2013 13:16:24 -0500 Subject: [PATCH 005/142] fine, 150ms. --- src/upgrade.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/upgrade.js b/src/upgrade.js index 75c05b7257..b4731bcad0 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -84,7 +84,7 @@ Upgrade.upgrade = function() { }, function(next) { RDB.hget('config', 'postDelay', function(err, postDelay) { - if(parseInt(postDelay, 10) > 1000) { + if(parseInt(postDelay, 10) > 150) { RDB.hset('config', 'postDelay', 10, function(err, success) { winston.info('[2013/11/11] Updated postDelay to 10 seconds.'); next(); From 9bea23bbfe98dbed2a4a94a422f4de1b248f1e64 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 11 Nov 2013 13:25:54 -0500 Subject: [PATCH 006/142] linting webserver.js --- src/upgrade.js | 7 ++- src/webserver.js | 155 +++++++++++++++++++++++++++++++---------------- 2 files changed, 108 insertions(+), 54 deletions(-) diff --git a/src/upgrade.js b/src/upgrade.js index b4731bcad0..f4040a5886 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -67,8 +67,11 @@ Upgrade.upgrade = function() { var multi = RDB.multi(); keys = keys.filter(function(key) { - if (key === 'notifications:next_nid') return false; - else return true; + if (key === 'notifications:next_nid') { + return false; + } else { + return true; + } }).map(function(key) { return key.slice(14); }); diff --git a/src/webserver.js b/src/webserver.js index eebbf5708c..aae015447d 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -28,15 +28,19 @@ var express = require('express'), logger = require('./logger.js'); (function (app) { + "use strict"; + var templates = null, clientScripts; // Minify client-side libraries meta.js.get(function (err, scripts) { clientScripts = scripts.map(function (script) { - return script = { + script = { script: script - } + }; + + return script; }); }); @@ -66,13 +70,13 @@ var express = require('express'), content: meta.config.title || 'NodeBB' }, { property: 'keywords', - content: meta.config['keywords'] || '' + content: meta.config.keywords || '' }], metaString = utils.buildMetaTags(defaultMetaTags.concat(options.metaTags || [])), linkTags = utils.buildLinkTags(options.linkTags || []), templateValues = { cssSrc: meta.config['theme:src'] || nconf.get('relative_path') + '/vendor/bootstrap/css/bootstrap.min.css', - pluginCSS: plugins.cssFiles.map(function(file) { return { path: file } }), + pluginCSS: plugins.cssFiles.map(function(file) { return { path: file }; }), title: meta.config.title || '', description: meta.config.description || '', 'brand:logo': meta.config['brand:logo'] || '', @@ -88,8 +92,9 @@ var express = require('express'), var uid = '0'; - if(options.req.user && options.req.user.uid) + if(options.req.user && options.req.user.uid) { uid = options.req.user.uid; + } user.isAdministrator(uid, function(isAdmin) { templateValues.adminDisplay = isAdmin ? 'show' : 'hide'; @@ -97,7 +102,7 @@ var express = require('express'), translator.translate(templates.header.parse(templateValues), function(template) { callback(null, template); }); - }) + }); }); @@ -249,11 +254,17 @@ var express = require('express'), res.json(200, {}); } else if (req.accepts('html')) { // respond with html page - if (process.env.NODE_ENV === 'development') winston.warn('Route requested but not found: ' + req.url); + if (process.env.NODE_ENV === 'development') { + winston.warn('Route requested but not found: ' + req.url); + } + res.redirect(nconf.get('relative_path') + '/404'); } else if (req.accepts('json')) { // respond with json - if (process.env.NODE_ENV === 'development') winston.warn('Route requested but not found: ' + req.url); + if (process.env.NODE_ENV === 'development') { + winston.warn('Route requested but not found: ' + req.url); + } + res.json({ error: 'Not found' }); @@ -284,7 +295,9 @@ var express = require('express'), winston.error('Errors were encountered while attempting to initialise NodeBB.'); process.exit(); } else { - if (process.env.NODE_ENV === 'development') winston.info('Middlewares loaded.'); + if (process.env.NODE_ENV === 'development') { + winston.info('Middlewares loaded.'); + } } }); }); @@ -293,16 +306,16 @@ var express = require('express'), templates = global.templates; // translate all static templates served by webserver here. ex. footer, logout - translator.translate(templates['footer'].toString(), function(parsedTemplate) { - templates['footer'] = parsedTemplate; + translator.translate(templates.footer.toString(), function(parsedTemplate) { + templates.footer = parsedTemplate; }); - translator.translate(templates['logout'].toString(), function(parsedTemplate) { - templates['logout'] = parsedTemplate; + translator.translate(templates.logout.toString(), function(parsedTemplate) { + templates.logout = parsedTemplate; }); winston.info('NodeBB Ready'); server.listen(nconf.get('PORT') || nconf.get('port'), nconf.get('bind_address')); - } + }; app.create_route = function (url, tpl) { // to remove return ''; @@ -336,7 +349,7 @@ var express = require('express'), req: req, res: res }, function (err, header) { - res.send((isNaN(parseInt(route, 10)) ? 200 : parseInt(route, 10)), header + app.create_route(route) + templates['footer']); + res.send((isNaN(parseInt(route, 10)) ? 200 : parseInt(route, 10)), header + app.create_route(route) + templates.footer); }); }); }(routes[i])); @@ -368,8 +381,11 @@ var express = require('express'), "categories": function (next) { categories.getAllCategories(function (returnData) { returnData.categories = returnData.categories.filter(function (category) { - if (category.disabled !== '1') return true; - else return false; + if (category.disabled !== '1') { + return true; + } else { + return false; + } }); next(null, returnData); @@ -380,12 +396,11 @@ var express = require('express'), data.header + '\n\t' + app.create_route('') + - templates['footer'] + templates.footer ); - }) + }); }); - app.get('/topic/:topic_id/:slug?', function (req, res) { var tid = req.params.topic_id; @@ -394,18 +409,26 @@ var express = require('express'), var rssPath = path.join(__dirname, '../', 'feeds/topics', tid + '.rss'), loadFeed = function () { fs.readFile(rssPath, function (err, data) { - if (err) res.type('text').send(404, "Unable to locate an rss feed at this location."); - else res.type('xml').set('Content-Length', data.length).send(data); + if (err) { + res.type('text').send(404, "Unable to locate an rss feed at this location."); + } else { + res.type('xml').set('Content-Length', data.length).send(data); + } }); }; if (!fs.existsSync(rssPath)) { feed.updateTopic(tid, function (err) { - if (err) res.redirect('/404'); - else loadFeed(); + if (err) { + res.redirect('/404'); + } else { + loadFeed(); + } }); - } else loadFeed(); + } else { + loadFeed(); + } return; } @@ -414,8 +437,9 @@ var express = require('express'), function (next) { topics.getTopicWithPosts(tid, ((req.user) ? req.user.uid : 0), 0, -1, function (err, topicData) { if (topicData) { - if (topicData.deleted === '1' && topicData.expose_tools === 0) + if (topicData.deleted === '1' && topicData.expose_tools === 0) { return next(new Error('Topic deleted'), null); + } } next(err, topicData); @@ -428,7 +452,9 @@ var express = require('express'), for (var x = 0, numPosts = topicData.posts.length; x < numPosts; x++) { timestamp = parseInt(topicData.posts[x].timestamp, 10); - if (timestamp > lastMod) lastMod = timestamp; + if (timestamp > lastMod) { + lastMod = timestamp; + } } app.build_header({ @@ -481,14 +507,17 @@ var express = require('express'), }); }, ], function (err, data) { - if (err) return res.redirect('404'); + if (err) { + return res.redirect('404'); + } + var topic_url = tid + (req.params.slug ? '/' + req.params.slug : ''); res.send( data.header + '\n\t' + '\n\t' + - templates['footer'] + templates.footer ); }); }); @@ -501,18 +530,26 @@ var express = require('express'), var rssPath = path.join(__dirname, '../', 'feeds/categories', cid + '.rss'), loadFeed = function () { fs.readFile(rssPath, function (err, data) { - if (err) res.type('text').send(404, "Unable to locate an rss feed at this location."); - else res.type('xml').set('Content-Length', data.length).send(data); + if (err) { + res.type('text').send(404, "Unable to locate an rss feed at this location."); + } else { + res.type('xml').set('Content-Length', data.length).send(data); + } }); }; if (!fs.existsSync(rssPath)) { feed.updateCategory(cid, function (err) { - if (err) res.redirect('/404'); - else loadFeed(); + if (err) { + res.redirect('/404'); + } else { + loadFeed(); + } }); - } else loadFeed(); + } else { + loadFeed(); + } return; } @@ -522,9 +559,11 @@ var express = require('express'), categories.getCategoryById(cid, 0, function (err, categoryData) { if (categoryData) { - if (categoryData.disabled === '1') + if (categoryData.disabled === '1') { return next(new Error('Category disabled'), null); + } } + next(err, categoryData); }); }, @@ -561,14 +600,17 @@ var express = require('express'), }); } ], function (err, data) { - if (err) return res.redirect('404'); + if (err) { + return res.redirect('404'); + } + var category_url = cid + (req.params.slug ? '/' + req.params.slug : ''); res.send( data.header + '\n\t' + '\n\t' + - templates['footer'] + templates.footer ); }); }); @@ -578,7 +620,7 @@ var express = require('express'), req: req, res: res }, function (err, header) { - res.send(header + '' + templates['footer']); + res.send(header + '' + templates.footer); }); }); @@ -599,19 +641,21 @@ var express = require('express'), app.get('/cid/:cid', function (req, res) { categories.getCategoryData(req.params.cid, function (err, data) { - if (data) + if (data) { res.send(data); - else + } else { res.send(404, "Category doesn't exist!"); + } }); }); app.get('/tid/:tid', function (req, res) { topics.getTopicData(req.params.tid, function (data) { - if (data) + if (data) { res.send(data); - else + } else { res.send(404, "Topic doesn't exist!"); + } }); }); @@ -621,22 +665,25 @@ var express = require('express'), req: req, res: res }, function (err, header) { - res.send(header + app.create_route("recent/" + req.params.term, null, "recent") + templates['footer']); + res.send(header + app.create_route("recent/" + req.params.term, null, "recent") + templates.footer); }); }); app.get('/pid/:pid', function (req, res) { posts.getPostData(req.params.pid, function (data) { - if (data) + if (data) { res.send(data); - else + } else { res.send(404, "Post doesn't exist!"); + } }); }); app.get('/outgoing', function (req, res) { - if (!req.query.url) return res.redirect('/404'); + if (!req.query.url) { + return res.redirect('/404'); + } app.build_header({ req: req, @@ -645,30 +692,34 @@ var express = require('express'), res.send( header + '\n\t' + - templates['footer'] + templates.footer ); }); }); app.get('/search', function (req, res) { - if (!req.user) + if (!req.user) { return res.redirect('/403'); + } + app.build_header({ req: req, res: res }, function (err, header) { - res.send(header + app.create_route("search", null, "search") + templates['footer']); + res.send(header + app.create_route("search", null, "search") + templates.footer); }); }); app.get('/search/:term', function (req, res) { - if (!req.user) + if (!req.user) { return res.redirect('/403'); + } + app.build_header({ req: req, res: res }, function (err, header) { - res.send(header + app.create_route("search/" + req.params.term, null, "search") + templates['footer']); + res.send(header + app.create_route("search/" + req.params.term, null, "search") + templates.footer); }); }); @@ -713,7 +764,7 @@ var express = require('express'), req: options.req, res: options.res }, function (err, header) { - res.send(header + options.content + templates['footer']); + res.send(header + options.content + templates.footer); }); }); }); From 37497fc5a06481df5420210236fb7a3f8bd20345 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 11 Nov 2013 14:06:26 -0500 Subject: [PATCH 007/142] fixed #393 - refactored basic route handling, moved some other routes to debug routes --- public/src/app.js | 4 +-- src/routes/debug.js | 30 +++++++++++++++++ src/webserver.js | 80 ++++++++++++++------------------------------- 3 files changed, 57 insertions(+), 57 deletions(-) diff --git a/public/src/app.js b/public/src/app.js index 026c66496c..6728b2a805 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -279,8 +279,8 @@ var socket, app.process_page = function () { app.populate_online_users(); - var url = window.location.href, - parts = url.split('/'), + var path = window.location.pathname, + parts = path.split('/'), active = parts[parts.length - 1]; jQuery('#main-nav li').removeClass('active'); diff --git a/src/routes/debug.js b/src/routes/debug.js index 807b85eb78..2d19bc11ea 100644 --- a/src/routes/debug.js +++ b/src/routes/debug.js @@ -1,5 +1,35 @@ var DebugRoute = function(app) { app.namespace('/debug', function() { + app.get('/cid/:cid', function (req, res) { + categories.getCategoryData(req.params.cid, function (err, data) { + if (data) { + res.send(data); + } else { + res.send(404, "Category doesn't exist!"); + } + }); + }); + + app.get('/tid/:tid', function (req, res) { + topics.getTopicData(req.params.tid, function (data) { + if (data) { + res.send(data); + } else { + res.send(404, "Topic doesn't exist!"); + } + }); + }); + + app.get('/pid/:pid', function (req, res) { + posts.getPostData(req.params.pid, function (data) { + if (data) { + res.send(data); + } else { + res.send(404, "Post doesn't exist!"); + } + }); + }); + app.get('/prune', function(req, res) { var Notifications = require('../notifications'); diff --git a/src/webserver.js b/src/webserver.js index aae015447d..6b94186235 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -318,7 +318,7 @@ var express = require('express'), }; app.create_route = function (url, tpl) { // to remove - return ''; + return ''; }; app.namespace(nconf.get('relative_path'), function () { @@ -331,29 +331,29 @@ var express = require('express'), // Basic Routes (entirely client-side parsed, goal is to move the rest of the crap in this file into this one section) (function () { - var routes = ['login', 'register', 'account', 'recent', 'unread', 'notifications', '403', '404']; + var routes = ['login', 'register', 'account', 'recent', '403', '404'], + loginRequired = ['unread', 'search', 'notifications']; - for (var i = 0, ii = routes.length; i < ii; i++) { - (function (route) { + async.each(routes.concat(loginRequired), function(route, next) { + app.get('/' + route, function (req, res) { + if ((route === 'login' || route === 'register') && (req.user && req.user.uid > 0)) { - app.get('/' + route, function (req, res) { - if ((route === 'login' || route === 'register') && (req.user && req.user.uid > 0)) { - - user.getUserField(req.user.uid, 'userslug', function (err, userslug) { - res.redirect('/user/' + userslug); - }); - return; - } - - app.build_header({ - req: req, - res: res - }, function (err, header) { - res.send((isNaN(parseInt(route, 10)) ? 200 : parseInt(route, 10)), header + app.create_route(route) + templates.footer); + user.getUserField(req.user.uid, 'userslug', function (err, userslug) { + res.redirect('/user/' + userslug); }); + return; + } else if (loginRequired.indexOf(route) !== -1 && !req.user) { + return res.redirect('/403'); + } + + app.build_header({ + req: req, + res: res + }, function (err, header) { + res.send((isNaN(parseInt(route, 10)) ? 200 : parseInt(route, 10)), header + app.create_route(route) + templates.footer); }); - }(routes[i])); - } + }); + }); }()); @@ -516,7 +516,7 @@ var express = require('express'), res.send( data.header + '\n\t' + - '\n\t' + + '\n\t' + templates.footer ); }); @@ -609,7 +609,7 @@ var express = require('express'), res.send( data.header + '\n\t' + - '\n\t' + + '\n\t' + templates.footer ); }); @@ -620,7 +620,7 @@ var express = require('express'), req: req, res: res }, function (err, header) { - res.send(header + '' + templates.footer); + res.send(header + '' + templates.footer); }); }); @@ -639,26 +639,6 @@ var express = require('express'), "Sitemap: " + nconf.get('url') + "sitemap.xml"); }); - app.get('/cid/:cid', function (req, res) { - categories.getCategoryData(req.params.cid, function (err, data) { - if (data) { - res.send(data); - } else { - res.send(404, "Category doesn't exist!"); - } - }); - }); - - app.get('/tid/:tid', function (req, res) { - topics.getTopicData(req.params.tid, function (data) { - if (data) { - res.send(data); - } else { - res.send(404, "Topic doesn't exist!"); - } - }); - }); - app.get('/recent/:term?', function (req, res) { // TODO consolidate with /recent route as well -> that can be combined into this area. See "Basic Routes" near top. app.build_header({ @@ -670,16 +650,6 @@ var express = require('express'), }); - app.get('/pid/:pid', function (req, res) { - posts.getPostData(req.params.pid, function (data) { - if (data) { - res.send(data); - } else { - res.send(404, "Post doesn't exist!"); - } - }); - }); - app.get('/outgoing', function (req, res) { if (!req.query.url) { return res.redirect('/404'); @@ -697,7 +667,7 @@ var express = require('express'), }); }); - app.get('/search', function (req, res) { +/* app.get('/search', function (req, res) { if (!req.user) { return res.redirect('/403'); } @@ -708,7 +678,7 @@ var express = require('express'), }, function (err, header) { res.send(header + app.create_route("search", null, "search") + templates.footer); }); - }); + });*/ app.get('/search/:term', function (req, res) { if (!req.user) { From a42b30fd402ae993473120d2efcf729e65a1ad2b Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 11 Nov 2013 14:15:27 -0500 Subject: [PATCH 008/142] removing commented out route --- src/webserver.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/webserver.js b/src/webserver.js index 6b94186235..fa334fb756 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -667,19 +667,6 @@ var express = require('express'), }); }); -/* app.get('/search', function (req, res) { - if (!req.user) { - return res.redirect('/403'); - } - - app.build_header({ - req: req, - res: res - }, function (err, header) { - res.send(header + app.create_route("search", null, "search") + templates.footer); - }); - });*/ - app.get('/search/:term', function (req, res) { if (!req.user) { return res.redirect('/403'); From 40108f92c918181a25bc5dfd37d87f255531fcab Mon Sep 17 00:00:00 2001 From: Baris Usakli Date: Mon, 11 Nov 2013 14:28:08 -0500 Subject: [PATCH 009/142] closes #492 --- public/templates/admin/users.tpl | 4 ++-- public/templates/users.tpl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/templates/admin/users.tpl b/public/templates/admin/users.tpl index 0a3bde904e..043be234b7 100644 --- a/public/templates/admin/users.tpl +++ b/public/templates/admin/users.tpl @@ -24,12 +24,12 @@ {users.username}
- {users.reputation} + {users.reputation}
- {users.postcount} + {users.postcount}
Ban diff --git a/public/templates/users.tpl b/public/templates/users.tpl index fe4f60b690..e48cb971f6 100644 --- a/public/templates/users.tpl +++ b/public/templates/users.tpl @@ -29,12 +29,12 @@ {users.username}
- {users.reputation} + {users.reputation}
- {users.postcount} + {users.postcount}
From d99577ffb26737e853d305211a3e2eb2efee0cca Mon Sep 17 00:00:00 2001 From: RefinedSoftwareLLC Date: Mon, 11 Nov 2013 12:29:18 -0700 Subject: [PATCH 010/142] Update README.md Added Wiki link because it is so easy to miss yet it is so important because of it's setup guides. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2d3bab3918..876ef61057 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ - # NodeBB **NodeBB** is a robust Node.js driven forum built on a redis database. It is powered by web sockets, and is compatible down to IE8. * [NodeBB Homepage](http://www.nodebb.org/ "NodeBB") * [Demo & Meta Discussion](http://try.nodebb.org) +* [Wiki Guides](https://github.com/designcreateplay/NodeBB/wiki) - includes setup for other platforms * [Join us on IRC](https://kiwiirc.com/client/irc.freenode.net/nodebb) - #nodebb on Freenode * [Follow on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter") * [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook") @@ -65,4 +65,4 @@ NodeBB can also be started with helper programs, such as `supervisor` and `forev ## Upgrading NodeBB -Detailed upgrade instructions are listed in [Upgrading NodeBB](https://github.com/designcreateplay/NodeBB/wiki/Upgrading-NodeBB) \ No newline at end of file +Detailed upgrade instructions are listed in [Upgrading NodeBB](https://github.com/designcreateplay/NodeBB/wiki/Upgrading-NodeBB) From 69fefc0625b43e8684330068cd49af2fd1dae2e6 Mon Sep 17 00:00:00 2001 From: Baris Usakli Date: Tue, 12 Nov 2013 12:03:02 -0500 Subject: [PATCH 011/142] closes #496 --- public/templates/admin/header.tpl | 6 +++--- public/templates/header.tpl | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/public/templates/admin/header.tpl b/public/templates/admin/header.tpl index e90c48465b..e96a39c4ff 100644 --- a/public/templates/admin/header.tpl +++ b/public/templates/admin/header.tpl @@ -52,13 +52,13 @@ diff --git a/src/routes/admin.js b/src/routes/admin.js index a071d8ecbe..d2f8871be3 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -19,21 +19,27 @@ var user = require('./../user.js'), }); } - Admin.build_header = function (res, callback) { + Admin.buildHeader = function (req, res, callback) { var custom_header = { 'plugins': [] }; - plugins.fireHook('filter:admin.header.build', custom_header, function(err, custom_header) { - callback(err, templates['admin/header'].parse({ - csrf: res.locals.csrf_token, - relative_path: nconf.get('relative_path'), - plugins: custom_header.plugins - })); + user.getUserFields(req.user.uid, ['username', 'userslug', 'picture'], function(err, userData) { + + plugins.fireHook('filter:admin.header.build', custom_header, function(err, custom_header) { + callback(err, templates['admin/header'].parse({ + csrf: res.locals.csrf_token, + relative_path: nconf.get('relative_path'), + plugins: custom_header.plugins, + userpicture: userData.picture, + username: userData.username, + userslug: userData.userslug + })); + }); }); } - Admin.create_routes = function (app) { + Admin.createRoutes = function (app) { (function () { var routes = [ @@ -46,7 +52,7 @@ var user = require('./../user.js'), for (var i = 0, ii = routes.length; i < ii; i++) { (function (route) { app.get('/admin/' + route, Admin.isAdmin, function (req, res) { - Admin.build_header(res, function(err, header) { + Admin.buildHeader(req, res, function(err, header) { res.send(header + app.create_route('admin/' + route) + templates['admin/footer']); }); }); @@ -58,7 +64,7 @@ var user = require('./../user.js'), for (var i = 0, ii = unit_tests.length; i < ii; i++) { (function (route) { app.get('/admin/testing/' + route, Admin.isAdmin, function (req, res) { - Admin.build_header(res, function(err, header) { + Admin.buildHeader(req, res, function(err, header) { res.send(header + app.create_route('admin/testing/' + route) + templates['admin/footer']); }); }); @@ -69,13 +75,13 @@ var user = require('./../user.js'), app.namespace('/admin', function () { app.get('/', Admin.isAdmin, function (req, res) { - Admin.build_header(res, function(err, header) { + Admin.buildHeader(req, res, function(err, header) { res.send(header + app.create_route('admin/index') + templates['admin/footer']); }); }); app.get('/index', Admin.isAdmin, function (req, res) { - Admin.build_header(res, function(err, header) { + Admin.buildHeader(req, res, function(err, header) { res.send(header + app.create_route('admin/index') + templates['admin/footer']); }); }); @@ -144,7 +150,7 @@ var user = require('./../user.js'), (function(route) { app[routes[route].method || 'get']('/admin' + routes[route].route, function(req, res) { routes[route].options(req, res, function(options) { - Admin.build_header(res, function (err, header) { + Admin.buildHeader(req, res, function (err, header) { res.send(header + options.content + templates['admin/footer']); }); }); @@ -158,7 +164,7 @@ var user = require('./../user.js'), app.namespace('/api/admin', function () { app.get('/index', function (req, res) { res.json({ - version: pkg.version + version: pkg.version, }); }); diff --git a/src/routes/api.js b/src/routes/api.js index 5b4e1c5f10..5142cf1202 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -12,7 +12,7 @@ var user = require('./../user.js'), (function (Api) { - Api.create_routes = function (app) { + Api.createRoutes = function (app) { app.namespace('/api', function () { app.get('/get_templates_listing', function (req, res) { utils.walk(path.join(__dirname, '../../', 'public/templates'), function (err, data) { diff --git a/src/routes/authentication.js b/src/routes/authentication.js index e4a69623b0..7b3eb3c0e9 100644 --- a/src/routes/authentication.js +++ b/src/routes/authentication.js @@ -89,7 +89,7 @@ return login_strategies; } - Auth.create_routes = function(app) { + Auth.createRoutes = function(app) { app.post('/logout', function(req, res) { if (req.user && req.user.uid > 0) { winston.info('[Auth] Session ' + req.sessionID + ' logout (uid: ' + req.user.uid + ')'); diff --git a/src/routes/user.js b/src/routes/user.js index 9f219c90f0..b43d770f5c 100644 --- a/src/routes/user.js +++ b/src/routes/user.js @@ -12,7 +12,7 @@ var user = require('./../user.js'), websockets = require('./../websockets.js'); (function (User) { - User.create_routes = function (app) { + User.createRoutes = function (app) { app.get('/uid/:uid', function (req, res) { diff --git a/src/webserver.js b/src/webserver.js index fa334fb756..839bcdbdfc 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -323,10 +323,10 @@ var express = require('express'), app.namespace(nconf.get('relative_path'), function () { - auth.create_routes(app); - admin.create_routes(app); - userRoute.create_routes(app); - apiRoute.create_routes(app); + auth.createRoutes(app); + admin.createRoutes(app); + userRoute.createRoutes(app); + apiRoute.createRoutes(app); // Basic Routes (entirely client-side parsed, goal is to move the rest of the crap in this file into this one section) From 411ba3542c6920af6774842b50e633feb304bac1 Mon Sep 17 00:00:00 2001 From: Baris Usakli Date: Tue, 12 Nov 2013 12:51:57 -0500 Subject: [PATCH 013/142] closes #493 --- public/src/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/app.js b/public/src/app.js index 6728b2a805..5b0f7a4c48 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -136,7 +136,7 @@ var socket, $.post(RELATIVE_PATH + '/logout', { _csrf: $('#csrf_token').val() }, function() { - window.location = RELATIVE_PATH + '/'; + window.location.reload(false); }); } From 79c52dfe8453327b0135dad6488ce63b83fcf67f Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 13 Nov 2013 12:46:55 -0500 Subject: [PATCH 014/142] updated schema handling so that nodebb won't run unless you are up to date -- fixed #498 --- app.js | 45 +++++++----- src/upgrade.js | 191 ++++++++++++++++++++++++++++--------------------- 2 files changed, 138 insertions(+), 98 deletions(-) diff --git a/app.js b/app.js index 2a4fb645ec..896dd82750 100644 --- a/app.js +++ b/app.js @@ -88,30 +88,41 @@ websockets = require('./src/websockets.js'), posts = require('./src/posts.js'), plugins = require('./src/plugins'), // Don't remove this - plugins initializes itself - Notifications = require('./src/notifications'); + Notifications = require('./src/notifications'), + Upgrade = require('./src/upgrade'); - websockets.init(SocketIO); + Upgrade.check(function(schema_ok) { + if (schema_ok || nconf.get('check-schema') === false) { + websockets.init(SocketIO); - global.templates = {}; - global.translator = translator; + global.templates = {}; + global.translator = translator; - translator.loadServer(); - - var customTemplates = meta.config['theme:templates'] ? path.join(__dirname, 'node_modules', meta.config['theme:id'], meta.config['theme:templates']) : false; + translator.loadServer(); - // todo: replace below with read directory code, derp. - templates.init([ - 'header', 'footer', 'logout', 'outgoing', 'admin/header', 'admin/footer', 'admin/index', - 'emails/reset', 'emails/reset_plaintext', 'emails/email_confirm', 'emails/email_confirm_plaintext', - 'emails/header', 'emails/footer', + var customTemplates = meta.config['theme:templates'] ? path.join(__dirname, 'node_modules', meta.config['theme:id'], meta.config['theme:templates']) : false; - 'noscript/header', 'noscript/home', 'noscript/category', 'noscript/topic' - ], customTemplates); - + // todo: replace below with read directory code, derp. + templates.init([ + 'header', 'footer', 'logout', 'outgoing', 'admin/header', 'admin/footer', 'admin/index', + 'emails/reset', 'emails/reset_plaintext', 'emails/email_confirm', 'emails/email_confirm_plaintext', + 'emails/header', 'emails/footer', - templates.ready(webserver.init); + 'noscript/header', 'noscript/home', 'noscript/category', 'noscript/topic' + ], customTemplates); - Notifications.init(); + + templates.ready(webserver.init); + + Notifications.init(); + } else { + winston.warn('Your NodeBB schema is out-of-date. Please run the following command to bring your dataset up to spec:'); + winston.warn(' node app --upgrade'); + winston.warn('To ignore this error (not recommended):'); + winston.warn(' node app --no-check-schema') + process.exit(); + } + }); }); } else if (nconf.get('setup') || !fs.existsSync(__dirname + '/config.json')) { // New install, ask setup questions diff --git a/src/upgrade.js b/src/upgrade.js index f4040a5886..5dcdbabda3 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -1,110 +1,139 @@ +"use strict"; + var RDB = require('./redis.js'), async = require('async'), winston = require('winston'), - notifications = require('./notifications') - Upgrade = {}; + notifications = require('./notifications'), + Upgrade = {}, + + schemaDate, thisSchemaDate; + +Upgrade.check = function(callback) { + var latestSchema = new Date(2013, 10, 11).getTime(); + + RDB.get('schemaDate', function(err, value) { + if (parseInt(value, 10) > latestSchema) { + callback(true); + } else { + callback(false); + } + }); +}; Upgrade.upgrade = function() { winston.info('Beginning Redis database schema update'); async.series([ function(next) { - RDB.hget('notifications:1', 'score', function(err, score) { - if (score) { - async.series([ - function(next) { - RDB.keys('uid:*:notifications:flag', function(err, keys) { - if (keys.length > 0) { - winston.info('[2013/10/03] Removing deprecated Notification Flags'); - async.each(keys, function(key, next) { - RDB.del(key, next); - }, next); - } else { - winston.info('[2013/10/03] No Notification Flags found. Good.'); - next(); - } - }); - }, - function(next) { - winston.info('[2013/10/03] Updating Notifications'); - RDB.keys('uid:*:notifications:*', function(err, keys) { - async.each(keys, function(key, next) { - RDB.zrange(key, 0, -1, function(err, nids) { - async.each(nids, function(nid, next) { - notifications.get(nid, null, function(notif_data) { - RDB.zadd(key, notif_data.datetime, nid, next); - }); - }, next); - }); - }, next); - }); - }, - function(next) { - RDB.keys('notifications:*', function(err, keys) { - if (keys.length > 0) { - winston.info('[2013/10/03] Removing Notification Scores'); - async.each(keys, function(key, next) { - if (key === 'notifications:next_nid') return next(); - RDB.hdel(key, 'score', next); - }, next); - } else { - winston.info('[2013/10/03] No Notification Scores found. Good.'); - next(); - } - }); - } - ], next); - } else { - winston.info('[2013/10/03] Updates to Notifications skipped.'); - next(); - } + RDB.get('schemaDate', function(err, value) { + schemaDate = value; + next(); }); }, function(next) { - RDB.exists('notifications', function(err, exists) { - if (!exists) { - RDB.keys('notifications:*', function(err, keys) { - var multi = RDB.multi(); + thisSchemaDate = new Date(2013, 9, 3).getTime(); + if (schemaDate < thisSchemaDate) { + async.series([ + function(next) { + RDB.keys('uid:*:notifications:flag', function(err, keys) { + if (keys.length > 0) { + winston.info('[2013/10/03] Removing deprecated Notification Flags'); + async.each(keys, function(key, next) { + RDB.del(key, next); + }, next); + } else { + winston.info('[2013/10/03] No Notification Flags found. Good.'); + next(); + } + }); + }, + function(next) { + winston.info('[2013/10/03] Updating Notifications'); + RDB.keys('uid:*:notifications:*', function(err, keys) { + async.each(keys, function(key, next) { + RDB.zrange(key, 0, -1, function(err, nids) { + async.each(nids, function(nid, next) { + notifications.get(nid, null, function(notif_data) { + RDB.zadd(key, notif_data.datetime, nid, next); + }); + }, next); + }); + }, next); + }); + }, + function(next) { + RDB.keys('notifications:*', function(err, keys) { + if (keys.length > 0) { + winston.info('[2013/10/03] Removing Notification Scores'); + async.each(keys, function(key, next) { + if (key === 'notifications:next_nid') { + return next(); + } - keys = keys.filter(function(key) { - if (key === 'notifications:next_nid') { - return false; + RDB.hdel(key, 'score', next); + }, next); } else { - return true; + winston.info('[2013/10/03] No Notification Scores found. Good.'); + next(); } - }).map(function(key) { - return key.slice(14); }); + } + ], next); + } else { + winston.info('[2013/10/03] Updates to Notifications skipped.'); + next(); + } + }, + function(next) { + thisSchemaDate = new Date(2013, 9, 23).getTime(); + if (schemaDate < thisSchemaDate) { + RDB.keys('notifications:*', function(err, keys) { + var multi = RDB.multi(); - winston.info('[2013/10/23] Adding existing notifications to set'); - RDB.sadd('notifications', keys, next); + keys = keys.filter(function(key) { + if (key === 'notifications:next_nid') { + return false; + } else { + return true; + } + }).map(function(key) { + return key.slice(14); }); - } else { - winston.info('[2013/10/23] Updates to Notifications skipped.'); - next(); - } - }); + + winston.info('[2013/10/23] Adding existing notifications to set'); + RDB.sadd('notifications', keys, next); + }); + } else { + winston.info('[2013/10/23] Updates to Notifications skipped.'); + next(); + } }, function(next) { - RDB.hget('config', 'postDelay', function(err, postDelay) { - if(parseInt(postDelay, 10) > 150) { - RDB.hset('config', 'postDelay', 10, function(err, success) { - winston.info('[2013/11/11] Updated postDelay to 10 seconds.'); - next(); - }); - } else { - winston.info('[2013/11/11] Update to postDelay skipped.'); + thisSchemaDate = new Date(2013, 10, 11).getTime(); + if (schemaDate < thisSchemaDate) { + RDB.hset('config', 'postDelay', 10, function(err, success) { + winston.info('[2013/11/11] Updated postDelay to 10 seconds.'); next(); - } - }); + }); + } else { + winston.info('[2013/11/11] Update to postDelay skipped.'); + next(); + } } // Add new schema updates here + // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 12!!! ], function(err) { if (!err) { - winston.info('Redis schema update complete!'); - process.exit(); + RDB.set('schemaDate', thisSchemaDate, function(err) { + if (!err) { + winston.info('[upgrade] Redis schema update complete!'); + process.exit(); + } else { + winston.error('[upgrade] Could not update NodeBB schema date!'); + } + }); } else { - winston.error('Errors were encountered while updating the NodeBB schema: ' + err.message); + winston.error('[upgrade] Errors were encountered while updating the NodeBB schema: ' + err.message); } }); }; From 1c80a1bad53f56c1c71d53990bf25c391e1ff2ee Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 13 Nov 2013 13:31:36 -0500 Subject: [PATCH 015/142] ninjafix --- src/upgrade.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/upgrade.js b/src/upgrade.js index 5dcdbabda3..0e77918b86 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -12,7 +12,7 @@ Upgrade.check = function(callback) { var latestSchema = new Date(2013, 10, 11).getTime(); RDB.get('schemaDate', function(err, value) { - if (parseInt(value, 10) > latestSchema) { + if (parseInt(value, 10) >= latestSchema) { callback(true); } else { callback(false); From 88154c3ebf8fa4a71d03762990d472e79d70f00d Mon Sep 17 00:00:00 2001 From: Baris Soner Usakli Date: Wed, 13 Nov 2013 13:51:40 -0500 Subject: [PATCH 016/142] closes #497 --- src/routes/user.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/routes/user.js b/src/routes/user.js index b43d770f5c..c0a96e80bb 100644 --- a/src/routes/user.js +++ b/src/routes/user.js @@ -207,11 +207,6 @@ var user = require('./../user.js'), is.on('end', function () { fs.unlinkSync(tempPath); - var imageUrl = nconf.get('upload_url') + filename; - - user.setUserField(uid, 'uploadedpicture', imageUrl); - user.setUserField(uid, 'picture', imageUrl); - require('node-imagemagick').crop({ srcPath: uploadPath, dstPath: uploadPath, @@ -220,8 +215,17 @@ var user = require('./../user.js'), }, function (err, stdout, stderr) { if (err) { winston.err(err); + res.send({ + error: 'Invalid image file!' + }); + return; } + var imageUrl = nconf.get('upload_url') + filename; + + user.setUserField(uid, 'uploadedpicture', imageUrl); + user.setUserField(uid, 'picture', imageUrl); + res.json({ path: imageUrl }); From e6bb66705d3d491600890158ac7dc2f87d157da2 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 13 Nov 2013 15:15:00 -0500 Subject: [PATCH 017/142] 0.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1eeaf8e3f7..767d955a12 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "nodebb", "license": "GPLv3 or later", "description": "NodeBB Forum", - "version": "0.0.7", + "version": "0.1.0", "homepage": "http://www.nodebb.org", "repository": { "type": "git", From 77c2f551d3897746e3bdfae37b552aeb232c6bef Mon Sep 17 00:00:00 2001 From: Baris Soner Usakli Date: Wed, 13 Nov 2013 20:03:44 -0500 Subject: [PATCH 018/142] upgrade script fix --- src/upgrade.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/upgrade.js b/src/upgrade.js index 0e77918b86..67b349f226 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -88,7 +88,6 @@ Upgrade.upgrade = function() { thisSchemaDate = new Date(2013, 9, 23).getTime(); if (schemaDate < thisSchemaDate) { RDB.keys('notifications:*', function(err, keys) { - var multi = RDB.multi(); keys = keys.filter(function(key) { if (key === 'notifications:next_nid') { @@ -101,7 +100,13 @@ Upgrade.upgrade = function() { }); winston.info('[2013/10/23] Adding existing notifications to set'); - RDB.sadd('notifications', keys, next); + + if(keys && Array.isArray(keys)) { + async.each(keys, function(key, cb) { + RDB.sadd('notifications', key, cb); + }, next); + } else next(); + }); } else { winston.info('[2013/10/23] Updates to Notifications skipped.'); From 01340c87bd906113b991c6143797439eef99a955 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 13 Nov 2013 23:08:44 -0500 Subject: [PATCH 019/142] handling missing notifications in update script --- src/upgrade.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/upgrade.js b/src/upgrade.js index 67b349f226..c2d98fbb40 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -54,7 +54,9 @@ Upgrade.upgrade = function() { RDB.zrange(key, 0, -1, function(err, nids) { async.each(nids, function(nid, next) { notifications.get(nid, null, function(notif_data) { - RDB.zadd(key, notif_data.datetime, nid, next); + if (notif_data) { + RDB.zadd(key, notif_data.datetime, nid, next); + } }); }, next); }); From 27fce2363dcb44110b94fb66ba8a6f4a6360d331 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 13 Nov 2013 23:09:22 -0500 Subject: [PATCH 020/142] ... and again --- src/upgrade.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/upgrade.js b/src/upgrade.js index c2d98fbb40..26ac52cc19 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -56,6 +56,8 @@ Upgrade.upgrade = function() { notifications.get(nid, null, function(notif_data) { if (notif_data) { RDB.zadd(key, notif_data.datetime, nid, next); + } else { + next(); } }); }, next); From bd04b2f921eec8627613b5e9c53c48668b6c3ca2 Mon Sep 17 00:00:00 2001 From: Jacob Gable Date: Thu, 14 Nov 2013 12:52:00 -0600 Subject: [PATCH 021/142] Add admin password confirmation on setup Closes #419 - Introduce a password:confirm question - Isolate password questions to they can be re-asked - Verify matching password, re-ask if not --- src/install.js | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/install.js b/src/install.js index 5795a4d605..b32b55622d 100644 --- a/src/install.js +++ b/src/install.js @@ -277,18 +277,32 @@ var async = require('async'), description: 'Administrator email address', pattern: /.+@.+/, required: true - }, { + }], + passwordQuestions = [{ name: 'password', description: 'Password', required: true, hidden: true, type: 'string' + }, { + name: 'password:confirm', + description: 'Confirm Password', + required: true, + hidden: true, + type: 'string' }], success = function(err, results) { if (!results) { return callback(new Error('aborted')); } + // Check if the passwords match + if (results['password:confirm'] !== results.password) { + winston.warn("Passwords did not match, please try again"); + // Re-prompt password questions. + return retryPassword(results); + } + nconf.set('bcrypt_rounds', 12); User.create(results.username, results.password, results.email, function (err, uid) { if (err) { @@ -306,8 +320,26 @@ var async = require('async'), } }); }); + }, + retryPassword = function (originalResults) { + // Ask only the password questions + prompt.get(passwordQuestions, function (err, results) { + if (!results) { + return callback(new Error('aborted')); + } + + // Update the original data with newly collected password + originalResults.password = results.password; + originalResults['password:confirm'] = results['password:confirm']; + + // Send back to success to handle + success(err, originalResults); + }); }; + // Add the password questions + questions = questions.concat(passwordQuestions); + if (!install.values) prompt.get(questions, success); else { var results = { From 535379d9d77d1b71c76e72ef73ee847819b979ec Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 14 Nov 2013 18:45:17 -0500 Subject: [PATCH 022/142] added password confirmation to automated setup --- src/install.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/install.js b/src/install.js index b32b55622d..8ce2a059ed 100644 --- a/src/install.js +++ b/src/install.js @@ -63,13 +63,14 @@ var async = require('async'), } if (setupVal && setupVal instanceof Object) { - if (setupVal['admin:username'] && setupVal['admin:password'] && setupVal['admin:email']) { + if (setupVal['admin:username'] && setupVal['admin:password'] && setupVal['admin:password:confirm'] && setupVal['admin:email']) { install.values = setupVal; next(); } else { winston.error('Required values are missing for automated setup:'); if (!setupVal['admin:username']) winston.error(' admin:username'); if (!setupVal['admin:password']) winston.error(' admin:password'); + if (!setupVal['admin:password:confirm']) winston.error(' admin:password:confirm'); if (!setupVal['admin:email']) winston.error(' admin:email'); process.exit(); } @@ -345,7 +346,8 @@ var async = require('async'), var results = { username: install.values['admin:username'], email: install.values['admin:email'], - password: install.values['admin:password'] + password: install.values['admin:password'], + 'password:confirm': install.values['admin:password:confirm'] }; success(null, results); From 84fa704b25e12159b48d54a499c1e9e25b55cfc9 Mon Sep 17 00:00:00 2001 From: Baris Usakli Date: Fri, 15 Nov 2013 13:39:09 -0500 Subject: [PATCH 023/142] refactor abit adding error checking to missing parts --- src/posts.js | 17 ++++---- src/routes/debug.js | 98 ++++++++++++++++++++++++++++----------------- src/routes/user.js | 16 -------- src/topics.js | 44 +++++++++++--------- src/websockets.js | 2 +- 5 files changed, 96 insertions(+), 81 deletions(-) diff --git a/src/posts.js b/src/posts.js index 07cf4f8a93..c4d914e9ab 100644 --- a/src/posts.js +++ b/src/posts.js @@ -131,17 +131,18 @@ var RDB = require('./redis.js'), }); }; - // TODO: this function is never called except from some debug route. clean up? Posts.getPostData = function(pid, callback) { RDB.hgetall('post:' + pid, function(err, data) { - if (err === null) { - plugins.fireHook('filter:post.get', data, function(err, newData) { - if (!err) callback(newData); - else callback(data); - }); - } else { - winston.error(err); + if(err) { + return callback(err, null); } + + plugins.fireHook('filter:post.get', data, function(err, newData) { + if(err) { + return callback(err, null); + } + callback(null, newData); + }); }); } diff --git a/src/routes/debug.js b/src/routes/debug.js index 2d19bc11ea..6ee816e702 100644 --- a/src/routes/debug.js +++ b/src/routes/debug.js @@ -1,50 +1,76 @@ + +var user = require('./../user'), + categories = require('./../categories'), + topics = require('./../topics'), + posts = require('./../posts'); + + var DebugRoute = function(app) { - app.namespace('/debug', function() { - app.get('/cid/:cid', function (req, res) { - categories.getCategoryData(req.params.cid, function (err, data) { - if (data) { - res.send(data); - } else { - res.send(404, "Category doesn't exist!"); - } - }); - }); - app.get('/tid/:tid', function (req, res) { - topics.getTopicData(req.params.tid, function (data) { - if (data) { - res.send(data); - } else { - res.send(404, "Topic doesn't exist!"); - } - }); + app.namespace('/debug', function() { + + app.get('/uid/:uid', function (req, res) { + + if (!req.params.uid) + return res.redirect('/404'); + + user.getUserData(req.params.uid, function (err, data) { + if (data) { + res.send(data); + } else { + res.json(404, { + error: "User doesn't exist!" + }); + } }); + }); - app.get('/pid/:pid', function (req, res) { - posts.getPostData(req.params.pid, function (data) { - if (data) { - res.send(data); - } else { - res.send(404, "Post doesn't exist!"); - } - }); + + app.get('/cid/:cid', function (req, res) { + categories.getCategoryData(req.params.cid, function (err, data) { + if (data) { + res.send(data); + } else { + res.send(404, "Category doesn't exist!"); + } }); + }); - app.get('/prune', function(req, res) { - var Notifications = require('../notifications'); + app.get('/tid/:tid', function (req, res) { + topics.getTopicData(req.params.tid, function (err, data) { + if (data) { + res.send(data); + } else { + res.send(404, "Topic doesn't exist!"); + } + }); + }); - Notifications.prune(new Date(), function() { - console.log('done'); - }); - res.send(); + app.get('/pid/:pid', function (req, res) { + posts.getPostData(req.params.pid, function (err, data) { + if (data) { + res.send(data); + } else { + res.send(404, "Post doesn't exist!"); + } }); + }); - app.get('/uuidtest', function(req, res) { - var Utils = require('../../public/src/utils.js'); + app.get('/prune', function(req, res) { + var Notifications = require('../notifications'); - res.send(Utils.generateUUID()); + Notifications.prune(new Date(), function() { + console.log('done'); }); + res.send(); + }); + + app.get('/uuidtest', function(req, res) { + var Utils = require('../../public/src/utils.js'); + + res.send(Utils.generateUUID()); }); - }; + }); +}; module.exports = DebugRoute; \ No newline at end of file diff --git a/src/routes/user.js b/src/routes/user.js index b43d770f5c..8ee9015cd5 100644 --- a/src/routes/user.js +++ b/src/routes/user.js @@ -14,22 +14,6 @@ var user = require('./../user.js'), (function (User) { User.createRoutes = function (app) { - app.get('/uid/:uid', function (req, res) { - - if (!req.params.uid) - return res.redirect('/404'); - - user.getUserData(req.params.uid, function (err, data) { - if (data) { - res.send(data); - } else { - res.json(404, { - error: "User doesn't exist!" - }); - } - }); - }); - app.namespace('/users', function () { app.get('', function (req, res) { app.build_header({ diff --git a/src/topics.js b/src/topics.js index cc383493a9..2c070f2a7a 100644 --- a/src/topics.js +++ b/src/topics.js @@ -20,28 +20,36 @@ var RDB = require('./redis.js'), Topics.getTopicData = function(tid, callback) { RDB.hgetall('topic:' + tid, function(err, data) { - if (err === null) { - if(data) { - data.title = validator.sanitize(data.title).escape(); - if(data.timestamp) { - data.relativeTime = new Date(parseInt(data.timestamp, 10)).toISOString(); - } - } + if(err) { + return callback(err, null); + } - callback(data); - } else { - console.log(err); + if(data) { + data.title = validator.sanitize(data.title).escape(); + if(data.timestamp) { + data.relativeTime = new Date(parseInt(data.timestamp, 10)).toISOString(); + } } + + callback(null, data); }); } Topics.getTopicDataWithUser = function(tid, callback) { - Topics.getTopicData(tid, function(topic) { + Topics.getTopicData(tid, function(err, topic) { + if(err) { + return callback(err, null); + } + user.getUserFields(topic.uid, ['username', 'userslug', 'picture'] , function(err, userData) { + if(err) { + return callback(err, null); + } + topic.username = userData.username; topic.userslug = userData.userslug topic.picture = userData.picture; - callback(topic); + callback(null, topic); }); }); } @@ -331,7 +339,7 @@ var RDB = require('./redis.js'), } function loadTopic(tid, callback) { - Topics.getTopicData(tid, function(topicData) { + Topics.getTopicData(tid, function(err, topicData) { if (!topicData) { return callback(null); } @@ -381,9 +389,7 @@ var RDB = require('./redis.js'), Topics.increaseViewCount(tid); function getTopicData(next) { - Topics.getTopicData(tid, function(topicData) { - next(null, topicData); - }); + Topics.getTopicData(tid, next); } function getTopicPosts(next) { @@ -442,9 +448,7 @@ var RDB = require('./redis.js'), Topics.getTopicForCategoryView = function(tid, uid, callback) { function getTopicData(next) { - Topics.getTopicDataWithUser(tid, function(topic) { - next(null, topic); - }); + Topics.getTopicDataWithUser(tid, next); } function getReadStatus(next) { @@ -517,7 +521,7 @@ var RDB = require('./redis.js'), }); async.each(tids, function(tid, next) { - Topics.getTopicDataWithUser(tid, function(topicData) { + Topics.getTopicDataWithUser(tid, function(err, topicData) { topics.push(topicData); next(); }); diff --git a/src/websockets.js b/src/websockets.js index cb01f26d0e..e3db21d2ae 100644 --- a/src/websockets.js +++ b/src/websockets.js @@ -688,7 +688,7 @@ module.exports.init = function(io) { socket.on('api:composer.push', function(data) { if (uid > 0 || meta.config.allowGuestPosting === '1') { if (parseInt(data.tid) > 0) { - topics.getTopicData(data.tid, function(topicData) { + topics.getTopicData(data.tid, function(err, topicData) { if (data.body) topicData.body = data.body; From 7c1b6d6ad2a0034fe66d1ef917371b9e85d324c3 Mon Sep 17 00:00:00 2001 From: Baris Usakli Date: Fri, 15 Nov 2013 14:57:50 -0500 Subject: [PATCH 024/142] lots of refactor for error handling --- src/categories.js | 44 ++---- src/favourites.js | 4 +- src/postTools.js | 14 +- src/posts.js | 381 +++++++++++++++++++++++++-------------------- src/redis.js | 17 +- src/threadTools.js | 2 +- src/topics.js | 109 +++++++------ src/websockets.js | 15 +- 8 files changed, 317 insertions(+), 269 deletions(-) diff --git a/src/categories.js b/src/categories.js index df155d69f1..f630cd7ebb 100644 --- a/src/categories.js +++ b/src/categories.js @@ -252,26 +252,28 @@ var RDB = require('./redis.js'), Categories.moveRecentReplies = function(tid, oldCid, cid, callback) { function movePost(pid, callback) { - posts.getPostField(pid, 'timestamp', function(timestamp) { + posts.getPostField(pid, 'timestamp', function(err, timestamp) { + if(err) { + return callback(err); + } + RDB.zrem('categories:recent_posts:cid:' + oldCid, pid); RDB.zadd('categories:recent_posts:cid:' + cid, timestamp, pid); + callback(null); }); } topics.getPids(tid, function(err, pids) { - if (!err) { - async.each(pids, movePost, function(err) { - if (!err) { - callback(null, 1); - } else { - winston.err(err); - callback(err, null); - } - }); - } else { - winston.err(err); - callback(err, null); + if(err) { + return callback(err, null); } + + async.each(pids, movePost, function(err) { + if(err) { + return callback(err, null); + } + callback(null, 1); + }); }); }; @@ -372,19 +374,6 @@ var RDB = require('./redis.js'), return callback(err, null); } - function getPostCategory(pid, callback) { - posts.getPostField(pid, 'tid', function(tid) { - - topics.getTopicField(tid, 'cid', function(err, postCid) { - if (err) { - return callback(err, null); - } - - return callback(null, postCid); - }); - }); - } - var index = 0, active = false; @@ -393,7 +382,7 @@ var RDB = require('./redis.js'), return active === false && index < pids.length; }, function(callback) { - getPostCategory(pids[index], function(err, postCid) { + posts.getCidByPid(pids[index], function(err, postCid) { if (err) { return callback(err); } @@ -411,7 +400,6 @@ var RDB = require('./redis.js'), return callback(err, null); } - callback(null, active); } ); diff --git a/src/favourites.js b/src/favourites.js index 6e6bd40990..5845646064 100644 --- a/src/favourites.js +++ b/src/favourites.js @@ -21,7 +21,7 @@ var RDB = require('./redis.js'), return; } - posts.getPostFields(pid, ['uid', 'timestamp'], function (postData) { + posts.getPostFields(pid, ['uid', 'timestamp'], function (err, postData) { Favourites.hasFavourited(pid, uid, function (hasFavourited) { if (hasFavourited === 0) { @@ -57,7 +57,7 @@ var RDB = require('./redis.js'), return; } - posts.getPostField(pid, 'uid', function (uid_of_poster) { + posts.getPostField(pid, 'uid', function (err, uid_of_poster) { Favourites.hasFavourited(pid, uid, function (hasFavourited) { if (hasFavourited === 1) { RDB.srem('pid:' + pid + ':users_favourited', uid); diff --git a/src/postTools.js b/src/postTools.js index c7e5633121..899ce9fb9a 100644 --- a/src/postTools.js +++ b/src/postTools.js @@ -34,7 +34,7 @@ var RDB = require('./redis.js'), } function getThreadPrivileges(next) { - posts.getPostField(pid, 'tid', function(tid) { + posts.getPostField(pid, 'tid', function(err, tid) { threadTools.privileges(tid, uid, function(privileges) { next(null, privileges); }); @@ -42,7 +42,7 @@ var RDB = require('./redis.js'), } function isOwnPost(next) { - posts.getPostField(pid, 'uid', function(author) { + posts.getPostField(pid, 'uid', function(err, author) { next(null, parseInt(author, 10) === parseInt(uid, 10)); }); } @@ -87,7 +87,7 @@ var RDB = require('./redis.js'), async.parallel([ function(next) { - posts.getPostField(pid, 'tid', function(tid) { + posts.getPostField(pid, 'tid', function(err, tid) { PostTools.isMain(pid, tid, function(isMainPost) { if (isMainPost) { topics.setTopicField(tid, 'title', title); @@ -132,7 +132,7 @@ var RDB = require('./redis.js'), RDB.decr('totalpostcount'); postSearch.remove(pid); - posts.getPostFields(pid, ['tid', 'uid'], function(postData) { + posts.getPostFields(pid, ['tid', 'uid'], function(err, postData) { RDB.hincrby('topic:' + postData.tid, 'postcount', -1); user.decrementUserFieldBy(postData.uid, 'postcount', 1, function(err, postcount) { @@ -150,7 +150,7 @@ var RDB = require('./redis.js'), if (err) winston.error('Could not delete topic (tid: ' + postData.tid + ')', err.stack); }); } else { - posts.getPostField(pid, 'timestamp', function(timestamp) { + posts.getPostField(pid, 'timestamp', function(err, timestamp) { topics.updateTimestamp(postData.tid, timestamp); }); } @@ -174,7 +174,7 @@ var RDB = require('./redis.js'), posts.setPostField(pid, 'deleted', 0); RDB.incr('totalpostcount'); - posts.getPostFields(pid, ['tid', 'uid', 'content'], function(postData) { + posts.getPostFields(pid, ['tid', 'uid', 'content'], function(err, postData) { RDB.hincrby('topic:' + postData.tid, 'postcount', 1); user.incrementUserFieldBy(postData.uid, 'postcount', 1); @@ -184,7 +184,7 @@ var RDB = require('./redis.js'), }); threadTools.getLatestUndeletedPid(postData.tid, function(err, pid) { - posts.getPostField(pid, 'timestamp', function(timestamp) { + posts.getPostField(pid, 'timestamp', function(err, timestamp) { topics.updateTimestamp(postData.tid, timestamp); }); }); diff --git a/src/posts.js b/src/posts.js index c4d914e9ab..6ae83c6b86 100644 --- a/src/posts.js +++ b/src/posts.js @@ -18,6 +18,176 @@ var RDB = require('./redis.js'), (function(Posts) { var customUserInfo = {}; + Posts.create = function(uid, tid, content, callback) { + if (uid === null) { + callback(new Error('invalid-user'), null); + return; + } + + topics.isLocked(tid, function(err, locked) { + if(err) { + return callback(err, null); + } else if(locked) { + callback(new Error('topic-locked'), null); + } + + RDB.incr('global:next_post_id', function(err, pid) { + if(err) { + return callback(err, null); + } + + plugins.fireHook('filter:post.save', content, function(err, newContent) { + if(err) { + return callback(err, null); + } + + content = newContent; + + var timestamp = Date.now(), + postData = { + 'pid': pid, + 'uid': uid, + 'tid': tid, + 'content': content, + 'timestamp': timestamp, + 'reputation': 0, + 'editor': '', + 'edited': 0, + 'deleted': 0, + 'fav_button_class': '', + 'fav_star_class': 'icon-star-empty', + 'show_banned': 'hide', + 'relativeTime': new Date(timestamp).toISOString(), + 'post_rep': '0', + 'edited-class': 'none', + 'relativeEditTime': '' + }; + + RDB.hmset('post:' + pid, postData); + + topics.addPostToTopic(tid, pid); + topics.increasePostCount(tid); + topics.updateTimestamp(tid, timestamp); + + RDB.incr('totalpostcount'); + + topics.getTopicFields(tid, ['cid', 'pinned'], function(err, topicData) { + + RDB.handle(err); + + var cid = topicData.cid; + + feed.updateTopic(tid); + + RDB.zadd('categories:recent_posts:cid:' + cid, timestamp, pid); + + if(topicData.pinned === '0') + RDB.zadd('categories:' + cid + ':tid', timestamp, tid); + + RDB.scard('cid:' + cid + ':active_users', function(err, amount) { + if (amount > 10) { + RDB.spop('cid:' + cid + ':active_users'); + } + + categories.addActiveUser(cid, uid); + }); + }); + + user.onNewPostMade(uid, tid, pid, timestamp); + + plugins.fireHook('filter:post.get', postData, function(err, newPostData) { + if(err) { + return callback(err, null); + } + + postData = newPostData; + + postTools.parse(postData.content, function(err, content) { + if(err) { + return callback(err, null); + } + postData.content = content; + + plugins.fireHook('action:post.save', postData); + + postSearch.index(content, pid); + + callback(null, postData); + }); + }); + }); + }); + }); + }; + + Posts.reply = function(tid, uid, content, callback) { + if(content) { + content = content.trim(); + } + + if (!content || content.length < meta.config.minimumPostLength) { + callback(new Error('content-too-short'), null); + return; + } + + Posts.create(uid, tid, content, function(err, postData) { + if(err) { + return callback(err, null); + } else if(!postData) { + 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) { + Posts.getCidByPid(postData.pid, function(err, cid) { + if(err) { + return next(err); + } + + RDB.del('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); + } + + var socketData = { + posts: [postData] + }; + + io.sockets.in('topic_' + tid).emit('event:new_post', socketData); + io.sockets.in('recent_posts').emit('event:new_post', socketData); + io.sockets.in('user/' + uid).emit('event:new_post', socketData); + + next(); + }); + } + ], function(err, results) { + if(err) { + return callback(err, null); + } + + callback(null, 'Reply successful'); + }); + }); + } + Posts.getPostsByTid = function(tid, start, end, callback) { RDB.lrange('tid:' + tid + ':posts', start, end, function(err, pids) { RDB.handle(err); @@ -86,7 +256,7 @@ var RDB = require('./redis.js'), function getPostSummary(pid, callback) { async.waterfall([ function(next) { - Posts.getPostFields(pid, ['pid', 'tid', 'content', 'uid', 'timestamp', 'deleted'], function(postData) { + Posts.getPostFields(pid, ['pid', 'tid', 'content', 'uid', 'timestamp', 'deleted'], function(err, postData) { if (postData.deleted === '1') return callback(null); else { postData.relativeTime = new Date(parseInt(postData.timestamp || 0, 10)).toISOString(); @@ -148,35 +318,31 @@ var RDB = require('./redis.js'), Posts.getPostFields = function(pid, fields, callback) { RDB.hmgetObject('post:' + pid, fields, function(err, data) { - if (err === null) { - // TODO: I think the plugins system needs an optional 'parameters' paramter so I don't have to do this: - data = data || {}; - data.pid = pid; - data.fields = fields; - - plugins.fireHook('filter:post.getFields', data, function(err, data) { - callback(data); - }); - } else { - console.log(err); + if(err) { + return callback(err, null); } + + // TODO: I think the plugins system needs an optional 'parameters' paramter so I don't have to do this: + data = data || {}; + data.pid = pid; + data.fields = fields; + + plugins.fireHook('filter:post.getFields', data, function(err, data) { + if(err) { + return callback(err, null); + } + callback(null, data); + }); }); } Posts.getPostField = function(pid, field, callback) { - RDB.hget('post:' + pid, field, function(err, data) { - if (err === null) { - // TODO: I think the plugins system needs an optional 'parameters' paramter so I don't have to do this: - data = data || {}; - data.pid = pid; - data.field = field; - - plugins.fireHook('filter:post.getField', data, function(err, data) { - callback(data); - }); - } else { - console.log(err); + Posts.getPostFields(pid, [field], function(err, data) { + if(err) { + return callback(err, null); } + + callback(null, data[field]); }); } @@ -193,8 +359,8 @@ var RDB = require('./redis.js'), var posts = [], multi = RDB.multi(); - for(var x=0,numPids=pids.length;x 10) { - RDB.spop('cid:' + cid + ':active_users'); - } - - categories.addActiveUser(cid, uid); - }); - }); - - user.onNewPostMade(uid, tid, pid, timestamp); - - async.parallel({ - content: function(next) { - plugins.fireHook('filter:post.get', postData, function(err, newPostData) { - if (!err) postData = newPostData; - - postTools.parse(postData.content, function(err, content) { - next(null, content); - }); - }); - } - }, function(err, results) { - postData.content = results.content; - callback(postData); - }); - - plugins.fireHook('action:post.save', postData); - - postSearch.index(content, pid); - }); - }); - } else { - callback(null); - } - }); - } - Posts.uploadPostImage = function(image, callback) { var imgur = require('./imgur'); imgur.setClientID(meta.config.imgurClientID); @@ -463,7 +502,7 @@ var RDB = require('./redis.js'), function reIndex(pid, callback) { - Posts.getPostField(pid, 'content', function(content) { + Posts.getPostField(pid, 'content', function(err, content) { postSearch.remove(pid, function() { if (content && content.length) { diff --git a/src/redis.js b/src/redis.js index 8b9ebbcdb8..6689158938 100644 --- a/src/redis.js +++ b/src/redis.js @@ -58,18 +58,17 @@ */ RedisDB.hmgetObject = function(key, fields, callback) { RedisDB.hmget(key, fields, function(err, data) { - if (err === null) { - var returnData = {}; + if(err) { + return callback(err, null); + } - for (var i = 0, ii = fields.length; i < ii; ++i) { - returnData[fields[i]] = data[i]; - } + var returnData = {}; - callback(null, returnData); - } else { - console.log(err); - callback(err, null); + for (var i = 0, ii = fields.length; i < ii; ++i) { + returnData[fields[i]] = data[i]; } + + callback(null, returnData); }); }; diff --git a/src/threadTools.js b/src/threadTools.js index c3f5297918..6a2b81c146 100644 --- a/src/threadTools.js +++ b/src/threadTools.js @@ -303,7 +303,7 @@ var RDB = require('./redis.js'), pids.reverse(); async.detectSeries(pids, function(pid, next) { - posts.getPostField(pid, 'deleted', function(deleted) { + posts.getPostField(pid, 'deleted', function(err, deleted) { if (deleted === '0') next(true); else next(false); }); diff --git a/src/topics.js b/src/topics.js index 2c070f2a7a..9a24db2d36 100644 --- a/src/topics.js +++ b/src/topics.js @@ -477,6 +477,9 @@ var RDB = require('./redis.js'), hasRead = results[1], teaser = results[2]; + topicData['pin-icon'] = topicData.pinned === '1' ? 'icon-pushpin' : 'none'; + topicData['lock-icon'] = topicData.locked === '1' ? 'icon-lock' : 'none'; + topicData.badgeclass = hasRead ? '' : 'badge-important'; topicData.teaser_text = teaser.text || ''; topicData.teaser_username = teaser.username || ''; @@ -550,15 +553,15 @@ var RDB = require('./redis.js'), } Topics.getTitleByPid = function(pid, callback) { - posts.getPostField(pid, 'tid', function(tid) { + posts.getPostField(pid, 'tid', function(err, tid) { Topics.getTopicField(tid, 'title', function(err, title) { callback(title); }); }); } - Topics.markUnRead = function(tid) { - RDB.del('tid:' + tid + ':read_by_uid'); + Topics.markUnRead = function(tid, callback) { + RDB.del('tid:' + tid + ':read_by_uid', callback); } Topics.markAsRead = function(tid, uid) { @@ -624,36 +627,44 @@ var RDB = require('./redis.js'), Topics.getTeaser = function(tid, callback) { threadTools.getLatestUndeletedPid(tid, function(err, pid) { - if (!err) { - posts.getPostFields(pid, ['pid', 'content', 'uid', 'timestamp'], function(postData) { - - user.getUserFields(postData.uid, ['username', 'userslug', 'picture'], function(err, userData) { - if (err) - return callback(err, null); - - var stripped = postData.content, - timestamp = postData.timestamp, - returnObj = { - "pid": postData.pid, - "username": userData.username, - "userslug": userData.userslug, - "picture": userData.picture, - "timestamp": timestamp - }; - - if (postData.content) { - stripped = postData.content.replace(/>.+\n\n/, ''); - postTools.parse(stripped, function(err, stripped) { - returnObj.text = utils.strip_tags(stripped); - callback(null, returnObj); - }); - } else { - returnObj.text = ''; + if (err) { + return callback(err, null); + } + + posts.getPostFields(pid, ['pid', 'content', 'uid', 'timestamp'], function(err, postData) { + if (err) { + return callback(err, null); + } else if(!postData) { + return callback(new Error('no-teaser-found')); + } + + user.getUserFields(postData.uid, ['username', 'userslug', 'picture'], function(err, userData) { + if (err) { + return callback(err, null); + } + + var stripped = postData.content, + timestamp = postData.timestamp, + returnObj = { + "pid": postData.pid, + "username": userData.username, + "userslug": userData.userslug, + "picture": userData.picture, + "timestamp": timestamp + }; + + if (postData.content) { + stripped = postData.content.replace(/>.+\n\n/, ''); + postTools.parse(stripped, function(err, stripped) { + returnObj.text = utils.strip_tags(stripped); callback(null, returnObj); - } - }); + }); + } else { + returnObj.text = ''; + callback(null, returnObj); + } }); - } else callback(new Error('no-teaser-found')); + }); }); } @@ -740,23 +751,26 @@ var RDB = require('./redis.js'), feed.updateCategory(category_id); - posts.create(uid, tid, content, function(postData) { - if (postData) { + posts.create(uid, tid, content, function(err, postData) { + if(err) { + return callback(err, null); + } else if(!postData) { + return callback(new Error('invalid-post'), null); + } - // Auto-subscribe the post creator to the newly created topic - threadTools.toggleFollow(tid, uid); + // Auto-subscribe the post creator to the newly created topic + threadTools.toggleFollow(tid, uid); - // Notify any users looking at the category that a new topic has arrived - Topics.getTopicForCategoryView(tid, uid, function(topicData) { - io.sockets.in('category_' + category_id).emit('event:new_topic', topicData); - io.sockets.in('recent_posts').emit('event:new_topic', topicData); - io.sockets.in('user/' + uid).emit('event:new_post', { - posts: postData - }); + // Notify any users looking at the category that a new topic has arrived + Topics.getTopicForCategoryView(tid, uid, function(topicData) { + io.sockets.in('category_' + category_id).emit('event:new_topic', topicData); + io.sockets.in('recent_posts').emit('event:new_topic', topicData); + io.sockets.in('user/' + uid).emit('event:new_post', { + posts: postData }); + }); - callback(null, postData); - } + callback(null, postData); }); }); }); @@ -784,7 +798,10 @@ var RDB = require('./redis.js'), Topics.isLocked = function(tid, callback) { Topics.getTopicField(tid, 'locked', function(err, locked) { - callback(locked); + if(err) { + return callback(err, null); + } + callback(null, locked === "1"); }); } @@ -806,7 +823,7 @@ var RDB = require('./redis.js'), Topics.getPids(tid, function(err, pids) { function getUid(pid, next) { - posts.getPostField(pid, 'uid', function(uid) { + posts.getPostField(pid, 'uid', function(err, uid) { if (err) return next(err); uids[uid] = 1; diff --git a/src/websockets.js b/src/websockets.js index e3db21d2ae..15193ea45c 100644 --- a/src/websockets.js +++ b/src/websockets.js @@ -368,6 +368,13 @@ module.exports.init = function(io) { posts.emitContentTooShortAlert(socket); } else if (err.message === 'too-many-posts') { posts.emitTooManyPostsAlert(socket); + } else { + socket.emit('event:alert', { + title: 'Error', + message: err.message, + type: 'warning', + timeout: 7500 + }); } return; } @@ -533,7 +540,7 @@ module.exports.init = function(io) { }); socket.on('api:posts.getRawPost', function(data) { - posts.getPostField(data.pid, 'content', function(raw) { + posts.getPostField(data.pid, 'content', function(err, raw) { socket.emit('api:posts.getRawPost', { post: raw }); @@ -714,9 +721,7 @@ module.exports.init = function(io) { async.parallel([ function(next) { - posts.getPostFields(data.pid, ['content'], function(raw) { - next(null, raw); - }); + posts.getPostFields(data.pid, ['content'], next); }, function(next) { topics.getTitleByPid(data.pid, function(title) { @@ -739,7 +744,7 @@ module.exports.init = function(io) { }); socket.on('api:composer.editCheck', function(pid) { - posts.getPostField(pid, 'tid', function(tid) { + posts.getPostField(pid, 'tid', function(err, tid) { postTools.isMain(pid, tid, function(isMain) { socket.emit('api:composer.editCheck', { titleEditable: isMain From 54d94f5988aa90a2d24ce7926ab170b301292b0a Mon Sep 17 00:00:00 2001 From: Baris Usakli Date: Fri, 15 Nov 2013 16:16:50 -0500 Subject: [PATCH 025/142] added topic tests --- src/redis.js | 1 + src/topics.js | 192 +++++++++++++++++++++++----------------------- src/websockets.js | 6 ++ tests/topics.js | 85 ++++++++++++++++++++ 4 files changed, 186 insertions(+), 98 deletions(-) create mode 100644 tests/topics.js diff --git a/src/redis.js b/src/redis.js index 6689158938..d55bd57dd5 100644 --- a/src/redis.js +++ b/src/redis.js @@ -58,6 +58,7 @@ */ RedisDB.hmgetObject = function(key, fields, callback) { RedisDB.hmget(key, fields, function(err, data) { + if(err) { return callback(err, null); } diff --git a/src/topics.js b/src/topics.js index 9a24db2d36..e086903b99 100644 --- a/src/topics.js +++ b/src/topics.js @@ -18,6 +18,100 @@ var RDB = require('./redis.js'), (function(Topics) { + Topics.post = function(uid, title, content, category_id, callback) { + if (!category_id) + throw new Error('Attempted to post without a category_id'); + + if (content) + content = content.trim(); + if (title) + title = title.trim(); + + if (!uid) { + callback(new Error('not-logged-in'), null); + return; + } else if (!title || title.length < meta.config.minimumTitleLength) { + callback(new Error('title-too-short'), null); + return; + } else if (!content || content.length < meta.config.miminumPostLength) { + callback(new Error('content-too-short'), null); + return; + } + + user.getUserField(uid, 'lastposttime', function(err, lastposttime) { + if (err) lastposttime = 0; + if (Date.now() - lastposttime < meta.config.postDelay * 1000) { + callback(new Error('too-many-posts'), null); + return; + } + + RDB.incr('next_topic_id', function(err, tid) { + RDB.handle(err); + + // Global Topics + if (uid == null) uid = 0; + if (uid !== null) { + RDB.sadd('topics:tid', tid); + } else { + // need to add some unique key sent by client so we can update this with the real uid later + RDB.lpush('topics:queued:tid', tid); + } + + var slug = tid + '/' + utils.slugify(title); + var timestamp = Date.now(); + RDB.hmset('topic:' + tid, { + 'tid': tid, + 'uid': uid, + 'cid': category_id, + 'title': title, + 'slug': slug, + 'timestamp': timestamp, + 'lastposttime': 0, + 'postcount': 0, + 'viewcount': 0, + 'locked': 0, + 'deleted': 0, + 'pinned': 0 + }); + + topicSearch.index(title, tid); + + user.addTopicIdToUser(uid, tid); + + // let everyone know that there is an unread topic in this category + RDB.del('cid:' + category_id + ':read_by_uid', function(err, data) { + Topics.markAsRead(tid, uid); + }); + + + // in future it may be possible to add topics to several categories, so leaving the door open here. + RDB.zadd('categories:' + category_id + ':tid', timestamp, tid); + RDB.hincrby('category:' + category_id, 'topic_count', 1); + RDB.incr('totaltopiccount'); + + feed.updateCategory(category_id); + + posts.create(uid, tid, content, function(err, postData) { + if(err) { + return callback(err, null); + } else if(!postData) { + return callback(new Error('invalid-post'), null); + } + + // Auto-subscribe the post creator to the newly created topic + threadTools.toggleFollow(tid, uid); + + Topics.getTopicForCategoryView(tid, uid, function(topicData) { + callback(null, { + topicData: topicData, + postData: postData + }); + }); + }); + }); + }); + }; + Topics.getTopicData = function(tid, callback) { RDB.hgetall('topic:' + tid, function(err, data) { if(err) { @@ -678,104 +772,6 @@ var RDB = require('./redis.js'), }); } - Topics.post = function(uid, title, content, category_id, callback) { - if (!category_id) - throw new Error('Attempted to post without a category_id'); - - if (content) - content = content.trim(); - if (title) - title = title.trim(); - - if (uid === 0) { - callback(new Error('not-logged-in'), null); - return; - } else if (!title || title.length < meta.config.minimumTitleLength) { - callback(new Error('title-too-short'), null); - return; - } else if (!content || content.length < meta.config.miminumPostLength) { - callback(new Error('content-too-short'), null); - return; - } - - user.getUserField(uid, 'lastposttime', function(err, lastposttime) { - if (err) lastposttime = 0; - if (Date.now() - lastposttime < meta.config.postDelay * 1000) { - callback(new Error('too-many-posts'), null); - return; - } - - RDB.incr('next_topic_id', function(err, tid) { - RDB.handle(err); - - // Global Topics - if (uid == null) uid = 0; - if (uid !== null) { - RDB.sadd('topics:tid', tid); - } else { - // need to add some unique key sent by client so we can update this with the real uid later - RDB.lpush('topics:queued:tid', tid); - } - - var slug = tid + '/' + utils.slugify(title); - var timestamp = Date.now(); - RDB.hmset('topic:' + tid, { - 'tid': tid, - 'uid': uid, - 'cid': category_id, - 'title': title, - 'slug': slug, - 'timestamp': timestamp, - 'lastposttime': 0, - 'postcount': 0, - 'viewcount': 0, - 'locked': 0, - 'deleted': 0, - 'pinned': 0 - }); - - topicSearch.index(title, tid); - - user.addTopicIdToUser(uid, tid); - - // let everyone know that there is an unread topic in this category - RDB.del('cid:' + category_id + ':read_by_uid', function(err, data) { - Topics.markAsRead(tid, uid); - }); - - - // in future it may be possible to add topics to several categories, so leaving the door open here. - RDB.zadd('categories:' + category_id + ':tid', timestamp, tid); - RDB.hincrby('category:' + category_id, 'topic_count', 1); - RDB.incr('totaltopiccount'); - - feed.updateCategory(category_id); - - posts.create(uid, tid, content, function(err, postData) { - if(err) { - return callback(err, null); - } else if(!postData) { - return callback(new Error('invalid-post'), null); - } - - // Auto-subscribe the post creator to the newly created topic - threadTools.toggleFollow(tid, uid); - - // Notify any users looking at the category that a new topic has arrived - Topics.getTopicForCategoryView(tid, uid, function(topicData) { - io.sockets.in('category_' + category_id).emit('event:new_topic', topicData); - io.sockets.in('recent_posts').emit('event:new_topic', topicData); - io.sockets.in('user/' + uid).emit('event:new_post', { - posts: postData - }); - }); - - callback(null, postData); - }); - }); - }); - }; - Topics.getTopicField = function(tid, field, callback) { RDB.hget('topic:' + tid, field, callback); } diff --git a/src/websockets.js b/src/websockets.js index 15193ea45c..326757cc2b 100644 --- a/src/websockets.js +++ b/src/websockets.js @@ -380,6 +380,12 @@ module.exports.init = function(io) { } if (result) { + io.sockets.in('category_' + data.category_id).emit('event:new_topic', topicData); + io.sockets.in('recent_posts').emit('event:new_topic', topicData); + io.sockets.in('user/' + uid).emit('event:new_post', { + posts: postData + }); + posts.getTopicPostStats(); socket.emit('event:alert', { diff --git a/tests/topics.js b/tests/topics.js new file mode 100644 index 0000000000..3b7655fbbf --- /dev/null +++ b/tests/topics.js @@ -0,0 +1,85 @@ + + + +var winston = require('winston'); + +process.on('uncaughtException', function (err) { + winston.error('Encountered error while running test suite: ' + err.message); +}); + +var assert = require('assert'), + RDB = require('../mocks/redismock'); + +// Reds is not technically used in this test suite, but its invocation is required to stop the included +// libraries from trying to connect to the default Redis host/port +var reds = require('reds'); +reds.createClient = function () { + return reds.client || (reds.client = RDB); +}; + +var Topics = require('../src/topics'); + +describe('Topics', function() { + var newTopic; + var newPost; + var userInfo; + + describe('.post', function() { + it('should post a new topic', function(done) { + var uid = 1, + cid = 1, + title = 'Test Topic Title', + content = 'The content of test topic'; + + Topics.post(uid, title, content, cid, function(err, result) { + assert.equal(err, null, 'was created with error'); + assert.ok(result); + + newTopic = result.topicData; + newPost = result.postData; + + done(); + }); + }); + + it('should fail posting a topic', function(done) { + var uid = null, + cid = 1, + title = 'Test Topic Title', + content = 'The content of test topic'; + + Topics.post(uid, title, content, cid, function(err, result) { + assert.equal(err.message, 'not-logged-in'); + done(); + }); + }); + }); + + describe('.getTopicData', function() { + it('should get Topic data', function(done) { + Topics.getTopicData(newTopic.tid, function(err, result) { + done.apply(this.arguments); + }); + }); + }); + + + describe('.getTopicDataWithUser', function() { + it('should get Topic data with user info', function(done) { + Topics.getTopicDataWithUser(newTopic.tid, function(err, result) { + + done.apply(this.arguments); + }); + }); + }); + + + after(function() { + RDB.send_command('flushdb', [], function(error){ + if(error){ + winston.error(error); + throw new Error(error); + } + }); + }); +}); \ No newline at end of file From a827888ee3d2166ac1a7011751299b242f743490 Mon Sep 17 00:00:00 2001 From: Baris Soner Usakli Date: Sun, 17 Nov 2013 12:23:19 -0500 Subject: [PATCH 026/142] closes #503 --- src/websockets.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/websockets.js b/src/websockets.js index 326757cc2b..f3255811a2 100644 --- a/src/websockets.js +++ b/src/websockets.js @@ -380,10 +380,10 @@ module.exports.init = function(io) { } if (result) { - io.sockets.in('category_' + data.category_id).emit('event:new_topic', topicData); - io.sockets.in('recent_posts').emit('event:new_topic', topicData); + io.sockets.in('category_' + data.category_id).emit('event:new_topic', result.topicData); + io.sockets.in('recent_posts').emit('event:new_topic', result.topicData); io.sockets.in('user/' + uid).emit('event:new_post', { - posts: postData + posts: result.postData }); posts.getTopicPostStats(); From 22eabf6620350a36e241c8bbc468d9c31bd2a2f4 Mon Sep 17 00:00:00 2001 From: Denis Wolf Date: Sun, 17 Nov 2013 23:35:18 +0200 Subject: [PATCH 027/142] tests: topic.js: naming flow fix for .post --- tests/topics.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/tests/topics.js b/tests/topics.js index 3b7655fbbf..149063bd0f 100644 --- a/tests/topics.js +++ b/tests/topics.js @@ -1,6 +1,3 @@ - - - var winston = require('winston'); process.on('uncaughtException', function (err) { @@ -19,19 +16,19 @@ reds.createClient = function () { var Topics = require('../src/topics'); -describe('Topics', function() { +describe('Topic\'s', function() { var newTopic; var newPost; var userInfo; describe('.post', function() { - it('should post a new topic', function(done) { - var uid = 1, - cid = 1, + it('should create a new topic with proper parameters', function(done) { + var userId = 1, + categoryId = 1, title = 'Test Topic Title', content = 'The content of test topic'; - Topics.post(uid, title, content, cid, function(err, result) { + Topics.post(userId, title, content, categoryId, function(err, result) { assert.equal(err, null, 'was created with error'); assert.ok(result); @@ -42,7 +39,7 @@ describe('Topics', function() { }); }); - it('should fail posting a topic', function(done) { + it('should fail to create new topic with wrong parameters', function(done) { var uid = null, cid = 1, title = 'Test Topic Title', From 6893bd8b046ef9da33f9efe90a5e2dde10a8d242 Mon Sep 17 00:00:00 2001 From: Denis Wolf Date: Sun, 17 Nov 2013 23:41:38 +0200 Subject: [PATCH 028/142] tests: topic.js: extract mock data init in .post --- tests/topics.js | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/tests/topics.js b/tests/topics.js index 149063bd0f..de875ab868 100644 --- a/tests/topics.js +++ b/tests/topics.js @@ -19,16 +19,21 @@ var Topics = require('../src/topics'); describe('Topic\'s', function() { var newTopic; var newPost; - var userInfo; describe('.post', function() { - it('should create a new topic with proper parameters', function(done) { - var userId = 1, - categoryId = 1, - title = 'Test Topic Title', - content = 'The content of test topic'; + var topic; + + beforeEach(function(){ + topic = { + userId: 1, + categoryId: 1, + title: 'Test Topic Title', + content: 'The content of test topic' + }; + }); - Topics.post(userId, title, content, categoryId, function(err, result) { + it('should create a new topic with proper parameters', function(done) { + Topics.post(topic.userId, topic.title, topic.content, topic.categoryId, function(err, result) { assert.equal(err, null, 'was created with error'); assert.ok(result); @@ -40,12 +45,9 @@ describe('Topic\'s', function() { }); it('should fail to create new topic with wrong parameters', function(done) { - var uid = null, - cid = 1, - title = 'Test Topic Title', - content = 'The content of test topic'; + topic.userId = null; - Topics.post(uid, title, content, cid, function(err, result) { + Topics.post(topic.userId, topic.title, topic.content, topic.categoryId, function(err, result) { assert.equal(err.message, 'not-logged-in'); done(); }); From cbbb7a7c8eebc892b241a20ec283909c96069ffa Mon Sep 17 00:00:00 2001 From: Denis Wolf Date: Sun, 17 Nov 2013 23:50:19 +0200 Subject: [PATCH 029/142] tests: topic.js: extract mock data init in getters --- tests/topics.js | 57 +++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/tests/topics.js b/tests/topics.js index de875ab868..16bc92ee32 100644 --- a/tests/topics.js +++ b/tests/topics.js @@ -17,29 +17,24 @@ reds.createClient = function () { var Topics = require('../src/topics'); describe('Topic\'s', function() { - var newTopic; - var newPost; + var topic; + + beforeEach(function(){ + topic = { + userId: 1, + categoryId: 1, + title: 'Test Topic Title', + content: 'The content of test topic' + }; + }); describe('.post', function() { - var topic; - - beforeEach(function(){ - topic = { - userId: 1, - categoryId: 1, - title: 'Test Topic Title', - content: 'The content of test topic' - }; - }); it('should create a new topic with proper parameters', function(done) { Topics.post(topic.userId, topic.title, topic.content, topic.categoryId, function(err, result) { assert.equal(err, null, 'was created with error'); assert.ok(result); - newTopic = result.topicData; - newPost = result.postData; - done(); }); }); @@ -54,25 +49,35 @@ describe('Topic\'s', function() { }); }); - describe('.getTopicData', function() { - it('should get Topic data', function(done) { - Topics.getTopicData(newTopic.tid, function(err, result) { - done.apply(this.arguments); + describe('Get methods', function() { + var newTopic; + var newPost; + + beforeEach(function(done){ + Topics.post(topic.userId, topic.title, topic.content, topic.categoryId, function(err, result) { + newTopic = result.topicData; + newPost = result.postData; + done(); }); }); - }); - - describe('.getTopicDataWithUser', function() { - it('should get Topic data with user info', function(done) { - Topics.getTopicDataWithUser(newTopic.tid, function(err, result) { + describe('.getTopicData', function() { + it('should get Topic data', function(done) { + Topics.getTopicData(newTopic.tid, function(err, result) { + done.apply(this.arguments); + }); + }); + }); - done.apply(this.arguments); + describe('.getTopicDataWithUser', function() { + it('should get Topic data with user info', function(done) { + Topics.getTopicDataWithUser(newTopic.tid, function(err, result) { + done.apply(this.arguments); + }); }); }); }); - after(function() { RDB.send_command('flushdb', [], function(error){ if(error){ From 7c3fa30c1304efb9244895de02d72b529a790c21 Mon Sep 17 00:00:00 2001 From: Denis Wolf Date: Mon, 18 Nov 2013 00:00:22 +0200 Subject: [PATCH 030/142] tests: topic.js: fixed description to mirror code, refactored asserts since mocha's 'done' can process errors in callback --- tests/topics.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/tests/topics.js b/tests/topics.js index 16bc92ee32..063451cc3e 100644 --- a/tests/topics.js +++ b/tests/topics.js @@ -62,18 +62,14 @@ describe('Topic\'s', function() { }); describe('.getTopicData', function() { - it('should get Topic data', function(done) { - Topics.getTopicData(newTopic.tid, function(err, result) { - done.apply(this.arguments); - }); + it('should not receive errors', function(done) { + Topics.getTopicData(newTopic.tid, done); }); }); describe('.getTopicDataWithUser', function() { - it('should get Topic data with user info', function(done) { - Topics.getTopicDataWithUser(newTopic.tid, function(err, result) { - done.apply(this.arguments); - }); + it('should not receive errors', function(done) { + Topics.getTopicDataWithUser(newTopic.tid, done); }); }); }); From 1f3f672d3f9e64323a86c72ab3e487c4df383366 Mon Sep 17 00:00:00 2001 From: Damian Bushong Date: Mon, 18 Nov 2013 02:27:48 -0600 Subject: [PATCH 031/142] /bin/bash to /bin/sh No bashisms present? Don't be explicit about needing bash, then. --- nodebb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nodebb b/nodebb index 3733935784..1b43292926 100755 --- a/nodebb +++ b/nodebb @@ -1,6 +1,6 @@ -#!/bin/bash +#!/bin/sh clear echo "Launching NodeBB in \"development\" mode." echo "To run the production build of NodeBB, please use \"forever\"." echo "More Information: https://github.com/designcreateplay/NodeBB/wiki/How-to-run-NodeBB" -NODE_ENV=development supervisor --extensions 'node|js|tpl' -- app $1 \ No newline at end of file +NODE_ENV=development supervisor --extensions 'node|js|tpl' -- app $1 From 51355a53d9bae0d1761d134105ea5a72ac7c151e Mon Sep 17 00:00:00 2001 From: Damian Bushong Date: Mon, 18 Nov 2013 02:39:08 -0600 Subject: [PATCH 032/142] cleanup webserver.js requires the .js in a require is implicit and unnecessary; sorted the requires out so that builtins are first, then npm deps, then locally provided libs. minor changes to some strings, just using single-quotes to match the style through the rest of the file. --- src/webserver.js | 50 +++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/src/webserver.js b/src/webserver.js index 839bcdbdfc..cb0613b60d 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -1,31 +1,33 @@ -var express = require('express'), +var path = require('path'), + fs = require('fs'), + + express = require('express'), express_namespace = require('express-namespace'), WebServer = express(), server = require('http').createServer(WebServer), RedisStore = require('connect-redis')(express), - path = require('path'), - RDB = require('./redis'), - utils = require('../public/src/utils.js'), - pkg = require('../package.json'), - fs = require('fs'), - - user = require('./user.js'), - categories = require('./categories.js'), - posts = require('./posts.js'), - topics = require('./topics.js'), - notifications = require('./notifications.js'), - admin = require('./routes/admin.js'), - userRoute = require('./routes/user.js'), - apiRoute = require('./routes/api.js'), - auth = require('./routes/authentication.js'), - meta = require('./meta.js'), - feed = require('./feed'), - plugins = require('./plugins'), nconf = require('nconf'), winston = require('winston'), validator = require('validator'), async = require('async'), - logger = require('./logger.js'); + + pkg = require('../package.json'), + + utils = require('../public/src/utils'), + RDB = require('./redis'), + user = require('./user'), + categories = require('./categories'), + posts = require('./posts'), + topics = require('./topics'), + notifications = require('./notifications'), + admin = require('./routes/admin'), + userRoute = require('./routes/user'), + apiRoute = require('./routes/api'), + auth = require('./routes/authentication'), + meta = require('./meta'), + feed = require('./feed'), + plugins = require('./plugins'), + logger = require('./logger'); (function (app) { "use strict"; @@ -144,7 +146,7 @@ var express = require('express'), res.locals.csrf_token = req.session._csrf; // Disable framing - res.setHeader("X-Frame-Options", "DENY"); + res.setHeader('X-Frame-Options', 'DENY'); next(); }); @@ -645,7 +647,7 @@ var express = require('express'), req: req, res: res }, function (err, header) { - res.send(header + app.create_route("recent/" + req.params.term, null, "recent") + templates.footer); + res.send(header + app.create_route('recent/' + req.params.term, null, 'recent') + templates.footer); }); }); @@ -676,7 +678,7 @@ var express = require('express'), req: req, res: res }, function (err, header) { - res.send(header + app.create_route("search/" + req.params.term, null, "search") + templates.footer); + res.send(header + app.create_route('search/' + req.params.term, null, 'search') + templates.footer); }); }); @@ -736,4 +738,4 @@ var express = require('express'), }(WebServer)); -global.server = server; \ No newline at end of file +global.server = server; From c8c355b319742e84b1a900f92370679cdedbfc4d Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 18 Nov 2013 08:19:04 -0500 Subject: [PATCH 033/142] added komodoproject file extensions to gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 478600f89f..283097a56e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ public/css/*.css *.swp Vagrantfile .vagrant -provision.sh \ No newline at end of file +provision.sh +*.komodoproject From 28dab60232b9b8e18cbd5b5e66177f6843c60fa9 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 18 Nov 2013 15:09:34 -0500 Subject: [PATCH 034/142] resolving notifs issue, I hope --- src/user.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/user.js b/src/user.js index c40e17fa6d..43b5dfdb6c 100644 --- a/src/user.js +++ b/src/user.js @@ -1009,7 +1009,10 @@ var utils = require('./../public/src/utils.js'), next(null, notif_data); }); }, function(err, notifs) { - notifs = notifs.sort(function(a, b) { + notifs = notifs.filter(function(notif) { + if (notif !== null) return true; + else return false; + }).sort(function(a, b) { return parseInt(b.datetime, 10) - parseInt(a.datetime, 10); }).map(function(notif) { notif.datetimeISO = new Date(parseInt(notif.datetime, 10)).toISOString(); From 586a181e0a3c9c065a1745c0936d6216a4e77d64 Mon Sep 17 00:00:00 2001 From: Baris Usakli Date: Mon, 18 Nov 2013 15:44:32 -0500 Subject: [PATCH 035/142] closes #507 --- public/src/forum/login.js | 6 ++++++ public/templates/login.tpl | 2 +- src/routes/api.js | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/public/src/forum/login.js b/public/src/forum/login.js index 96e6c49e94..7ff2ff5c1d 100644 --- a/public/src/forum/login.js +++ b/public/src/forum/login.js @@ -63,6 +63,12 @@ define(function() { }); document.querySelector('#content input').focus(); + + if(!config.emailSetup) + $('#reset-link').addClass('hide'); + else + $('#reset-link').removeClass('hide'); + }; return Login; diff --git a/public/templates/login.tpl b/public/templates/login.tpl index 10d9d08948..365fe6e477 100644 --- a/public/templates/login.tpl +++ b/public/templates/login.tpl @@ -40,7 +40,7 @@
-   [[login:forgot_password]] +   [[login:forgot_password]]
diff --git a/src/routes/api.js b/src/routes/api.js index 5142cf1202..0788dd0cbe 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -31,6 +31,7 @@ var user = require('./../user.js'), config.maximumUsernameLength = meta.config.maximumUsernameLength; config.minimumPasswordLength = meta.config.minimumPasswordLength; config.useOutgoingLinksPage = meta.config.useOutgoingLinksPage; + config.emailSetup = !!meta.config['email:from']; res.json(200, config); }); From cc0fe66e3e3fb544a1a202dfd91bc23df10c118c Mon Sep 17 00:00:00 2001 From: Baris Usakli Date: Mon, 18 Nov 2013 15:56:12 -0500 Subject: [PATCH 036/142] minor tweak to notif filter :) --- src/user.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/user.js b/src/user.js index 43b5dfdb6c..f988d105ec 100644 --- a/src/user.js +++ b/src/user.js @@ -1010,8 +1010,7 @@ var utils = require('./../public/src/utils.js'), }); }, function(err, notifs) { notifs = notifs.filter(function(notif) { - if (notif !== null) return true; - else return false; + return notif !== null; }).sort(function(a, b) { return parseInt(b.datetime, 10) - parseInt(a.datetime, 10); }).map(function(notif) { From 7c950cc35080784f996f4faf62f6d0fc7736de90 Mon Sep 17 00:00:00 2001 From: Baris Usakli Date: Mon, 18 Nov 2013 16:22:43 -0500 Subject: [PATCH 037/142] require cleanup in user.js, removed user.latest not used anymore --- src/user.js | 43 +++++++++++-------------------------------- src/websockets.js | 4 ---- 2 files changed, 11 insertions(+), 36 deletions(-) diff --git a/src/user.js b/src/user.js index f988d105ec..a51d842e60 100644 --- a/src/user.js +++ b/src/user.js @@ -1,16 +1,17 @@ -var utils = require('./../public/src/utils.js'), - RDB = require('./redis.js'), - emailjs = require('emailjs'), - meta = require('./meta.js'), - emailjsServer = emailjs.server.connect(meta.config['email:smtp:host'] || '127.0.0.1'), - bcrypt = require('bcrypt'), - Groups = require('./groups'), - notifications = require('./notifications.js'), - topics = require('./topics.js'), +var bcrypt = require('bcrypt'), async = require('async'), + emailjs = require('emailjs'), nconf = require('nconf'), + winston = require('winston'), userSearch = require('reds').createSearch('nodebbusersearch'), - winston = require('winston'); + + utils = require('./../public/src/utils'), + RDB = require('./redis'), + meta = require('./meta'), + emailjsServer = emailjs.server.connect(meta.config['email:smtp:host'] || '127.0.0.1'), + Groups = require('./groups'), + notifications = require('./notifications'), + topics = require('./topics'); (function(User) { 'use strict'; @@ -114,13 +115,6 @@ var utils = require('./../public/src/utils.js'), userSearch.index(username, uid); - if (typeof io !== 'undefined') { - io.sockets.emit('user.latest', { - userslug: userslug, - username: username - }); - } - if (password !== undefined) { User.hashPassword(password, function(err, hash) { User.setUserField(uid, 'password', hash); @@ -642,21 +636,6 @@ var utils = require('./../public/src/utils.js'), }); }; - User.latest = function(socket) { - RDB.zrevrange('users:joindate', 0, 0, function(err, uid) { - RDB.handle(err); - - User.getUserFields(uid, ['username', 'userslug'], function(err, userData) { - if (!err && userData) { - socket.emit('user.latest', { - userslug: userData.userslug, - username: userData.username - }); - } - }); - }); - }; - User.getUidByUsername = function(username, callback) { RDB.hget('username:uid', username, callback); }; diff --git a/src/websockets.js b/src/websockets.js index f3255811a2..41f42a944c 100644 --- a/src/websockets.js +++ b/src/websockets.js @@ -247,10 +247,6 @@ module.exports.init = function(io) { posts.getTopicPostStats(); }); - socket.on('user.latest', function(data) { - user.latest(socket, data); - }); - socket.on('user.email.exists', function(data) { user.email.exists(socket, data.email); }); From 635fba1e45f0c5c64f5bd865388a642efeb0522e Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 18 Nov 2013 20:25:43 -0500 Subject: [PATCH 038/142] upping cerlean minver --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 767d955a12..1bb13f6258 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "nodebb-plugin-mentions": "~0.1.14", "nodebb-plugin-markdown": "~0.1.8", "nodebb-theme-vanilla": "designcreateplay/nodebb-theme-vanilla", - "nodebb-theme-cerulean": "0.0.6", + "nodebb-theme-cerulean": "0.0.7", "cron": "~1.0.1" }, "optionalDependencies": { From 7e4faa32705218fba7221bd14c26ab4cfb8db6f6 Mon Sep 17 00:00:00 2001 From: Baris Usakli Date: Tue, 19 Nov 2013 12:38:13 -0500 Subject: [PATCH 039/142] closes #514 --- public/templates/notifications.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/templates/notifications.tpl b/public/templates/notifications.tpl index 7731de32e0..0c610a42b2 100644 --- a/public/templates/notifications.tpl +++ b/public/templates/notifications.tpl @@ -5,7 +5,7 @@
  • - {notifications.text} + {notifications.text}

    From 0b922d3f60f496f743c72c37395f54dac3995033 Mon Sep 17 00:00:00 2001 From: Baris Usakli Date: Tue, 19 Nov 2013 13:04:12 -0500 Subject: [PATCH 040/142] possible fix for #516 --- app.js | 1 + src/routes/admin.js | 2 +- src/routes/user.js | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index 896dd82750..e3e8457545 100644 --- a/app.js +++ b/app.js @@ -64,6 +64,7 @@ nconf.set('url', nconf.get('base_url') + (nconf.get('use_port') ? ':' + nconf.get('port') : '') + nconf.get('relative_path') + '/'); nconf.set('upload_url', nconf.get('url') + 'uploads/'); + nconf.set('base_dir', __dirname); winston.info('Initializing NodeBB v' + pkg.version + ', on port ' + nconf.get('port') + ', using Redis store at ' + nconf.get('redis:host') + ':' + nconf.get('redis:port') + '.'); winston.info('NodeBB instance bound to: ' + ((nconf.get('bind_address') === "0.0.0.0" || !nconf.get('bind_address')) ? 'Any address (0.0.0.0)' : nconf.get('bind_address'))); diff --git a/src/routes/admin.js b/src/routes/admin.js index d2f8871be3..481891ad00 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -111,7 +111,7 @@ var user = require('./../user.js'), } var filename = 'site-logo' + extension; - var uploadPath = path.join(process.cwd(), nconf.get('upload_path'), filename); + var uploadPath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), filename); winston.info('Attempting upload to: ' + uploadPath); diff --git a/src/routes/user.js b/src/routes/user.js index 27320e25e6..9b6d0d4a9d 100644 --- a/src/routes/user.js +++ b/src/routes/user.js @@ -159,7 +159,7 @@ var user = require('./../user.js'), return; } - var absolutePath = path.join(process.cwd(), nconf.get('upload_path'), path.basename(oldpicture)); + var absolutePath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), path.basename(oldpicture)); fs.unlink(absolutePath, function (err) { if (err) { @@ -181,7 +181,7 @@ var user = require('./../user.js'), } var filename = uid + '-profileimg' + extension; - var uploadPath = path.join(process.cwd(), nconf.get('upload_path'), filename); + var uploadPath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), filename); winston.info('Attempting upload to: ' + uploadPath); From 11e3b0da7dda40d54b5cff718a929ec7eef06889 Mon Sep 17 00:00:00 2001 From: Baris Usakli Date: Tue, 19 Nov 2013 15:03:58 -0500 Subject: [PATCH 041/142] added spacing between share and edit buttons --- public/templates/topic.tpl | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/public/templates/topic.tpl b/public/templates/topic.tpl index 668c565d68..76240725f3 100644 --- a/public/templates/topic.tpl +++ b/public/templates/topic.tpl @@ -63,17 +63,17 @@ - -
    - - -
    - -
    - - - - +
    +
    + + + + +
    +
    + + +
    From 9fbb139e67ac008560241889c265bad5565b3403 Mon Sep 17 00:00:00 2001 From: Baris Soner Usakli Date: Wed, 20 Nov 2013 12:22:59 -0500 Subject: [PATCH 042/142] fix post delete state after more posts are loaded --- public/src/forum/topic.js | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/public/src/forum/topic.js b/public/src/forum/topic.js index a55be43a53..f865993289 100644 --- a/public/src/forum/topic.js +++ b/public/src/forum/topic.js @@ -17,6 +17,17 @@ define(function() { google_url = templates.get('google-share-url'); + function fixDeleteStateForPosts() { + var postEls = document.querySelectorAll('#post-container li[data-deleted]'); + for (var x = 0, numPosts = postEls.length; x < numPosts; x++) { + if (postEls[x].getAttribute('data-deleted') === '1') { + toggle_post_delete_state(postEls[x].getAttribute('data-pid')); + } + postEls[x].removeAttribute('data-deleted'); + } + } + + jQuery('document').ready(function() { app.addCommasToNumbers(); @@ -182,12 +193,8 @@ define(function() { }); } - // Fix delete state for this thread's posts - var postEls = document.querySelectorAll('#post-container li[data-deleted]'); - for (var x = 0, numPosts = postEls.length; x < numPosts; x++) { - if (postEls[x].getAttribute('data-deleted') === '1') toggle_post_delete_state(postEls[x].getAttribute('data-pid')); - postEls[x].removeAttribute('data-deleted'); - } + fixDeleteStateForPosts(); + // Follow Thread State var followEl = $('.main-post .follow'), @@ -259,7 +266,9 @@ define(function() { var bottom = ($(document).height() - $(window).height()) * 0.9; if ($(window).scrollTop() > bottom && !app.infiniteLoaderActive && $('#post-container').children().length) { - app.loadMorePosts(tid); + app.loadMorePosts(tid, function(posts) { + fixDeleteStateForPosts(); + }); } }); } From 2cf55dcf9f1aab64c4edf02a9a9395c5d9b7f0e3 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 21 Nov 2013 12:28:10 -0500 Subject: [PATCH 043/142] added action:page.load hook --- public/src/ajaxify.js | 52 ++++++++++++++++++++++++++++++++----------- src/routes/plugins.js | 21 ++++++++++++++++- 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index be38d66057..f233627fde 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -1,5 +1,6 @@ -var ajaxify = {}; +"use strict"; +var ajaxify = {}; (function ($) { /*global app, templates, utils*/ @@ -23,8 +24,9 @@ var ajaxify = {}; window.onpopstate = function (event) { // "quiet": If set to true, will not call pushState - if (event !== null && event.state && event.state.url !== undefined) + if (event !== null && event.state && event.state.url !== undefined) { ajaxify.go(event.state.url, null, null, true); + } }; var pagination; @@ -35,7 +37,10 @@ var ajaxify = {}; app.enter_room('global'); pagination = pagination || document.getElementById('pagination'); - if (pagination) pagination.parentNode.style.display = 'none'; + if (pagination) { + pagination.parentNode.style.display = 'none'; + } + window.onscroll = null; // end @@ -65,8 +70,20 @@ var ajaxify = {}; if (quiet !== true) { if (window.history && window.history.pushState) { window.history.pushState({ - "url": url - }, url, RELATIVE_PATH + "/" + url); + url: url + }, url, RELATIVE_PATH + '/' + url); + + $.ajax(RELATIVE_PATH + '/plugins/fireHook', { + type: 'PUT', + data: { + _csrf: $('#csrf_token').val(), + hook: 'page.load', + args: { + template: tpl_url, + url: '/' + url + } + } + }); } } @@ -78,7 +95,9 @@ var ajaxify = {}; templates.load_template(function () { exec_body_scripts(content); require(['forum/' + tpl_url], function(script) { - if (script && script.init) script.init(); + if (script && script.init) { + script.init(); + } }); if (callback) { @@ -88,10 +107,13 @@ var ajaxify = {}; app.process_page(); jQuery('#content, #footer').stop(true, true).fadeIn(200, function () { - if (window.location.hash) + if (window.location.hash) { hash = window.location.hash; - if (hash) + } + + if (hash) { app.scrollToPost(hash.substr(1)); + } }); utils.refreshTitle(url); @@ -105,23 +127,27 @@ var ajaxify = {}; }; $('document').ready(function () { - if (!window.history || !window.history.pushState) return; // no ajaxification for old browsers + if (!window.history || !window.history.pushState) { + return; // no ajaxification for old browsers + } content = content || document.getElementById('content'); // Enhancing all anchors to ajaxify... $(document.body).on('click', 'a', function (e) { function hrefEmpty(href) { - return href == 'javascript:;' || href == window.location.href + "#" || href.slice(-1) === "#"; + return href === 'javascript:;' || href === window.location.href + "#" || href.slice(-1) === "#"; } - if (hrefEmpty(this.href) || this.target !== '' || this.protocol === 'javascript:') + if (hrefEmpty(this.href) || this.target !== '' || this.protocol === 'javascript:') { return; + } - if(!window.location.pathname.match(/\/(403|404)$/g)) + if(!window.location.pathname.match(/\/(403|404)$/g)) { app.previousUrl = window.location.href; + } - if (this.getAttribute('data-ajaxify') == 'false') { + if (this.getAttribute('data-ajaxify') === 'false') { return; } diff --git a/src/routes/plugins.js b/src/routes/plugins.js index 33e384e8d2..d4243426ed 100644 --- a/src/routes/plugins.js +++ b/src/routes/plugins.js @@ -1,9 +1,28 @@ +"use strict"; + var nconf = require('nconf'), path = require('path'), fs = require('fs'), Plugins = require('../plugins'), PluginRoutes = function(app) { + app.get('/plugins/fireHook', function(req, res) { + // GET = filter + Plugins.fireHook('filter:' + req.query.hook, req.query.args, function(err, returnData) { + if (typeof returnData === 'object') { + res.json(200, returnData); + } else { + res.send(200, returnData); + } + }); + }); + + app.put('/plugins/fireHook', function(req, res) { + // PUT = action + Plugins.fireHook('action:' + req.body.hook, req.body.args); + res.send(200); + }); + // Static Assets app.get('/plugins/:id/*', function(req, res) { var relPath = req.url.replace('/plugins/' + req.params.id, ''); @@ -15,7 +34,7 @@ var nconf = require('nconf'), } else { res.redirect('/404'); } - }) + }); } else { res.redirect('/404'); } From d631a4b2e5f1a1ad3ee3d62068eb2ee1e258c567 Mon Sep 17 00:00:00 2001 From: Baris Usakli Date: Thu, 21 Nov 2013 13:53:19 -0500 Subject: [PATCH 044/142] topic.tpl changes, moved users to the bottom of main post, added reply thread tools buttons under main post, added posts and view count --- public/src/forum/topic.js | 63 +++++++++++++++++++------------------- public/templates/topic.tpl | 42 ++++++++++++++++++++----- 2 files changed, 66 insertions(+), 39 deletions(-) diff --git a/public/src/forum/topic.js b/public/src/forum/topic.js index f865993289..4ee458b501 100644 --- a/public/src/forum/topic.js +++ b/public/src/forum/topic.js @@ -32,12 +32,10 @@ define(function() { app.addCommasToNumbers(); + app.enter_room('topic_' + tid); - var room = 'topic_' + tid, - adminTools = document.getElementById('thread-tools'); - - app.enter_room(room); - + if($('#post-container .sub-posts').length) + $('.topic-main-buttons').removeClass('hide'); $('.twitter-share').on('click', function () { window.open(twitter_url, '_blank', 'width=550,height=420,scrollbars=no,status=no'); @@ -61,11 +59,10 @@ define(function() { if (expose_tools === '1') { var moveThreadModal = $('#move_thread_modal'); - - adminTools.style.visibility = 'inherit'; + $('.thread-tools').removeClass('hide'); // Add events to the thread tools - $('#delete_thread').on('click', function(e) { + $('.delete_thread').on('click', function(e) { if (thread_state.deleted !== '1') { bootbox.confirm('Are you sure you want to delete this thread?', function(confirm) { if (confirm) { @@ -84,7 +81,7 @@ define(function() { return false; }); - $('#lock_thread').on('click', function(e) { + $('.lock_thread').on('click', function(e) { if (thread_state.locked !== '1') { socket.emit('api:topic.lock', { tid: tid @@ -97,7 +94,7 @@ define(function() { return false; }); - $('#pin_thread').on('click', function(e) { + $('.pin_thread').on('click', function(e) { if (thread_state.pinned !== '1') { socket.emit('api:topic.pin', { tid: tid @@ -110,7 +107,7 @@ define(function() { return false; }); - $('#move_thread').on('click', function(e) { + $('.move_thread').on('click', function(e) { moveThreadModal.modal('show'); return false; }); @@ -289,7 +286,7 @@ define(function() { } }; $('#post-container').on('click', '.post_reply', reply_fn); - $('#post_reply').on('click', reply_fn); + $('.topic-main-buttons').on('click', '.post_reply', reply_fn); $('#post-container').on('click', '.quote', function() { if (thread_state.locked !== '1') { @@ -390,12 +387,15 @@ define(function() { var userLink = $('').append(userIcon); userLink.attr('data-uid', uid); + var div = $('
    '); + div.append(userLink); + userLink.tooltip({ - placement: 'left', + placement: 'top', title: username }); - return userLink; + return div; } } @@ -437,7 +437,7 @@ define(function() { activeEl.find('.anonymous-box').remove(); if(anonymousCount || remainingUsers) { - var anonLink = $(''); + var anonLink = $('
    '); activeEl.append(anonLink); var title = ''; @@ -449,7 +449,7 @@ define(function() { title = anonymousCount + ' guest(s)'; anonLink.tooltip({ - placement: 'left', + placement: 'top', title: title }); } @@ -575,19 +575,19 @@ define(function() { } function set_locked_state(locked, alert) { - var threadReplyBtn = document.getElementById('post_reply'), + var threadReplyBtn = $('.topic-main-buttons .post_reply'), postReplyBtns = document.querySelectorAll('#post-container .post_reply'), quoteBtns = document.querySelectorAll('#post-container .quote'), editBtns = document.querySelectorAll('#post-container .edit'), deleteBtns = document.querySelectorAll('#post-container .delete'), numPosts = document.querySelectorAll('#post_container li[data-pid]').length, - lockThreadEl = document.getElementById('lock_thread'), + lockThreadEl = $('.lock_thread'), x; if (locked === true) { - lockThreadEl.innerHTML = ' Unlock Thread'; - threadReplyBtn.disabled = true; - threadReplyBtn.innerHTML = 'Locked '; + lockThreadEl.html(' Unlock Thread'); + threadReplyBtn.attr('disabled', true); + threadReplyBtn.html('Locked '); for (x = 0; x < numPosts; x++) { postReplyBtns[x].innerHTML = 'Locked '; quoteBtns[x].style.display = 'none'; @@ -607,9 +607,9 @@ define(function() { thread_state.locked = '1'; } else { - lockThreadEl.innerHTML = ' Lock Thread'; - threadReplyBtn.disabled = false; - threadReplyBtn.innerHTML = 'Reply'; + lockThreadEl.html(' Lock Thread'); + threadReplyBtn.attr('disabled', false); + threadReplyBtn.html('Reply'); for (x = 0; x < numPosts; x++) { postReplyBtns[x].innerHTML = 'Reply '; quoteBtns[x].style.display = 'inline-block'; @@ -632,13 +632,14 @@ define(function() { } function set_delete_state(deleted) { - var deleteThreadEl = document.getElementById('delete_thread'), - deleteTextEl = deleteThreadEl.getElementsByTagName('span')[0], + var deleteThreadEl = $('.delete_thread'), + deleteTextEl = $('.delete_thread span'), + //deleteThreadEl.getElementsByTagName('span')[0], threadEl = $('#post-container'), deleteNotice = document.getElementById('thread-deleted') || document.createElement('div'); if (deleted) { - deleteTextEl.innerHTML = ' Restore Thread'; + deleteTextEl.html(' Restore Thread'); threadEl.addClass('deleted'); // Spawn a 'deleted' notice at the top of the page @@ -649,7 +650,7 @@ define(function() { thread_state.deleted = '1'; } else { - deleteTextEl.innerHTML = ' Delete Thread'; + deleteTextEl.html(' Delete Thread'); threadEl.removeClass('deleted'); deleteNotice.parentNode.removeChild(deleteNotice); @@ -658,10 +659,10 @@ define(function() { } function set_pinned_state(pinned, alert) { - var pinEl = document.getElementById('pin_thread'); + var pinEl = $('.pin_thread'); if (pinned) { - pinEl.innerHTML = ' Unpin Thread'; + pinEl.html(' Unpin Thread'); if (alert) { app.alert({ 'alert_id': 'thread_pin', @@ -674,7 +675,7 @@ define(function() { thread_state.pinned = '1'; } else { - pinEl.innerHTML = ' Pin Thread'; + pinEl.html(' Pin Thread'); if (alert) { app.alert({ 'alert_id': 'thread_pin', diff --git a/public/templates/topic.tpl b/public/templates/topic.tpl index 76240725f3..5f40fc78d1 100644 --- a/public/templates/topic.tpl +++ b/public/templates/topic.tpl @@ -20,7 +20,7 @@
  • {topic_name}
  • - +
      @@ -98,6 +98,32 @@ +
      +
      + posts + {postcount} | + views + {viewcount} | + browsing +
      +
      + +
      +
      +
    • @@ -169,17 +195,17 @@
      -
      - -
    • -
      +
      - posts - {postcount} | - views - {viewcount} | - browsing + + posts + {postcount} | + views + {viewcount} | + browsing +
      - +
      - +