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