diff --git a/nodebb b/nodebb index d33baeba04..26ec782725 100755 --- a/nodebb +++ b/nodebb @@ -56,7 +56,7 @@ case "$1" in 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 -q --extensions 'node|js|tpl' -- loader "$@" + NODE_ENV=development supervisor -q --extensions 'node|js|tpl' -- app "$@" ;; *) diff --git a/public/src/forum/admin/index.js b/public/src/forum/admin/index.js index 1870884328..7c9f6db3bd 100644 --- a/public/src/forum/admin/index.js +++ b/public/src/forum/admin/index.js @@ -35,6 +35,17 @@ define(function() { $('.restart').on('click', function() { socket.emit('admin.restart'); }); + + socket.emit('admin.getVisitorCount', function(err, data) { + if(err) { + return app.alertError(err.message); + } + + var uniqueVisitors = $('#unique-visitors'); + for(var key in data) { + uniqueVisitors.find('#' + key).text(data[key]); + } + }); }; Admin.updateRoomUsage = function(err, data) { diff --git a/public/src/forum/topic.js b/public/src/forum/topic.js index 84eafc8621..9a86aaec32 100644 --- a/public/src/forum/topic.js +++ b/public/src/forum/topic.js @@ -141,16 +141,16 @@ define(['composer', 'forum/pagination'], function(composer, pagination) { } loadingEl.remove(); - categoriesEl.on('click', function(e) { - var el = $(e.target); + categoriesEl.on('click', 'li[data-cid]', function(e) { + var el = $(this); if (el.is('li')) { - confirmCat.html(e.target.innerHTML); + confirmCat.html(el.html()); confirmDiv.css({display: 'block'}); targetCid = el.attr('data-cid'); - targetCatLabel = e.html(); + targetCatLabel = el.html(); commitEl.prop('disabled', false); } - }, false); + }); commitEl.on('click', function() { if (!commitEl.prop('disabled') && targetCid) { @@ -322,11 +322,10 @@ define(['composer', 'forum/pagination'], function(composer, pagination) { localStorage.removeItem('topic:' + tid + ':bookmark'); } }); - updateHeader(); - } else { - updateHeader(); } + updateHeader(); + $('#post-container').on('mouseenter', '.favourite-tooltip', function(e) { if (!$(this).data('users-loaded')) { $(this).data('users-loaded', "true"); diff --git a/public/templates/admin/index.tpl b/public/templates/admin/index.tpl index 2d548c459c..3fac2b0917 100644 --- a/public/templates/admin/index.tpl +++ b/public/templates/admin/index.tpl @@ -36,4 +36,29 @@ </div> </div> </div> + <div class="col-sm-6"> + <div class="panel panel-default"> + <div class="panel-heading">Unique Visitors</div> + <div class="panel-body"> + <div id="unique-visitors"> + <div class="text-center pull-left"> + <div id="day"></div> + <div>Day</div> + </div> + <div class="text-center pull-left"> + <div id="week"></div> + <div>Week</div> + </div> + <div class="text-center pull-left"> + <div id="month"></div> + <div>Month</div> + </div> + <div class="text-center pull-left"> + <div id="alltime"></div> + <div>All Time</div> + </div> + </div> + </div> + </div> + </div> </div> \ No newline at end of file diff --git a/src/meta.js b/src/meta.js index f4411904fd..93f53e04d3 100644 --- a/src/meta.js +++ b/src/meta.js @@ -3,6 +3,7 @@ var fs = require('fs'), async = require('async'), winston = require('winston'), nconf = require('nconf'), + _ = require('underscore'), utils = require('./../public/src/utils'), translator = require('./../public/src/translator'), @@ -250,14 +251,19 @@ var fs = require('fs'), jsPath = path.normalize(jsPath); if (jsPath.substring(0, 7) === 'plugins') { - var paths = jsPath.split(path.sep), - mappedPath = paths[1]; + var matches = _.map(plugins.staticDirs, function(realPath, mappedPath) { + if (jsPath.match(mappedPath)) { + return mappedPath; + } else { + return null; + } + }).filter(function(a) { return a; }); - if (plugins.staticDirs[mappedPath]) { - jsPath = jsPath.replace(path.join('plugins', mappedPath), ''); - return path.join(plugins.staticDirs[mappedPath], jsPath); + if (matches.length) { + var relPath = jsPath.slice(new String('plugins/' + matches[0]).length); + return plugins.staticDirs[matches[0]] + relPath; } else { - winston.warn('[meta.scripts.get] Could not resolve mapped path: ' + mappedPath + '. Are you sure it is defined by a plugin?'); + winston.warn('[meta.scripts.get] Could not resolve mapped path: ' + jsPath + '. Are you sure it is defined by a plugin?'); return null; } } else { diff --git a/src/plugins.js b/src/plugins.js index 4d01840498..f334d8b0da 100644 --- a/src/plugins.js +++ b/src/plugins.js @@ -246,24 +246,31 @@ var fs = require('fs'), `data.priority`, the relative priority of the method when it is eventually called (default: 10) */ - if (data.hook && data.method) { + var method; + + if (data.hook && data.method && typeof data.method === 'string' && data.method.length > 0) { data.id = id; if (!data.priority) data.priority = 10; - data.method = data.method.split('.').reduce(function(memo, prop) { - if (memo[prop]) { + method = data.method.split('.').reduce(function(memo, prop) { + if (memo !== null && memo[prop]) { return memo[prop]; } else { - // Couldn't find method by path, assuming property with periods in it (evil!) - Plugins.libraries[data.id][data.method]; + // Couldn't find method by path, aborting + return null; } }, Plugins.libraries[data.id]); + if (method === null) { + winston.warn('[plugins/' + id + '] Hook method mismatch: ' + data.hook + ' => ' + data.method); + return callback(); + } + + // Write the actual method reference to the hookObj + data.method = method; + Plugins.loadedHooks[data.hook] = Plugins.loadedHooks[data.hook] || []; Plugins.loadedHooks[data.hook].push(data); - if (global.env === 'development') { - winston.info('[plugins] Hook registered: ' + data.hook + ' will call ' + id); - } callback(); } else return; }; diff --git a/src/postTools.js b/src/postTools.js index a097df2d30..b610e19415 100644 --- a/src/postTools.js +++ b/src/postTools.js @@ -90,9 +90,7 @@ var winston = require('winston'), topics.setTopicField(tid, 'thumb', options.topic_thumb); - db.searchRemove('topic', tid, function() { - db.searchIndex('topic', title, tid); - }); + plugins.fireHook('action:topic.edit', tid); } posts.getPostData(pid, function(err, postData) { diff --git a/src/posts.js b/src/posts.js index 18d71deb0f..ac428870d0 100644 --- a/src/posts.js +++ b/src/posts.js @@ -92,10 +92,10 @@ var db = require('./database'), return next(err); } - postData.content = content; - plugins.fireHook('action:post.save', postData); + postData.content = content; + next(null, postData); }); } diff --git a/src/routes/api.js b/src/routes/api.js index a57f17d2b1..3d61acedf3 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -30,6 +30,8 @@ var path = require('path'), user.updateLastOnlineTime(req.user.uid); } + db.sortedSetAdd('ip:recent', Date.now(), req.ip || 'Unknown'); + next(); }); @@ -222,15 +224,13 @@ var path = require('path'), return res.redirect('/404'); } - var limit = 50; - function searchPosts(callback) { Plugins.fireHook('filter:search.query', { index: 'post', - query: req.params.terms + query: req.params.term }, function(err, pids) { if (err) { - return callback(err, null); + return callback(err); } posts.getPostSummaryByPids(pids, false, callback); @@ -240,10 +240,10 @@ var path = require('path'), function searchTopics(callback) { Plugins.fireHook('filter:search.query', { index: 'topic', - query: req.params.terms + query: req.params.term }, function(err, tids) { if (err) { - return callback(err, null); + return callback(err); } topics.getTopicsByTids(tids, 0, callback); diff --git a/src/routes/plugins.js b/src/routes/plugins.js index 774fee7217..00f37d8d02 100644 --- a/src/routes/plugins.js +++ b/src/routes/plugins.js @@ -44,7 +44,7 @@ var nconf = require('nconf'), if (matches) { async.map(matches, function(mappedPath, next) { - var filePath = path.join(plugins.staticDirs[mappedPath], relPath.slice(mappedPath.length)); + var filePath = path.join(plugins.staticDirs[mappedPath], decodeURIComponent(relPath.slice(mappedPath.length))); fs.exists(filePath, function(exists) { if (exists) { diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js index 3d5fd87726..316d77a347 100644 --- a/src/socket.io/admin.js +++ b/src/socket.io/admin.js @@ -9,6 +9,7 @@ var groups = require('../groups'), categories = require('../categories'), CategoryTools = require('../categoryTools'), logger = require('../logger'), + db = require('../database'), admin = { user: require('../admin/user'), categories: require('../admin/categories') @@ -35,6 +36,30 @@ SocketAdmin.restart = function(socket, data, callback) { meta.restart(); }; + +SocketAdmin.getVisitorCount = function(socket, data, callback) { + var terms = { + day: 86400000, + week: 604800000, + month: 2592000000 + }; + var now = Date.now(); + async.parallel({ + day: function(next) { + db.sortedSetCount('ip:recent', now - terms.day, now, next); + }, + week: function(next) { + db.sortedSetCount('ip:recent', now - terms.week, now, next); + }, + month: function(next) { + db.sortedSetCount('ip:recent', now - terms.month, now, next); + }, + alltime: function(next) { + db.sortedSetCount('ip:recent', 0, now, next); + } + }, callback); +} + /* Topics */ SocketAdmin.topics = {}; diff --git a/src/webserver.js b/src/webserver.js index 220cb6e9b3..44b2186d93 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -392,9 +392,6 @@ process.on('uncaughtException', function(err) { // Disable framing res.setHeader('X-Frame-Options', 'SAMEORIGIN'); - // Log IP address - db.sortedSetAdd('ip:recent', +new Date(), req.ip || 'Unknown'); - next(); });