From ba9a5ad7d7d080bb6e127c5da4b390772e361ad2 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Sun, 23 Nov 2014 15:53:42 -0500 Subject: [PATCH 1/7] fixed #2448 --- src/meta/css.js | 3 ++- src/middleware/middleware.js | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/meta/css.js b/src/meta/css.js index 4006b524ae..fb1478dc1f 100644 --- a/src/meta/css.js +++ b/src/meta/css.js @@ -126,7 +126,8 @@ module.exports = function(Meta) { function minify(source, paths, destination, callback) { less.render(source, { - paths: paths + paths: paths, + compress: true }, function(err, lessOutput) { if (err) { winston.error('[meta/css] Could not minify LESS/CSS: ' + err.message); diff --git a/src/middleware/middleware.js b/src/middleware/middleware.js index a9b55c31e6..1d9ae900c6 100644 --- a/src/middleware/middleware.js +++ b/src/middleware/middleware.js @@ -315,7 +315,9 @@ middleware.renderHeader = function(req, res, callback) { var less = require('less'); - less.render(meta.config.customCSS, function(err, lessObject) { + less.render(meta.config.customCSS, { + compress: true + }, function(err, lessObject) { if (err) { winston.error('[less] Could not convert custom LESS to CSS! Please check your syntax.'); return next(null, ''); From f5fce863f966f2494101e8db3e0a9b8683208d94 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Sun, 23 Nov 2014 17:02:25 -0500 Subject: [PATCH 2/7] plugin warnings for v0.5.4+ when installing plugins with no suggested version, #2438 --- public/src/admin/extend/plugins.js | 40 ++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/public/src/admin/extend/plugins.js b/public/src/admin/extend/plugins.js index 5c1a1229ca..52c9144360 100644 --- a/public/src/admin/extend/plugins.js +++ b/public/src/admin/extend/plugins.js @@ -35,7 +35,22 @@ define('admin/extend/plugins', function() { Plugins.suggest(pluginID, function(err, payload) { if (!err) { - Plugins.toggleInstall(pluginID, payload.version); + require(['semver'], function(semver) { + if (payload.version !== 'latest') { + Plugins.toggleInstall(pluginID, payload.version); + } else if (payload.version === 'latest') { + bootbox.confirm( + '

No Compatibility Infomation Found

This plugin did not specify a specific version for installation given your NodeBB version. Full compatibility cannot be guaranteed, and may cause your NodeBB to no longer start properly.

' + + '

In the event that NodeBB cannot boot properly:

' + + '
$ ./nodebb reset plugin="' + pluginID + '"
' + + '

Continue installation of latest version of this plugin?

' + , function(confirm) { + if (confirm) { + Plugins.toggleInstall(pluginID, 'latest'); + } + }); + } + }); } else { bootbox.confirm('

NodeBB could not reach the package manager, proceed with installation of latest version?

Server returned (' + err.status + '): ' + err.responseText + '
', function(confirm) { if (confirm) { @@ -55,7 +70,7 @@ define('admin/extend/plugins', function() { Plugins.suggest(pluginID, function(err, payload) { if (!err) { require(['semver'], function(semver) { - if (payload.version === 'latest' || semver.gt(payload.version, parent.find('.currentVersion').text())) { + if (payload.version !== 'latest' && semver.gt(payload.version, parent.find('.currentVersion').text())) { btn.attr('disabled', true).find('i').attr('class', 'fa fa-refresh fa-spin'); socket.emit('admin.plugins.upgrade', { id: pluginID, @@ -68,6 +83,27 @@ define('admin/extend/plugins', function() { parent.find('.currentVersion').text(payload.version); btn.remove(); }); + } else if (payload.version === 'latest') { + bootbox.confirm( + '

No Compatibility Infomation Found

This plugin did not specify a specific version for installation given your NodeBB version. Full compatibility cannot be guaranteed, and may cause your NodeBB to no longer start properly.

' + + '

In the event that NodeBB cannot boot properly:

' + + '
$ ./nodebb reset plugin="' + pluginID + '"
' + + '

Continue installation of latest version of this plugin?

' + , function(confirm) { + if (confirm) { + socket.emit('admin.plugins.upgrade', { + id: pluginID, + version: payload.version + }, function(err) { + if (err) { + return app.alertError(err.message); + } + parent.find('.fa-exclamation-triangle').remove(); + parent.find('.currentVersion').text(payload.version); + btn.remove(); + }); + } + }); } else { bootbox.alert('

