mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-15 18:26:15 +01:00
Merge remote-tracking branch 'origin/master' into socket.io1.x
This commit is contained in:
@@ -79,6 +79,9 @@
|
|||||||
"browsing": "Browsing Settings",
|
"browsing": "Browsing Settings",
|
||||||
"open_links_in_new_tab": "Open outgoing links in new tab?",
|
"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_reply_to": "Follow topics that you reply to.",
|
||||||
"follow_topics_you_create": "Follow topics you create."
|
"follow_topics_you_create": "Follow topics you create."
|
||||||
}
|
}
|
||||||
|
|||||||
41
public/src/admin/advanced/logs.js
Normal file
41
public/src/admin/advanced/logs.js
Normal file
@@ -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;
|
||||||
|
});
|
||||||
@@ -35,7 +35,22 @@ define('admin/extend/plugins', function() {
|
|||||||
|
|
||||||
Plugins.suggest(pluginID, function(err, payload) {
|
Plugins.suggest(pluginID, function(err, payload) {
|
||||||
if (!err) {
|
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(
|
||||||
|
'<div class="alert alert-warning"><p><strong>No Compatibility Infomation Found</strong></p><p>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.</p></div>' +
|
||||||
|
'<p>In the event that NodeBB cannot boot properly:</p>' +
|
||||||
|
'<pre><code>$ ./nodebb reset plugin="' + pluginID + '"</code></pre>' +
|
||||||
|
'<p>Continue installation of latest version of this plugin?</p>'
|
||||||
|
, function(confirm) {
|
||||||
|
if (confirm) {
|
||||||
|
Plugins.toggleInstall(pluginID, 'latest');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
bootbox.confirm('<p>NodeBB could not reach the package manager, proceed with installation of latest version?</p><div class="alert alert-danger"><strong>Server returned (' + err.status + ')</strong>: ' + err.responseText + '</div>', function(confirm) {
|
bootbox.confirm('<p>NodeBB could not reach the package manager, proceed with installation of latest version?</p><div class="alert alert-danger"><strong>Server returned (' + err.status + ')</strong>: ' + err.responseText + '</div>', function(confirm) {
|
||||||
if (confirm) {
|
if (confirm) {
|
||||||
@@ -55,7 +70,7 @@ define('admin/extend/plugins', function() {
|
|||||||
Plugins.suggest(pluginID, function(err, payload) {
|
Plugins.suggest(pluginID, function(err, payload) {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
require(['semver'], function(semver) {
|
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');
|
btn.attr('disabled', true).find('i').attr('class', 'fa fa-refresh fa-spin');
|
||||||
socket.emit('admin.plugins.upgrade', {
|
socket.emit('admin.plugins.upgrade', {
|
||||||
id: pluginID,
|
id: pluginID,
|
||||||
@@ -68,6 +83,27 @@ define('admin/extend/plugins', function() {
|
|||||||
parent.find('.currentVersion').text(payload.version);
|
parent.find('.currentVersion').text(payload.version);
|
||||||
btn.remove();
|
btn.remove();
|
||||||
});
|
});
|
||||||
|
} else if (payload.version === 'latest') {
|
||||||
|
bootbox.confirm(
|
||||||
|
'<div class="alert alert-warning"><p><strong>No Compatibility Infomation Found</strong></p><p>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.</p></div>' +
|
||||||
|
'<p>In the event that NodeBB cannot boot properly:</p>' +
|
||||||
|
'<pre><code>$ ./nodebb reset plugin="' + pluginID + '"</code></pre>' +
|
||||||
|
'<p>Continue installation of latest version of this plugin?</p>'
|
||||||
|
, 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 {
|
} else {
|
||||||
bootbox.alert('<p>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.');
|
bootbox.alert('<p>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.');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -488,14 +488,16 @@ var socket,
|
|||||||
});
|
});
|
||||||
|
|
||||||
Mousetrap.bind('ctrl+f', function(e) {
|
Mousetrap.bind('ctrl+f', function(e) {
|
||||||
// If in topic, open search window and populate, otherwise regular behaviour
|
if (config.topicSearchEnabled) {
|
||||||
var match = ajaxify.currentPage.match(/^topic\/([\d]+)/),
|
// If in topic, open search window and populate, otherwise regular behaviour
|
||||||
tid;
|
var match = ajaxify.currentPage.match(/^topic\/([\d]+)/),
|
||||||
if (match) {
|
tid;
|
||||||
e.preventDefault();
|
if (match) {
|
||||||
tid = match[1];
|
e.preventDefault();
|
||||||
searchInput.val('in:topic-' + tid + ' ');
|
tid = match[1];
|
||||||
prepareSearch();
|
searchInput.val('in:topic-' + tid + ' ');
|
||||||
|
prepareSearch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -539,7 +541,9 @@ var socket,
|
|||||||
|
|
||||||
handleStatusChange();
|
handleStatusChange();
|
||||||
|
|
||||||
handleSearch();
|
if (config.searchEnabled) {
|
||||||
|
handleSearch();
|
||||||
|
}
|
||||||
|
|
||||||
$('#logout-link').on('click', app.logout);
|
$('#logout-link').on('click', app.logout);
|
||||||
|
|
||||||
|
|||||||
@@ -187,14 +187,9 @@ adminController.events.get = function(req, res, next) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
adminController.logs.get = function(req, res, next) {
|
adminController.logs.get = function(req, res, next) {
|
||||||
var logPath = path.join('logs', path.sep, 'output.log');
|
meta.logs.get(function(err, logs) {
|
||||||
fs.readFile(logPath, function(err, data) {
|
|
||||||
if (err || !data) {
|
|
||||||
data = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
res.render('admin/advanced/logs', {
|
res.render('admin/advanced/logs', {
|
||||||
data: validator.escape(data.toString())
|
data: validator.escape(logs)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ apiController.getConfig = function(req, res, next) {
|
|||||||
config.requireEmailConfirmation = parseInt(meta.config.requireEmailConfirmation, 10) === 1;
|
config.requireEmailConfirmation = parseInt(meta.config.requireEmailConfirmation, 10) === 1;
|
||||||
config.topicPostSort = meta.config.topicPostSort || 'oldest_to_newest';
|
config.topicPostSort = meta.config.topicPostSort || 'oldest_to_newest';
|
||||||
config.csrf_token = req.csrfToken();
|
config.csrf_token = req.csrfToken();
|
||||||
|
config.searchEnabled = plugins.hasListeners('filter:search.query');
|
||||||
|
|
||||||
if (!req.user) {
|
if (!req.user) {
|
||||||
if (res.locals.isAPI) {
|
if (res.locals.isAPI) {
|
||||||
@@ -75,6 +76,7 @@ apiController.getConfig = function(req, res, next) {
|
|||||||
config.userLang = settings.language || config.defaultLang;
|
config.userLang = settings.language || config.defaultLang;
|
||||||
config.openOutgoingLinksInNewTab = settings.openOutgoingLinksInNewTab;
|
config.openOutgoingLinksInNewTab = settings.openOutgoingLinksInNewTab;
|
||||||
config.topicPostSort = settings.topicPostSort || config.topicPostSort;
|
config.topicPostSort = settings.topicPostSort || config.topicPostSort;
|
||||||
|
config.topicSearchEnabled = settings.topicSearchEnabled || false;
|
||||||
|
|
||||||
if (res.locals.isAPI) {
|
if (res.locals.isAPI) {
|
||||||
res.status(200).json(config);
|
res.status(200).json(config);
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ var async = require('async'),
|
|||||||
require('./meta/css')(Meta);
|
require('./meta/css')(Meta);
|
||||||
require('./meta/sounds')(Meta);
|
require('./meta/sounds')(Meta);
|
||||||
require('./meta/settings')(Meta);
|
require('./meta/settings')(Meta);
|
||||||
|
require('./meta/logs')(Meta);
|
||||||
Meta.templates = require('./meta/templates');
|
Meta.templates = require('./meta/templates');
|
||||||
|
|
||||||
/* Assorted */
|
/* Assorted */
|
||||||
|
|||||||
@@ -59,16 +59,43 @@ module.exports = function(Meta) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Meta.configs.setMultiple = function(data, callback) {
|
Meta.configs.setMultiple = function(data, callback) {
|
||||||
db.setObject('config', data, function(err) {
|
processConfig(data, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
db.setObject('config', data, function(err) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
updateConfig(data);
|
updateConfig(data);
|
||||||
callback();
|
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) {
|
function updateConfig(data) {
|
||||||
var msg = {action: 'config:update', data: data};
|
var msg = {action: 'config:update', data: data};
|
||||||
if (process.send) {
|
if (process.send) {
|
||||||
|
|||||||
@@ -126,7 +126,8 @@ module.exports = function(Meta) {
|
|||||||
|
|
||||||
function minify(source, paths, destination, callback) {
|
function minify(source, paths, destination, callback) {
|
||||||
less.render(source, {
|
less.render(source, {
|
||||||
paths: paths
|
paths: paths,
|
||||||
|
compress: true
|
||||||
}, function(err, lessOutput) {
|
}, function(err, lessOutput) {
|
||||||
if (err) {
|
if (err) {
|
||||||
winston.error('[meta/css] Could not minify LESS/CSS: ' + err.message);
|
winston.error('[meta/css] Could not minify LESS/CSS: ' + err.message);
|
||||||
|
|||||||
28
src/meta/logs.js
Normal file
28
src/meta/logs.js
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -305,24 +305,10 @@ middleware.renderHeader = function(req, res, callback) {
|
|||||||
async.parallel({
|
async.parallel({
|
||||||
customCSS: function(next) {
|
customCSS: function(next) {
|
||||||
templateValues.useCustomCSS = parseInt(meta.config.useCustomCSS, 10) === 1;
|
templateValues.useCustomCSS = parseInt(meta.config.useCustomCSS, 10) === 1;
|
||||||
if (!templateValues.useCustomCSS) {
|
if (!templateValues.useCustomCSS || !meta.config.customCSS || !meta.config.renderedCustomCSS) {
|
||||||
return next(null, '');
|
return next(null, '');
|
||||||
}
|
}
|
||||||
|
next(null, meta.config.renderedCustomCSS);
|
||||||
if (!meta.config.customCSS) {
|
|
||||||
return next(null, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
var less = require('less');
|
|
||||||
|
|
||||||
less.render(meta.config.customCSS, 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);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
customJS: function(next) {
|
customJS: function(next) {
|
||||||
templateValues.useCustomJS = parseInt(meta.config.useCustomJS, 10) === 1;
|
templateValues.useCustomJS = parseInt(meta.config.useCustomJS, 10) === 1;
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ var async = require('async'),
|
|||||||
config: {},
|
config: {},
|
||||||
settings: {},
|
settings: {},
|
||||||
email: {},
|
email: {},
|
||||||
analytics: {}
|
analytics: {},
|
||||||
|
logs: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
SocketAdmin.before = function(socket, method, next) {
|
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) {
|
function getHourlyStatsForSet(set, hours, callback) {
|
||||||
var hour = new Date(),
|
var hour = new Date(),
|
||||||
terms = {},
|
terms = {},
|
||||||
|
|||||||
@@ -84,6 +84,11 @@ module.exports = (function(Digest) {
|
|||||||
|
|
||||||
notifications = notifications.filter(Boolean);
|
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<notifications.length; ++i) {
|
for(var i=0; i<notifications.length; ++i) {
|
||||||
if (notifications[i].image.indexOf('http') !== 0) {
|
if (notifications[i].image.indexOf('http') !== 0) {
|
||||||
notifications[i].image = nconf.get('url') + notifications[i].image;
|
notifications[i].image = nconf.get('url') + notifications[i].image;
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ module.exports = function(User) {
|
|||||||
settings.followTopicsOnReply = parseInt(settings.followTopicsOnReply, 10) === 1;
|
settings.followTopicsOnReply = parseInt(settings.followTopicsOnReply, 10) === 1;
|
||||||
settings.sendChatNotifications = parseInt(settings.sendChatNotifications, 10) === 1;
|
settings.sendChatNotifications = parseInt(settings.sendChatNotifications, 10) === 1;
|
||||||
settings.restrictChat = parseInt(settings.restrictChat, 10) === 1;
|
settings.restrictChat = parseInt(settings.restrictChat, 10) === 1;
|
||||||
|
settings.topicSearchEnabled = parseInt(settings.topicSearchEnabled, 10) === 1;
|
||||||
|
|
||||||
callback(null, settings);
|
callback(null, settings);
|
||||||
});
|
});
|
||||||
@@ -94,7 +95,8 @@ module.exports = function(User) {
|
|||||||
followTopicsOnCreate: data.followTopicsOnCreate,
|
followTopicsOnCreate: data.followTopicsOnCreate,
|
||||||
followTopicsOnReply: data.followTopicsOnReply,
|
followTopicsOnReply: data.followTopicsOnReply,
|
||||||
sendChatNotifications: data.sendChatNotifications,
|
sendChatNotifications: data.sendChatNotifications,
|
||||||
restrictChat: data.restrictChat
|
restrictChat: data.restrictChat,
|
||||||
|
topicSearchEnabled: data.topicSearchEnabled
|
||||||
}, callback);
|
}, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,4 +7,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-lg-3">
|
||||||
|
<div class="panel panel-default affix">
|
||||||
|
<div class="panel-heading">Logs Control Panel</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<button class="btn btn-primary" data-action="reload"><i class="fa fa-refresh"></i> Reload Logs</button>
|
||||||
|
<button class="btn btn-warning" data-action="clear"><i class="fa fa-eraser"></i> Clear Logs</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user