From b60dbe7d1e9ac5b1df5a36a0194e747742da7c41 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Mon, 29 May 2017 12:14:55 -0600 Subject: [PATCH] Unwrap meta modules --- src/meta.js | 20 +- src/meta/configs.js | 250 ++++++++--------- src/meta/css.js | 286 ++++++++++--------- src/meta/dependencies.js | 117 ++++---- src/meta/errors.js | 94 ++++--- src/meta/js.js | 580 +++++++++++++++++++-------------------- src/meta/logs.js | 22 +- src/meta/settings.js | 99 ++++--- src/meta/sounds.js | 201 +++++++------- src/meta/tags.js | 272 +++++++++--------- src/meta/themes.js | 293 ++++++++++---------- 11 files changed, 1111 insertions(+), 1123 deletions(-) diff --git a/src/meta.js b/src/meta.js index 59c3447cf4..cb2a381d6d 100644 --- a/src/meta.js +++ b/src/meta.js @@ -12,16 +12,16 @@ var Meta = module.exports; Meta.reloadRequired = false; -require('./meta/configs')(Meta); -require('./meta/themes')(Meta); -require('./meta/js')(Meta); -require('./meta/css')(Meta); -require('./meta/sounds')(Meta); -require('./meta/settings')(Meta); -require('./meta/logs')(Meta); -require('./meta/errors')(Meta); -require('./meta/tags')(Meta); -require('./meta/dependencies')(Meta); +Meta.configs = require('./meta/configs'); +Meta.themes = require('./meta/themes'); +Meta.js = require('./meta/js'); +Meta.css = require('./meta/css'); +Meta.sounds = require('./meta/sounds'); +Meta.settings = require('./meta/settings'); +Meta.logs = require('./meta/logs'); +Meta.errors = require('./meta/errors'); +Meta.tags = require('./meta/tags'); +Meta.dependencies = require('./meta/dependencies'); Meta.templates = require('./meta/templates'); Meta.blacklist = require('./meta/blacklist'); Meta.languages = require('./meta/languages'); diff --git a/src/meta/configs.js b/src/meta/configs.js index daaa807d19..925ff61255 100644 --- a/src/meta/configs.js +++ b/src/meta/configs.js @@ -6,142 +6,142 @@ var nconf = require('nconf'); var db = require('../database'); var pubsub = require('../pubsub'); +var Meta = require('../meta'); var cacheBuster = require('./cacheBuster'); -module.exports = function (Meta) { - Meta.config = {}; - Meta.configs = {}; - - Meta.configs.init = function (callback) { - delete Meta.config; - - async.waterfall([ - function (next) { - Meta.configs.list(next); - }, - function (config, next) { - cacheBuster.read(function (err, buster) { - if (err) { - return next(err); - } - - config['cache-buster'] = 'v=' + (buster || Date.now()); - - Meta.config = config; - next(); - }); - }, - ], callback); - }; - - Meta.configs.list = function (callback) { - db.getObject('config', function (err, config) { - config = config || {}; - config.version = nconf.get('version'); - config.registry = nconf.get('registry'); - callback(err, config); - }); - }; - - Meta.configs.get = function (field, callback) { - db.getObjectField('config', field, callback); - }; - - Meta.configs.getFields = function (fields, callback) { - db.getObjectFields('config', fields, callback); - }; - - Meta.configs.set = function (field, value, callback) { - callback = callback || function () {}; - if (!field) { - return callback(new Error('[[error:invalid-data]]')); - } +var Configs = module.exports; - var data = {}; - data[field] = value; - Meta.configs.setMultiple(data, callback); - }; - - - Meta.configs.setMultiple = function (data, callback) { - async.waterfall([ - function (next) { - processConfig(data, next); - }, - function (next) { - db.setObject('config', data, next); - }, - function (next) { - updateConfig(data); - setImmediate(next); - }, - ], callback); - }; +Meta.config = {}; - function processConfig(data, callback) { - if (data.customCSS) { - return saveRenderedCss(data, callback); - } - setImmediate(callback); - } +Configs.init = function (callback) { + Meta.config = null; - function saveRenderedCss(data, callback) { - var less = require('less'); - async.waterfall([ - function (next) { - less.render(data.customCSS, { - compress: true, - }, next); - }, - function (lessObject, next) { - data.renderedCustomCSS = lessObject.css; - setImmediate(next); - }, - ], callback); - } + async.waterfall([ + function (next) { + Configs.list(next); + }, + function (config, next) { + cacheBuster.read(function (err, buster) { + if (err) { + return next(err); + } - function updateConfig(config) { - updateLocalConfig(config); - pubsub.publish('config:update', config); - } + config['cache-buster'] = 'v=' + (buster || Date.now()); - function updateLocalConfig(config) { - for (var field in config) { - if (config.hasOwnProperty(field)) { - Meta.config[field] = config[field]; - } - } + Meta.config = config; + next(); + }); + }, + ], callback); +}; + +Configs.list = function (callback) { + db.getObject('config', function (err, config) { + config = config || {}; + config.version = nconf.get('version'); + config.registry = nconf.get('registry'); + callback(err, config); + }); +}; + +Configs.get = function (field, callback) { + db.getObjectField('config', field, callback); +}; + +Configs.getFields = function (fields, callback) { + db.getObjectFields('config', fields, callback); +}; + +Configs.set = function (field, value, callback) { + callback = callback || function () {}; + if (!field) { + return callback(new Error('[[error:invalid-data]]')); } - pubsub.on('config:update', function onConfigReceived(config) { - if (typeof config === 'object' && Meta.config) { - updateLocalConfig(config); + var data = {}; + data[field] = value; + Configs.setMultiple(data, callback); +}; + + +Configs.setMultiple = function (data, callback) { + async.waterfall([ + function (next) { + processConfig(data, next); + }, + function (next) { + db.setObject('config', data, next); + }, + function (next) { + updateConfig(data); + setImmediate(next); + }, + ], callback); +}; + +function processConfig(data, callback) { + if (data.customCSS) { + return saveRenderedCss(data, callback); + } + setImmediate(callback); +} + +function saveRenderedCss(data, callback) { + var less = require('less'); + async.waterfall([ + function (next) { + less.render(data.customCSS, { + compress: true, + }, next); + }, + function (lessObject, next) { + data.renderedCustomCSS = lessObject.css; + setImmediate(next); + }, + ], callback); +} + +function updateConfig(config) { + updateLocalConfig(config); + pubsub.publish('config:update', config); +} + +function updateLocalConfig(config) { + for (var field in config) { + if (config.hasOwnProperty(field)) { + Meta.config[field] = config[field]; } - }); + } +} - Meta.configs.setOnEmpty = function (values, callback) { - async.waterfall([ - function (next) { - db.getObject('config', next); - }, - function (data, next) { - data = data || {}; - var empty = {}; - Object.keys(values).forEach(function (key) { - if (!data.hasOwnProperty(key)) { - empty[key] = values[key]; - } - }); - if (Object.keys(empty).length) { - db.setObject('config', empty, next); - } else { - setImmediate(next); +pubsub.on('config:update', function onConfigReceived(config) { + if (typeof config === 'object' && Meta.config) { + updateLocalConfig(config); + } +}); + +Configs.setOnEmpty = function (values, callback) { + async.waterfall([ + function (next) { + db.getObject('config', next); + }, + function (data, next) { + data = data || {}; + var empty = {}; + Object.keys(values).forEach(function (key) { + if (!data.hasOwnProperty(key)) { + empty[key] = values[key]; } - }, - ], callback); - }; + }); + if (Object.keys(empty).length) { + db.setObject('config', empty, next); + } else { + setImmediate(next); + } + }, + ], callback); +}; - Meta.configs.remove = function (field, callback) { - db.deleteObjectField('config', field, callback); - }; +Configs.remove = function (field, callback) { + db.deleteObjectField('config', field, callback); }; diff --git a/src/meta/css.js b/src/meta/css.js index f2abe608ac..3db85a50f3 100644 --- a/src/meta/css.js +++ b/src/meta/css.js @@ -11,158 +11,156 @@ var db = require('../database'); var file = require('../file'); var minifier = require('./minifier'); -module.exports = function (Meta) { - Meta.css = {}; - - var buildImports = { - client: function (source) { - return '@import "./theme";\n' + source + '\n' + [ - '@import "font-awesome";', - '@import (inline) "../public/vendor/jquery/css/smoothness/jquery-ui.css";', - '@import (inline) "../public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css";', - '@import (inline) "../public/vendor/colorpicker/colorpicker.css";', - '@import (inline) "../node_modules/cropperjs/dist/cropper.css";', - '@import "../../public/less/flags.less";', - '@import "../../public/less/blacklist.less";', - '@import "../../public/less/generics.less";', - '@import "../../public/less/mixins.less";', - '@import "../../public/less/global.less";', - ].map(function (str) { - return str.replace(/\//g, path.sep); - }).join('\n'); - }, - admin: function (source) { - return source + '\n' + [ - '@import "font-awesome";', - '@import "../public/less/admin/admin";', - '@import "../public/less/generics.less";', - '@import (inline) "../public/vendor/colorpicker/colorpicker.css";', - '@import (inline) "../public/vendor/jquery/css/smoothness/jquery-ui.css";', - '@import (inline) "../public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css";', - '@import (inline) "../public/vendor/mdl/material.css";', - ].map(function (str) { - return str.replace(/\//g, path.sep); - }).join('\n'); - }, - }; +var CSS = module.exports; + +var buildImports = { + client: function (source) { + return '@import "./theme";\n' + source + '\n' + [ + '@import "font-awesome";', + '@import (inline) "../public/vendor/jquery/css/smoothness/jquery-ui.css";', + '@import (inline) "../public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css";', + '@import (inline) "../public/vendor/colorpicker/colorpicker.css";', + '@import (inline) "../node_modules/cropperjs/dist/cropper.css";', + '@import "../../public/less/flags.less";', + '@import "../../public/less/blacklist.less";', + '@import "../../public/less/generics.less";', + '@import "../../public/less/mixins.less";', + '@import "../../public/less/global.less";', + ].map(function (str) { + return str.replace(/\//g, path.sep); + }).join('\n'); + }, + admin: function (source) { + return source + '\n' + [ + '@import "font-awesome";', + '@import "../public/less/admin/admin";', + '@import "../public/less/generics.less";', + '@import (inline) "../public/vendor/colorpicker/colorpicker.css";', + '@import (inline) "../public/vendor/jquery/css/smoothness/jquery-ui.css";', + '@import (inline) "../public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css";', + '@import (inline) "../public/vendor/mdl/material.css";', + ].map(function (str) { + return str.replace(/\//g, path.sep); + }).join('\n'); + }, +}; - function filterMissingFiles(filepaths, callback) { - async.filter(filepaths, function (filepath, next) { - file.exists(path.join(__dirname, '../../node_modules', filepath), function (err, exists) { - if (!exists) { - winston.warn('[meta/css] File not found! ' + filepath); - } +function filterMissingFiles(filepaths, callback) { + async.filter(filepaths, function (filepath, next) { + file.exists(path.join(__dirname, '../../node_modules', filepath), function (err, exists) { + if (!exists) { + winston.warn('[meta/css] File not found! ' + filepath); + } + + next(err, exists); + }); + }, callback); +} + +function getImports(files, prefix, extension, callback) { + var pluginDirectories = []; + var source = ''; + + files.forEach(function (styleFile) { + if (styleFile.endsWith(extension)) { + source += prefix + path.sep + styleFile + '";'; + } else { + pluginDirectories.push(styleFile); + } + }); + + async.each(pluginDirectories, function (directory, next) { + file.walk(directory, function (err, styleFiles) { + if (err) { + return next(err); + } - next(err, exists); + styleFiles.forEach(function (styleFile) { + source += prefix + path.sep + styleFile + '";'; }); - }, callback); - } - function getImports(files, prefix, extension, callback) { - var pluginDirectories = []; - var source = ''; + next(); + }); + }, function (err) { + callback(err, source); + }); +} + +function getBundleMetadata(target, callback) { + var paths = [ + path.join(__dirname, '../../node_modules'), + path.join(__dirname, '../../public/vendor/fontawesome/less'), + ]; + + async.waterfall([ + function (next) { + if (target !== 'client') { + return next(null, null); + } - files.forEach(function (styleFile) { - if (styleFile.endsWith(extension)) { - source += prefix + path.sep + styleFile + '";'; - } else { - pluginDirectories.push(styleFile); + db.getObjectFields('config', ['theme:type', 'theme:id'], next); + }, + function (themeData, next) { + if (target === 'client') { + var themeId = (themeData['theme:id'] || 'nodebb-theme-persona'); + var baseThemePath = path.join(nconf.get('themes_path'), (themeData['theme:type'] && themeData['theme:type'] === 'local' ? themeId : 'nodebb-theme-vanilla')); + paths.unshift(baseThemePath); } - }); - async.each(pluginDirectories, function (directory, next) { - file.walk(directory, function (err, styleFiles) { - if (err) { - return next(err); - } + async.parallel({ + less: function (cb) { + async.waterfall([ + function (next) { + filterMissingFiles(plugins.lessFiles, next); + }, + function (lessFiles, next) { + getImports(lessFiles, '\n@import ".', '.less', next); + }, + ], cb); + }, + css: function (cb) { + async.waterfall([ + function (next) { + filterMissingFiles(plugins.cssFiles, next); + }, + function (cssFiles, next) { + getImports(cssFiles, '\n@import (inline) ".', '.css', next); + }, + ], cb); + }, + }, next); + }, + function (result, next) { + var cssImports = result.css; + var lessImports = result.less; - styleFiles.forEach(function (styleFile) { - source += prefix + path.sep + styleFile + '";'; - }); + var imports = cssImports + '\n' + lessImports; + imports = buildImports[target](imports); - next(); - }); - }, function (err) { - callback(err, source); - }); - } - - function getBundleMetadata(target, callback) { - var paths = [ - path.join(__dirname, '../../node_modules'), - path.join(__dirname, '../../public/vendor/fontawesome/less'), - ]; - - async.waterfall([ - function (next) { - if (target !== 'client') { - return next(null, null); - } - - db.getObjectFields('config', ['theme:type', 'theme:id'], next); - }, - function (themeData, next) { - if (target === 'client') { - var themeId = (themeData['theme:id'] || 'nodebb-theme-persona'); - var baseThemePath = path.join(nconf.get('themes_path'), (themeData['theme:type'] && themeData['theme:type'] === 'local' ? themeId : 'nodebb-theme-vanilla')); - paths.unshift(baseThemePath); - } - - async.parallel({ - less: function (cb) { - async.waterfall([ - function (next) { - filterMissingFiles(plugins.lessFiles, next); - }, - function (lessFiles, next) { - getImports(lessFiles, '\n@import ".', '.less', next); - }, - ], cb); - }, - css: function (cb) { - async.waterfall([ - function (next) { - filterMissingFiles(plugins.cssFiles, next); - }, - function (cssFiles, next) { - getImports(cssFiles, '\n@import (inline) ".', '.css', next); - }, - ], cb); - }, - }, next); - }, - function (result, next) { - var cssImports = result.css; - var lessImports = result.less; - - var imports = cssImports + '\n' + lessImports; - imports = buildImports[target](imports); - - next(null, imports); - }, - ], function (err, imports) { - if (err) { - return callback(err); - } + next(null, imports); + }, + ], function (err, imports) { + if (err) { + return callback(err); + } + + callback(null, { paths: paths, imports: imports }); + }); +} + +CSS.buildBundle = function (target, fork, callback) { + async.waterfall([ + function (next) { + getBundleMetadata(target, next); + }, + function (data, next) { + var minify = global.env !== 'development'; + minifier.css.bundle(data.imports, data.paths, minify, fork, next); + }, + function (bundle, next) { + var filename = (target === 'client' ? 'stylesheet' : 'admin') + '.css'; - callback(null, { paths: paths, imports: imports }); - }); - } - - Meta.css.buildBundle = function (target, fork, callback) { - async.waterfall([ - function (next) { - getBundleMetadata(target, next); - }, - function (data, next) { - var minify = global.env !== 'development'; - minifier.css.bundle(data.imports, data.paths, minify, fork, next); - }, - function (bundle, next) { - var filename = (target === 'client' ? 'stylesheet' : 'admin') + '.css'; - - fs.writeFile(path.join(__dirname, '../../build/public', filename), bundle.code, next); - }, - ], callback); - }; + fs.writeFile(path.join(__dirname, '../../build/public', filename), bundle.code, next); + }, + ], callback); }; diff --git a/src/meta/dependencies.js b/src/meta/dependencies.js index 3a16f0e9e5..db403732ec 100644 --- a/src/meta/dependencies.js +++ b/src/meta/dependencies.js @@ -9,73 +9,72 @@ require('colors'); var pkg = require('../../package.json'); -module.exports = function (Meta) { - Meta.dependencies = {}; - var depsMissing = false; - var depsOutdated = false; +var Dependencies = module.exports; - Meta.dependencies.check = function (callback) { - var modules = Object.keys(pkg.dependencies); +var depsMissing = false; +var depsOutdated = false; - winston.verbose('Checking dependencies for outdated modules'); +Dependencies.check = function (callback) { + var modules = Object.keys(pkg.dependencies); - async.each(modules, Meta.dependencies.checkModule, function (err) { - if (err) { - return callback(err); - } + winston.verbose('Checking dependencies for outdated modules'); - if (depsMissing) { - callback(new Error('dependencies-missing')); - } else if (depsOutdated) { - callback(global.env !== 'development' ? new Error('dependencies-out-of-date') : null); - } else { - callback(null); - } - }); - }; + async.each(modules, Dependencies.checkModule, function (err) { + if (err) { + return callback(err); + } + + if (depsMissing) { + callback(new Error('dependencies-missing')); + } else if (depsOutdated) { + callback(global.env !== 'development' ? new Error('dependencies-out-of-date') : null); + } else { + callback(null); + } + }); +}; - Meta.dependencies.checkModule = function (moduleName, callback) { - fs.readFile(path.join(__dirname, '../../node_modules/', moduleName, 'package.json'), { - encoding: 'utf-8', - }, function (err, pkgData) { - if (err) { - // If a bundled plugin/theme is not present, skip the dep check (#3384) - if (err.code === 'ENOENT' && (moduleName === 'nodebb-rewards-essentials' || moduleName.startsWith('nodebb-plugin') || moduleName.startsWith('nodebb-theme'))) { - winston.warn('[meta/dependencies] Bundled plugin ' + moduleName + ' not found, skipping dependency check.'); - return callback(null, true); - } - return callback(err); +Dependencies.checkModule = function (moduleName, callback) { + fs.readFile(path.join(__dirname, '../../node_modules/', moduleName, 'package.json'), { + encoding: 'utf-8', + }, function (err, pkgData) { + if (err) { + // If a bundled plugin/theme is not present, skip the dep check (#3384) + if (err.code === 'ENOENT' && (moduleName === 'nodebb-rewards-essentials' || moduleName.startsWith('nodebb-plugin') || moduleName.startsWith('nodebb-theme'))) { + winston.warn('[meta/dependencies] Bundled plugin ' + moduleName + ' not found, skipping dependency check.'); + return callback(null, true); } + return callback(err); + } - pkgData = Meta.dependencies.parseModuleData(moduleName, pkgData); + pkgData = Dependencies.parseModuleData(moduleName, pkgData); - var satisfies = Meta.dependencies.doesSatisfy(pkgData, pkg.dependencies[moduleName]); - callback(null, satisfies); - }); - }; + var satisfies = Dependencies.doesSatisfy(pkgData, pkg.dependencies[moduleName]); + callback(null, satisfies); + }); +}; - Meta.dependencies.parseModuleData = function (moduleName, pkgData) { - try { - pkgData = JSON.parse(pkgData); - } catch (e) { - winston.warn('[' + 'missing'.red + '] ' + moduleName.bold + ' is a required dependency but could not be found\n'); - depsMissing = true; - return null; - } - return pkgData; - }; +Dependencies.parseModuleData = function (moduleName, pkgData) { + try { + pkgData = JSON.parse(pkgData); + } catch (e) { + winston.warn('[' + 'missing'.red + '] ' + moduleName.bold + ' is a required dependency but could not be found\n'); + depsMissing = true; + return null; + } + return pkgData; +}; - Meta.dependencies.doesSatisfy = function (moduleData, packageJSONVersion) { - if (!moduleData) { - return false; - } - var versionOk = !semver.validRange(packageJSONVersion) || semver.satisfies(moduleData.version, packageJSONVersion); - var githubRepo = moduleData._resolved && moduleData._resolved.indexOf('//github.com') !== -1; - var satisfies = versionOk || githubRepo; - if (!satisfies) { - winston.warn('[' + 'outdated'.yellow + '] ' + moduleData.name.bold + ' installed v' + moduleData.version + ', package.json requires ' + packageJSONVersion + '\n'); - depsOutdated = true; - } - return satisfies; - }; +Dependencies.doesSatisfy = function (moduleData, packageJSONVersion) { + if (!moduleData) { + return false; + } + var versionOk = !semver.validRange(packageJSONVersion) || semver.satisfies(moduleData.version, packageJSONVersion); + var githubRepo = moduleData._resolved && moduleData._resolved.indexOf('//github.com') !== -1; + var satisfies = versionOk || githubRepo; + if (!satisfies) { + winston.warn('[' + 'outdated'.yellow + '] ' + moduleData.name.bold + ' installed v' + moduleData.version + ', package.json requires ' + packageJSONVersion + '\n'); + depsOutdated = true; + } + return satisfies; }; diff --git a/src/meta/errors.js b/src/meta/errors.js index f269490bbf..38e206e501 100644 --- a/src/meta/errors.js +++ b/src/meta/errors.js @@ -8,61 +8,59 @@ var cronJob = require('cron').CronJob; var db = require('../database'); var analytics = require('../analytics'); -module.exports = function (Meta) { - Meta.errors = {}; +var Errors = module.exports; - var counters = {}; +var counters = {}; - new cronJob('0 * * * * *', function () { - Meta.errors.writeData(); - }, null, true); +new cronJob('0 * * * * *', function () { + Errors.writeData(); +}, null, true); - Meta.errors.writeData = function () { - var dbQueue = []; - if (Object.keys(counters).length > 0) { - for (var key in counters) { - if (counters.hasOwnProperty(key)) { - dbQueue.push(async.apply(db.sortedSetIncrBy, 'errors:404', counters[key], key)); - } +Errors.writeData = function () { + var dbQueue = []; + if (Object.keys(counters).length > 0) { + for (var key in counters) { + if (counters.hasOwnProperty(key)) { + dbQueue.push(async.apply(db.sortedSetIncrBy, 'errors:404', counters[key], key)); } - counters = {}; - async.series(dbQueue, function (err) { - if (err) { - winston.error(err); - } - }); } - }; + counters = {}; + async.series(dbQueue, function (err) { + if (err) { + winston.error(err); + } + }); + } +}; - Meta.errors.log404 = function (route, callback) { - callback = callback || function () {}; - if (!route) { - return setImmediate(callback); - } - route = route.replace(/\/$/, ''); // remove trailing slashes - analytics.increment('errors:404'); - counters[route] = counters[route] || 0; - counters[route] += 1; - setImmediate(callback); - }; +Errors.log404 = function (route, callback) { + callback = callback || function () {}; + if (!route) { + return setImmediate(callback); + } + route = route.replace(/\/$/, ''); // remove trailing slashes + analytics.increment('errors:404'); + counters[route] = counters[route] || 0; + counters[route] += 1; + setImmediate(callback); +}; - Meta.errors.get = function (escape, callback) { - async.waterfall([ - function (next) { - db.getSortedSetRevRangeWithScores('errors:404', 0, 199, next); - }, - function (data, next) { - data = data.map(function (nfObject) { - nfObject.value = escape ? validator.escape(String(nfObject.value || '')) : nfObject.value; - return nfObject; - }); +Errors.get = function (escape, callback) { + async.waterfall([ + function (next) { + db.getSortedSetRevRangeWithScores('errors:404', 0, 199, next); + }, + function (data, next) { + data = data.map(function (nfObject) { + nfObject.value = escape ? validator.escape(String(nfObject.value || '')) : nfObject.value; + return nfObject; + }); - next(null, data); - }, - ], callback); - }; + next(null, data); + }, + ], callback); +}; - Meta.errors.clear = function (callback) { - db.delete('errors:404', callback); - }; +Errors.clear = function (callback) { + db.delete('errors:404', callback); }; diff --git a/src/meta/js.js b/src/meta/js.js index b239ee21fe..8f483779da 100644 --- a/src/meta/js.js +++ b/src/meta/js.js @@ -10,341 +10,339 @@ var file = require('../file'); var plugins = require('../plugins'); var minifier = require('./minifier'); -module.exports = function (Meta) { - Meta.js = {}; - - Meta.js.scripts = { - base: [ - 'node_modules/jquery/dist/jquery.js', - 'node_modules/socket.io-client/dist/socket.io.js', - 'public/vendor/jquery/timeago/jquery.timeago.js', - 'public/vendor/jquery/js/jquery.form.min.js', - 'public/vendor/visibility/visibility.min.js', - 'node_modules/bootstrap/dist/js/bootstrap.js', - 'public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.min.js', - 'public/vendor/jquery/textcomplete/jquery.textcomplete.js', - 'public/vendor/requirejs/require.js', - 'public/src/require-config.js', - 'public/vendor/bootbox/bootbox.js', - 'public/vendor/bootbox/wrapper.js', - 'public/vendor/tinycon/tinycon.js', - 'public/vendor/xregexp/xregexp.js', - 'public/vendor/xregexp/unicode/unicode-base.js', - 'node_modules/templates.js/lib/templates.js', - 'public/src/utils.js', - 'public/src/sockets.js', - 'public/src/app.js', - 'public/src/ajaxify.js', - 'public/src/overrides.js', - 'public/src/widgets.js', - 'node_modules/promise-polyfill/promise.js', - ], - - // files listed below are only available client-side, or are bundled in to reduce # of network requests on cold load - rjs: [ - 'public/src/client/footer.js', - 'public/src/client/chats.js', - 'public/src/client/infinitescroll.js', - 'public/src/client/pagination.js', - 'public/src/client/recent.js', - 'public/src/client/unread.js', - 'public/src/client/topic.js', - 'public/src/client/topic/events.js', - 'public/src/client/topic/fork.js', - 'public/src/client/topic/move.js', - 'public/src/client/topic/posts.js', - 'public/src/client/topic/images.js', - 'public/src/client/topic/postTools.js', - 'public/src/client/topic/threadTools.js', - 'public/src/client/categories.js', - 'public/src/client/category.js', - 'public/src/client/category/tools.js', - - 'public/src/modules/translator.js', - 'public/src/modules/notifications.js', - 'public/src/modules/chat.js', - 'public/src/modules/components.js', - 'public/src/modules/sort.js', - 'public/src/modules/navigator.js', - 'public/src/modules/topicSelect.js', - 'public/src/modules/categorySelector.js', - 'public/src/modules/share.js', - 'public/src/modules/search.js', - 'public/src/modules/alerts.js', - 'public/src/modules/taskbar.js', - 'public/src/modules/helpers.js', - 'public/src/modules/string.js', - 'public/src/modules/flags.js', - 'public/src/modules/storage.js', - ], - - // modules listed below are built (/src/modules) so they can be defined anonymously - modules: { - 'Chart.js': 'node_modules/chart.js/dist/Chart.min.js', - 'mousetrap.js': 'node_modules/mousetrap/mousetrap.min.js', - 'cropper.js': 'node_modules/cropperjs/dist/cropper.min.js', - 'jqueryui.js': 'public/vendor/jquery/js/jquery-ui.js', - 'zxcvbn.js': 'node_modules/zxcvbn/dist/zxcvbn.js', - ace: 'node_modules/ace-builds/src-min', - }, - }; - - var basePath = path.resolve(__dirname, '../..'); - - function minifyModules(modules, fork, callback) { - var moduleDirs = modules.reduce(function (prev, mod) { - var dir = path.resolve(path.dirname(mod.destPath)); - if (prev.indexOf(dir) === -1) { - prev.push(dir); - } - return prev; - }, []); - - async.eachLimit(moduleDirs, 1000, mkdirp, function (err) { - if (err) { - return callback(err); - } - - var filtered = modules.reduce(function (prev, mod) { - if (mod.srcPath.endsWith('.min.js') || path.dirname(mod.srcPath).endsWith('min')) { - prev.skip.push(mod); - } else { - prev.minify.push(mod); - } - - return prev; - }, { minify: [], skip: [] }); - - async.parallel([ - function (cb) { - minifier.js.minifyBatch(filtered.minify, fork, cb); - }, - function (cb) { - async.eachLimit(filtered.skip, 500, function (mod, next) { - file.link(mod.srcPath, mod.destPath, next); - }, cb); - }, - ], callback); - }); - } +var JS = module.exports; + +JS.scripts = { + base: [ + 'node_modules/jquery/dist/jquery.js', + 'node_modules/socket.io-client/dist/socket.io.js', + 'public/vendor/jquery/timeago/jquery.timeago.js', + 'public/vendor/jquery/js/jquery.form.min.js', + 'public/vendor/visibility/visibility.min.js', + 'node_modules/bootstrap/dist/js/bootstrap.js', + 'public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.min.js', + 'public/vendor/jquery/textcomplete/jquery.textcomplete.js', + 'public/vendor/requirejs/require.js', + 'public/src/require-config.js', + 'public/vendor/bootbox/bootbox.js', + 'public/vendor/bootbox/wrapper.js', + 'public/vendor/tinycon/tinycon.js', + 'public/vendor/xregexp/xregexp.js', + 'public/vendor/xregexp/unicode/unicode-base.js', + 'node_modules/templates.js/lib/templates.js', + 'public/src/utils.js', + 'public/src/sockets.js', + 'public/src/app.js', + 'public/src/ajaxify.js', + 'public/src/overrides.js', + 'public/src/widgets.js', + 'node_modules/promise-polyfill/promise.js', + ], + + // files listed below are only available client-side, or are bundled in to reduce # of network requests on cold load + rjs: [ + 'public/src/client/footer.js', + 'public/src/client/chats.js', + 'public/src/client/infinitescroll.js', + 'public/src/client/pagination.js', + 'public/src/client/recent.js', + 'public/src/client/unread.js', + 'public/src/client/topic.js', + 'public/src/client/topic/events.js', + 'public/src/client/topic/fork.js', + 'public/src/client/topic/move.js', + 'public/src/client/topic/posts.js', + 'public/src/client/topic/images.js', + 'public/src/client/topic/postTools.js', + 'public/src/client/topic/threadTools.js', + 'public/src/client/categories.js', + 'public/src/client/category.js', + 'public/src/client/category/tools.js', + + 'public/src/modules/translator.js', + 'public/src/modules/notifications.js', + 'public/src/modules/chat.js', + 'public/src/modules/components.js', + 'public/src/modules/sort.js', + 'public/src/modules/navigator.js', + 'public/src/modules/topicSelect.js', + 'public/src/modules/categorySelector.js', + 'public/src/modules/share.js', + 'public/src/modules/search.js', + 'public/src/modules/alerts.js', + 'public/src/modules/taskbar.js', + 'public/src/modules/helpers.js', + 'public/src/modules/string.js', + 'public/src/modules/flags.js', + 'public/src/modules/storage.js', + ], + + // modules listed below are built (/src/modules) so they can be defined anonymously + modules: { + 'Chart.js': 'node_modules/chart.js/dist/Chart.min.js', + 'mousetrap.js': 'node_modules/mousetrap/mousetrap.min.js', + 'cropper.js': 'node_modules/cropperjs/dist/cropper.min.js', + 'jqueryui.js': 'public/vendor/jquery/js/jquery-ui.js', + 'zxcvbn.js': 'node_modules/zxcvbn/dist/zxcvbn.js', + ace: 'node_modules/ace-builds/src-min', + }, +}; - function linkModules(callback) { - var modules = Meta.js.scripts.modules; - - async.eachLimit(Object.keys(modules), 1000, function (relPath, next) { - var srcPath = path.join(__dirname, '../../', modules[relPath]); - var destPath = path.join(__dirname, '../../build/public/src/modules', relPath); - - async.parallel({ - dir: function (cb) { - mkdirp(path.dirname(destPath), function (err) { - cb(err); - }); - }, - stats: function (cb) { - fs.stat(srcPath, cb); - }, - }, function (err, res) { - if (err) { - return next(err); - } - if (res.stats.isDirectory()) { - return file.linkDirs(srcPath, destPath, next); - } +var basePath = path.resolve(__dirname, '../..'); - if (process.platform === 'win32') { - fs.readFile(srcPath, function (err, file) { - if (err) { - return next(err); - } +function minifyModules(modules, fork, callback) { + var moduleDirs = modules.reduce(function (prev, mod) { + var dir = path.resolve(path.dirname(mod.destPath)); + if (prev.indexOf(dir) === -1) { + prev.push(dir); + } + return prev; + }, []); - fs.writeFile(destPath, file, next); - }); - } else { - file.link(srcPath, destPath, next); - } - }); - }, callback); - } + async.eachLimit(moduleDirs, 1000, mkdirp, function (err) { + if (err) { + return callback(err); + } - var moduleDirs = ['modules', 'admin', 'client']; + var filtered = modules.reduce(function (prev, mod) { + if (mod.srcPath.endsWith('.min.js') || path.dirname(mod.srcPath).endsWith('min')) { + prev.skip.push(mod); + } else { + prev.minify.push(mod); + } - function getModuleList(callback) { - var modules = Object.keys(Meta.js.scripts.modules).map(function (relPath) { - return { - srcPath: path.join(__dirname, '../../', Meta.js.scripts.modules[relPath]), - destPath: path.join(__dirname, '../../build/public/src/modules', relPath), - }; - }); + return prev; + }, { minify: [], skip: [] }); - var coreDirs = moduleDirs.map(function (dir) { - return { - srcPath: path.join(__dirname, '../../public/src', dir), - destPath: path.join(__dirname, '../../build/public/src', dir), - }; - }); + async.parallel([ + function (cb) { + minifier.js.minifyBatch(filtered.minify, fork, cb); + }, + function (cb) { + async.eachLimit(filtered.skip, 500, function (mod, next) { + file.link(mod.srcPath, mod.destPath, next); + }, cb); + }, + ], callback); + }); +} - modules = modules.concat(coreDirs); +function linkModules(callback) { + var modules = JS.scripts.modules; - var moduleFiles = []; - async.eachLimit(modules, 1000, function (module, next) { - var srcPath = module.srcPath; - var destPath = module.destPath; + async.eachLimit(Object.keys(modules), 1000, function (relPath, next) { + var srcPath = path.join(__dirname, '../../', modules[relPath]); + var destPath = path.join(__dirname, '../../build/public/src/modules', relPath); - fs.stat(srcPath, function (err, stats) { - if (err) { - return next(err); - } - if (!stats.isDirectory()) { - moduleFiles.push(module); - return next(); - } + async.parallel({ + dir: function (cb) { + mkdirp(path.dirname(destPath), function (err) { + cb(err); + }); + }, + stats: function (cb) { + fs.stat(srcPath, cb); + }, + }, function (err, res) { + if (err) { + return next(err); + } + if (res.stats.isDirectory()) { + return file.linkDirs(srcPath, destPath, next); + } - file.walk(srcPath, function (err, files) { + if (process.platform === 'win32') { + fs.readFile(srcPath, function (err, file) { if (err) { return next(err); } - var mods = files.filter(function (filePath) { - return path.extname(filePath) === '.js'; - }).map(function (filePath) { - return { - srcPath: path.normalize(filePath), - destPath: path.join(destPath, path.relative(srcPath, filePath)), - }; - }); - - moduleFiles = moduleFiles.concat(mods).map(function (mod) { - mod.filename = path.relative(basePath, mod.srcPath).replace(/\\/g, '/'); - return mod; - }); - - next(); + fs.writeFile(destPath, file, next); }); - }); - }, function (err) { - callback(err, moduleFiles); + } else { + file.link(srcPath, destPath, next); + } }); - } + }, callback); +} - function clearModules(callback) { - var builtPaths = moduleDirs.map(function (p) { - return path.join(__dirname, '../../build/public/src', p); - }); - async.each(builtPaths, function (builtPath, next) { - rimraf(builtPath, next); - }, function (err) { - callback(err); - }); - } +var moduleDirs = ['modules', 'admin', 'client']; - Meta.js.buildModules = function (fork, callback) { - async.waterfall([ - clearModules, - function (next) { - if (global.env === 'development') { - return linkModules(callback); - } +function getModuleList(callback) { + var modules = Object.keys(JS.scripts.modules).map(function (relPath) { + return { + srcPath: path.join(__dirname, '../../', JS.scripts.modules[relPath]), + destPath: path.join(__dirname, '../../build/public/src/modules', relPath), + }; + }); - getModuleList(next); - }, - function (modules, next) { - minifyModules(modules, fork, next); - }, - ], callback); - }; + var coreDirs = moduleDirs.map(function (dir) { + return { + srcPath: path.join(__dirname, '../../public/src', dir), + destPath: path.join(__dirname, '../../build/public/src', dir), + }; + }); + + modules = modules.concat(coreDirs); + + var moduleFiles = []; + async.eachLimit(modules, 1000, function (module, next) { + var srcPath = module.srcPath; + var destPath = module.destPath; - Meta.js.linkStatics = function (callback) { - rimraf(path.join(__dirname, '../../build/public/plugins'), function (err) { + fs.stat(srcPath, function (err, stats) { if (err) { - return callback(err); + return next(err); + } + if (!stats.isDirectory()) { + moduleFiles.push(module); + return next(); } - async.eachLimit(Object.keys(plugins.staticDirs), 1000, function (mappedPath, next) { - var sourceDir = plugins.staticDirs[mappedPath]; - var destDir = path.join(__dirname, '../../build/public/plugins', mappedPath); - mkdirp(path.dirname(destDir), function (err) { - if (err) { - return next(err); - } + file.walk(srcPath, function (err, files) { + if (err) { + return next(err); + } - file.linkDirs(sourceDir, destDir, next); + var mods = files.filter(function (filePath) { + return path.extname(filePath) === '.js'; + }).map(function (filePath) { + return { + srcPath: path.normalize(filePath), + destPath: path.join(destPath, path.relative(srcPath, filePath)), + }; }); - }, callback); - }); - }; - function getBundleScriptList(target, callback) { - var pluginDirectories = []; + moduleFiles = moduleFiles.concat(mods).map(function (mod) { + mod.filename = path.relative(basePath, mod.srcPath).replace(/\\/g, '/'); + return mod; + }); - if (target === 'admin') { - target = 'acp'; - } - var pluginScripts = plugins[target + 'Scripts'].filter(function (path) { - if (path.endsWith('.js')) { - return true; + next(); + }); + }); + }, function (err) { + callback(err, moduleFiles); + }); +} + +function clearModules(callback) { + var builtPaths = moduleDirs.map(function (p) { + return path.join(__dirname, '../../build/public/src', p); + }); + async.each(builtPaths, function (builtPath, next) { + rimraf(builtPath, next); + }, function (err) { + callback(err); + }); +} + +JS.buildModules = function (fork, callback) { + async.waterfall([ + clearModules, + function (next) { + if (global.env === 'development') { + return linkModules(callback); } - pluginDirectories.push(path); - return false; - }); + getModuleList(next); + }, + function (modules, next) { + minifyModules(modules, fork, next); + }, + ], callback); +}; + +JS.linkStatics = function (callback) { + rimraf(path.join(__dirname, '../../build/public/plugins'), function (err) { + if (err) { + return callback(err); + } + async.eachLimit(Object.keys(plugins.staticDirs), 1000, function (mappedPath, next) { + var sourceDir = plugins.staticDirs[mappedPath]; + var destDir = path.join(__dirname, '../../build/public/plugins', mappedPath); - async.each(pluginDirectories, function (directory, next) { - file.walk(directory, function (err, scripts) { + mkdirp(path.dirname(destDir), function (err) { if (err) { return next(err); } - pluginScripts = pluginScripts.concat(scripts); - next(); + file.linkDirs(sourceDir, destDir, next); }); - }, function (err) { + }, callback); + }); +}; + +function getBundleScriptList(target, callback) { + var pluginDirectories = []; + + if (target === 'admin') { + target = 'acp'; + } + var pluginScripts = plugins[target + 'Scripts'].filter(function (path) { + if (path.endsWith('.js')) { + return true; + } + + pluginDirectories.push(path); + return false; + }); + + async.each(pluginDirectories, function (directory, next) { + file.walk(directory, function (err, scripts) { if (err) { - return callback(err); + return next(err); } - var scripts = Meta.js.scripts.base.concat(pluginScripts); + pluginScripts = pluginScripts.concat(scripts); + next(); + }); + }, function (err) { + if (err) { + return callback(err); + } - if (target === 'client' && global.env !== 'development') { - scripts = scripts.concat(Meta.js.scripts.rjs); - } + var scripts = JS.scripts.base.concat(pluginScripts); - scripts = scripts.map(function (script) { - var srcPath = path.resolve(basePath, script).replace(/\\/g, '/'); - return { - srcPath: srcPath, - filename: path.relative(basePath, srcPath).replace(/\\/g, '/'), - }; - }); + if (target === 'client' && global.env !== 'development') { + scripts = scripts.concat(JS.scripts.rjs); + } - callback(null, scripts); + scripts = scripts.map(function (script) { + var srcPath = path.resolve(basePath, script).replace(/\\/g, '/'); + return { + srcPath: srcPath, + filename: path.relative(basePath, srcPath).replace(/\\/g, '/'), + }; }); - } - Meta.js.buildBundle = function (target, fork, callback) { - var fileNames = { - client: 'nodebb.min.js', - admin: 'acp.min.js', - }; + callback(null, scripts); + }); +} - async.waterfall([ - function (next) { - getBundleScriptList(target, next); - }, - function (files, next) { - var minify = global.env !== 'development'; - var filePath = path.join(__dirname, '../../build/public', fileNames[target]); - - minifier.js.bundle({ - files: files, - filename: fileNames[target], - destPath: filePath, - }, minify, fork, next); - }, - ], callback); +JS.buildBundle = function (target, fork, callback) { + var fileNames = { + client: 'nodebb.min.js', + admin: 'acp.min.js', }; - Meta.js.killMinifier = function () { - minifier.killAll(); - }; + async.waterfall([ + function (next) { + getBundleScriptList(target, next); + }, + function (files, next) { + var minify = global.env !== 'development'; + var filePath = path.join(__dirname, '../../build/public', fileNames[target]); + + minifier.js.bundle({ + files: files, + filename: fileNames[target], + destPath: filePath, + }, minify, fork, next); + }, + ], callback); +}; + +JS.killMinifier = function () { + minifier.killAll(); }; diff --git a/src/meta/logs.js b/src/meta/logs.js index 30dd983a36..1f950d6057 100644 --- a/src/meta/logs.js +++ b/src/meta/logs.js @@ -3,18 +3,16 @@ var path = require('path'); var fs = require('fs'); -module.exports = function (Meta) { - Meta.logs = { - path: path.join(__dirname, '..', '..', 'logs', 'output.log'), - }; +var Logs = module.exports; - Meta.logs.get = function (callback) { - fs.readFile(Meta.logs.path, { - encoding: 'utf-8', - }, callback); - }; +Logs.path = path.join(__dirname, '..', '..', 'logs', 'output.log'); - Meta.logs.clear = function (callback) { - fs.truncate(Meta.logs.path, 0, callback); - }; +Logs.get = function (callback) { + fs.readFile(Logs.path, { + encoding: 'utf-8', + }, callback); +}; + +Logs.clear = function (callback) { + fs.truncate(Logs.path, 0, callback); }; diff --git a/src/meta/settings.js b/src/meta/settings.js index a1d13b248d..18eae100c3 100644 --- a/src/meta/settings.js +++ b/src/meta/settings.js @@ -4,61 +4,60 @@ var async = require('async'); var db = require('../database'); var plugins = require('../plugins'); +var Meta = require('../meta'); -module.exports = function (Meta) { - Meta.settings = {}; +var Settings = module.exports; - Meta.settings.get = function (hash, callback) { - db.getObject('settings:' + hash, function (err, settings) { - callback(err, settings || {}); - }); - }; - - Meta.settings.getOne = function (hash, field, callback) { - db.getObjectField('settings:' + hash, field, callback); - }; - - Meta.settings.set = function (hash, values, callback) { - async.waterfall([ - function (next) { - db.setObject('settings:' + hash, values, next); - }, - function (next) { - plugins.fireHook('action:settings.set', { - plugin: hash, - settings: values, - }); +Settings.get = function (hash, callback) { + db.getObject('settings:' + hash, function (err, settings) { + callback(err, settings || {}); + }); +}; - Meta.reloadRequired = true; - next(); - }, - ], callback); - }; +Settings.getOne = function (hash, field, callback) { + db.getObjectField('settings:' + hash, field, callback); +}; - Meta.settings.setOne = function (hash, field, value, callback) { - db.setObjectField('settings:' + hash, field, value, callback); - }; +Settings.set = function (hash, values, callback) { + async.waterfall([ + function (next) { + db.setObject('settings:' + hash, values, next); + }, + function (next) { + plugins.fireHook('action:settings.set', { + plugin: hash, + settings: values, + }); + + Meta.reloadRequired = true; + next(); + }, + ], callback); +}; - Meta.settings.setOnEmpty = function (hash, values, callback) { - async.waterfall([ - function (next) { - db.getObject('settings:' + hash, next); - }, - function (settings, next) { - settings = settings || {}; - var empty = {}; - Object.keys(values).forEach(function (key) { - if (!settings.hasOwnProperty(key)) { - empty[key] = values[key]; - } - }); +Settings.setOne = function (hash, field, value, callback) { + db.setObjectField('settings:' + hash, field, value, callback); +}; - if (Object.keys(empty).length) { - db.setObject('settings:' + hash, empty, next); - } else { - next(); +Settings.setOnEmpty = function (hash, values, callback) { + async.waterfall([ + function (next) { + db.getObject('settings:' + hash, next); + }, + function (settings, next) { + settings = settings || {}; + var empty = {}; + Object.keys(values).forEach(function (key) { + if (!settings.hasOwnProperty(key)) { + empty[key] = values[key]; } - }, - ], callback); - }; + }); + + if (Object.keys(empty).length) { + db.setObject('settings:' + hash, empty, next); + } else { + next(); + } + }, + ], callback); }; diff --git a/src/meta/sounds.js b/src/meta/sounds.js index e7bb7c3599..ec89b471da 100644 --- a/src/meta/sounds.js +++ b/src/meta/sounds.js @@ -9,125 +9,124 @@ var async = require('async'); var file = require('../file'); var plugins = require('../plugins'); var user = require('../user'); +var Meta = require('../meta'); var soundsPath = path.join(__dirname, '../../build/public/sounds'); var uploadsPath = path.join(__dirname, '../../public/uploads/sounds'); -module.exports = function (Meta) { - Meta.sounds = {}; +var Sounds = module.exports; - Meta.sounds.addUploads = function addUploads(callback) { - fs.readdir(uploadsPath, function (err, files) { - if (err) { - if (err.code !== 'ENOENT') { - return callback(err); - } - - files = []; +Sounds.addUploads = function addUploads(callback) { + fs.readdir(uploadsPath, function (err, files) { + if (err) { + if (err.code !== 'ENOENT') { + return callback(err); } - var uploadSounds = files.reduce(function (prev, fileName) { - var name = fileName.split('.'); - if (!name.length || !name[0].length) { - return prev; - } - name = name[0]; - name = name[0].toUpperCase() + name.slice(1); + files = []; + } - prev[name] = fileName; + var uploadSounds = files.reduce(function (prev, fileName) { + var name = fileName.split('.'); + if (!name.length || !name[0].length) { return prev; - }, {}); - - plugins.soundpacks = plugins.soundpacks.filter(function (pack) { - return pack.name !== 'Uploads'; - }); - if (Object.keys(uploadSounds).length) { - plugins.soundpacks.push({ - name: 'Uploads', - id: 'uploads', - dir: uploadsPath, - sounds: uploadSounds, - }); } + name = name[0]; + name = name[0].toUpperCase() + name.slice(1); + + prev[name] = fileName; + return prev; + }, {}); - callback(); + plugins.soundpacks = plugins.soundpacks.filter(function (pack) { + return pack.name !== 'Uploads'; }); - }; + if (Object.keys(uploadSounds).length) { + plugins.soundpacks.push({ + name: 'Uploads', + id: 'uploads', + dir: uploadsPath, + sounds: uploadSounds, + }); + } - Meta.sounds.build = function build(callback) { - Meta.sounds.addUploads(function (err) { - if (err) { - return callback(err); - } + callback(); + }); +}; - var map = plugins.soundpacks.map(function (pack) { - return Object.keys(pack.sounds).reduce(function (prev, soundName) { - var soundPath = pack.sounds[soundName]; - prev[pack.name + ' | ' + soundName] = pack.id + '/' + soundPath; - return prev; - }, {}); - }); - map.unshift({}); - map = Object.assign.apply(null, map); - - async.series([ - function (next) { - rimraf(soundsPath, next); - }, - function (next) { - mkdirp(soundsPath, next); - }, - function (cb) { - async.parallel([ - function (next) { - fs.writeFile(path.join(soundsPath, 'fileMap.json'), JSON.stringify(map), next); - }, - function (next) { - async.each(plugins.soundpacks, function (pack, next) { - file.linkDirs(pack.dir, path.join(soundsPath, pack.id), next); - }, next); - }, - ], cb); - }, - ], function (err) { - callback(err); - }); - }); - }; +Sounds.build = function build(callback) { + Sounds.addUploads(function (err) { + if (err) { + return callback(err); + } - var keys = ['chat-incoming', 'chat-outgoing', 'notification']; + var map = plugins.soundpacks.map(function (pack) { + return Object.keys(pack.sounds).reduce(function (prev, soundName) { + var soundPath = pack.sounds[soundName]; + prev[pack.name + ' | ' + soundName] = pack.id + '/' + soundPath; + return prev; + }, {}); + }); + map.unshift({}); + map = Object.assign.apply(null, map); - Meta.sounds.getUserSoundMap = function getUserSoundMap(uid, callback) { - async.parallel({ - defaultMapping: function (next) { - Meta.configs.getFields(keys, next); + async.series([ + function (next) { + rimraf(soundsPath, next); }, - userSettings: function (next) { - user.getSettings(uid, next); + function (next) { + mkdirp(soundsPath, next); }, - }, function (err, results) { - if (err) { - return callback(err); - } - - var userSettings = results.userSettings; - userSettings = { - notification: userSettings.notificationSound, - 'chat-incoming': userSettings.incomingChatSound, - 'chat-outgoing': userSettings.outgoingChatSound, - }; - var defaultMapping = results.defaultMapping || {}; - var soundMapping = {}; - - keys.forEach(function (key) { - if (userSettings[key] || userSettings[key] === '') { - soundMapping[key] = userSettings[key] || ''; - } else { - soundMapping[key] = defaultMapping[key] || ''; - } - }); + function (cb) { + async.parallel([ + function (next) { + fs.writeFile(path.join(soundsPath, 'fileMap.json'), JSON.stringify(map), next); + }, + function (next) { + async.each(plugins.soundpacks, function (pack, next) { + file.linkDirs(pack.dir, path.join(soundsPath, pack.id), next); + }, next); + }, + ], cb); + }, + ], function (err) { + callback(err); + }); + }); +}; - callback(null, soundMapping); +var keys = ['chat-incoming', 'chat-outgoing', 'notification']; + +Sounds.getUserSoundMap = function getUserSoundMap(uid, callback) { + async.parallel({ + defaultMapping: function (next) { + Meta.configs.getFields(keys, next); + }, + userSettings: function (next) { + user.getSettings(uid, next); + }, + }, function (err, results) { + if (err) { + return callback(err); + } + + var userSettings = results.userSettings; + userSettings = { + notification: userSettings.notificationSound, + 'chat-incoming': userSettings.incomingChatSound, + 'chat-outgoing': userSettings.outgoingChatSound, + }; + var defaultMapping = results.defaultMapping || {}; + var soundMapping = {}; + + keys.forEach(function (key) { + if (userSettings[key] || userSettings[key] === '') { + soundMapping[key] = userSettings[key] || ''; + } else { + soundMapping[key] = defaultMapping[key] || ''; + } }); - }; + + callback(null, soundMapping); + }); }; diff --git a/src/meta/tags.js b/src/meta/tags.js index ac0b395a23..34ed3c43a9 100644 --- a/src/meta/tags.js +++ b/src/meta/tags.js @@ -4,163 +4,163 @@ var nconf = require('nconf'); var validator = require('validator'); var async = require('async'); var winston = require('winston'); + var plugins = require('../plugins'); +var Meta = require('../meta'); + +var Tags = module.exports; + +Tags.parse = function (req, meta, link, callback) { + async.parallel({ + tags: function (next) { + var defaultTags = [{ + name: 'viewport', + content: 'width=device-width, initial-scale=1.0', + }, { + name: 'content-type', + content: 'text/html; charset=UTF-8', + noEscape: true, + }, { + name: 'apple-mobile-web-app-capable', + content: 'yes', + }, { + name: 'mobile-web-app-capable', + content: 'yes', + }, { + property: 'og:site_name', + content: Meta.config.title || 'NodeBB', + }, { + name: 'msapplication-badge', + content: 'frequency=30; polling-uri=' + nconf.get('url') + '/sitemap.xml', + noEscape: true, + }]; + + if (Meta.config.keywords) { + defaultTags.push({ + name: 'keywords', + content: Meta.config.keywords, + }); + } -module.exports = function (Meta) { - Meta.tags = {}; + if (Meta.config['brand:logo']) { + defaultTags.push({ + name: 'msapplication-square150x150logo', + content: Meta.config['brand:logo'], + noEscape: true, + }); + } - Meta.tags.parse = function (req, meta, link, callback) { - async.parallel({ - tags: function (next) { - var defaultTags = [{ - name: 'viewport', - content: 'width=device-width, initial-scale=1.0', + plugins.fireHook('filter:meta.getMetaTags', defaultTags, next); + }, + links: function (next) { + var defaultLinks = [{ + rel: 'icon', + type: 'image/x-icon', + href: nconf.get('relative_path') + '/favicon.ico' + (Meta.config['cache-buster'] ? '?' + Meta.config['cache-buster'] : ''), + }, { + rel: 'manifest', + href: nconf.get('relative_path') + '/manifest.json', + }]; + + if (plugins.hasListeners('filter:search.query')) { + defaultLinks.push({ + rel: 'search', + type: 'application/opensearchdescription+xml', + href: nconf.get('relative_path') + '/osd.xml', + }); + } + + // Touch icons for mobile-devices + if (Meta.config['brand:touchIcon']) { + defaultLinks.push({ + rel: 'apple-touch-icon', + href: nconf.get('relative_path') + '/apple-touch-icon', }, { - name: 'content-type', - content: 'text/html; charset=UTF-8', - noEscape: true, + rel: 'icon', + sizes: '36x36', + href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-36.png', }, { - name: 'apple-mobile-web-app-capable', - content: 'yes', + rel: 'icon', + sizes: '48x48', + href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-48.png', }, { - name: 'mobile-web-app-capable', - content: 'yes', + rel: 'icon', + sizes: '72x72', + href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-72.png', }, { - property: 'og:site_name', - content: Meta.config.title || 'NodeBB', + rel: 'icon', + sizes: '96x96', + href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-96.png', }, { - name: 'msapplication-badge', - content: 'frequency=30; polling-uri=' + nconf.get('url') + '/sitemap.xml', - noEscape: true, - }]; - - if (Meta.config.keywords) { - defaultTags.push({ - name: 'keywords', - content: Meta.config.keywords, - }); - } - - if (Meta.config['brand:logo']) { - defaultTags.push({ - name: 'msapplication-square150x150logo', - content: Meta.config['brand:logo'], - noEscape: true, - }); - } - - plugins.fireHook('filter:meta.getMetaTags', defaultTags, next); - }, - links: function (next) { - var defaultLinks = [{ rel: 'icon', - type: 'image/x-icon', - href: nconf.get('relative_path') + '/favicon.ico' + (Meta.config['cache-buster'] ? '?' + Meta.config['cache-buster'] : ''), + sizes: '144x144', + href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-144.png', }, { - rel: 'manifest', - href: nconf.get('relative_path') + '/manifest.json', - }]; - - if (plugins.hasListeners('filter:search.query')) { - defaultLinks.push({ - rel: 'search', - type: 'application/opensearchdescription+xml', - href: nconf.get('relative_path') + '/osd.xml', - }); - } - - // Touch icons for mobile-devices - if (Meta.config['brand:touchIcon']) { - defaultLinks.push({ - rel: 'apple-touch-icon', - href: nconf.get('relative_path') + '/apple-touch-icon', - }, { - rel: 'icon', - sizes: '36x36', - href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-36.png', - }, { - rel: 'icon', - sizes: '48x48', - href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-48.png', - }, { - rel: 'icon', - sizes: '72x72', - href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-72.png', - }, { - rel: 'icon', - sizes: '96x96', - href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-96.png', - }, { - rel: 'icon', - sizes: '144x144', - href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-144.png', - }, { - rel: 'icon', - sizes: '192x192', - href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-192.png', - }); - } - plugins.fireHook('filter:meta.getLinkTags', defaultLinks, next); - }, - }, function (err, results) { - if (err) { - return callback(err); + rel: 'icon', + sizes: '192x192', + href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-192.png', + }); } + plugins.fireHook('filter:meta.getLinkTags', defaultLinks, next); + }, + }, function (err, results) { + if (err) { + return callback(err); + } - meta = results.tags.concat(meta || []).map(function (tag) { - if (!tag || typeof tag.content !== 'string') { - winston.warn('Invalid meta tag. ', tag); - return tag; - } - - if (!tag.noEscape) { - tag.content = validator.escape(String(tag.content)); - } - + meta = results.tags.concat(meta || []).map(function (tag) { + if (!tag || typeof tag.content !== 'string') { + winston.warn('Invalid meta tag. ', tag); return tag; - }); + } - addIfNotExists(meta, 'property', 'og:title', Meta.config.title || 'NodeBB'); + if (!tag.noEscape) { + tag.content = validator.escape(String(tag.content)); + } - var ogUrl = nconf.get('url') + req.path; - addIfNotExists(meta, 'property', 'og:url', ogUrl); + return tag; + }); - addIfNotExists(meta, 'name', 'description', Meta.config.description); - addIfNotExists(meta, 'property', 'og:description', Meta.config.description); + addIfNotExists(meta, 'property', 'og:title', Meta.config.title || 'NodeBB'); - var ogImage = Meta.config['og:image'] || Meta.config['brand:logo'] || ''; - if (ogImage && !ogImage.startsWith('http')) { - ogImage = nconf.get('url') + ogImage; - } - addIfNotExists(meta, 'property', 'og:image', ogImage); - if (ogImage) { - addIfNotExists(meta, 'property', 'og:image:width', 200); - addIfNotExists(meta, 'property', 'og:image:height', 200); - } + var ogUrl = nconf.get('url') + req.path; + addIfNotExists(meta, 'property', 'og:url', ogUrl); - link = results.links.concat(link || []); + addIfNotExists(meta, 'name', 'description', Meta.config.description); + addIfNotExists(meta, 'property', 'og:description', Meta.config.description); - callback(null, { - meta: meta, - link: link, - }); - }); - }; + var ogImage = Meta.config['og:image'] || Meta.config['brand:logo'] || ''; + if (ogImage && !ogImage.startsWith('http')) { + ogImage = nconf.get('url') + ogImage; + } + addIfNotExists(meta, 'property', 'og:image', ogImage); + if (ogImage) { + addIfNotExists(meta, 'property', 'og:image:width', 200); + addIfNotExists(meta, 'property', 'og:image:height', 200); + } - function addIfNotExists(meta, keyName, tagName, value) { - var exists = false; - meta.forEach(function (tag) { - if (tag[keyName] === tagName) { - exists = true; - } + link = results.links.concat(link || []); + + callback(null, { + meta: meta, + link: link, }); + }); +}; - if (!exists && value) { - var data = { - content: validator.escape(String(value)), - }; - data[keyName] = tagName; - meta.push(data); +function addIfNotExists(meta, keyName, tagName, value) { + var exists = false; + meta.forEach(function (tag) { + if (tag[keyName] === tagName) { + exists = true; } + }); + + if (!exists && value) { + var data = { + content: validator.escape(String(value)), + }; + data[keyName] = tagName; + meta.push(data); } -}; +} diff --git a/src/meta/themes.js b/src/meta/themes.js index 9bca68c431..d034bd8a96 100644 --- a/src/meta/themes.js +++ b/src/meta/themes.js @@ -9,168 +9,167 @@ var async = require('async'); var file = require('../file'); var db = require('../database'); - -module.exports = function (Meta) { - Meta.themes = {}; - - Meta.themes.get = function (callback) { - var themePath = nconf.get('themes_path'); - if (typeof themePath !== 'string') { - return callback(null, []); - } - - async.waterfall([ - function (next) { - fs.readdir(themePath, next); - }, - function (files, next) { - async.filter(files, function (file, next) { - fs.stat(path.join(themePath, file), function (err, fileStat) { - if (err) { - if (err.code === 'ENOENT') { - return next(null, false); - } - return next(err); - } - - next(null, (fileStat.isDirectory() && file.slice(0, 13) === 'nodebb-theme-')); - }); - }, next); - }, - function (themes, next) { - async.map(themes, function (theme, next) { - var config = path.join(themePath, theme, 'theme.json'); - - fs.readFile(config, function (err, file) { - if (err) { - if (err.code === 'ENOENT') { - return next(null, null); - } - return next(err); +var Meta = require('../meta'); + +var Themes = module.exports; + +Themes.get = function (callback) { + var themePath = nconf.get('themes_path'); + if (typeof themePath !== 'string') { + return callback(null, []); + } + + async.waterfall([ + function (next) { + fs.readdir(themePath, next); + }, + function (files, next) { + async.filter(files, function (file, next) { + fs.stat(path.join(themePath, file), function (err, fileStat) { + if (err) { + if (err.code === 'ENOENT') { + return next(null, false); } - try { - var configObj = JSON.parse(file.toString()); - - // Minor adjustments for API output - configObj.type = 'local'; - if (configObj.screenshot) { - configObj.screenshot_url = nconf.get('relative_path') + '/css/previews/' + configObj.id; - } else { - configObj.screenshot_url = nconf.get('relative_path') + '/assets/images/themes/default.png'; - } - next(null, configObj); - } catch (err) { - winston.error('[themes] Unable to parse theme.json ' + theme); - next(null, null); + return next(err); + } + + next(null, (fileStat.isDirectory() && file.slice(0, 13) === 'nodebb-theme-')); + }); + }, next); + }, + function (themes, next) { + async.map(themes, function (theme, next) { + var config = path.join(themePath, theme, 'theme.json'); + + fs.readFile(config, function (err, file) { + if (err) { + if (err.code === 'ENOENT') { + return next(null, null); } - }); - }, next); - }, - function (themes, next) { - themes = themes.filter(Boolean); - next(null, themes); - }, - ], callback); - }; - - Meta.themes.set = function (data, callback) { - var themeData = { - 'theme:type': data.type, - 'theme:id': data.id, - 'theme:staticDir': '', - 'theme:templates': '', - 'theme:src': '', - }; - - switch (data.type) { - case 'local': - async.waterfall([ - async.apply(Meta.configs.get, 'theme:id'), - function (current, next) { - async.series([ - async.apply(db.sortedSetRemove, 'plugins:active', current), - async.apply(db.sortedSetAdd, 'plugins:active', 0, data.id), - ], function (err) { - next(err); - }); - }, - function (next) { - fs.readFile(path.join(nconf.get('themes_path'), data.id, 'theme.json'), function (err, config) { - if (!err) { - config = JSON.parse(config.toString()); - next(null, config); + return next(err); + } + try { + var configObj = JSON.parse(file.toString()); + + // Minor adjustments for API output + configObj.type = 'local'; + if (configObj.screenshot) { + configObj.screenshot_url = nconf.get('relative_path') + '/css/previews/' + configObj.id; } else { - next(err); + configObj.screenshot_url = nconf.get('relative_path') + '/assets/images/themes/default.png'; } - }); - }, - function (config, next) { - themeData['theme:staticDir'] = config.staticDir ? config.staticDir : ''; - themeData['theme:templates'] = config.templates ? config.templates : ''; - themeData['theme:src'] = ''; - - Meta.configs.setMultiple(themeData, next); + next(null, configObj); + } catch (err) { + winston.error('[themes] Unable to parse theme.json ' + theme); + next(null, null); + } + }); + }, next); + }, + function (themes, next) { + themes = themes.filter(Boolean); + next(null, themes); + }, + ], callback); +}; - // Re-set the themes path (for when NodeBB is reloaded) - Meta.themes.setPath(config); - }, - ], callback); - - Meta.reloadRequired = true; - break; - - case 'bootswatch': - Meta.configs.setMultiple({ - 'theme:src': data.src, - bootswatchSkin: data.id.toLowerCase(), - }, callback); - break; - } +Themes.set = function (data, callback) { + var themeData = { + 'theme:type': data.type, + 'theme:id': data.id, + 'theme:staticDir': '', + 'theme:templates': '', + 'theme:src': '', }; - Meta.themes.setupPaths = function (callback) { + switch (data.type) { + case 'local': async.waterfall([ + async.apply(Meta.configs.get, 'theme:id'), + function (current, next) { + async.series([ + async.apply(db.sortedSetRemove, 'plugins:active', current), + async.apply(db.sortedSetAdd, 'plugins:active', 0, data.id), + ], function (err) { + next(err); + }); + }, function (next) { - async.parallel({ - themesData: Meta.themes.get, - currentThemeId: function (next) { - db.getObjectField('config', 'theme:id', next); - }, - }, next); + fs.readFile(path.join(nconf.get('themes_path'), data.id, 'theme.json'), function (err, config) { + if (!err) { + config = JSON.parse(config.toString()); + next(null, config); + } else { + next(err); + } + }); }, - function (data, next) { - var themeId = data.currentThemeId || 'nodebb-theme-persona'; - - var themeObj = data.themesData.filter(function (themeObj) { - return themeObj.id === themeId; - })[0]; - - if (process.env.NODE_ENV === 'development') { - winston.info('[themes] Using theme ' + themeId); - } + function (config, next) { + themeData['theme:staticDir'] = config.staticDir ? config.staticDir : ''; + themeData['theme:templates'] = config.templates ? config.templates : ''; + themeData['theme:src'] = ''; - if (!themeObj) { - return callback(new Error('[[error:theme-not-found]]')); - } + Meta.configs.setMultiple(themeData, next); - Meta.themes.setPath(themeObj); - next(); + // Re-set the themes path (for when NodeBB is reloaded) + Themes.setPath(config); }, ], callback); - }; - Meta.themes.setPath = function (themeObj) { - // Theme's templates path - var themePath = nconf.get('base_templates_path'); - var fallback = path.join(nconf.get('themes_path'), themeObj.id, 'templates'); + Meta.reloadRequired = true; + break; - if (themeObj.templates) { - themePath = path.join(nconf.get('themes_path'), themeObj.id, themeObj.templates); - } else if (file.existsSync(fallback)) { - themePath = fallback; - } + case 'bootswatch': + Meta.configs.setMultiple({ + 'theme:src': data.src, + bootswatchSkin: data.id.toLowerCase(), + }, callback); + break; + } +}; - nconf.set('theme_templates_path', themePath); - nconf.set('theme_config', path.join(nconf.get('themes_path'), themeObj.id, 'theme.json')); - }; +Themes.setupPaths = function (callback) { + async.waterfall([ + function (next) { + async.parallel({ + themesData: Themes.get, + currentThemeId: function (next) { + db.getObjectField('config', 'theme:id', next); + }, + }, next); + }, + function (data, next) { + var themeId = data.currentThemeId || 'nodebb-theme-persona'; + + var themeObj = data.themesData.filter(function (themeObj) { + return themeObj.id === themeId; + })[0]; + + if (process.env.NODE_ENV === 'development') { + winston.info('[themes] Using theme ' + themeId); + } + + if (!themeObj) { + return callback(new Error('[[error:theme-not-found]]')); + } + + Themes.setPath(themeObj); + next(); + }, + ], callback); +}; + +Themes.setPath = function (themeObj) { + // Theme's templates path + var themePath = nconf.get('base_templates_path'); + var fallback = path.join(nconf.get('themes_path'), themeObj.id, 'templates'); + + if (themeObj.templates) { + themePath = path.join(nconf.get('themes_path'), themeObj.id, themeObj.templates); + } else if (file.existsSync(fallback)) { + themePath = fallback; + } + + nconf.set('theme_templates_path', themePath); + nconf.set('theme_config', path.join(nconf.get('themes_path'), themeObj.id, 'theme.json')); };