Your version of NodeBB (v' + app.config.version + ') is only cleared to upgrade to v' + payload.version + ' of this plugin. Please update your NodeBB if you wish to install a newer version of this plugin.'); } From fa39b5e46bb867d4f9cbc0f1e131ad0bf3b2a24f Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 24 Nov 2014 12:20:28 -0500 Subject: [PATCH 3/7] closed #2446 --- public/src/admin/advanced/logs.js | 41 +++++++++++++++++++++++++++++++ src/controllers/admin.js | 9 ++----- src/meta.js | 1 + src/meta/logs.js | 28 +++++++++++++++++++++ src/socket.io/admin.js | 11 ++++++++- src/views/admin/advanced/logs.tpl | 9 +++++++ 6 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 public/src/admin/advanced/logs.js create mode 100644 src/meta/logs.js diff --git a/public/src/admin/advanced/logs.js b/public/src/admin/advanced/logs.js new file mode 100644 index 0000000000..c44f6bf93f --- /dev/null +++ b/public/src/admin/advanced/logs.js @@ -0,0 +1,41 @@ +"use strict"; +/* global define, socket */ + +define('admin/advanced/logs', function() { + var Logs = {}; + + Logs.init = function() { + var logsEl = $('.logs pre'); + + // Affix menu + $('.affix').affix(); + + $('.logs').find('button[data-action]').on('click', function(e) { + var btnEl = $(this), + action = btnEl.attr('data-action'); + + switch(action) { + case 'reload': + socket.emit('admin.logs.get', function(err, logs) { + if (!err) { + logsEl.text(logs); + } else { + app.alertError(err.message); + } + }); + break; + + case 'clear': + socket.emit('admin.logs.clear', function(err) { + if (!err) { + app.alertSuccess('Logs Cleared!') + btnEl.prev().click(); + } + }); + break; + } + }); + }; + + return Logs; +}); diff --git a/src/controllers/admin.js b/src/controllers/admin.js index 52bb3b7f18..5a43e1ba6c 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -187,14 +187,9 @@ adminController.events.get = function(req, res, next) { }; adminController.logs.get = function(req, res, next) { - var logPath = path.join('logs', path.sep, 'output.log'); - fs.readFile(logPath, function(err, data) { - if (err || !data) { - data = ''; - } - + meta.logs.get(function(err, logs) { res.render('admin/advanced/logs', { - data: validator.escape(data.toString()) + data: validator.escape(logs) }); }); }; diff --git a/src/meta.js b/src/meta.js index 86f3b2ac9b..fdaa2d8107 100644 --- a/src/meta.js +++ b/src/meta.js @@ -20,6 +20,7 @@ var async = require('async'), require('./meta/css')(Meta); require('./meta/sounds')(Meta); require('./meta/settings')(Meta); + require('./meta/logs')(Meta); Meta.templates = require('./meta/templates'); /* Assorted */ diff --git a/src/meta/logs.js b/src/meta/logs.js new file mode 100644 index 0000000000..01ee677887 --- /dev/null +++ b/src/meta/logs.js @@ -0,0 +1,28 @@ +'use strict'; + +var path = require('path'), + fs = require('fs'), + winston = require('winston'); + +module.exports = function(Meta) { + + Meta.logs = { + path: path.join('logs', path.sep, 'output.log') + }; + + Meta.logs.get = function(callback) { + fs.readFile(this.path, { + encoding: 'utf-8' + }, function(err, logs) { + if (err) { + winston.error('[meta/logs] Could not retrieve logs: ' + err.message); + } + + callback(undefined, logs || ''); + }); + } + + Meta.logs.clear = function(callback) { + fs.truncate(this.path, 0, callback); + } +}; \ No newline at end of file diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js index db7d62d8e3..a2f09aa8b1 100644 --- a/src/socket.io/admin.js +++ b/src/socket.io/admin.js @@ -32,7 +32,8 @@ var async = require('async'), config: {}, settings: {}, email: {}, - analytics: {} + analytics: {}, + logs: {} }; SocketAdmin.before = function(socket, method, next) { @@ -207,6 +208,14 @@ SocketAdmin.analytics.get = function(socket, data, callback) { } }; +SocketAdmin.logs.get = function(socket, data, callback) { + meta.logs.get(callback); +}; + +SocketAdmin.logs.clear = function(socket, data, callback) { + meta.logs.clear(callback); +}; + function getHourlyStatsForSet(set, hours, callback) { var hour = new Date(), terms = {}, diff --git a/src/views/admin/advanced/logs.tpl b/src/views/admin/advanced/logs.tpl index e4f8d2b2fc..0311573d25 100644 --- a/src/views/admin/advanced/logs.tpl +++ b/src/views/admin/advanced/logs.tpl @@ -7,4 +7,13 @@ +

