Merge remote-tracking branch 'origin/develop'

v1.18.x
Julian Lam 8 years ago
commit 1034626896

@ -23,9 +23,9 @@ addons:
packages:
- g++-4.8
node_js:
- "8"
- "7"
- "6"
- "4"
branches:
only:
- master

@ -73,8 +73,9 @@ module.exports = function (grunt) {
watch: {
lessUpdated_Client: {
files: [
'public/*.less',
'node_modules/nodebb-*/*.less', 'node_modules/nodebb-*/**/*.less',
'public/less/*.less',
'!public/less/admin/**/*.less',
'node_modules/nodebb-*/**/*.less',
'!node_modules/nodebb-*/node_modules/**',
'!node_modules/nodebb-*/.git/**',
],
@ -83,7 +84,12 @@ module.exports = function (grunt) {
},
},
lessUpdated_Admin: {
files: ['public/**/*.less'],
files: [
'public/less/admin/**/*.less',
'node_modules/nodebb-*/**/*.less',
'!node_modules/nodebb-*/node_modules/**',
'!node_modules/nodebb-*/.git/**',
],
options: {
interval: 1000,
},
@ -91,9 +97,9 @@ module.exports = function (grunt) {
clientUpdated: {
files: [
'public/src/**/*.js',
'node_modules/nodebb-*/*.js', 'node_modules/nodebb-*/**/*.js',
'node_modules/nodebb-*/**/*.js',
'!node_modules/nodebb-*/node_modules/**',
'node_modules/templates.js/lib/templates.js',
'node_modules/benchpressjs/build/benchpress.js',
'!node_modules/nodebb-*/.git/**',
],
options: {
@ -109,7 +115,7 @@ module.exports = function (grunt) {
templatesUpdated: {
files: [
'src/views/**/*.tpl',
'node_modules/nodebb-*/*.tpl', 'node_modules/nodebb-*/**/*.tpl',
'node_modules/nodebb-*/**/*.tpl',
'!node_modules/nodebb-*/node_modules/**',
'!node_modules/nodebb-*/.git/**',
],

@ -37,6 +37,7 @@ var winston = require('winston');
var path = require('path');
var pkg = require('./package.json');
var file = require('./src/file');
var debug = require('./src/meta/debugParams')().execArgv.length;
global.env = process.env.NODE_ENV || 'production';
@ -52,6 +53,22 @@ winston.add(winston.transports.Console, {
stringify: (!!nconf.get('json-logging')),
});
if (debug) {
var winstonCommon = require('winston/lib/winston/common');
// Override to use real console.log etc for VSCode debugger
winston.transports.Console.prototype.log = function (level, message, meta, callback) {
const output = winstonCommon.log(Object.assign({}, this, {
level,
message,
meta,
}));
console[level in console ? level : 'log'](output);
setImmediate(callback, null, true);
};
}
// Alternate configuration file support
var configFile = path.join(__dirname, '/config.json');

@ -9,6 +9,8 @@ var less = require('less');
var async = require('async');
var uglify = require('uglify-js');
var nconf = require('nconf');
var Benchpress = require('benchpressjs');
var app = express();
var server;
@ -35,7 +37,7 @@ web.install = function (port) {
winston.info('Launching web installer on port', port);
app.use(express.static('public', {}));
app.engine('tpl', require('templates.js').__express);
app.engine('tpl', Benchpress.__express);
app.set('view engine', 'tpl');
app.set('views', path.join(__dirname, '../src/views'));
app.use(bodyParser.urlencoded({

@ -17,44 +17,45 @@
"coveralls": "istanbul cover _mocha --report lcovonly -- -R dot && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage"
},
"dependencies": {
"ace-builds": "^1.2.6",
"async": "2.4.1",
"autoprefixer": "7.1.1",
"ace-builds": "^1.2.8",
"async": "2.5.0",
"autoprefixer": "7.1.3",
"bcryptjs": "2.4.3",
"body-parser": "^1.9.0",
"benchpressjs": "^1.0.3",
"body-parser": "^1.17.2",
"bootstrap": "^3.3.7",
"chart.js": "^2.4.0",
"colors": "^1.1.0",
"compression": "^1.1.0",
"chart.js": "^2.6.0",
"colors": "^1.1.2",
"compression": "^1.7.0",
"connect-ensure-login": "^0.1.1",
"connect-flash": "^0.1.1",
"connect-mongo": "1.3.2",
"connect-multiparty": "^2.0.0",
"connect-redis": "3.3.0",
"cookie-parser": "^1.3.3",
"cron": "^1.0.5",
"cookie-parser": "^1.4.3",
"cron": "^1.2.1",
"cropperjs": "^0.8.1",
"csurf": "^1.6.1",
"daemon": "~1.1.0",
"express": "^4.14.0",
"express-session": "^1.8.2",
"csurf": "^1.9.0",
"daemon": "^1.1.0",
"express": "^4.15.4",
"express-session": "^1.15.5",
"express-useragent": "1.0.7",
"html-to-text": "3.3.0",
"ipaddr.js": "^1.5.0",
"ipaddr.js": "^1.5.2",
"jimp": "0.2.28",
"jquery": "^3.1.0",
"json-2-csv": "^2.0.22",
"less": "^2.0.0",
"jquery": "^3.2.1",
"json-2-csv": "^2.1.1",
"less": "^2.7.2",
"lodash": "^4.17.4",
"logrotate-stream": "^0.2.3",
"lru-cache": "4.0.2",
"mime": "^1.3.4",
"minimist": "^1.1.1",
"mkdirp": "~0.5.0",
"mongodb": "2.2.27",
"morgan": "^1.3.2",
"mousetrap": "^1.5.3",
"nconf": "~0.8.2",
"logrotate-stream": "^0.2.5",
"lru-cache": "4.1.1",
"mime": "^1.4.0",
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
"mongodb": "2.2.31",
"morgan": "^1.8.2",
"mousetrap": "^1.6.1",
"nconf": "^0.8.4",
"nodebb-plugin-composer-default": "5.0.7",
"nodebb-plugin-dbsearch": "2.0.6",
"nodebb-plugin-emoji-extended": "1.1.1",
@ -69,34 +70,31 @@
"nodebb-theme-slick": "1.1.1",
"nodebb-theme-vanilla": "7.0.0",
"nodebb-widget-essentials": "3.0.4",
"nodemailer": "2.6.4",
"nodemailer-sendmail-transport": "1.0.0",
"nodemailer-smtp-transport": "^2.4.1",
"nodemailer": "4.1.0",
"passport": "^0.3.0",
"passport-local": "1.0.0",
"postcss": "6.0.1",
"postcss-clean": "1.0.2",
"postcss": "6.0.10",
"postcss-clean": "1.0.3",
"promise-polyfill": "^6.0.2",
"prompt": "^1.0.0",
"redis": "2.7.1",
"redis": "2.8.0",
"request": "2.81.0",
"rimraf": "2.6.1",
"rss": "^1.0.0",
"sanitize-html": "^1.13.0",
"semver": "^5.1.0",
"serve-favicon": "^2.1.5",
"sitemap": "^1.4.0",
"socket.io": "2.0.1",
"socket.io-client": "2.0.1",
"socket.io-redis": "5.0.1",
"rss": "^1.2.2",
"sanitize-html": "^1.14.0",
"semver": "^5.4.1",
"serve-favicon": "^2.4.3",
"sitemap": "^1.13.0",
"socket.io": "2.0.3",
"socket.io-client": "2.0.3",
"socket.io-redis": "5.2.0",
"socketio-wildcard": "2.0.0",
"spdx-license-list": "^3.0.1",
"string": "^3.0.0",
"templates.js": "0.3.11",
"string": "^3.3.3",
"toobusy-js": "^0.5.1",
"uglify-js": "^3.0.11",
"validator": "7.0.0",
"winston": "^2.1.0",
"uglify-js": "^3.0.28",
"validator": "8.1.0",
"winston": "^2.3.1",
"xml": "^1.0.1",
"xregexp": "3.2.0",
"zxcvbn": "^4.4.2"
@ -106,11 +104,11 @@
"eslint": "^3.12.0",
"eslint-config-airbnb-base": "^11.1.0",
"eslint-plugin-import": "^2.2.0",
"grunt": "~1.0.0",
"grunt": "^1.0.1",
"grunt-contrib-watch": "^1.0.0",
"istanbul": "^0.4.2",
"jsdom": "9.12.0",
"mocha": "~3.2.0",
"jsdom": "^11.2.0",
"mocha": "^3.5.0",
"mocha-lcov-reporter": "^1.2.0",
"xmlhttprequest": "1.8.0",
"xmlhttprequest-ssl": "1.5.3"
@ -119,7 +117,7 @@
"url": "https://github.com/NodeBB/NodeBB/issues"
},
"engines": {
"node": ">=4"
"node": ">=6"
},
"maintainers": [
{

@ -10,7 +10,6 @@
"overrides": true,
"componentHandler": true,
"bootbox": true,
"templates": true,
"Visibility": true,
"Tinycon": true,
"Promise": true

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

@ -4,13 +4,21 @@
"address-help": "The following email address refers to the email that the recipient will see in the \"From\" and \"Reply To\" fields.",
"from": "From Name",
"from-help": "The from name to display in the email.",
"gmail-routing": "Gmail Routing",
"gmail-routing-help1": "There have been reports of Gmail Routing not working on accounts with heightened security. In those scenarios, you will have to <a href=\"https://www.google.com/settings/security/lesssecureapps\">configure your GMail account to allow less secure apps</a>.",
"gmail-routing-help2": "For more information about this workaround, <a href=\"https://nodemailer.com/using-gmail/\">please consult this NodeMailer article on the issue.</a> An alternative would be to utilise a third-party emailer plugin such as SendGrid, Mailgun, etc. <a href=\"{config.relative_path}/admin/extend/plugins\">Browse available plugins here</a>.",
"gmail-transport": "Route emails through a Gmail/Google Apps account",
"gmail-transport.username": "Username",
"gmail-transport.username-help": "Enter the full email address here, especially if you are using a Google Apps managed domain.",
"gmail-transport.password": "Password",
"smtp-transport": "SMTP Transport",
"smtp-transport.enabled": "Use an external email server to send emails",
"smtp-transport-help": "You can select from a list of well-known services or enter a custom one.",
"smtp-transport.service": "Select a service",
"smtp-transport.service-custom": "Custom Service",
"smtp-transport.service-help": "Select a service name above in order to use the known information about it. Alternatively, select 'Custom Service' and enter the details below.",
"smtp-transport.gmail-warning1": "There have been reports of the Gmail service not working on accounts with heightened security. In those scenarios, you will have to <a href=\"https://www.google.com/settings/security/lesssecureapps\">configure your GMail account to allow less secure apps</a>.",
"smtp-transport.gmail-warning2": "For more information about this workaround, <a href=\"https://nodemailer.com/usage/using-gmail/\">please consult this NodeMailer article on the issue.</a> An alternative would be to utilise a third-party emailer plugin such as SendGrid, Mailgun, etc. <a href=\"{config.relative_path}/admin/extend/plugins\">Browse available plugins here</a>.",
"smtp-transport.host": "SMTP Host",
"smtp-transport.port": "SMTP Port",
"smtp-transport.username": "Username",
"smtp-transport.username-help": "<b>For the Gmail service,</b> enter the full email address here, especially if you are using a Google Apps managed domain.",
"smtp-transport.password": "Password",
"template": "Edit Email Template",
"template.select": "Select Email Template",
"template.revert": "Revert to Original",

@ -1,5 +1,5 @@
.settings {
.row {
> .row {
margin-bottom: 30px;
}

@ -3,4 +3,5 @@
height: 450px;
display: block;
border: 1px solid #eee;
padding: 5px 10px;
}

@ -99,6 +99,10 @@
$('#main-menu li').removeClass('active');
$('#main-menu a').removeClass('active').filter('[href="' + url + '"]').each(function () {
var menu = $(this);
if (menu.parent().attr('data-link')) {
return;
}
menu
.parent().addClass('active')
.parents('.menu-item').addClass('active');

@ -1,7 +1,7 @@
'use strict';
define('admin/appearance/skins', ['translator'], function (translator) {
define('admin/appearance/skins', ['translator', 'benchpress'], function (translator, Benchpress) {
var Skins = {};
Skins.init = function () {
@ -52,7 +52,7 @@ define('admin/appearance/skins', ['translator'], function (translator) {
Skins.render = function (bootswatch) {
var themeContainer = $('#bootstrap_themes');
templates.parse('admin/partials/theme_list', {
Benchpress.parse('admin/partials/theme_list', {
themes: bootswatch.themes.map(function (theme) {
return {
type: 'bootswatch',

@ -1,7 +1,7 @@
'use strict';
define('admin/appearance/themes', ['translator'], function (translator) {
define('admin/appearance/themes', ['translator', 'benchpress'], function (translator, Benchpress) {
var Themes = {};
Themes.init = function () {
@ -73,7 +73,7 @@ define('admin/appearance/themes', ['translator'], function (translator) {
if (!themes.length) {
instListEl.append($('<li/ >').addClass('no-themes').translateHtml('[[admin/appearance/themes:no-themes]]'));
} else {
templates.parse('admin/partials/theme_list', {
Benchpress.parse('admin/partials/theme_list', {
themes: themes,
}, function (html) {
translator.translate(html, function (html) {

@ -1,7 +1,7 @@
'use strict';
define('admin/extend/plugins', ['jqueryui', 'translator'], function (jqueryui, translator) {
define('admin/extend/plugins', ['jqueryui', 'translator', 'benchpress'], function (jqueryui, translator, Benchpress) {
var Plugins = {};
Plugins.init = function () {
var pluginsList = $('.plugins');
@ -21,6 +21,7 @@ define('admin/extend/plugins', ['jqueryui', 'translator'], function (jqueryui, t
var pluginEl = $(this).parents('li');
pluginID = pluginEl.attr('data-plugin-id');
var btn = $('#' + pluginID + ' [data-action="toggleActive"]');
var pluginData = ajaxify.data.installed[pluginEl.attr('data-plugin-index')];
function toggleActivate() {
@ -57,7 +58,7 @@ define('admin/extend/plugins', ['jqueryui', 'translator'], function (jqueryui, t
}
if (pluginData.license && pluginData.active !== true) {
templates.parse('admin/partials/plugins/license', pluginData, function (html) {
Benchpress.parse('admin/partials/plugins/license', pluginData, function (html) {
bootbox.dialog({
title: '[[admin/extend/plugins:license.title]]',
message: html,

@ -1,7 +1,7 @@
'use strict';
define('admin/extend/rewards', ['translator'], function (translator) {
define('admin/extend/rewards', ['translator', 'benchpress'], function (translator, Benchpress) {
var rewards = {};
@ -139,7 +139,7 @@ define('admin/extend/rewards', ['translator'], function (translator) {
rewards: available,
};
templates.parse('admin/extend/rewards', 'active', data, function (li) {
Benchpress.parse('admin/extend/rewards', 'active', data, function (li) {
translator.translate(li, function (li) {
li = $(li);
ul.append(li);

@ -1,7 +1,7 @@
'use strict';
define('admin/general/dashboard', ['semver', 'Chart', 'translator'], function (semver, Chart, translator) {
define('admin/general/dashboard', ['semver', 'Chart', 'translator', 'benchpress'], function (semver, Chart, translator, Benchpress) {
var Admin = {};
var intervals = {
rooms: false,
@ -317,7 +317,7 @@ define('admin/general/dashboard', ['semver', 'Chart', 'translator'], function (s
$('[data-action="updateGraph"][data-units="custom"]').on('click', function () {
var targetEl = $(this);
templates.parse('admin/partials/pageviews-range-select', {}, function (html) {
Benchpress.parse('admin/partials/pageviews-range-select', {}, function (html) {
var modal = bootbox.dialog({
title: '[[admin/general/dashboard:page-views-custom]]',
message: html,

@ -1,7 +1,7 @@
'use strict';
define('admin/general/navigation', ['translator', 'iconSelect', 'jqueryui'], function (translator, iconSelect) {
define('admin/general/navigation', ['translator', 'iconSelect', 'benchpress', 'jqueryui'], function (translator, iconSelect, Benchpress) {
var navigation = {};
var available;
@ -68,7 +68,7 @@ define('admin/general/navigation', ['translator', 'iconSelect', 'jqueryui'], fun
data.enabled = false;
data.index = (parseInt($('#enabled').children().last().attr('data-index'), 10) || 0) + 1;
templates.parse('admin/general/navigation', 'navigation', { navigation: [data] }, function (li) {
Benchpress.parse('admin/general/navigation', 'navigation', { navigation: [data] }, function (li) {
translator.translate(li, function (li) {
li = $(translator.unescape(li));
el.after(li);
@ -76,7 +76,7 @@ define('admin/general/navigation', ['translator', 'iconSelect', 'jqueryui'], fun
});
});
templates.parse('admin/general/navigation', 'enabled', { enabled: [data] }, function (li) {
Benchpress.parse('admin/general/navigation', 'enabled', { enabled: [data] }, function (li) {
translator.translate(li, function (li) {
li = $(translator.unescape(li));
$('#enabled').append(li);

@ -1,7 +1,7 @@
'use strict';
define('admin/manage/categories', ['vendor/jquery/serializeObject/jquery.ba-serializeobject.min', 'translator'], function (serialize, translator) {
define('admin/manage/categories', ['vendor/jquery/serializeObject/jquery.ba-serializeobject.min', 'translator', 'benchpress'], function (serialize, translator, Benchpress) {
var Categories = {};
var newCategoryId = -1;
var sortables;
@ -45,7 +45,7 @@ define('admin/manage/categories', ['vendor/jquery/serializeObject/jquery.ba-seri
return app.alertError(err.message);
}
templates.parse('admin/partials/categories/create', {
Benchpress.parse('admin/partials/categories/create', {
categories: categories,
}, function (html) {
var modal = bootbox.dialog({
@ -185,7 +185,7 @@ define('admin/manage/categories', ['vendor/jquery/serializeObject/jquery.ba-seri
}
function continueRender() {
templates.parse('admin/partials/categories/category-rows', {
Benchpress.parse('admin/partials/categories/category-rows', {
cid: parentId,
categories: categories,
}, function (html) {

@ -8,7 +8,8 @@ define('admin/manage/category', [
'autocomplete',
'translator',
'categorySelector',
], function (uploader, iconSelect, colorpicker, autocomplete, translator, categorySelector) {
'benchpress',
], function (uploader, iconSelect, colorpicker, autocomplete, translator, categorySelector, Benchpress) {
var Category = {};
var modified_categories = {};
@ -248,7 +249,7 @@ define('admin/manage/category', [
return app.alertError(err.message);
}
templates.parse('admin/partials/categories/privileges', {
Benchpress.parse('admin/partials/categories/privileges', {
privileges: privileges,
}, function (html) {
translator.translate(html, function (html) {
@ -407,7 +408,7 @@ define('admin/manage/category', [
callback = categories;
categories = ajaxify.data.allCategories;
}
templates.parse('admin/partials/categories/select-category', {
Benchpress.parse('admin/partials/categories/select-category', {
categories: categories,
}, function (html) {
translator.translate(html, function (html) {

@ -6,7 +6,8 @@ define('admin/manage/group', [
'iconSelect',
'admin/modules/colorpicker',
'translator',
], function (memberList, iconSelect, colorpicker, translator) {
'benchpress',
], function (memberList, iconSelect, colorpicker, translator, Benchpress) {
var Groups = {};
Groups.init = function () {
@ -103,7 +104,7 @@ define('admin/manage/group', [
'icon:text': userLabel.attr('data-usericon-text'),
};
templates.parse('admin/partials/groups/memberlist', 'members', {
Benchpress.parse('admin/partials/groups/memberlist', 'members', {
group: {
isOwner: ajaxify.data.group.isOwner,
members: [member],

@ -1,7 +1,7 @@
'use strict';
define('admin/manage/groups', ['translator'], function (translator) {
define('admin/manage/groups', ['translator', 'benchpress'], function (translator, Benchpress) {
var Groups = {};
var intervalId = 0;
@ -94,7 +94,7 @@ define('admin/manage/groups', ['translator'], function (translator) {
return app.alertError(err.message);
}
templates.parse('admin/manage/groups', 'groups', {
Benchpress.parse('admin/manage/groups', 'groups', {
groups: groups,
}, function (html) {
translator.translate(html, function (html) {

@ -1,7 +1,7 @@
'use strict';
define('admin/manage/ip-blacklist', ['Chart'], function (Chart) {
define('admin/manage/ip-blacklist', ['Chart', 'benchpress'], function (Chart, Benchpress) {
var Blacklist = {};
Blacklist.init = function () {
@ -32,7 +32,7 @@ define('admin/manage/ip-blacklist', ['Chart'], function (Chart) {
return app.alertError(err.message);
}
templates.parse('admin/partials/blacklist-validate', data, function (html) {
Benchpress.parse('admin/partials/blacklist-validate', data, function (html) {
bootbox.alert(html);
});
});

@ -1,7 +1,7 @@
'use strict';
define('admin/manage/users', ['translator'], function (translator) {
define('admin/manage/users', ['translator', 'benchpress'], function (translator, Benchpress) {
var Users = {};
Users.init = function () {
@ -81,7 +81,7 @@ define('admin/manage/users', ['translator'], function (translator) {
return false; // specifically to keep the menu open
}
templates.parse('admin/partials/temporary-ban', {}, function (html) {
Benchpress.parse('admin/partials/temporary-ban', {}, function (html) {
bootbox.dialog({
className: 'ban-modal',
title: '[[user:ban_account]]',
@ -248,7 +248,7 @@ define('admin/manage/users', ['translator'], function (translator) {
function handleUserCreate() {
$('#createUser').on('click', function () {
templates.parse('admin/partials/create_user_modal', {}, function (html) {
Benchpress.parse('admin/partials/create_user_modal', {}, function (html) {
bootbox.dialog({
message: html,
title: '[[admin/manage/users:alerts.create]]',
@ -323,7 +323,7 @@ define('admin/manage/users', ['translator'], function (translator) {
return app.alertError(err.message);
}
templates.parse('admin/manage/users', 'users', data, function (html) {
Benchpress.parse('admin/manage/users', 'users', data, function (html) {
translator.translate(html, function (html) {
html = $(html);
$('.users-table tr').not(':first').remove();

@ -17,6 +17,13 @@ define('admin/settings', ['uploader'], function (uploader) {
$(this).prepend('<a name="' + anchor + '"></a>');
$('.section-content ul').append('<li><a href="#' + anchor + '">' + header + '</a></li>');
});
var scrollTo = $('a[name="' + window.location.hash.replace('#', '') + '"]');
if (scrollTo.length) {
$('html, body').animate({
scrollTop: (scrollTo.offset().top) + 'px',
}, 400);
}
};
Settings.prepare = function (callback) {

@ -9,11 +9,13 @@ define('admin/settings/email', ['ace/ace', 'admin/settings'], function (ace) {
configureEmailTester();
configureEmailEditor();
handleDigestHourChange();
handleSmtpServiceChange();
$(window).on('action:admin.settingsLoaded action:admin.settingsSaved', handleDigestHourChange);
$(window).on('action:admin.settingsSaved', function () {
socket.emit('admin.user.restartJobs');
});
$('[id="email:smtpTransport:service"]').change(handleSmtpServiceChange);
};
function configureEmailTester() {
@ -100,5 +102,10 @@ define('admin/settings/email', ['ace/ace', 'admin/settings'], function (ace) {
});
}
function handleSmtpServiceChange() {
var isCustom = $('[id="email:smtpTransport:service"]').val() === 'nodebb-custom-smtp';
$('[id="email:smtpTransport:custom-service"]')[isCustom ? 'slideDown' : 'slideUp'](isCustom);
}
return module;
});

@ -10,13 +10,15 @@ $(document).ready(function () {
var ajaxifyTimer;
var translator;
var Benchpress;
var retry = true;
var previousBodyClass = '';
// Dumb hack to fool ajaxify into thinking translator is still a global
// When ajaxify is migrated to a require.js module, then this can be merged into the "define" call
require(['translator'], function (_translator) {
require(['translator', 'benchpress'], function (_translator, _Benchpress) {
translator = _translator;
Benchpress = _Benchpress;
});
$(window).on('popstate', function (ev) {
@ -174,7 +176,7 @@ $(document).ready(function () {
function renderTemplate(url, tpl_url, data, callback) {
$(window).trigger('action:ajaxify.loadingTemplates', {});
templates.parse(tpl_url, data, function (template) {
Benchpress.parse(tpl_url, data, function (template) {
translator.translate(template, function (translatedTemplate) {
translatedTemplate = translator.unescape(translatedTemplate);
$('body').removeClass(previousBodyClass).addClass(data.bodyClass);
@ -328,20 +330,10 @@ $(document).ready(function () {
};
ajaxify.loadTemplate = function (template, callback) {
if (templates.cache[template]) {
callback(templates.cache[template]);
} else {
$.ajax({
url: config.relative_path + '/assets/templates/' + template + '.tpl?' + config['cache-buster'],
type: 'GET',
success: function (data) {
callback(data.toString());
},
error: function (error) {
throw new Error('Unable to load template: ' + template + ' (' + error.statusText + ')');
},
});
}
require([config.relative_path + '/assets/templates/' + template + '.jst'], callback, function (err) {
console.error('Unable to load template: ' + template);
throw err;
});
};
function ajaxifyAnchors() {
@ -424,7 +416,9 @@ $(document).ready(function () {
});
}
templates.registerLoader(ajaxify.loadTemplate);
require(['benchpress'], function (Benchpress) {
Benchpress.registerLoader(ajaxify.loadTemplate);
});
if (window.history && window.history.pushState) {
// Progressive Enhancement, ajaxify available only to modern browsers
@ -432,9 +426,4 @@ $(document).ready(function () {
}
app.load();
$('[type="text/tpl"][data-template]').each(function () {
templates.cache[$(this).attr('data-template')] = $('<div/>').html($(this).html()).text();
$(this).parent().remove();
});
});

@ -11,7 +11,21 @@ app.cacheBuster = null;
(function () {
var showWelcomeMessage = !!utils.params().loggedin;
templates.setGlobal('config', config);
require(['benchpress'], function (Benchpress) {
Benchpress.setGlobal('config', config);
if (Object.defineProperty) {
Object.defineProperty(window, 'templates', {
configurable: true,
enumerable: true,
get: function () {
console.warn('[deprecated] Accessing benchpress (formerly known as templates.js) globally is deprecated. Use `require(["benchpress"], function (Benchpress) { ... })` instead');
return Benchpress;
},
});
} else {
window.templates = Benchpress;
}
});
app.cacheBuster = config['cache-buster'];
@ -35,7 +49,8 @@ app.cacheBuster = null;
app.handleSearch();
}
$('body').on('click', '#new_topic', function () {
$('body').on('click', '#new_topic', function (e) {
e.preventDefault();
app.newTopic();
});
@ -89,7 +104,8 @@ app.cacheBuster = null;
});
};
app.logout = function () {
app.logout = function (e) {
e.preventDefault();
$(window).trigger('action:app.logout');
/*
@ -608,7 +624,7 @@ app.cacheBuster = null;
};
app.parseAndTranslate = function (template, blockName, data, callback) {
require(['translator'], function (translator) {
require(['translator', 'benchpress'], function (translator, Benchpress) {
function translate(html, callback) {
translator.translate(html, function (translatedHTML) {
translatedHTML = translator.unescape(translatedHTML);
@ -617,13 +633,13 @@ app.cacheBuster = null;
}
if (typeof blockName === 'string') {
templates.parse(template, blockName, data, function (html) {
Benchpress.parse(template, blockName, data, function (html) {
translate(html, callback);
});
} else {
callback = data;
data = blockName;
templates.parse(template, data, function (html) {
Benchpress.parse(template, data, function (html) {
translate(html, callback);
});
}

@ -1,7 +1,7 @@
'use strict';
define('forum/account/edit', ['forum/account/header', 'translator', 'components', 'pictureCropper'], function (header, translator, components, pictureCropper) {
define('forum/account/edit', ['forum/account/header', 'translator', 'components', 'pictureCropper', 'benchpress'], function (header, translator, components, pictureCropper, Benchpress) {
var AccountEdit = {};
AccountEdit.init = function () {
@ -84,7 +84,7 @@ define('forum/account/edit', ['forum/account/header', 'translator', 'components'
return memo || cur.type === 'uploaded';
}, false);
templates.parse('partials/modals/change_picture_modal', {
Benchpress.parse('partials/modals/change_picture_modal', {
pictures: pictures,
uploaded: uploaded,
icon: { text: ajaxify.data['icon:text'], bgColor: ajaxify.data['icon:bgColor'] },
@ -229,7 +229,7 @@ define('forum/account/edit', ['forum/account/header', 'translator', 'components'
modal.find('[data-action="upload-url"]').on('click', function () {
modal.modal('hide');
templates.parse('partials/modals/upload_picture_from_url_modal', {}, function (html) {
Benchpress.parse('partials/modals/upload_picture_from_url_modal', {}, function (html) {
translator.translate(html, function (html) {
var uploadModal = $(html);
uploadModal.modal('show');

@ -6,7 +6,8 @@ define('forum/account/header', [
'pictureCropper',
'components',
'translator',
], function (coverPhoto, pictureCropper, components, translator) {
'benchpress',
], function (coverPhoto, pictureCropper, components, translator, Benchpress) {
var AccountHeader = {};
var isAdminOrSelfOrGlobalMod;
@ -114,7 +115,7 @@ define('forum/account/header', [
}
function banAccount() {
templates.parse('admin/partials/temporary-ban', {}, function (html) {
Benchpress.parse('admin/partials/temporary-ban', {}, function (html) {
bootbox.dialog({
className: 'ban-modal',
title: '[[user:ban_account]]',

@ -1,7 +1,7 @@
'use strict';
define('forum/categories', ['components', 'translator'], function (components, translator) {
define('forum/categories', ['components', 'translator', 'benchpress'], function (components, translator, Benchpress) {
var categories = {};
$(window).on('action:ajaxify.start', function (ev, data) {
@ -58,7 +58,7 @@ define('forum/categories', ['components', 'translator'], function (components, t
}
function parseAndTranslate(posts, callback) {
templates.parse('categories', '(categories.)?posts', { categories: { posts: posts } }, function (html) {
Benchpress.parse('partials/categories/lastpost', 'posts', { posts: posts }, function (html) {
translator.translate(html, function (translatedHTML) {
translatedHTML = $(translatedHTML);
translatedHTML.find('.post-content img:not(.not-responsive)').addClass('img-responsive');

@ -12,7 +12,8 @@ define('forum/category', [
'topicSelect',
'forum/pagination',
'storage',
], function (infinitescroll, share, navigator, categoryTools, sort, components, translator, topicSelect, pagination, storage) {
'benchpress',
], function (infinitescroll, share, navigator, categoryTools, sort, components, translator, topicSelect, pagination, storage, Benchpress) {
var Category = {};
$(window).on('action:ajaxify.start', function (ev, data) {
@ -204,7 +205,7 @@ define('forum/category', [
var editable = !!$('.thread-tools').length;
templates.parse('category', 'topics', {
Benchpress.parse('category', 'topics', {
privileges: { editable: editable },
showSelect: editable,
topics: [topic],
@ -267,7 +268,7 @@ define('forum/category', [
var topics = $('[component="category/topic"]');
var afterEl = direction > 0 ? topics.last() : topics.first();
var after = (parseInt(afterEl.attr('data-index'), 10) || 0) + 1;
var after = (parseInt(afterEl.attr('data-index'), 10) || 0) + (direction > 0 ? 1 : 0);
loadTopicsAfter(after, direction);
};
@ -284,8 +285,7 @@ define('forum/category', [
cid: ajaxify.data.cid,
after: after,
direction: direction,
author: params.author,
tag: params.tag,
query: params,
categoryTopicSort: config.categoryTopicSort,
}, function (data, done) {
if (data.topics && data.topics.length) {

@ -8,7 +8,8 @@ define('forum/chats', [
'forum/chats/recent',
'forum/chats/search',
'forum/chats/messages',
], function (components, translator, mousetrap, recentChats, search, messages) {
'benchpress',
], function (components, translator, mousetrap, recentChats, search, messages, Benchpress) {
var Chats = {
initialised: false,
};
@ -23,6 +24,8 @@ define('forum/chats', [
Chats.addGlobalEventListeners();
}
recentChats.init();
Chats.addEventListeners();
Chats.createTagsInput($('[component="chat/messages"] .users-tag-input'), ajaxify.data);
Chats.createAutoComplete($('[component="chat/input"]'));
@ -48,15 +51,6 @@ define('forum/chats', [
};
Chats.addEventListeners = function () {
$('[component="chat/recent"]').on('click', '[component="chat/leave"]', function () {
Chats.leave($(this).parents('[data-roomid]'));
return false;
});
$('[component="chat/recent"]').on('click', '[component="chat/recent/room"]', function () {
Chats.switchChat($(this).attr('data-roomid'));
});
Chats.addSendHandlers(ajaxify.data.roomId, $('.chat-input'), $('.expanded-chat button[data-action="send"]'));
$('[data-action="pop-out"]').on('click', function () {
@ -79,8 +73,6 @@ define('forum/chats', [
Chats.addEditDeleteHandler(components.get('chat/messages'), ajaxify.data.roomId);
recentChats.init();
Chats.addRenameHandler(ajaxify.data.roomId, $('[component="chat/room/name"]'));
Chats.addScrollHandler(ajaxify.data.roomId, ajaxify.data.uid, $('.chat-content'));
Chats.addCharactersLeftHandler(components.get('chat/input'));
@ -319,7 +311,36 @@ define('forum/chats', [
};
Chats.switchChat = function (roomid) {
ajaxify.go('user/' + ajaxify.data.userslug + '/chats/' + roomid);
var url = 'user/' + ajaxify.data.userslug + '/chats/' + roomid;
if (self.fetch) {
fetch(config.relative_path + '/api/' + url, { credentials: 'include' })
.then(function (response) {
if (response.ok) {
response.json().then(function (payload) {
app.parseAndTranslate('partials/chats/message-window', payload, function (html) {
components.get('chat/main-wrapper').html(html);
Chats.resizeMainWindow();
ajaxify.data = payload;
Chats.setActive();
Chats.addEventListeners();
if (history.pushState) {
history.pushState({
url: 'user/' + payload.userslug + '/chats/' + payload.roomId,
}, null, window.location.protocol + '//' + window.location.host + config.relative_path + '/user/' + payload.userslug + '/chats/' + payload.roomId);
}
});
});
} else {
console.warn('[search] Received ' + response.status);
}
})
.catch(function (error) {
console.warn('[search] ' + error.message);
});
} else {
ajaxify.go(url);
}
};
Chats.addGlobalEventListeners = function () {
@ -346,7 +367,7 @@ define('forum/chats', [
roomEl.addClass('unread');
} else {
var recentEl = components.get('chat/recent');
templates.parse('partials/chats/recent_room', {
Benchpress.parse('partials/chats/recent_room', {
rooms: {
roomId: data.roomId,
lastUser: data.message.fromUser,
@ -375,17 +396,19 @@ define('forum/chats', [
Chats.resizeMainWindow = function () {
var messagesList = $('.expanded-chat .chat-content');
var searchHeight = $('.chat-search').height();
var searchListHeight = $('[component="chat/search/list"]').outerHeight(true) - $('[component="chat/search/list"]').height();
var fromTop = components.get('chat/recent').offset().top;
if (messagesList.length) {
var margin = $('.expanded-chat ul').outerHeight(true) - $('.expanded-chat ul').height();
var inputHeight = $('.chat-input').outerHeight(true);
var fromTop = messagesList.offset().top;
var searchHeight = $('.chat-search').height();
var searchListHeight = $('[component="chat/search/list"]').outerHeight(true) - $('[component="chat/search/list"]').height();
messagesList.height($(window).height() - (fromTop + inputHeight + (margin * 4)));
components.get('chat/recent').height($('.expanded-chat').height() - (searchHeight + searchListHeight));
$('[component="chat/search/list"]').css('max-height', (components.get('chat/recent').height() / 2) + 'px');
} else {
components.get('chat/recent').height($(window).height() - (fromTop + searchHeight + searchListHeight));
}
Chats.setActive();

@ -1,7 +1,7 @@
'use strict';
define('forum/chats/messages', ['components', 'sounds', 'translator'], function (components, sounds, translator) {
define('forum/chats/messages', ['components', 'sounds', 'translator', 'benchpress'], function (components, sounds, translator, Benchpress) {
var messages = {};
messages.sendMessage = function (roomId, inputEl) {
@ -86,7 +86,7 @@ define('forum/chats/messages', ['components', 'sounds', 'translator'], function
messages.parseMessage = function (data, callback) {
templates.parse('partials/chats/message' + (Array.isArray(data) ? 's' : ''), {
Benchpress.parse('partials/chats/message' + (Array.isArray(data) ? 's' : ''), {
messages: data,
}, function (html) {
translator.translate(html, callback);

@ -5,12 +5,28 @@ define('forum/chats/recent', function () {
var recent = {};
recent.init = function () {
$('[component="chat/recent"]').on('scroll', function () {
var $this = $(this);
var bottom = ($this[0].scrollHeight - $this.height()) * 0.9;
if ($this.scrollTop() > bottom) {
loadMoreRecentChats();
}
require(['forum/chats'], function (Chats) {
$('[component="chat/recent"]').on('click', '[component="chat/leave"]', function () {
Chats.leave($(this).parents('[data-roomid]'));
return false;
});
$('[component="chat/recent"]').on('click', '[component="chat/recent/room"]', function () {
var env = utils.findBootstrapEnvironment();
if (env === 'xs' || env === 'sm') {
app.openChat($(this).attr('data-roomid'));
} else {
Chats.switchChat($(this).attr('data-roomid'));
}
});
$('[component="chat/recent"]').on('scroll', function () {
var $this = $(this);
var bottom = ($this[0].scrollHeight - $this.height()) * 0.9;
if ($this.scrollTop() > bottom) {
loadMoreRecentChats();
}
});
});
};

@ -2,7 +2,7 @@
/* globals define */
define('forum/flags/detail', ['forum/flags/list', 'components', 'translator'], function (FlagsList, components, translator) {
define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'benchpress'], function (FlagsList, components, translator, Benchpress) {
var Flags = {};
Flags.init = function () {
@ -48,7 +48,7 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator'], f
};
Flags.reloadNotes = function (notes) {
templates.parse('flags/detail', 'notes', {
Benchpress.parse('flags/detail', 'notes', {
notes: notes,
}, function (html) {
var wrapperEl = components.get('flag/notes');
@ -60,7 +60,7 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator'], f
};
Flags.reloadHistory = function (history) {
templates.parse('flags/detail', 'history', {
Benchpress.parse('flags/detail', 'history', {
history: history,
}, function (html) {
translator.translate(html, function (translated) {

@ -1,7 +1,7 @@
'use strict';
define('forum/groups/list', ['forum/infinitescroll'], function (infinitescroll) {
define('forum/groups/list', ['forum/infinitescroll', 'benchpress'], function (infinitescroll, Benchpress) {
var Groups = {};
Groups.init = function () {
@ -44,7 +44,7 @@ define('forum/groups/list', ['forum/infinitescroll'], function (infinitescroll)
after: $('[component="groups/container"]').attr('data-nextstart'),
}, function (data, done) {
if (data && data.groups.length) {
templates.parse('partials/groups/list', {
Benchpress.parse('partials/groups/list', {
groups: data.groups,
}, function (html) {
$('#groups-list').append(html);
@ -78,7 +78,7 @@ define('forum/groups/list', ['forum/infinitescroll'], function (infinitescroll)
groups = groups.filter(function (group) {
return group.name !== 'registered-users' && group.name !== 'guests';
});
templates.parse('partials/groups/list', {
Benchpress.parse('partials/groups/list', {
groups: groups,
}, function (html) {
groupsEl.empty().append(html);

@ -74,6 +74,7 @@ define('forum/login', [], function () {
} else {
$('#content #username').focus();
}
$('#content #noscript').val('false');
};
return Login;

@ -16,6 +16,7 @@ define('forum/register', ['translator', 'zxcvbn'], function (translator, zxcvbn)
handleLanguageOverride();
$('#referrer').val(app.previousUrl);
$('#content #noscript').val('false');
email.on('blur', function () {
if (email.val().length) {

@ -9,7 +9,8 @@ define('forum/topic/events', [
'forum/topic/images',
'components',
'translator',
], function (postTools, threadTools, posts, images, components, translator) {
'benchpress',
], function (postTools, threadTools, posts, images, components, translator, Benchpress) {
var Events = {};
var events = {
@ -139,7 +140,7 @@ define('forum/topic/events', [
editedISO: utils.toISOString(data.post.edited),
};
templates.parse('partials/topic/post-editor', editData, function (html) {
Benchpress.parse('partials/topic/post-editor', editData, function (html) {
translator.translate(html, function (translated) {
html = $(translated);
editorEl.replaceWith(html);
@ -150,7 +151,7 @@ define('forum/topic/events', [
});
if (data.topic.tags && tagsUpdated(data.topic.tags)) {
templates.parse('partials/post_bar', 'tags', { tags: data.topic.tags }, function (html) {
Benchpress.parse('partials/post_bar', 'tags', { tags: data.topic.tags }, function (html) {
var tags = $('.tags');
tags.fadeOut(250, function () {

@ -8,7 +8,8 @@ define('forum/topic/postTools', [
'translator',
'forum/topic/votes',
'forum/topic/move-post',
], function (share, navigator, components, translator, votes, movePost) {
'benchpress',
], function (share, navigator, components, translator, votes, movePost, Benchpress) {
var PostTools = {};
var staleReplyAnyway = false;
@ -44,7 +45,7 @@ define('forum/topic/postTools', [
}
data.posts.display_move_tools = data.posts.display_move_tools && index !== 0;
templates.parse('partials/topic/post-menu-list', data, function (html) {
Benchpress.parse('partials/topic/post-menu-list', data, function (html) {
translator.translate(html, function (html) {
dropdownMenu.html(html);
$(window).trigger('action:post.tools.load');
@ -85,7 +86,8 @@ define('forum/topic/postTools', [
onReplyClicked($(this), tid);
});
$('.topic').on('click', '[component="topic/reply"]', function () {
$('.topic').on('click', '[component="topic/reply"]', function (e) {
e.preventDefault();
onReplyClicked($(this), tid);
});

@ -7,7 +7,8 @@ define('forum/topic/threadTools', [
'forum/topic/delete-posts',
'components',
'translator',
], function (fork, move, deletePosts, components, translator) {
'benchpress',
], function (fork, move, deletePosts, components, translator, Benchpress) {
var ThreadTools = {};
ThreadTools.init = function (tid) {
@ -138,7 +139,7 @@ define('forum/topic/threadTools', [
return app.alertError(err);
}
templates.parse('partials/topic/topic-menu-list', data, function (html) {
Benchpress.parse('partials/topic/topic-menu-list', data, function (html) {
translator.translate(html, function (html) {
dropdownMenu.html(html);
$(window).trigger('action:topic.tools.load');

@ -1,7 +1,7 @@
'use strict';
define('forum/topic/votes', ['components', 'translator'], function (components, translator) {
define('forum/topic/votes', ['components', 'translator', 'benchpress'], function (components, translator, Benchpress) {
var Votes = {};
Votes.addVoteHandler = function () {
@ -92,7 +92,7 @@ define('forum/topic/votes', ['components', 'translator'], function (components,
return app.alertError(err.message);
}
templates.parse('partials/modals/votes_modal', data, function (html) {
Benchpress.parse('partials/modals/votes_modal', data, function (html) {
translator.translate(html, function (translated) {
var dialog = bootbox.dialog({
title: 'Voters',

@ -1,7 +1,7 @@
'use strict';
define('forum/users', ['translator'], function (translator) {
define('forum/users', ['translator', 'benchpress'], function (translator, Benchpress) {
var Users = {};
var searchTimeoutID = 0;
@ -98,11 +98,11 @@ define('forum/users', ['translator'], function (translator) {
}
function renderSearchResults(data) {
templates.parse('partials/paginator', { pagination: data.pagination }, function (html) {
Benchpress.parse('partials/paginator', { pagination: data.pagination }, function (html) {
$('.pagination-container').replaceWith(html);
});
templates.parse('users', 'users', data, function (html) {
Benchpress.parse('users', 'users', data, function (html) {
translator.translate(html, function (translated) {
translated = $(translated);
$('#users-container').html(translated);

@ -1,7 +1,7 @@
'use strict';
define('alerts', ['translator', 'components'], function (translator, components) {
define('alerts', ['translator', 'components', 'benchpress'], function (translator, components, Benchpress) {
var module = {};
module.alert = function (params) {
@ -19,7 +19,7 @@ define('alerts', ['translator', 'components'], function (translator, components)
};
function createNew(params) {
templates.parse('alert', params, function (alertTpl) {
Benchpress.parse('alert', params, function (alertTpl) {
translator.translate(alertTpl, function (translatedHTML) {
var alert = $('#' + params.alert_id);
if (alert.length) {

@ -10,7 +10,8 @@ define('chat', [
'forum/chats/messages',
'translator',
'scrollStop',
], function (components, taskbar, S, sounds, Chats, ChatsMessages, translator, scrollStop) {
'benchpress',
], function (components, taskbar, S, sounds, Chats, ChatsMessages, translator, scrollStop, Benchpress) {
var module = {};
var newMessage = false;
@ -125,7 +126,7 @@ define('chat', [
return room.teaser;
});
templates.parse('partials/chats/dropdown', {
Benchpress.parse('partials/chats/dropdown', {
rooms: rooms,
}, function (html) {
translator.translate(html, function (translated) {

@ -1,7 +1,7 @@
'use strict';
define('flags', [], function () {
define('flags', ['benchpress'], function (Benchpress) {
var Flag = {};
var flagModal;
var flagCommit;
@ -31,7 +31,7 @@ define('flags', [], function () {
};
function parseModal(tplData, callback) {
templates.parse('partials/modals/flag_modal', tplData, function (html) {
Benchpress.parse('partials/modals/flag_modal', tplData, function (html) {
require(['translator'], function (translator) {
translator.translate(html, callback);
});

@ -3,15 +3,13 @@
(function (factory) {
if (typeof module === 'object' && module.exports) {
var relative_path = require('nconf').get('relative_path');
module.exports = factory(require('../utils'), require('templates.js'), require('string'), relative_path);
module.exports = factory(require('../utils'), require('benchpressjs'), require('string'), relative_path);
} else if (typeof define === 'function' && define.amd) {
define('helpers', ['string'], function (string) {
return factory(utils, templates, string, config.relative_path);
define('helpers', ['benchpress', 'string'], function (Benchpress, string) {
return factory(utils, Benchpress, string, config.relative_path);
});
} else {
window.helpers = factory(utils, templates, window.String, config.relative_path);
}
}(function (utils, templates, S, relative_path) {
}(function (utils, Benchpress, S, relative_path) {
var helpers = {
displayMenuItem: displayMenuItem,
buildMetaTag: buildMetaTag,
@ -30,8 +28,13 @@
renderDigestAvatar: renderDigestAvatar,
userAgentIcons: userAgentIcons,
register: register,
__escape: identity,
};
function identity(str) {
return str;
}
function displayMenuItem(data, index) {
var item = data.navigation[index];
if (!item) {
@ -270,7 +273,7 @@
function register() {
Object.keys(helpers).forEach(function (helperName) {
templates.registerHelper(helperName, helpers[helperName]);
Benchpress.registerHelper(helperName, helpers[helperName]);
});
}

@ -1,7 +1,7 @@
'use strict';
define('iconSelect', function () {
define('iconSelect', ['benchpress'], function (Benchpress) {
var iconSelect = {};
iconSelect.init = function (el, onModified) {
@ -19,7 +19,7 @@ define('iconSelect', function () {
}
}
templates.parse('partials/fontawesome', {}, function (html) {
Benchpress.parse('partials/fontawesome', {}, function (html) {
html = $(html);
html.find('.fa-icons').prepend($('<i class="fa fa-nbb-none"></i>'));

@ -1,7 +1,7 @@
'use strict';
define('notifications', ['sounds', 'translator', 'components', 'navigator'], function (sounds, translator, components, navigator) {
define('notifications', ['sounds', 'translator', 'components', 'navigator', 'benchpress'], function (sounds, translator, components, navigator, Benchpress) {
var Notifications = {};
var unreadNotifs = {};
@ -143,7 +143,7 @@ define('notifications', ['sounds', 'translator', 'components', 'navigator'], fun
}
translator.toggleTimeagoShorthand();
templates.parse('partials/notifications_list', { notifications: notifs }, function (html) {
Benchpress.parse('partials/notifications_list', { notifications: notifs }, function (html) {
notifList.translateHtml(html);
});
});

@ -1,7 +1,7 @@
'use strict';
define('pictureCropper', ['translator', 'cropper'], function (translator, cropper) {
define('pictureCropper', ['translator', 'cropper', 'benchpress'], function (translator, cropper, Benchpress) {
var module = {};
module.show = function (data, callback) {
@ -32,7 +32,7 @@ define('pictureCropper', ['translator', 'cropper'], function (translator, croppe
module.handleImageCrop = function (data, callback) {
$('#crop-picture-modal').remove();
templates.parse('modals/crop_picture', {
Benchpress.parse('modals/crop_picture', {
url: data.url,
}, function (cropperHtml) {
translator.translate(cropperHtml, function (translated) {
@ -179,7 +179,7 @@ define('pictureCropper', ['translator', 'cropper'], function (translator, croppe
}
function parseModal(tplVals, callback) {
templates.parse('partials/modals/upload_file_modal', tplVals, function (html) {
Benchpress.parse('partials/modals/upload_file_modal', tplVals, function (html) {
translator.translate(html, callback);
});
}

@ -1,13 +1,13 @@
'use strict';
define('taskbar', function () {
define('taskbar', ['benchpress'], function (Benchpress) {
var taskbar = {};
taskbar.init = function () {
var self = this;
templates.parse('modules/taskbar', {}, function (html) {
Benchpress.parse('modules/taskbar', {}, function (html) {
self.taskbar = $(html);
self.tasklist = self.taskbar.find('ul');
$(document.body).append(self.taskbar);

@ -39,6 +39,9 @@ define('topicSelect', ['components'], function (components) {
TopicSelect.getSelectedTids = function () {
var tids = [];
if (!topicsContainer) {
return tids;
}
topicsContainer.find('[component="category/topic"].selected').each(function () {
tids.push($(this).attr('data-tid'));
});
@ -46,8 +49,10 @@ define('topicSelect', ['components'], function (components) {
};
TopicSelect.unselectAll = function () {
topicsContainer.find('[component="category/topic"].selected').removeClass('selected');
topicsContainer.find('[component="topic/select"]').toggleClass('fa-check-square-o', false).toggleClass('fa-square-o', true);
if (topicsContainer) {
topicsContainer.find('[component="category/topic"].selected').removeClass('selected');
topicsContainer.find('[component="topic/select"]').toggleClass('fa-check-square-o', false).toggleClass('fa-square-o', true);
}
};
function selectRange(clickedTid) {

@ -1,7 +1,7 @@
'use strict';
define('uploader', ['translator'], function (translator) {
define('uploader', ['translator', 'benchpress'], function (translator, Benchpress) {
var module = {};
module.open = function (route, params, fileSize, callback) {
@ -102,7 +102,7 @@ define('uploader', ['translator'], function (translator) {
}
function parseModal(tplVals, callback) {
templates.parse('partials/modals/upload_file_modal', tplVals, function (html) {
Benchpress.parse('partials/modals/upload_file_modal', tplVals, function (html) {
translator.translate(html, callback);
});
}

@ -447,13 +447,5 @@
};
}
// DEPRECATED: remove in 1.6
if (typeof String.prototype.rtrim !== 'function') {
String.prototype.rtrim = function () {
console.warn('[deprecated] `String.prototype.rtrim` is deprecated as of NodeBB v1.5; use `utils.rtrim` instead.');
return utils.rtrim(this);
};
}
return utils;
}));

@ -14,10 +14,12 @@ function filterDirectories(directories) {
// get the relative path
return dir.replace(/^.*(admin.*?).tpl$/, '$1');
}).filter(function (dir) {
// exclude .jst files
// exclude partials
// only include subpaths
// exclude category.tpl, group.tpl, category-analytics.tpl
return !dir.includes('/partials/') &&
return !dir.endsWith('.jst') &&
!dir.includes('/partials/') &&
/\/.*\//.test(dir) &&
!/manage\/(category|group|category-analytics)$/.test(dir);
});

@ -35,17 +35,13 @@ Categories.getCategoryById = function (data, callback) {
return next(new Error('[[error:invalid-cid]]'));
}
category = categories[0];
data.category = category;
async.parallel({
topics: function (next) {
Categories.getCategoryTopics(data, next);
},
topicCount: function (next) {
if (Array.isArray(data.set)) {
db.sortedSetIntersectCard(data.set, next);
} else {
next(null, category.topic_count);
}
Categories.getTopicCount(data, next);
},
isIgnored: function (next) {
Categories.isIgnored([data.cid], data.uid, next);

@ -19,7 +19,7 @@ module.exports = function (Categories) {
}, { alwaysStartAt: 0 }, next);
},
function (next) {
Categories.getPinnedTids('cid:' + cid + ':tids:pinned', 0, -1, next);
db.getSortedSetRevRange('cid:' + cid + ':tids:pinned', 0, -1, next);
},
function (pinnedTids, next) {
async.eachLimit(pinnedTids, 10, function (tid, next) {

@ -1,10 +1,12 @@
'use strict';
var async = require('async');
var _ = require('lodash');
var db = require('../database');
var topics = require('../topics');
var plugins = require('../plugins');
var meta = require('../meta');
module.exports = function (Categories) {
Categories.getCategoryTopics = function (data, callback) {
@ -13,7 +15,7 @@ module.exports = function (Categories) {
plugins.fireHook('filter:category.topics.prepare', data, next);
},
function (data, next) {
Categories.getTopicIds(data.cid, data.set, data.reverse, data.start, data.stop, next);
Categories.getTopicIds(data, next);
},
function (tids, next) {
topics.getTopicsByTids(tids, data.uid, next);
@ -35,38 +37,62 @@ module.exports = function (Categories) {
], callback);
};
Categories.getTopicIds = function (cid, set, reverse, start, stop, callback) {
Categories.getTopicIds = function (data, callback) {
var pinnedTids;
var pinnedCount;
var totalPinnedCount;
async.waterfall([
function (next) {
Categories.getPinnedTids(cid, 0, -1, next);
var dataForPinned = _.cloneDeep(data);
dataForPinned.start = 0;
dataForPinned.stop = -1;
async.parallel({
pinnedTids: async.apply(Categories.getPinnedTids, dataForPinned),
set: async.apply(Categories.buildTopicsSortedSet, data),
direction: async.apply(Categories.getSortedSetRangeDirection, data.sort),
}, next);
},
function (_pinnedTids, next) {
totalPinnedCount = _pinnedTids.length;
function (results, next) {
var totalPinnedCount = results.pinnedTids.length;
pinnedTids = _pinnedTids.slice(start, stop === -1 ? undefined : stop + 1);
pinnedTids = results.pinnedTids.slice(data.start, data.stop === -1 ? undefined : data.stop + 1);
pinnedCount = pinnedTids.length;
var pinnedCount = pinnedTids.length;
var topicsPerPage = stop - start + 1;
var topicsPerPage = data.stop - data.start + 1;
var normalTidsToGet = Math.max(0, topicsPerPage - pinnedCount);
if (!normalTidsToGet && stop !== -1) {
if (!normalTidsToGet && data.stop !== -1) {
return next(null, []);
}
if (plugins.hasListeners('filter:categories.getTopicIds')) {
return plugins.fireHook('filter:categories.getTopicIds', {
tids: [],
data: data,
pinnedTids: pinnedTids,
allPinnedTids: results.pinnedTids,
totalPinnedCount: totalPinnedCount,
normalTidsToGet: normalTidsToGet,
}, function (err, data) {
callback(err, data && data.tids);
});
}
var set = results.set;
var direction = results.direction;
var start = data.start;
if (start > 0 && totalPinnedCount) {
start -= totalPinnedCount - pinnedCount;
}
stop = stop === -1 ? stop : start + normalTidsToGet - 1;
var stop = data.stop === -1 ? data.stop : start + normalTidsToGet - 1;
if (Array.isArray(set)) {
db[reverse ? 'getSortedSetRevIntersect' : 'getSortedSetIntersect']({ sets: set, start: start, stop: stop }, next);
db[direction === 'highest-to-lowest' ? 'getSortedSetRevIntersect' : 'getSortedSetIntersect']({ sets: set, start: start, stop: stop }, next);
} else {
db[reverse ? 'getSortedSetRevRange' : 'getSortedSetRange'](set, start, stop, next);
db[direction === 'highest-to-lowest' ? 'getSortedSetRevRange' : 'getSortedSetRange'](set, start, stop, next);
}
},
function (normalTids, next) {
@ -79,12 +105,85 @@ module.exports = function (Categories) {
], callback);
};
Categories.getTopicCount = function (data, callback) {
if (plugins.hasListeners('filter:categories.getTopicCount')) {
return plugins.fireHook('filter:categories.getTopicCount', {
topicCount: data.category.topic_count,
data: data,
}, function (err, data) {
callback(err, data && data.topicCount);
});
}
async.waterfall([
function (next) {
Categories.buildTopicsSortedSet(data, next);
},
function (set, next) {
if (Array.isArray(set)) {
db.sortedSetIntersectCard(set, next);
} else {
next(null, data.category.topic_count);
}
},
], callback);
};
Categories.buildTopicsSortedSet = function (data, callback) {
var cid = data.cid;
var set = 'cid:' + cid + ':tids';
var sort = data.sort || (data.settings && data.settings.categoryTopicSort) || meta.config.categoryTopicSort || 'newest_to_oldest';
if (sort === 'most_posts') {
set = 'cid:' + cid + ':tids:posts';
}
if (data.targetUid) {
set = 'cid:' + cid + ':uid:' + data.targetUid + ':tids';
}
if (data.tag) {
if (Array.isArray(data.tag)) {
set = [set].concat(data.tag.map(function (tag) {
return 'tag:' + tag + ':topics';
}));
} else {
set = [set, 'tag:' + data.tag + ':topics'];
}
}
plugins.fireHook('filter:categories.buildTopicsSortedSet', {
set: set,
data: data,
}, function (err, data) {
callback(err, data && data.set);
});
};
Categories.getSortedSetRangeDirection = function (sort, callback) {
sort = sort || 'newest_to_oldest';
var direction = sort === 'newest_to_oldest' || sort === 'most_posts' ? 'highest-to-lowest' : 'lowest-to-highest';
plugins.fireHook('filter:categories.getSortedSetRangeDirection', {
sort: sort,
direction: direction,
}, function (err, data) {
callback(err, data && data.direction);
});
};
Categories.getAllTopicIds = function (cid, start, stop, callback) {
db.getSortedSetRange(['cid:' + cid + ':tids:pinned', 'cid:' + cid + ':tids'], start, stop, callback);
};
Categories.getPinnedTids = function (cid, start, stop, callback) {
db.getSortedSetRevRange('cid:' + cid + ':tids:pinned', start, stop, callback);
Categories.getPinnedTids = function (data, callback) {
if (plugins.hasListeners('filter:categories.getPinnedTids')) {
return plugins.fireHook('filter:categories.getPinnedTids', {
pinnedTids: [],
data: data,
}, function (err, data) {
callback(err, data && data.pinnedTids);
});
}
db.getSortedSetRevRange('cid:' + data.cid + ':tids:pinned', data.start, data.stop, callback);
};
Categories.modifyTopicsByPrivilege = function (topics, privileges) {

@ -7,26 +7,22 @@ var meta = require('../../meta');
var user = require('../../user');
var helpers = require('../helpers');
var chatsController = {};
var chatsController = module.exports;
chatsController.get = function (req, res, callback) {
if (parseInt(meta.config.disableChat, 10) === 1) {
return callback();
}
var uid;
var username;
var recentChats;
async.waterfall([
function (next) {
async.parallel({
uid: async.apply(user.getUidByUserslug, req.params.userslug),
username: async.apply(user.getUsernameByUserslug, req.params.userslug),
}, next);
user.getUidByUserslug(req.params.userslug, next);
},
function (results, next) {
uid = results.uid;
username = results.username;
function (_uid, next) {
uid = _uid;
if (!uid) {
return callback();
}
@ -45,7 +41,6 @@ chatsController.get = function (req, res, callback) {
nextStart: recentChats.nextStart,
allowed: true,
title: '[[pages:chats]]',
breadcrumbs: helpers.buildBreadcrumbs([{ text: username, url: '/user/' + req.params.userslug }, { text: '[[pages:chats]]' }]),
});
}
messaging.isUserInRoom(req.uid, req.params.roomid, next);
@ -66,37 +61,30 @@ chatsController.get = function (req, res, callback) {
}),
}, next);
},
], function (err, data) {
if (err) {
return callback(err);
}
var room = data.room;
room.messages = data.messages;
function (data) {
var room = data.room;
room.messages = data.messages;
room.isOwner = parseInt(room.owner, 10) === parseInt(req.uid, 10);
room.users = data.users.filter(function (user) {
return user && parseInt(user.uid, 10) && parseInt(user.uid, 10) !== req.uid;
});
room.isOwner = parseInt(room.owner, 10) === parseInt(req.uid, 10);
room.users = data.users.filter(function (user) {
return user && parseInt(user.uid, 10) && parseInt(user.uid, 10) !== req.uid;
});
room.canReply = data.canReply;
room.groupChat = room.hasOwnProperty('groupChat') ? room.groupChat : room.users.length > 2;
room.rooms = recentChats.rooms;
room.uid = uid;
room.userslug = req.params.userslug;
room.nextStart = recentChats.nextStart;
room.usernames = messaging.generateUsernames(room.users, req.uid);
room.title = room.roomName || room.usernames || '[[pages:chats]]';
room.breadcrumbs = helpers.buildBreadcrumbs([
{ text: username, url: '/user/' + req.params.userslug },
{ text: '[[pages:chats]]', url: '/user/' + req.params.userslug + '/chats' },
{ text: room.roomName || room.usernames || '[[pages:chats]]' },
]);
room.maximumUsersInChatRoom = parseInt(meta.config.maximumUsersInChatRoom, 10) || 0;
room.maximumChatMessageLength = parseInt(meta.config.maximumChatMessageLength, 10) || 1000;
room.showUserInput = !room.maximumUsersInChatRoom || room.maximumUsersInChatRoom > 2;
room.canReply = data.canReply;
room.groupChat = room.hasOwnProperty('groupChat') ? room.groupChat : room.users.length > 2;
room.rooms = recentChats.rooms;
room.uid = uid;
room.userslug = req.params.userslug;
room.nextStart = recentChats.nextStart;
room.usernames = messaging.generateUsernames(room.users, req.uid);
room.title = room.roomName || room.usernames || '[[pages:chats]]';
room.maximumUsersInChatRoom = parseInt(meta.config.maximumUsersInChatRoom, 10) || 0;
room.maximumChatMessageLength = parseInt(meta.config.maximumChatMessageLength, 10) || 1000;
room.showUserInput = !room.maximumUsersInChatRoom || room.maximumUsersInChatRoom > 2;
res.render('chats', room);
});
res.render('chats', room);
},
], callback);
};
chatsController.redirectToChat = function (req, res, next) {
@ -104,14 +92,15 @@ chatsController.redirectToChat = function (req, res, next) {
if (!req.uid) {
return next();
}
user.getUserField(req.uid, 'userslug', function (err, userslug) {
if (err || !userslug) {
return next(err);
}
helpers.redirect(res, '/user/' + userslug + '/chats' + (roomid ? '/' + roomid : ''));
});
async.waterfall([
function (next) {
user.getUserField(req.uid, 'userslug', next);
},
function (userslug, next) {
if (!userslug) {
return next();
}
helpers.redirect(res, '/user/' + userslug + '/chats' + (roomid ? '/' + roomid : ''));
},
], next);
};
module.exports = chatsController;

@ -75,6 +75,17 @@ notificationsController.get = function (req, res, next) {
user.notifications.getNotifications(nids, req.uid, next);
},
function (notifications, next) {
plugins.fireHook('filter:notifications.get', {
notifications: notifications,
}, function (err, data) {
if (err) {
return next(err);
}
next(null, data.notifications);
});
},
function (notifications) {
res.render('notifications', {
notifications: notifications,

@ -1,9 +1,13 @@
'use strict';
var async = require('async');
var nconf = require('nconf');
var fs = require('fs');
var path = require('path');
var meta = require('../../meta');
var file = require('../../file');
var emailer = require('../../emailer');
var settingsController = module.exports;
@ -22,44 +26,54 @@ settingsController.get = function (req, res, next) {
function renderEmail(req, res, next) {
var fs = require('fs');
var path = require('path');
var file = require('../../file');
var emailsPath = path.join(nconf.get('views_dir'), 'emails');
async.waterfall([
function (next) {
file.walk(emailsPath, next);
},
function (emails, next) {
async.map(emails, function (email, next) {
var path = email.replace(emailsPath, '').substr(1).replace('.tpl', '');
async.parallel({
emails: function (cb) {
async.waterfall([
function (next) {
file.walk(emailsPath, next);
},
function (emails, next) {
// exclude .jst files
emails = emails.filter(function (email) {
return !email.endsWith('.jst');
});
async.waterfall([
function (next) {
fs.readFile(email, next);
},
function (original, next) {
var text = meta.config['email:custom:' + path] ? meta.config['email:custom:' + path] : original.toString();
async.map(emails, function (email, next) {
var path = email.replace(emailsPath, '').substr(1).replace('.tpl', '');
next(null, {
path: path,
fullpath: email,
text: text,
original: original.toString(),
});
},
], next);
}, next);
},
function (emails) {
res.render('admin/settings/email', {
emails: emails,
sendable: emails.filter(function (email) {
return email.path.indexOf('_plaintext') === -1 && email.path.indexOf('partials') === -1;
}),
});
async.waterfall([
function (next) {
fs.readFile(email, next);
},
function (original, next) {
var text = meta.config['email:custom:' + path] ? meta.config['email:custom:' + path] : original.toString();
next(null, {
path: path,
fullpath: email,
text: text,
original: original.toString(),
});
},
], next);
}, next);
},
], cb);
},
], next);
services: emailer.listServices,
}, function (err, results) {
if (err) {
return next(err);
}
res.render('admin/settings/email', {
emails: results.emails,
sendable: results.emails.filter(function (email) {
return email.path.indexOf('_plaintext') === -1 && email.path.indexOf('partials') === -1;
}),
services: results.services,
});
});
}

@ -146,6 +146,25 @@ function uploadImage(filename, folder, uploadedFile, req, res, next) {
file.saveFileToLocal(filename, folder, uploadedFile.path, next);
}
},
function (imageData, next) {
// Post-processing for site-logo
if (path.basename(filename, path.extname(filename)) === 'site-logo' && folder === 'system') {
var uploadPath = path.join(nconf.get('upload_path'), folder, 'site-logo-x50.png');
async.series([
async.apply(image.resizeImage, {
path: uploadedFile.path,
target: uploadPath,
extension: 'png',
height: 50,
}),
async.apply(meta.configs.set, 'brand:emailLogo', path.join(nconf.get('upload_url'), 'system/site-logo-x50.png')),
], function (err) {
next(err, imageData);
});
} else {
setImmediate(next, null, imageData);
}
},
], function (err, image) {
file.delete(uploadedFile.path);
if (err) {

@ -14,6 +14,7 @@ var plugins = require('../plugins');
var utils = require('../utils');
var Password = require('../password');
var translator = require('../translator');
var helpers = require('./helpers');
var sockets = require('../socket.io');
@ -49,6 +50,10 @@ authenticationController.register = function (req, res) {
return next(new Error('[[error:username-too-long]]'));
}
if (userData.password !== userData['password-confirm']) {
return next(new Error('[[user:change_password_error_match]]'));
}
user.isPasswordValid(userData.password, next);
},
function (next) {
@ -67,7 +72,7 @@ authenticationController.register = function (req, res) {
},
], function (err, data) {
if (err) {
return res.status(400).send(err.message);
return helpers.noScriptErrors(req, res, err.message, 400);
}
if (data.uid && req.body.userLang) {
@ -96,6 +101,10 @@ function registerAndLoginUser(req, res, userData, callback) {
}
userData.register = true;
req.session.registration = userData;
if (req.body.noscript === 'true') {
return res.redirect(nconf.get('relative_path') + '/register/complete');
}
return res.json({ referrer: nconf.get('relative_path') + '/register/complete' });
},
function (next) {
@ -200,22 +209,22 @@ authenticationController.login = function (req, res, next) {
} else if (loginWith.indexOf('username') !== -1 && !validator.isEmail(req.body.username)) {
continueLogin(req, res, next);
} else {
res.status(500).send('[[error:wrong-login-type-' + loginWith + ']]');
var err = '[[error:wrong-login-type-' + loginWith + ']]';
helpers.noScriptErrors(req, res, err, 500);
}
};
function continueLogin(req, res, next) {
passport.authenticate('local', function (err, userData, info) {
if (err) {
return res.status(403).send(err.message);
return helpers.noScriptErrors(req, res, err.message, 403);
}
if (!userData) {
if (typeof info === 'object') {
info = '[[error:invalid-username-or-password]]';
}
return res.status(403).send(info);
return helpers.noScriptErrors(req, res, info, 403);
}
var passwordExpiry = userData.passwordExpiry !== undefined ? parseInt(userData.passwordExpiry, 10) : null;
@ -235,7 +244,7 @@ function continueLogin(req, res, next) {
req.session.passwordExpired = true;
user.reset.generate(userData.uid, function (err, code) {
if (err) {
return res.status(403).send(err.message);
return helpers.noScriptErrors(req, res, err.message, 403);
}
res.status(200).send(nconf.get('relative_path') + '/reset/' + code);
@ -243,16 +252,21 @@ function continueLogin(req, res, next) {
} else {
authenticationController.doLogin(req, userData.uid, function (err) {
if (err) {
return res.status(403).send(err.message);
return helpers.noScriptErrors(req, res, err.message, 403);
}
var destination;
if (!req.session.returnTo) {
res.status(200).send(nconf.get('relative_path') + '/');
destination = nconf.get('relative_path') + '/';
} else {
var next = req.session.returnTo;
destination = req.session.returnTo;
delete req.session.returnTo;
}
res.status(200).send(next);
if (req.body.noscript === 'true') {
res.redirect(destination + '?loggedin');
} else {
res.status(200).send(destination);
}
});
}
@ -414,7 +428,11 @@ authenticationController.logout = function (req, res, next) {
function () {
// Force session check for all connected socket.io clients with the same session id
sockets.in('sess_' + req.sessionID).emit('checkSession', 0);
res.status(200).send('');
if (req.body.noscript === 'true') {
res.redirect(nconf.get('relative_path') + '/');
} else {
res.status(200).send('');
}
},
], next);
};

@ -28,6 +28,8 @@ categoryController.get = function (req, res, callback) {
return callback();
}
var topicIndex = utils.isNumber(req.params.topic_index) ? parseInt(req.params.topic_index, 10) - 1 : 0;
async.waterfall([
function (next) {
async.parallel({
@ -62,7 +64,7 @@ categoryController.get = function (req, res, callback) {
}
settings = results.userSettings;
var topicIndex = utils.isNumber(req.params.topic_index) ? parseInt(req.params.topic_index, 10) - 1 : 0;
var topicCount = parseInt(results.categoryData.topic_count, 10);
pageCount = Math.max(1, Math.ceil(topicCount / settings.topicsPerPage));
@ -82,52 +84,22 @@ categoryController.get = function (req, res, callback) {
topicIndex = 0;
}
var set = 'cid:' + cid + ':tids';
var reverse = false;
// `sort` qs has priority over user setting
var sort = req.query.sort || settings.categoryTopicSort;
if (sort === 'newest_to_oldest') {
reverse = true;
} else if (sort === 'most_posts') {
reverse = true;
set = 'cid:' + cid + ':tids:posts';
}
user.getUidByUserslug(req.query.author, next);
},
function (targetUid, next) {
var start = ((currentPage - 1) * settings.topicsPerPage) + topicIndex;
var stop = start + settings.topicsPerPage - 1;
var payload = {
categories.getCategoryById({
uid: req.uid,
cid: cid,
set: set,
reverse: reverse,
start: start,
stop: stop,
uid: req.uid,
sort: req.query.sort || settings.categoryTopicSort,
settings: settings,
};
async.waterfall([
function (next) {
user.getUidByUserslug(req.query.author, next);
},
function (uid, next) {
payload.targetUid = uid;
if (uid) {
payload.set = 'cid:' + cid + ':uid:' + uid + ':tids';
}
if (req.query.tag) {
if (Array.isArray(req.query.tag)) {
payload.set = [payload.set].concat(req.query.tag.map(function (tag) {
return 'tag:' + tag + ':topics';
}));
} else {
payload.set = [payload.set, 'tag:' + req.query.tag + ':topics'];
}
}
categories.getCategoryById(payload, next);
},
], next);
query: req.query,
tag: req.query.tag,
targetUid: targetUid,
}, next);
},
function (categoryData, next) {
categories.modifyTopicsByPrivilege(categoryData.topics, userPrivileges);

@ -14,6 +14,24 @@ var middleware = require('../middleware');
var helpers = module.exports;
helpers.noScriptErrors = function (req, res, error, httpStatus) {
if (req.body.noscript !== 'true') {
return res.status(httpStatus).send(error);
}
var middleware = require('../middleware');
var httpStatusString = httpStatus.toString();
middleware.buildHeader(req, res, function () {
res.status(httpStatus).render(httpStatusString, {
path: req.path,
loggedIn: true,
error: error,
returnLink: true,
title: '[[global:' + httpStatusString + '.title]]',
});
});
};
helpers.notAllowed = function (req, res, error) {
plugins.fireHook('filter:helpers.notAllowed', {
req: req,

@ -7,6 +7,7 @@ var validator = require('validator');
var meta = require('../meta');
var user = require('../user');
var plugins = require('../plugins');
var topics = require('../topics');
var helpers = require('./helpers');
var Controllers = module.exports;
@ -279,6 +280,47 @@ Controllers.compose = function (req, res, next) {
});
};
Controllers.composePost = function (req, res) {
var body = req.body;
var data = {
uid: req.uid,
req: req,
timestamp: Date.now(),
content: body.content,
};
req.body.noscript = 'true';
if (!data.content) {
return helpers.noScriptErrors(req, res, '[[error:invalid-data]]', 400);
}
if (body.tid) {
data.tid = body.tid;
topics.reply(data, function (err, result) {
if (err) {
return helpers.noScriptErrors(req, res, err.message, 400);
}
user.updateOnlineUsers(result.uid);
res.redirect(nconf.get('relative_path') + '/post/' + result.pid);
});
} else if (body.cid) {
data.cid = body.cid;
data.title = body.title;
data.tags = [];
data.thumb = '';
topics.post(data, function (err, result) {
if (err) {
return helpers.noScriptErrors(req, res, err.message, 400);
}
res.redirect(nconf.get('relative_path') + '/topic/' + result.topicData.slug);
});
}
};
Controllers.confirmEmail = function (req, res) {
user.email.confirm(req.params.code, function (err) {
res.render('confirm', {

@ -6,6 +6,7 @@ var user = require('../user');
var categories = require('../categories');
var flags = require('../flags');
var analytics = require('../analytics');
var plugins = require('../plugins');
var modsController = module.exports;
modsController.flags = {};
@ -13,11 +14,13 @@ modsController.flags = {};
modsController.flags.list = function (req, res, next) {
var filters;
var hasFilter;
var validFilters = ['assignee', 'state', 'reporterId', 'type', 'targetUid', 'cid', 'quick'];
async.waterfall([
function (next) {
async.parallel({
isAdminOrGlobalMod: async.apply(user.isAdminOrGlobalMod, req.uid),
moderatedCids: async.apply(user.getModeratedCids, req.uid),
validFilters: async.apply(plugins.fireHook, 'filter:flags.validateFilters', { filters: validFilters }),
}, next);
},
function (results, next) {
@ -29,10 +32,12 @@ modsController.flags.list = function (req, res, next) {
res.locals.cids = results.moderatedCids;
}
validFilters = results.validFilters.filters;
// Parse query string params for filters
hasFilter = false;
var valid = ['assignee', 'state', 'reporterId', 'type', 'targetUid', 'cid', 'quick'];
filters = valid.reduce(function (memo, cur) {
filters = validFilters.reduce(function (memo, cur) {
if (req.query.hasOwnProperty(cur)) {
memo[cur] = req.query[cur];
}

@ -3,10 +3,9 @@
var async = require('async');
var winston = require('winston');
var nconf = require('nconf');
var templates = require('templates.js');
var Benchpress = require('benchpressjs');
var nodemailer = require('nodemailer');
var sendmailTransport = require('nodemailer-sendmail-transport');
var smtpTransport = require('nodemailer-smtp-transport');
var wellKnownServices = require('nodemailer/lib/well-known/services');
var htmlToText = require('html-to-text');
var url = require('url');
@ -14,10 +13,15 @@ var User = require('./user');
var Plugins = require('./plugins');
var meta = require('./meta');
var translator = require('./translator');
var pubsub = require('./pubsub');
var transports = {
sendmail: nodemailer.createTransport(sendmailTransport()),
gmail: undefined,
sendmail: nodemailer.createTransport({
sendmail: true,
newline: 'unix',
}),
smtp: undefined,
// gmail: undefined,
};
var app;
@ -25,26 +29,63 @@ var fallbackTransport;
var Emailer = module.exports;
Emailer.listServices = function (callback) {
var services = Object.keys(wellKnownServices);
setImmediate(callback, null, services);
};
Emailer._defaultPayload = {};
Emailer.registerApp = function (expressApp) {
app = expressApp;
var logo = null;
if (meta.configs.hasOwnProperty('brand:emailLogo')) {
logo = (!meta.config['brand:emailLogo'].startsWith('http') ? nconf.get('url') : '') + meta.config['brand:emailLogo'];
}
Emailer._defaultPayload = {
url: nconf.get('url'),
site_title: meta.config.title || 'NodeBB',
logo: {
src: logo,
height: meta.config['brand:emailLogo:height'],
width: meta.config['brand:emailLogo:width'],
},
};
// Enable Gmail transport if enabled in ACP
if (parseInt(meta.config['email:GmailTransport:enabled'], 10) === 1) {
transports.gmail = nodemailer.createTransport(smtpTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
if (parseInt(meta.config['email:smtpTransport:enabled'], 10) === 1) {
var smtpOptions = {
auth: {
user: meta.config['email:GmailTransport:user'],
pass: meta.config['email:GmailTransport:pass'],
user: meta.config['email:smtpTransport:user'],
pass: meta.config['email:smtpTransport:pass'],
},
}));
fallbackTransport = transports.gmail;
};
if (meta.config['email:smtpTransport:serice'] === 'nodebb-custom-smtp') {
smtpOptions.port = meta.config['email:smtpTransport:port'];
smtpOptions.host = meta.config['email:smtpTransport:host'];
smtpOptions.secure = true;
} else {
smtpOptions.service = meta.config['email:smtpTransport:service'];
}
transports.smtp = nodemailer.createTransport(smtpOptions);
fallbackTransport = transports.smtp;
} else {
fallbackTransport = transports.sendmail;
}
// Update default payload if new logo is uploaded
pubsub.on('config:update', function (config) {
if (config) {
Emailer._defaultPayload.logo.src = config['brand:emailLogo'];
Emailer._defaultPayload.logo.height = config['brand:emailLogo:height'];
Emailer._defaultPayload.logo.width = config['brand:emailLogo:width'];
}
});
return Emailer;
};
@ -55,6 +96,9 @@ Emailer.send = function (template, uid, params, callback) {
return callback();
}
// Combined passed-in payload with default values
params = Object.assign({}, Emailer._defaultPayload, params);
async.waterfall([
function (next) {
async.parallel({
@ -144,9 +188,9 @@ Emailer.sendViaFallback = function (data, callback) {
};
function render(tpl, params, next) {
if (meta.config['email:custom:' + tpl.replace('emails/', '')]) {
var text = templates.parse(meta.config['email:custom:' + tpl.replace('emails/', '')], params);
next(null, text);
var customTemplate = meta.config['email:custom:' + tpl.replace('emails/', '')];
if (customTemplate) {
Benchpress.compileParse(customTemplate, params, next);
} else {
app.render(tpl, params, next);
}

@ -1,7 +1,6 @@
'use strict';
var async = require('async');
var nconf = require('nconf');
var winston = require('winston');
var user = require('../user');
@ -124,8 +123,6 @@ module.exports = function (Messaging) {
subject: '[[email:notif.chat.subject, ' + messageObj.fromUser.username + ']]',
summary: '[[notifications:new_message_from, ' + messageObj.fromUser.username + ']]',
message: messageObj,
site_title: meta.config.title || 'NodeBB',
url: nconf.get('url'),
roomId: messageObj.roomId,
username: userData.username,
userslug: userData.userslug,

@ -3,6 +3,7 @@
var async = require('async');
var nconf = require('nconf');
var path = require('path');
var db = require('../database');
var pubsub = require('../pubsub');
@ -80,13 +81,30 @@ Configs.setMultiple = function (data, callback) {
};
function processConfig(data, callback) {
if (data.customCSS) {
return saveRenderedCss(data, callback);
}
setImmediate(callback);
async.parallel([
async.apply(saveRenderedCss, data),
function (next) {
var image = require('../image');
if (data['brand:logo']) {
image.size(path.join(nconf.get('upload_path'), 'system', 'site-logo-x50.png'), function (err, size) {
data['brand:emailLogo:height'] = size.height;
data['brand:emailLogo:width'] = size.width;
next(err);
});
} else {
setImmediate(next);
}
},
], function (err) {
callback(err);
});
}
function saveRenderedCss(data, callback) {
if (!data.customCSS) {
return setImmediate(callback);
}
var less = require('less');
async.waterfall([
function (next) {

@ -118,6 +118,20 @@ function getBundleMetadata(target, callback) {
},
], cb);
},
acpLess: function (cb) {
if (target === 'client') {
return cb(null, '');
}
async.waterfall([
function (next) {
filterMissingFiles(plugins.acpLessFiles, next);
},
function (acpLessFiles, next) {
getImports(acpLessFiles, '\n@import ".', '.less', next);
},
], cb);
},
css: function (cb) {
async.waterfall([
function (next) {
@ -133,8 +147,9 @@ function getBundleMetadata(target, callback) {
function (result, next) {
var cssImports = result.css;
var lessImports = result.less;
var acpLessImports = result.acpLess;
var imports = cssImports + '\n' + lessImports;
var imports = cssImports + '\n' + lessImports + '\n' + acpLessImports;
imports = buildImports[target](imports);
next(null, { paths: paths, imports: imports });

@ -29,7 +29,7 @@ JS.scripts = {
'public/vendor/tinycon/tinycon.js',
'public/vendor/xregexp/xregexp.js',
'public/vendor/xregexp/unicode/unicode-base.js',
'node_modules/templates.js/lib/templates.js',
'node_modules/benchpressjs/build/benchpress.js',
'public/src/utils.js',
'public/src/sockets.js',
'public/src/app.js',

@ -11,6 +11,8 @@ var nconf = require('nconf');
var plugins = require('../plugins');
var file = require('../file');
var viewsPath = nconf.get('views_dir');
var Templates = module.exports;
Templates.compile = function (callback) {
@ -18,7 +20,6 @@ Templates.compile = function (callback) {
var themeConfig = require(nconf.get('theme_config'));
var baseTemplatesPaths = themeConfig.baseTheme ? getBaseTemplates(themeConfig.baseTheme) : [nconf.get('base_templates_path')];
var viewsPath = nconf.get('views_dir');
function processImports(paths, relativePath, source, callback) {
var regex = /<!-- IMPORT (.+?) -->/;
@ -63,9 +64,9 @@ Templates.compile = function (callback) {
var source = file.toString();
processImports(paths, relativePath, source, next);
},
function (compiled, next) {
function (source, next) {
mkdirp(path.join(viewsPath, path.dirname(relativePath)), function (err) {
next(err, compiled);
next(err, source);
});
},
function (compiled, next) {
@ -74,6 +75,9 @@ Templates.compile = function (callback) {
], next);
}, next);
},
function (next) {
rimraf(path.join(viewsPath, '*.jst'), next);
},
function (next) {
winston.verbose('[meta/templates] Successfully compiled templates.');
next();
@ -99,7 +103,6 @@ function getBaseTemplates(theme) {
function preparePaths(baseTemplatesPaths, callback) {
var coreTemplatesPath = nconf.get('core_templates_path');
var viewsPath = nconf.get('views_dir');
var pluginTemplates;
async.waterfall([
function (next) {

@ -2,11 +2,13 @@
var async = require('async');
var path = require('path');
var fs = require('fs');
var csrf = require('csurf');
var validator = require('validator');
var nconf = require('nconf');
var ensureLoggedIn = require('connect-ensure-login');
var toobusy = require('toobusy-js');
var Benchpress = require('benchpressjs');
var plugins = require('../plugins');
var meta = require('../meta');
@ -186,3 +188,33 @@ middleware.delayLoading = function (req, res, next) {
// Introduces an artificial delay during load so that brute force attacks are effectively mitigated
setTimeout(next, 1000);
};
var viewsDir = nconf.get('views_dir');
middleware.templatesOnDemand = function (req, res, next) {
var filePath = req.filePath || path.join(viewsDir, req.path);
if (!filePath.endsWith('.jst')) {
return next();
}
async.waterfall([
function (cb) {
file.exists(filePath, cb);
},
function (exists, cb) {
if (exists) {
return next();
}
fs.readFile(filePath.replace(/\.jst$/, '.tpl'), cb);
},
function (source, cb) {
Benchpress.precompile({
source: source.toString(),
minify: global.env !== 'development',
}, cb);
},
function (compiled, cb) {
fs.writeFile(filePath, compiled, cb);
},
], next);
};

@ -30,6 +30,7 @@ Plugins.loadedHooks = {};
Plugins.staticDirs = {};
Plugins.cssFiles = [];
Plugins.lessFiles = [];
Plugins.acpLessFiles = [];
Plugins.clientScripts = [];
Plugins.acpScripts = [];
Plugins.libraryPaths = [];
@ -83,6 +84,7 @@ Plugins.reload = function (callback) {
Plugins.versionWarning = [];
Plugins.cssFiles.length = 0;
Plugins.lessFiles.length = 0;
Plugins.acpLessFiles.length = 0;
Plugins.clientScripts.length = 0;
Plugins.acpScripts.length = 0;
Plugins.libraryPaths.length = 0;
@ -302,39 +304,76 @@ Plugins.normalise = function (apiReturn, callback) {
});
};
Plugins.nodeModulesPath = path.join(__dirname, '../node_modules');
Plugins.showInstalled = function (callback) {
var npmPluginPath = path.join(__dirname, '../node_modules');
var pluginNamePattern = /^(@.*?\/)?nodebb-(theme|plugin|widget|rewards)-.*$/;
async.waterfall([
async.apply(fs.readdir, npmPluginPath),
function (next) {
fs.readdir(Plugins.nodeModulesPath, next);
},
function (dirs, next) {
dirs = dirs.filter(function (dir) {
return dir.startsWith('nodebb-plugin-') ||
dir.startsWith('nodebb-widget-') ||
dir.startsWith('nodebb-rewards-') ||
dir.startsWith('nodebb-theme-');
}).map(function (dir) {
return path.join(npmPluginPath, dir);
});
var pluginPaths = [];
async.filter(dirs, function (dir, callback) {
fs.stat(dir, function (err, stats) {
if (err) {
if (err.code === 'ENOENT') {
return callback(null, false);
}
return callback(err);
}
callback(null, stats.isDirectory());
});
}, next);
async.each(dirs, function (dirname, next) {
var dirPath = path.join(Plugins.nodeModulesPath, dirname);
async.waterfall([
function (cb) {
fs.stat(dirPath, function (err, stats) {
if (err && err.code !== 'ENOENT') {
return cb(err);
}
if (err || !stats.isDirectory()) {
return next();
}
if (pluginNamePattern.test(dirname)) {
pluginPaths.push(dirname);
return next();
}
if (dirname[0] !== '@') {
return next();
}
fs.readdir(dirPath, cb);
});
},
function (subdirs, cb) {
async.each(subdirs, function (subdir, next) {
if (!pluginNamePattern.test(subdir)) {
return next();
}
var subdirPath = path.join(dirPath, subdir);
fs.stat(subdirPath, function (err, stats) {
if (err && err.code !== 'ENOENT') {
return next(err);
}
if (err || !stats.isDirectory()) {
return next();
}
pluginPaths.push(dirname + '/' + subdir);
next();
});
}, cb);
},
], next);
}, function (err) {
next(err, pluginPaths);
});
},
function (files, next) {
function (dirs, next) {
dirs = dirs.map(function (dir) {
return path.join(Plugins.nodeModulesPath, dir);
});
var plugins = [];
async.each(files, function (file, next) {
async.each(dirs, function (file, next) {
async.waterfall([
function (next) {
Plugins.loadPluginInfo(file, next);

@ -25,6 +25,9 @@ module.exports = function (Plugins) {
lessFiles: function (next) {
Plugins.data.getFiles(pluginData, 'less', next);
},
acpLessFiles: function (next) {
Plugins.data.getFiles(pluginData, 'acpLess', next);
},
clientScripts: function (next) {
Plugins.data.getScripts(pluginData, 'client', next);
},
@ -60,6 +63,7 @@ module.exports = function (Plugins) {
Object.assign(Plugins.staticDirs, results.staticDirs || {});
add(Plugins.cssFiles, results.cssFiles);
add(Plugins.lessFiles, results.lessFiles);
add(Plugins.acpLessFiles, results.acpLessFiles);
add(Plugins.clientScripts, results.clientScripts);
add(Plugins.acpScripts, results.acpScripts);
Object.assign(meta.js.scripts.modules, results.modules || {});
@ -79,6 +83,7 @@ module.exports = function (Plugins) {
Plugins.prepareForBuild = function (targets, callback) {
Plugins.cssFiles.length = 0;
Plugins.lessFiles.length = 0;
Plugins.acpLessFiles.length = 0;
Plugins.clientScripts.length = 0;
Plugins.acpScripts.length = 0;
Plugins.soundpacks.length = 0;
@ -91,7 +96,7 @@ module.exports = function (Plugins) {
'client js bundle': ['clientScripts'],
'admin js bundle': ['acpScripts'],
'client side styles': ['cssFiles', 'lessFiles'],
'admin control panel styles': ['cssFiles', 'lessFiles'],
'admin control panel styles': ['cssFiles', 'lessFiles', 'acpLessFiles'],
sounds: ['soundpack'],
languages: ['languageData'],
};

@ -4,11 +4,12 @@ var nconf = require('nconf');
var winston = require('winston');
var path = require('path');
var async = require('async');
var express = require('express');
var meta = require('../meta');
var controllers = require('../controllers');
var plugins = require('../plugins');
var user = require('../user');
var express = require('express');
var accountRoutes = require('./accounts');
var metaRoutes = require('./meta');
@ -34,6 +35,8 @@ function mainRoutes(app, middleware, controllers) {
setupPageRoute(app, '/search', middleware, [], controllers.search.search);
setupPageRoute(app, '/reset/:code?', middleware, [middleware.delayLoading], controllers.reset);
setupPageRoute(app, '/tos', middleware, [], controllers.termsOfUse);
app.post('/compose', middleware.applyCSRF, controllers.composePost);
}
function modRoutes(app, middleware, controllers) {
@ -145,6 +148,7 @@ module.exports = function (app, middleware, hotswapIds, callback) {
}
app.use(middleware.privateUploads);
app.use(relativePath + '/assets/templates', middleware.templatesOnDemand);
var statics = [
{ route: '/assets', path: path.join(__dirname, '../../build/public') },

@ -42,6 +42,7 @@ function searchInContent(data, callback) {
var matchCount = 0;
var pids;
var metadata;
async.waterfall([
function (next) {
async.parallel({
@ -96,6 +97,7 @@ function searchInContent(data, callback) {
function (_metadata, next) {
metadata = _metadata;
matchCount = metadata.pids.length;
if (data.page) {
var start = Math.max(0, (data.page - 1)) * 10;
metadata.pids = metadata.pids.slice(start, start + 10);
@ -128,7 +130,10 @@ function filterAndSort(pids, data, callback) {
sortPosts(posts, data);
pids = posts.map(function (post) {
plugins.fireHook('filter:search.filterAndSort', { pids: pids, posts: posts, data: data }, next);
},
function (result, next) {
pids = result.posts.map(function (post) {
return post && post.pid;
});
@ -138,25 +143,12 @@ function filterAndSort(pids, data, callback) {
}
function getMatchedPosts(pids, data, callback) {
var postFields = ['pid', 'tid', 'timestamp', 'deleted'];
var topicFields = ['deleted'];
var postFields = ['pid', 'uid', 'tid', 'timestamp', 'deleted'];
var categoryFields = [];
if (data.replies) {
topicFields.push('postcount');
}
if (data.sortBy && data.sortBy !== 'relevance') {
if (data.sortBy.startsWith('category')) {
topicFields.push('cid');
} else if (data.sortBy.startsWith('topic.')) {
topicFields.push(data.sortBy.split('.')[1]);
} else if (data.sortBy.startsWith('user.')) {
postFields.push('uid');
} else if (data.sortBy.startsWith('category.')) {
if (data.sortBy.startsWith('category.')) {
categoryFields.push(data.sortBy.split('.')[1]);
} else if (data.sortBy.startsWith('teaser')) {
topicFields.push('teaserPid');
}
}
@ -166,7 +158,6 @@ function getMatchedPosts(pids, data, callback) {
var keys = pids.map(function (pid) {
return 'post:' + pid;
});
db.getObjectsFields(keys, postFields, next);
},
function (_posts, next) {
@ -192,14 +183,14 @@ function getMatchedPosts(pids, data, callback) {
var topicKeys = posts.map(function (post) {
return 'topic:' + post.tid;
});
db.getObjectsFields(topicKeys, topicFields, next);
db.getObjects(topicKeys, next);
},
function (_topics, next) {
topicsData = _topics;
async.parallel({
teasers: function (next) {
if (topicFields.indexOf('teaserPid') !== -1) {
if (data.sortBy && data.sortBy.startsWith('teaser')) {
var teaserKeys = topicsData.map(function (topic) {
return 'post:' + topic.teaserPid;
});

@ -2,7 +2,6 @@
var async = require('async');
var winston = require('winston');
var nconf = require('nconf');
var meta = require('../meta');
var plugins = require('../plugins');
@ -228,8 +227,6 @@ SocketAdmin.email.test = function (socket, data, callback) {
var site_title = meta.config.title || 'NodeBB';
var payload = {
subject: '[' + site_title + '] Test Email',
site_title: site_title,
url: nconf.get('url'),
};
switch (data.template) {
@ -252,7 +249,7 @@ SocketAdmin.email.test = function (socket, data, callback) {
case 'welcome':
userEmail.sendValidationEmail(socket.uid, {
force: 1,
});
}, callback);
break;
default:

@ -59,6 +59,7 @@ SocketCategories.loadMore = function (socket, data, callback) {
if (!data) {
return callback(new Error('[[error:invalid-data]]'));
}
data.query = data.query || {};
var userPrivileges;
async.waterfall([
function (next) {
@ -70,8 +71,8 @@ SocketCategories.loadMore = function (socket, data, callback) {
user.getSettings(socket.uid, next);
},
targetUid: function (next) {
if (data.author) {
user.getUidByUserslug(data.author, next);
if (data.query.author) {
user.getUidByUserslug(data.query.author, next);
} else {
next();
}
@ -84,44 +85,28 @@ SocketCategories.loadMore = function (socket, data, callback) {
return callback(new Error('[[error:no-privileges]]'));
}
var infScrollTopicsPerPage = 20;
var set = 'cid:' + data.cid + ':tids';
var reverse = false;
if (data.categoryTopicSort === 'newest_to_oldest') {
reverse = true;
} else if (data.categoryTopicSort === 'most_posts') {
reverse = true;
set = 'cid:' + data.cid + ':tids:posts';
}
var sort = data.sort || data.categoryTopicSort;
var start = Math.max(0, parseInt(data.after, 10));
if (data.direction === -1) {
start -= reverse ? infScrollTopicsPerPage : -infScrollTopicsPerPage;
start -= infScrollTopicsPerPage;
}
var stop = start + infScrollTopicsPerPage - 1;
start = Math.max(0, start);
stop = Math.max(0, stop);
if (results.targetUid) {
set = 'cid:' + data.cid + ':uid:' + results.targetUid + ':tids';
}
if (data.tag) {
set = [set, 'tag:' + data.tag + ':topics'];
}
categories.getCategoryTopics({
uid: socket.uid,
cid: data.cid,
set: set,
reverse: reverse,
start: start,
stop: stop,
uid: socket.uid,
targetUid: results.targetUid,
sort: sort,
settings: results.settings,
query: data.query,
tag: data.query.tag,
targetUid: results.targetUid,
}, next);
},
function (data, next) {

@ -128,7 +128,6 @@ SocketUser.reset.commit = function (socket, data, callback) {
emailer.send('reset_notify', uid, {
username: username,
date: parsedDate,
site_title: meta.config.title || 'NodeBB',
subject: '[[email:reset.notify.subject]]',
});

@ -107,7 +107,6 @@ module.exports = function (SocketUser) {
var siteTitle = meta.config.title || 'NodeBB';
var data = {
subject: '[[email:banned.subject, ' + siteTitle + ']]',
site_title: siteTitle,
username: username,
until: until ? utils.toISOString(until) : false,
reason: reason,

@ -182,7 +182,8 @@ Topics.getTopicWithPosts = function (topicData, set, uid, start, stop, reverse,
function (next) {
async.parallel({
posts: async.apply(getMainPostAndReplies, topicData, set, uid, start, stop, reverse),
category: async.apply(Topics.getCategoryData, topicData.tid),
category: async.apply(categories.getCategoryData, topicData.cid),
tagWhitelist: async.apply(categories.getTagWhitelist, [topicData.cid]),
threadTools: async.apply(plugins.fireHook, 'filter:topic.thread_tools', { topic: topicData, uid: uid, tools: [] }),
isFollowing: async.apply(Topics.isFollowing, [topicData.tid], uid),
isIgnoring: async.apply(Topics.isIgnoring, [topicData.tid], uid),
@ -205,6 +206,7 @@ Topics.getTopicWithPosts = function (topicData, set, uid, start, stop, reverse,
function (results, next) {
topicData.posts = results.posts;
topicData.category = results.category;
topicData.tagWhitelist = results.tagWhitelist[0];
topicData.thread_tools = results.threadTools.tools;
topicData.isFollowing = results.isFollowing[0];
topicData.isNotFollowing = !results.isFollowing[0] && !results.isIgnoring[0];

@ -2,7 +2,6 @@
'use strict';
var async = require('async');
var nconf = require('nconf');
var S = require('string');
var winston = require('winston');
@ -259,13 +258,10 @@ module.exports = function (Topics) {
subject: '[' + (meta.config.title || 'NodeBB') + '] ' + title,
intro: '[[notifications:user_posted_to, ' + postData.user.username + ', ' + titleEscaped + ']]',
postBody: postData.content.replace(/"\/\//g, '"https://'),
site_title: meta.config.title || 'NodeBB',
username: data.userData.username,
userslug: data.userData.userslug,
url: nconf.get('url') + '/topic/' + postData.topic.tid,
topicSlug: postData.topic.slug,
postCount: postData.topic.postcount,
base_url: nconf.get('url'),
showUnsubscribe: true,
}, next);
} else {
winston.debug('[topics.notifyFollowers] uid ' + toUid + ' does not have post notifications enabled, skipping.');

@ -27,7 +27,12 @@ module.exports = function (Topics) {
async.waterfall([
function (next) {
if (cid) {
categories.getTopicIds(cid, 'cid:' + cid + ':tids', true, 0, 199, next);
categories.getTopicIds({
cid: cid,
start: 0,
stop: 199,
sort: 'newest_to_oldest',
}, next);
} else {
db.getSortedSetRevRange('topics:recent', 0, 199, next);
}

@ -73,7 +73,11 @@ module.exports = function (Topics) {
Topics.getTopicField(tid, 'cid', next);
},
function (cid, next) {
categories.getTopicIds(cid, 'cid:' + cid + ':tids', true, 0, 9, next);
categories.getTopicIds({
cid: cid,
start: 0,
stop: 9,
}, next);
},
], callback);
}

@ -157,16 +157,13 @@ module.exports = function (Topics) {
var topicData;
async.waterfall([
function (next) {
Topics.exists(tid, next);
},
function (exists, next) {
if (!exists) {
return callback(new Error('[[error:no-topic]]'));
}
Topics.getTopicFields(tid, ['uid', 'tid', 'cid', 'lastposttime', 'postcount'], next);
Topics.getTopicData(tid, next);
},
function (_topicData, next) {
topicData = _topicData;
if (!topicData) {
return callback(new Error('[[error:no-topic]]'));
}
privileges.categories.isAdminOrMod(_topicData.cid, uid, next);
},
function (isAdminOrMod, next) {

@ -0,0 +1,53 @@
'use strict';
var image = require('../../image');
var meta = require('../../meta');
var async = require('async');
var path = require('path');
var nconf = require('nconf');
var fs = require('fs');
module.exports = {
name: 'Generate email logo for use in email header',
timestamp: Date.UTC(2017, 6, 17),
method: function (callback) {
var skip = false;
async.series([
function (next) {
// Resize existing logo (if present) to email header size
var uploadPath = path.join(nconf.get('upload_path'), 'system', 'site-logo-x50.png');
var sourcePath = meta.config['brand:logo'] ? path.join(nconf.get('upload_path'), 'system', path.basename(meta.config['brand:logo'])) : null;
if (!sourcePath) {
skip = true;
return setImmediate(next);
}
fs.access(sourcePath, function (err) {
if (err) {
skip = true;
return setImmediate(next);
}
image.resizeImage({
path: sourcePath,
target: uploadPath,
extension: 'png',
height: 50,
}, next);
});
},
function (next) {
if (skip) {
return setImmediate(next);
}
meta.configs.setMultiple({
'brand:logo': path.join(nconf.get('upload_path'), 'system', path.basename(meta.config['brand:logo'])),
}, next);
},
], callback);
},
};

@ -89,7 +89,6 @@ module.exports = function (User) {
var title = meta.config.title || meta.config.browserTitle || 'NodeBB';
translator.translate('[[email:welcome-to, ' + title + ']]', meta.config.defaultLang, function (subject) {
var data = {
site_title: title,
username: username,
subject: subject,
template: 'registration_accepted',

@ -136,8 +136,6 @@ Digest.send = function (data, callback) {
subject: '[' + meta.config.title + '] [[email:digest.subject, ' + (now.getFullYear() + '/' + (now.getMonth() + 1) + '/' + now.getDate()) + ']]',
username: userObj.username,
userslug: userObj.userslug,
url: nconf.get('url'),
site_title: meta.config.title || meta.config.browserTitle || 'NodeBB',
notifications: notifications,
recent: data.topics,
interval: data.interval,

@ -102,7 +102,6 @@ UserEmail.sendValidationEmail = function (uid, options, callback) {
var title = meta.config.title || meta.config.browserTitle || 'NodeBB';
translator.translate('[[email:welcome-to, ' + title + ']]', meta.config.defaultLang, function (subject) {
var data = {
site_title: title,
username: username,
confirm_link: confirm_link,
confirm_code: confirm_code,

@ -85,6 +85,9 @@ module.exports = function (User) {
template: 'invitation',
};
// Append default data to this email payload
data = Object.assign({}, emailer._defaultPayload, data);
emailer.sendToEmail('invitation', email, meta.config.defaultLang, data, next);
});
},

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save