From eaae5b52cdb2ac26c829c080a45004b2e96f478a Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Tue, 20 Mar 2018 06:32:17 -0600 Subject: [PATCH] ACP quick actions (#6374) * ACP quick actions - Moved restart, build & restart, and logout into separate buttons - Moved buttons on mobile into the side menu - Added version and upgrade alert to header / mobile menu - Moved version checking to server-side with a cache for rate limiting - Changed "reload" translations to "rebuild and restart" * Change info alert to black-on-white to match focused search bar * Fix tests * Fallback for failed fetch of latest version --- public/language/en-GB/admin/admin.json | 2 +- .../en-GB/admin/general/dashboard.json | 5 +- public/language/en-GB/admin/menu.json | 7 +- public/less/admin/header.less | 39 +++++++---- public/less/admin/mobile.less | 22 ++++++- public/src/admin/general/dashboard.js | 37 ----------- src/admin/versions.js | 55 ++++++++++++++++ src/controllers/admin/dashboard.js | 24 +++++-- src/middleware/admin.js | 20 +++++- src/views/admin/general/dashboard.tpl | 21 +++++- src/views/admin/partials/menu.tpl | 66 ++++++++----------- .../admin/partials/quick_actions/alerts.tpl | 10 +++ .../admin/partials/quick_actions/buttons.tpl | 21 ++++++ test/mocks/databasemock.js | 3 + 14 files changed, 229 insertions(+), 103 deletions(-) create mode 100644 src/admin/versions.js create mode 100644 src/views/admin/partials/quick_actions/alerts.tpl create mode 100644 src/views/admin/partials/quick_actions/buttons.tpl diff --git a/public/language/en-GB/admin/admin.json b/public/language/en-GB/admin/admin.json index 9c01f56006..cca6420575 100644 --- a/public/language/en-GB/admin/admin.json +++ b/public/language/en-GB/admin/admin.json @@ -1,5 +1,5 @@ { - "alert.confirm-reload": "Are you sure you wish to reload NodeBB?", + "alert.confirm-reload": "Are you sure you wish to rebuild and restart NodeBB?", "alert.confirm-restart": "Are you sure you wish to restart NodeBB?", "acp-title": "%1 | NodeBB Admin Control Panel", diff --git a/public/language/en-GB/admin/general/dashboard.json b/public/language/en-GB/admin/general/dashboard.json index 3b4ed54444..4d287c409d 100644 --- a/public/language/en-GB/admin/general/dashboard.json +++ b/public/language/en-GB/admin/general/dashboard.json @@ -23,10 +23,11 @@ "running-version": "You are running NodeBB v%1.", "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", + "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", + "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", + "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", "notices": "Notices", "restart-not-required": "Restart not required", diff --git a/public/language/en-GB/admin/menu.json b/public/language/en-GB/admin/menu.json index 51099e9af4..bebff6aa37 100644 --- a/public/language/en-GB/admin/menu.json +++ b/public/language/en-GB/admin/menu.json @@ -63,7 +63,7 @@ "development/logger": "Logger", "development/info": "Info", - "reload-forum": "Reload Forum", + "reload-forum": "Rebuild & Restart Forum", "restart-forum": "Restart Forum", "logout": "Log out", "view-forum": "View Forum", @@ -74,5 +74,8 @@ "search.keep-typing": "Type more to see results...", "search.start-typing": "Start typing to see results...", - "connection-lost": "Connection to %1 has been lost, attempting to reconnect..." + "connection-lost": "Connection to %1 has been lost, attempting to reconnect...", + + "alerts.version": "Running NodeBB v%1", + "alerts.upgrade": "Upgrade to v%1" } \ No newline at end of file diff --git a/public/less/admin/header.less b/public/less/admin/header.less index 7dd9540c38..76c49602c8 100644 --- a/public/less/admin/header.less +++ b/public/less/admin/header.less @@ -16,10 +16,31 @@ font-weight: 300; } - #user_label { - position: absolute; - right: 30px; - bottom: 125px; + .quick-actions { + position: static; + padding: 15px; + display: flex; + flex-direction: row-reverse; + margin: 0; + + > * { + margin-right: 20px; + } + + > .menu-button { + margin-right: 0; + padding: 0 5px; + } + + .alert { + font-size: 14px; + margin-bottom: 0; + + &.alert-info { + background-color: #eee; + color: #333; + } + } .dropdown { margin-right: 0px; @@ -29,7 +50,7 @@ } } - .fa-home { + .fa { margin-top: 12px; font-size: 25px; } @@ -46,9 +67,6 @@ } #acp-search { - margin-top: 2px; - margin-right: 20px; - input { padding: 10px 20px; width: 250px; @@ -96,9 +114,6 @@ } .reconnect-spinner { - left: auto; - right: 380px; - bottom: initial; - top: 14px; + line-height: 44px; } } diff --git a/public/less/admin/mobile.less b/public/less/admin/mobile.less index c192351885..021145b1ae 100644 --- a/public/less/admin/mobile.less +++ b/public/less/admin/mobile.less @@ -45,7 +45,6 @@ width: 22px; background: none; border: none; - vertical-align: 10%; margin-right: 10px; margin-left: -5px; outline: none !important; @@ -86,6 +85,23 @@ .menu-section { margin: 25px 0; + + &.quick-actions { + margin: 0; + + .button-group { + display: flex; + justify-content: center; + } + + .alert { + border-radius: 0; + + .span { + display: block; + } + } + } } .menu-section-title { @@ -99,9 +115,9 @@ } .menu-section-list { - padding:0; + padding: 0; margin: 10px 0; - list-style:none; + list-style: none; a { display: block; diff --git a/public/src/admin/general/dashboard.js b/public/src/admin/general/dashboard.js index 6d144d5338..b071e6412f 100644 --- a/public/src/admin/general/dashboard.js +++ b/public/src/admin/general/dashboard.js @@ -8,7 +8,6 @@ define('admin/general/dashboard', ['semver', 'Chart', 'translator', 'benchpress' graphs: false, }; var isMobile = false; - var isPrerelease = /^v?\d+\.\d+\.\d+-.+$/; var graphData = { rooms: {}, traffic: {}, @@ -42,42 +41,6 @@ define('admin/general/dashboard', ['semver', 'Chart', 'translator', 'benchpress' isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); - $.get('https://api.github.com/repos/NodeBB/NodeBB/tags', function (releases) { - // Re-sort the releases, as they do not follow Semver (wrt pre-releases) - releases = releases.sort(function (a, b) { - a = a.name.replace(/^v/, ''); - b = b.name.replace(/^v/, ''); - return semver.lt(a, b) ? 1 : -1; - }).filter(function (version) { - return !isPrerelease.test(version.name); // filter out automated prerelease versions - }); - - var version = $('#version').html(); - var latestVersion = releases[0].name.slice(1); - var checkEl = $('.version-check'); - var text; - - // Alter box colour accordingly - if (semver.eq(latestVersion, version)) { - checkEl.removeClass('alert-info').addClass('alert-success'); - text = '[[admin/general/dashboard:up-to-date]]'; - } else if (semver.gt(latestVersion, version)) { - checkEl.removeClass('alert-info').addClass('alert-warning'); - if (!isPrerelease.test(version)) { - text = '[[admin/general/dashboard:upgrade-available, ' + latestVersion + ']]'; - } else { - text = '[[admin/general/dashboard:prerelease-upgrade-available, ' + latestVersion + ']]'; - } - } else if (isPrerelease.test(version)) { - checkEl.removeClass('alert-info').addClass('alert-info'); - text = '[[admin/general/dashboard:prerelease-warning]]'; - } - - translator.translate(text, function (text) { - checkEl.append(text); - }); - }); - $('[data-toggle="tooltip"]').tooltip(); setupRealtimeButton(); diff --git a/src/admin/versions.js b/src/admin/versions.js new file mode 100644 index 0000000000..bc29002f94 --- /dev/null +++ b/src/admin/versions.js @@ -0,0 +1,55 @@ +'use strict'; + +var semver = require('semver'); +var request = require('request'); + +var meta = require('../meta'); + +var versionCache = ''; +var versionCacheLastModified = ''; + +var isPrerelease = /^v?\d+\.\d+\.\d+-.+$/; + +function getLatestVersion(callback) { + var headers = { + Accept: 'application/vnd.github.v3+json', + 'User-Agent': 'NodeBB Admin Control Panel/' + meta.config.title, + }; + + if (versionCacheLastModified) { + headers['If-Modified-Since'] = versionCacheLastModified; + } + + request('https://api.github.com/repos/NodeBB/NodeBB/tags', { + json: true, + headers: headers, + }, function (err, res, releases) { + if (err) { + return callback(err); + } + + if (res.statusCode === 304) { + return callback(null, versionCache); + } + + if (res.statusCode !== 200) { + return callback(Error(res.statusMessage)); + } + + releases = releases.filter(function (version) { + return !isPrerelease.test(version.name); // filter out automated prerelease versions + }).map(function (version) { + return version.name.replace(/^v/, ''); + }).sort(function (a, b) { + return semver.lt(a, b) ? 1 : -1; + }); + + versionCache = releases[0]; + versionCacheLastModified = res.headers['last-modified']; + + callback(null, versionCache); + }); +} + +exports.getLatestVersion = getLatestVersion; +exports.isPrerelease = isPrerelease; diff --git a/src/controllers/admin/dashboard.js b/src/controllers/admin/dashboard.js index b5ae760727..6d804faf47 100644 --- a/src/controllers/admin/dashboard.js +++ b/src/controllers/admin/dashboard.js @@ -2,7 +2,10 @@ var async = require('async'); var nconf = require('nconf'); +var semver = require('semver'); +var winston = require('winston'); +var versions = require('../../admin/versions'); var db = require('../../database'); var meta = require('../../meta'); var plugins = require('../../plugins'); @@ -13,9 +16,7 @@ dashboardController.get = function (req, res, next) { async.waterfall([ function (next) { async.parallel({ - stats: function (next) { - getStats(next); - }, + stats: getStats, notices: function (next) { var notices = [ { @@ -41,11 +42,26 @@ dashboardController.get = function (req, res, next) { plugins.fireHook('filter:admin.notices', notices, next); }, + latestVersion: function (next) { + versions.getLatestVersion(function (err, result) { + if (err) { + winston.error('[acp] Failed to fetch latest version', err); + } + + next(null, err ? null : result); + }); + }, }, next); }, function (results) { + var version = nconf.get('version'); + res.render('admin/general/dashboard', { - version: nconf.get('version'), + version: version, + lookupFailed: results.latestVersion === null, + latestVersion: results.latestVersion, + upgradeAvailable: results.latestVersion && semver.gt(results.latestVersion, version), + currentPrerelease: versions.isPrerelease.test(version), notices: results.notices, stats: results.stats, canRestart: !!process.send, diff --git a/src/middleware/admin.js b/src/middleware/admin.js index 3086f045cc..1c72a31906 100644 --- a/src/middleware/admin.js +++ b/src/middleware/admin.js @@ -2,10 +2,14 @@ var async = require('async'); var winston = require('winston'); +var jsesc = require('jsesc'); +var nconf = require('nconf'); +var semver = require('semver'); + var user = require('../user'); var meta = require('../meta'); var plugins = require('../plugins'); -var jsesc = require('jsesc'); +var versions = require('../admin/versions'); var controllers = { api: require('../controllers/api'), @@ -54,6 +58,15 @@ module.exports = function (middleware) { configs: function (next) { meta.configs.list(next); }, + latestVersion: function (next) { + versions.getLatestVersion(function (err, result) { + if (err) { + winston.error('[acp] Failed to fetch latest version', err); + } + + next(null, err ? null : result); + }); + }, }, next); }, function (results, next) { @@ -67,6 +80,8 @@ module.exports = function (middleware) { }); acpPath = acpPath.join(' > '); + var version = nconf.get('version'); + var templateValues = { config: res.locals.config, configJSON: jsesc(JSON.stringify(res.locals.config), { isScriptContext: true }), @@ -81,6 +96,9 @@ module.exports = function (middleware) { env: !!process.env.NODE_ENV, title: (acpPath || 'Dashboard') + ' | NodeBB Admin Control Panel', bodyClass: data.bodyClass, + version: version, + latestVersion: results.latestVersion, + upgradeAvailable: results.latestVersion && semver.gt(results.latestVersion, version), }; templateValues.template = { name: res.locals.template }; diff --git a/src/views/admin/general/dashboard.tpl b/src/views/admin/general/dashboard.tpl index 3090e2dc8b..c58bd6bef4 100644 --- a/src/views/admin/general/dashboard.tpl +++ b/src/views/admin/general/dashboard.tpl @@ -65,8 +65,27 @@
[[admin/general/dashboard:updates]]
-
+

[[admin/general/dashboard:running-version, {version}]]

+

+ + [[admin/general/dashboard:latest-lookup-failed]] + + + + [[admin/general/dashboard:prerelease-upgrade-available, {latestVersion}]] + + [[admin/general/dashboard:upgrade-available, {latestVersion}]] + + + + [[admin/general/dashboard:prerelease-warning]] + + [[admin/general/dashboard:up-to-date]] + + + +

[[admin/general/dashboard:keep-updated]] diff --git a/src/views/admin/partials/menu.tpl b/src/views/admin/partials/menu.tpl index 3656513925..d6ad00f0db 100644 --- a/src/views/admin/partials/menu.tpl +++ b/src/views/admin/partials/menu.tpl @@ -1,8 +1,18 @@

- - - \ No newline at end of file diff --git a/src/views/admin/partials/quick_actions/alerts.tpl b/src/views/admin/partials/quick_actions/alerts.tpl new file mode 100644 index 0000000000..96f34a4848 --- /dev/null +++ b/src/views/admin/partials/quick_actions/alerts.tpl @@ -0,0 +1,10 @@ +
+ [[admin/menu:alerts.version, {version}]] + + + + [[admin/menu:alerts.upgrade, {latestVersion}]] + + + +
\ No newline at end of file diff --git a/src/views/admin/partials/quick_actions/buttons.tpl b/src/views/admin/partials/quick_actions/buttons.tpl new file mode 100644 index 0000000000..1687d437b0 --- /dev/null +++ b/src/views/admin/partials/quick_actions/buttons.tpl @@ -0,0 +1,21 @@ +
  • + + + +
  • +
  • + + + +
  • +
  • + + + +
  • + +
  • + + + +
  • \ No newline at end of file diff --git a/test/mocks/databasemock.js b/test/mocks/databasemock.js index 0bef41c466..ab99b38e4c 100644 --- a/test/mocks/databasemock.js +++ b/test/mocks/databasemock.js @@ -12,6 +12,7 @@ var nconf = require('nconf'); var url = require('url'); var errorText; +var packageInfo = require('../../package'); nconf.file({ file: path.join(__dirname, '../../config.json') }); nconf.defaults({ @@ -120,6 +121,8 @@ before(function (done) { nconf.set('theme_config', path.join(nconf.get('themes_path'), 'nodebb-theme-persona', 'theme.json')); nconf.set('bcrypt_rounds', 1); + nconf.set('version', packageInfo.version); + meta.dependencies.check(next); }, function (next) {