+
+
Logs Control Panel
+
+ + +
+
+
From 1c21ca78dcc3bc1e38614fb52d1e11169ff02581 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 24 Nov 2014 12:38:44 -0500 Subject: [PATCH 4/7] preventing ctrl-f hijacking if no search plugin is installed --- public/src/app.js | 4 +++- src/controllers/api.js | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/public/src/app.js b/public/src/app.js index 09828f14c1..9dc3f17579 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -539,7 +539,9 @@ var socket, handleStatusChange(); - handleSearch(); + if (config.searchEnabled) { + handleSearch(); + } $('#logout-link').on('click', app.logout); diff --git a/src/controllers/api.js b/src/controllers/api.js index e6c5d478c1..083126fa08 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -53,6 +53,7 @@ apiController.getConfig = function(req, res, next) { config.requireEmailConfirmation = parseInt(meta.config.requireEmailConfirmation, 10) === 1; config.topicPostSort = meta.config.topicPostSort || 'oldest_to_newest'; config.csrf_token = req.csrfToken(); + config.searchEnabled = plugins.hasListeners('filter:search.query'); if (!req.user) { if (res.locals.isAPI) { From 1b526026a0533ff3badb2c87b79f2cf84e79e4eb Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 24 Nov 2014 12:48:21 -0500 Subject: [PATCH 5/7] closed #2442 --- public/language/en_GB/user.json | 3 +++ public/src/app.js | 18 ++++++++++-------- src/controllers/api.js | 1 + src/user/settings.js | 4 +++- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/public/language/en_GB/user.json b/public/language/en_GB/user.json index 2562dfe575..eaf15bff36 100644 --- a/public/language/en_GB/user.json +++ b/public/language/en_GB/user.json @@ -79,6 +79,9 @@ "browsing": "Browsing Settings", "open_links_in_new_tab": "Open outgoing links in new tab?", + "enable_topic_searching": "Enable In-Topic Searching", + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen.", + "follow_topics_you_reply_to": "Follow topics that you reply to.", "follow_topics_you_create": "Follow topics you create." } diff --git a/public/src/app.js b/public/src/app.js index 9dc3f17579..1bd80d9fac 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -488,14 +488,16 @@ var socket, }); Mousetrap.bind('ctrl+f', function(e) { - // If in topic, open search window and populate, otherwise regular behaviour - var match = ajaxify.currentPage.match(/^topic\/([\d]+)/), - tid; - if (match) { - e.preventDefault(); - tid = match[1]; - searchInput.val('in:topic-' + tid + ' '); - prepareSearch(); + if (config.topicSearchEnabled) { + // If in topic, open search window and populate, otherwise regular behaviour + var match = ajaxify.currentPage.match(/^topic\/([\d]+)/), + tid; + if (match) { + e.preventDefault(); + tid = match[1]; + searchInput.val('in:topic-' + tid + ' '); + prepareSearch(); + } } }); }); diff --git a/src/controllers/api.js b/src/controllers/api.js index 083126fa08..5f96861299 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -76,6 +76,7 @@ apiController.getConfig = function(req, res, next) { config.userLang = settings.language || config.defaultLang; config.openOutgoingLinksInNewTab = settings.openOutgoingLinksInNewTab; config.topicPostSort = settings.topicPostSort || config.topicPostSort; + config.topicSearchEnabled = settings.topicSearchEnabled || false; if (res.locals.isAPI) { res.status(200).json(config); diff --git a/src/user/settings.js b/src/user/settings.js index 99ca426d20..0e857833db 100644 --- a/src/user/settings.js +++ b/src/user/settings.js @@ -30,6 +30,7 @@ module.exports = function(User) { settings.followTopicsOnReply = parseInt(settings.followTopicsOnReply, 10) === 1; settings.sendChatNotifications = parseInt(settings.sendChatNotifications, 10) === 1; settings.restrictChat = parseInt(settings.restrictChat, 10) === 1; + settings.topicSearchEnabled = parseInt(settings.topicSearchEnabled, 10) === 1; callback(null, settings); }); @@ -94,7 +95,8 @@ module.exports = function(User) { followTopicsOnCreate: data.followTopicsOnCreate, followTopicsOnReply: data.followTopicsOnReply, sendChatNotifications: data.sendChatNotifications, - restrictChat: data.restrictChat + restrictChat: data.restrictChat, + topicSearchEnabled: data.topicSearchEnabled }, callback); }; From a31525c79f129bd35e0cfa04434eefa6dfcc33d9 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 24 Nov 2014 13:21:51 -0500 Subject: [PATCH 6/7] Skipping digest sending if no notifs and no new topics, #2455 --- src/user/digest.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/user/digest.js b/src/user/digest.js index 6c3adba7f7..a253ae62af 100644 --- a/src/user/digest.js +++ b/src/user/digest.js @@ -84,6 +84,11 @@ module.exports = (function(Digest) { notifications = notifications.filter(Boolean); + // If there are no notifications and no new topics, don't bother sending a digest + if (!notifications.length && !data.topics.topics.length) { + return next(); + } + for(var i=0; i Date: Mon, 24 Nov 2014 13:28:44 -0500 Subject: [PATCH 7/7] closes #2451 --- src/meta/configs.js | 33 ++++++++++++++++++++++++++++++--- src/middleware/middleware.js | 22 +++------------------- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/meta/configs.js b/src/meta/configs.js index 4b314c758f..ae32d45599 100644 --- a/src/meta/configs.js +++ b/src/meta/configs.js @@ -59,16 +59,43 @@ module.exports = function(Meta) { }; Meta.configs.setMultiple = function(data, callback) { - db.setObject('config', data, function(err) { + processConfig(data, function(err) { if (err) { return callback(err); } + db.setObject('config', data, function(err) { + if (err) { + return callback(err); + } - updateConfig(data); - callback(); + updateConfig(data); + callback(); + }); }); }; + function processConfig(data, callback) { + if (data.customCSS) { + saveRenderedCss(data, callback); + return; + } + callback(); + } + + function saveRenderedCss(data, callback) { + var less = require('less'); + less.render(data.customCSS, { + compress: true + }, function(err, lessObject) { + if (err) { + winston.error('[less] Could not convert custom LESS to CSS! Please check your syntax.'); + return callback(null, ''); + } + data.renderedCustomCSS = lessObject.css; + callback(null, lessObject.css); + }); + } + function updateConfig(data) { var msg = {action: 'config:update', data: data}; if (process.send) { diff --git a/src/middleware/middleware.js b/src/middleware/middleware.js index 1d9ae900c6..ad2b6eb408 100644 --- a/src/middleware/middleware.js +++ b/src/middleware/middleware.js @@ -198,7 +198,7 @@ middleware.checkAccountPermissions = function(req, res, next) { middleware.buildHeader = function(req, res, next) { res.locals.renderHeader = true; - + middleware.applyCSRF(req, res, function() { async.parallel({ config: function(next) { @@ -305,26 +305,10 @@ middleware.renderHeader = function(req, res, callback) { async.parallel({ customCSS: function(next) { templateValues.useCustomCSS = parseInt(meta.config.useCustomCSS, 10) === 1; - if (!templateValues.useCustomCSS) { - return next(null, ''); - } - - if (!meta.config.customCSS) { + if (!templateValues.useCustomCSS || !meta.config.customCSS || !meta.config.renderedCustomCSS) { return next(null, ''); } - - var less = require('less'); - - less.render(meta.config.customCSS, { - compress: true - }, function(err, lessObject) { - if (err) { - winston.error('[less] Could not convert custom LESS to CSS! Please check your syntax.'); - return next(null, ''); - } - - next(null, lessObject.css); - }); + next(null, meta.config.renderedCustomCSS); }, customJS: function(next) { templateValues.useCustomJS = parseInt(meta.config.useCustomJS, 10) === 1;