From 9cf71d460ad19ee818ee3aa22dfed9a039c44bc8 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 2 Sep 2015 16:18:26 -0400 Subject: [PATCH 001/117] updated dev version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f55d5c6eff..91dc038584 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "nodebb", "license": "GPL-3.0", "description": "NodeBB Forum", - "version": "0.8.0", + "version": "0.8.1", "homepage": "http://www.nodebb.org", "repository": { "type": "git", From 54f9a09e6ab85317dd3410409a9c0fd3dd50042c Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 2 Sep 2015 18:17:58 -0400 Subject: [PATCH 002/117] some more logging --- install/web.js | 8 +++++--- src/install.js | 4 ++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/install/web.js b/install/web.js index 0c7fb2521f..557fcef862 100644 --- a/install/web.js +++ b/install/web.js @@ -30,7 +30,7 @@ web.install = function(port) { app.set('views', path.join(__dirname, '../src/views')); app.use(bodyParser.urlencoded({ extended: true - })); + })); async.parallel([compileLess, compileJS], function() { setupRoutes(); @@ -78,6 +78,8 @@ function install(req, res) { } } + winston.info('running nodebb setup with env ', process.env); + var child = require('child_process').fork('app', ['--setup'], { env: process.env }); @@ -107,10 +109,10 @@ function launch(req, res) { process.stdout.write(' "./nodebb stop" to stop the NodeBB server\n'); process.stdout.write(' "./nodebb log" to view server output\n'); process.stdout.write(' "./nodebb restart" to restart NodeBB\n'); - + child.unref(); process.exit(0); - + } function compileLess(callback) { diff --git a/src/install.js b/src/install.js index caafcbfacc..b8526b6521 100644 --- a/src/install.js +++ b/src/install.js @@ -488,6 +488,8 @@ function enableDefaultPlugins(next) { ], customDefaults = nconf.get('defaultPlugins'); + winston.info('[install/defaultPlugins] customDefaults', customDefaults); + if (customDefaults && customDefaults.length) { try { customDefaults = JSON.parse(customDefaults); @@ -502,6 +504,8 @@ function enableDefaultPlugins(next) { return array.indexOf(plugin) === index; }); + winston.info('[install/enableDefaultPlugins] activating default plugins', defaultEnabled); + var db = require('./database'); var order = defaultEnabled.map(function(plugin, index) { return index; From d402737a7531d82a58297de41c98ecb3d93184d9 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 2 Sep 2015 18:41:05 -0400 Subject: [PATCH 003/117] add file transport --- install/web.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/install/web.js b/install/web.js index 557fcef862..7d2cfad21a 100644 --- a/install/web.js +++ b/install/web.js @@ -12,6 +12,16 @@ var winston = require('winston'), app = express(), server; +winston.add(winston.transports.File, { + filename: 'logs/webinstall.log', + colorize: true, + timestamp: function() { + var date = new Date(); + return date.getDate() + '/' + (date.getMonth() + 1) + ' ' + date.toTimeString().substr(0,5) + ' [' + global.process.pid + ']'; + }, + level: 'verbose' +}); + var web = {}, scripts = [ 'public/vendor/xregexp/xregexp.js', @@ -78,8 +88,6 @@ function install(req, res) { } } - winston.info('running nodebb setup with env ', process.env); - var child = require('child_process').fork('app', ['--setup'], { env: process.env }); From 1a741da3d45cb9dfa76053b61517f5f13d44778e Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 3 Sep 2015 11:19:58 -0400 Subject: [PATCH 004/117] translating parsed template in client side code --- public/src/admin/appearance/themes.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/public/src/admin/appearance/themes.js b/public/src/admin/appearance/themes.js index 17bae639d7..901f64b688 100644 --- a/public/src/admin/appearance/themes.js +++ b/public/src/admin/appearance/themes.js @@ -76,8 +76,10 @@ define('admin/appearance/themes', function() { templates.parse('admin/partials/theme_list', { themes: themes }, function(html) { - instListEl.html(html); - highlightSelectedTheme(config['theme:id']); + translator.translate(html, function(html) { + instListEl.html(html); + highlightSelectedTheme(config['theme:id']); + }); }); } }); From 4962fb4851564b888f6958e637476468d72f55be Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 4 Sep 2015 14:13:59 -0400 Subject: [PATCH 005/117] closes #3546 --- public/src/installer/install.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/src/installer/install.js b/public/src/installer/install.js index 15b3514f03..56645f0301 100644 --- a/public/src/installer/install.js +++ b/public/src/installer/install.js @@ -75,7 +75,7 @@ $('document').ready(function() { } function validateConfirmPassword(field) { - if ($('[name="admin:password"]').val() !== $('[name="admin:password:confirm"]').val()) { + if ($('[name="admin:password"]').val() !== $('[name="admin:passwordConfirm"]').val()) { parent.addClass('error'); help.html('Passwords do not match.'); } else { @@ -101,7 +101,7 @@ $('document').ready(function() { return validateUsername(field); case 'admin:password': return validatePassword(field); - case 'admin:password:confirm': + case 'admin:passwordConfirm': return validateConfirmPassword(field); case 'admin:email': return validateEmail(field); From 4ff1d6c0888763c05fa447711f03759a6f20bc57 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 4 Sep 2015 21:26:26 -0400 Subject: [PATCH 006/117] because github SEO is good for you --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9062b049ae..b12553b1c5 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Code Climate](https://codeclimate.com/github/NodeBB/NodeBB/badges/gpa.svg)](https://codeclimate.com/github/NodeBB/NodeBB) [![Documentation Status](https://readthedocs.org/projects/nodebb/badge/?version=latest)](https://readthedocs.org/projects/nodebb/?badge=latest) -**NodeBB Forum Software** is powered by Node.js and built on either a Redis or MongoDB database. It utilizes web sockets for instant interactions and real-time notifications. NodeBB is compatible down to IE8 and has many modern features out of the box such as social network integration and streaming discussions. +[**NodeBB Forum Software**](https://nodebb.org) is powered by Node.js and built on either a Redis or MongoDB database. It utilizes web sockets for instant interactions and real-time notifications. NodeBB is compatible down to IE8 and has many modern features out of the box such as social network integration and streaming discussions. Additional functionality is enabled through the use of third-party plugins. @@ -72,4 +72,4 @@ Detailed upgrade instructions are listed in [Upgrading NodeBB](https://docs.node NodeBB is licensed under the **GNU General Public License v3 (GPL-3)** (http://www.gnu.org/copyleft/gpl.html). -Interested in a sublicense agreement for use of NodeBB in a non-free/restrictive environment? Contact us at sales@nodebb.org. \ No newline at end of file +Interested in a sublicense agreement for use of NodeBB in a non-free/restrictive environment? Contact us at sales@nodebb.org. From d0cd73f663c981cfaa9e35a64252e54af45648cc Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 4 Sep 2015 21:29:26 -0400 Subject: [PATCH 007/117] updated blurb --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b12553b1c5..200a444b6d 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Code Climate](https://codeclimate.com/github/NodeBB/NodeBB/badges/gpa.svg)](https://codeclimate.com/github/NodeBB/NodeBB) [![Documentation Status](https://readthedocs.org/projects/nodebb/badge/?version=latest)](https://readthedocs.org/projects/nodebb/?badge=latest) -[**NodeBB Forum Software**](https://nodebb.org) is powered by Node.js and built on either a Redis or MongoDB database. It utilizes web sockets for instant interactions and real-time notifications. NodeBB is compatible down to IE8 and has many modern features out of the box such as social network integration and streaming discussions. +[**NodeBB Forum Software**](https://nodebb.org) is powered by Node.js and built on either a Redis or MongoDB database. It utilizes web sockets for instant interactions and real-time notifications. NodeBB has many modern features out of the box such as social network integration and streaming discussions, while still making sure to be compatible with older browsers. Additional functionality is enabled through the use of third-party plugins. From b6a43fb5cf6bfa85c1e3b4cf987feb3a6316e40b Mon Sep 17 00:00:00 2001 From: psychobunny Date: Sat, 5 Sep 2015 14:11:21 -0400 Subject: [PATCH 008/117] fixes #3562 --- public/src/admin/admin.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/public/src/admin/admin.js b/public/src/admin/admin.js index a1ac7376f3..773ded5f5c 100644 --- a/public/src/admin/admin.js +++ b/public/src/admin/admin.js @@ -105,7 +105,9 @@ } function launchSnackbar(params) { - translator.translate("" + params.title + "" + params.message, function(html) { + var message = (params.title ? "" + params.title + "" : '') + (params.message ? params.message : ''); + + translator.translate(message, function(html) { var bar = $.snackbar({ content: html, timeout: 3000, From 31fef4f429fbb10e0fb81de5e7ed35c6f2c3f33c Mon Sep 17 00:00:00 2001 From: psychobunny Date: Sat, 5 Sep 2015 14:12:51 -0400 Subject: [PATCH 009/117] hint. damn those globals wtb browserify --- public/src/admin/admin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/admin/admin.js b/public/src/admin/admin.js index 773ded5f5c..a601893e85 100644 --- a/public/src/admin/admin.js +++ b/public/src/admin/admin.js @@ -1,5 +1,5 @@ "use strict"; -/*global componentHandler, define, socket, app, ajaxify, utils, bootbox, Mousetrap, Hammer, RELATIVE_PATH*/ +/*global config, translator, componentHandler, define, socket, app, ajaxify, utils, bootbox, Mousetrap, Hammer, Slideout, RELATIVE_PATH*/ (function() { $(document).ready(function() { From cf193581e42a73774bf687991fdacde975b8e40f Mon Sep 17 00:00:00 2001 From: barisusakli Date: Sun, 6 Sep 2015 00:49:39 -0400 Subject: [PATCH 010/117] check and log all fs.unlink errors --- src/controllers/admin/uploads.js | 25 +++++++++++++++++++++---- src/controllers/uploads.js | 11 ++++++++--- src/user/picture.js | 6 +++++- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/controllers/admin/uploads.js b/src/controllers/admin/uploads.js index 8cdf5f81f3..cbf6e7f650 100644 --- a/src/controllers/admin/uploads.js +++ b/src/controllers/admin/uploads.js @@ -3,6 +3,7 @@ var fs = require('fs'), path = require('path'), nconf = require('nconf'), + winston = require('winston'), file = require('../../file'), plugins = require('../../plugins'); @@ -17,7 +18,11 @@ uploadsController.uploadCategoryPicture = function(req, res, next) { try { params = JSON.parse(req.body.params); } catch (e) { - fs.unlink(uploadedFile.path); + fs.unlink(uploadedFile.path, function(err) { + if (err) { + winston.error(err); + } + }); return next(e); } @@ -33,7 +38,11 @@ uploadsController.uploadFavicon = function(req, res, next) { if (validateUpload(req, res, next, uploadedFile, allowedTypes)) { file.saveFileToLocal('favicon.ico', 'system', uploadedFile.path, function(err, image) { - fs.unlink(uploadedFile.path); + fs.unlink(uploadedFile.path, function(err) { + if (err) { + winston.error(err); + } + }); if (err) { return next(err); } @@ -62,7 +71,11 @@ function upload(name, req, res, next) { function validateUpload(req, res, next, uploadedFile, allowedTypes) { if (allowedTypes.indexOf(uploadedFile.type) === -1) { - fs.unlink(uploadedFile.path); + fs.unlink(uploadedFile.path, function(err) { + if (err) { + winston.error(err); + } + }); res.json({error: '[[error:invalid-image-type, ' + allowedTypes.join(', ') + ']]'}); return false; @@ -73,7 +86,11 @@ function validateUpload(req, res, next, uploadedFile, allowedTypes) { function uploadImage(filename, folder, uploadedFile, req, res, next) { function done(err, image) { - fs.unlink(uploadedFile.path); + fs.unlink(uploadedFile.path, function(err) { + if (err) { + winston.error(err); + } + }); if (err) { return next(err); } diff --git a/src/controllers/uploads.js b/src/controllers/uploads.js index 1c99026463..be5d80bf23 100644 --- a/src/controllers/uploads.js +++ b/src/controllers/uploads.js @@ -136,9 +136,14 @@ function uploadFile(uid, uploadedFile, callback) { } function deleteTempFiles(files) { - for(var i=0; i Date: Tue, 8 Sep 2015 02:17:22 -0400 Subject: [PATCH 011/117] fix browser title on home page --- src/middleware/middleware.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middleware/middleware.js b/src/middleware/middleware.js index 207356013a..cc77ccd8db 100644 --- a/src/middleware/middleware.js +++ b/src/middleware/middleware.js @@ -457,7 +457,7 @@ function redirectToLogin(req, res) { } function modifyTitle(obj) { - var title = '[[pages:home]] | ' + validator.escape(meta.config.title || 'NodeBB'); + var title = '[[pages:home]] | ' + validator.escape(meta.config.browserTitle || meta.config.title || 'NodeBB'); obj.browserTitle = title; if (obj.metaTags) { From dd61116060dd233cdb09dcda272da54213dfe12b Mon Sep 17 00:00:00 2001 From: Henry Wright Date: Tue, 8 Sep 2015 23:44:02 +0100 Subject: [PATCH 012/117] Replace Less variable that is undefined if Bootstrap is absent --- public/less/generics.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/less/generics.less b/public/less/generics.less index 4ce04c4337..f9ef30469a 100644 --- a/public/less/generics.less +++ b/public/less/generics.less @@ -26,7 +26,7 @@ padding: 0.5rem 1rem; &:hover { - background: @gray-lighter; + background: #eee; } img { @@ -40,4 +40,4 @@ display: inline-block; } } -} \ No newline at end of file +} From b4d465223a8f7e344c44c6377b8e575106198589 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 9 Sep 2015 12:25:00 -0400 Subject: [PATCH 013/117] if there is no description tag add one, fix missing winston --- src/meta/tags.js | 97 +++++++++++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 35 deletions(-) diff --git a/src/meta/tags.js b/src/meta/tags.js index 022a3ef778..64e183e43e 100644 --- a/src/meta/tags.js +++ b/src/meta/tags.js @@ -1,46 +1,55 @@ +'use strict'; + var nconf = require('nconf'), validator = require('validator'), async = require('async'), + winston = require('winston'), plugins = require('../plugins'); module.exports = function(Meta) { Meta.tags = {}; Meta.tags.parse = function(meta, link, callback) { - async.parallel([ - async.apply(plugins.fireHook, 'filter:meta.getMetaTags', [{ - name: 'viewport', - content: 'width=device-width, initial-scale=1.0, user-scalable=no' - }, { - name: 'content-type', - content: 'text/html; charset=UTF-8' - }, { - name: 'apple-mobile-web-app-capable', - content: 'yes' - }, { - property: 'og:site_name', - content: Meta.config.title || 'NodeBB' - }, { - name: 'keywords', - content: Meta.config.keywords || '' - }, { - name: 'msapplication-badge', - content: 'frequency=30; polling-uri=' + nconf.get('url') + '/sitemap.xml' - }, { - name: 'msapplication-square150x150logo', - content: Meta.config['brand:logo'] || '' - }]), - async.apply(plugins.fireHook, 'filter:meta.getLinkTags', [{ - rel: "icon", - type: "image/x-icon", - href: nconf.get('relative_path') + '/favicon.ico' - }, { - rel: 'apple-touch-icon', - href: nconf.get('relative_path') + '/apple-touch-icon' - }]) - ], function(err, tags) { - meta = tags[0].concat(meta || []).map(function(tag) { - if(!tag || typeof tag.content !== 'string') { + async.parallel({ + tags: function(next) { + var defaultTags = [{ + name: 'viewport', + content: 'width=device-width, initial-scale=1.0, user-scalable=no' + }, { + name: 'content-type', + content: 'text/html; charset=UTF-8' + }, { + name: 'apple-mobile-web-app-capable', + content: 'yes' + }, { + property: 'og:site_name', + content: Meta.config.title || 'NodeBB' + }, { + name: 'keywords', + content: Meta.config.keywords || '' + }, { + name: 'msapplication-badge', + content: 'frequency=30; polling-uri=' + nconf.get('url') + '/sitemap.xml' + }, { + name: 'msapplication-square150x150logo', + content: Meta.config['brand:logo'] || '' + }]; + plugins.fireHook('filter:meta.getMetaTags', defaultTags, next); + }, + links: function(next) { + var defaultLinks = [{ + rel: "icon", + type: "image/x-icon", + href: nconf.get('relative_path') + '/favicon.ico' + }, { + rel: 'apple-touch-icon', + href: nconf.get('relative_path') + '/apple-touch-icon' + }]; + plugins.fireHook('filter:meta.getLinkTags', defaultLinks, next); + } + }, function(err, results) { + meta = results.tags.concat(meta || []).map(function(tag) { + if (!tag || typeof tag.content !== 'string') { winston.warn('Invalid meta tag. ', tag); return tag; } @@ -49,7 +58,9 @@ module.exports = function(Meta) { return tag; }); - link = tags[1].concat(link || []); + addDescription(meta); + + link = results.links.concat(link || []); callback(null, { meta: meta, @@ -57,4 +68,20 @@ module.exports = function(Meta) { }); }); }; + + function addDescription(meta) { + var hasDescription = false; + meta.forEach(function(tag) { + if (tag.name === 'description') { + hasDescription = true; + } + }); + + if (!hasDescription) { + meta.push({ + name: 'description', + content: validator.escape(Meta.config.description || '') + }); + } + } }; \ No newline at end of file From 3baad542237d2561112cf570b40cdc0c4e0b63e8 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 9 Sep 2015 19:57:12 -0400 Subject: [PATCH 014/117] Close #3578 This commit introduces notification creation if a user requests membership to a group (in which case notifications are sent to all group owners), and user invites to a group (in which case a notification is sent to the invitee). --- public/language/en_GB/groups.json | 4 ++++ src/groups.js | 4 ++++ src/groups/membership.js | 40 +++++++++++++++++++++++++++++-- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/public/language/en_GB/groups.json b/public/language/en_GB/groups.json index 958efd13e0..d800d74b1c 100644 --- a/public/language/en_GB/groups.json +++ b/public/language/en_GB/groups.json @@ -14,6 +14,10 @@ "invited.none": "There are no invited members at this time", "invited.uninvite": "Rescind Invitation", "invited.search": "Search for a user to invite to this group", + "invited.notification_title": "You have been invited to join %1", + + "request.notification_title": "Group Membership Request from %1", + "request.notification_text": "%1 has requested to become a member of %2", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", diff --git a/src/groups.js b/src/groups.js index 13bae53e5f..24ee5a9fd8 100644 --- a/src/groups.js +++ b/src/groups.js @@ -197,6 +197,10 @@ var async = require('async'), }); }; + Groups.getOwners = function(groupName, callback) { + db.getSetMembers('group:' + groupName + ':owners', callback); + }; + Groups.getOwnersAndMembers = function(groupName, uid, start, stop, callback) { async.parallel({ owners: function (next) { diff --git a/src/groups/membership.js b/src/groups/membership.js index 51ee633787..c8cf9a9871 100644 --- a/src/groups/membership.js +++ b/src/groups/membership.js @@ -5,7 +5,9 @@ var async = require('async'), _ = require('underscore'), user = require('../user'), + utils = require('../../public/src/utils'), plugins = require('../plugins'), + notifications = require('../notifications'), db = require('./../database'); module.exports = function(Groups) { @@ -66,7 +68,29 @@ module.exports = function(Groups) { }; Groups.requestMembership = function(groupName, uid, callback) { - inviteOrRequestMembership(groupName, uid, 'request', callback); + async.waterfall([ + async.apply(inviteOrRequestMembership, groupName, uid, 'request'), + function(next) { + user.getUserField(uid, 'username', function(err, username) { + if (err) { + return next(err); + } + next(null, { + bodyShort: '[[groups:request.notification_title, ' + username + ']]', + bodyLong: '[[groups:request.notification_text, ' + username + ', ' + groupName + ']]', + nid: 'group:' + groupName + ':uid:' + uid + ':request', + path: '/groups/' + utils.slugify(groupName) + }); + }); + }, + async.apply(notifications.create), + function(notification, next) { + Groups.getOwners(groupName, function(err, ownerUids) { + next(null, notification, ownerUids); + }); + }, + async.apply(notifications.push) + ], callback); }; Groups.acceptMembership = function(groupName, uid, callback) { @@ -87,7 +111,19 @@ module.exports = function(Groups) { }; Groups.invite = function(groupName, uid, callback) { - inviteOrRequestMembership(groupName, uid, 'invite', callback); + async.waterfall([ + async.apply(inviteOrRequestMembership, groupName, uid, 'invite'), + async.apply(notifications.create, { + bodyShort: '[[groups:invited.notification_title, ' + groupName + ']]', + bodyLong: '', + nid: 'group:' + groupName + ':uid:' + uid + ':invite', + path: '/groups/' + utils.slugify(groupName) + }), + function(notification, next) { + next(null, notification, [uid]); + }, + async.apply(notifications.push) + ], callback); }; function inviteOrRequestMembership(groupName, uid, type, callback) { From edbe8b2e04c3deb479f885b4c6f16052a280a944 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 10 Sep 2015 01:30:52 -0400 Subject: [PATCH 015/117] typo in ACP --- src/views/admin/settings/general.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/admin/settings/general.tpl b/src/views/admin/settings/general.tpl index 2d04a9ad89..871c690900 100644 --- a/src/views/admin/settings/general.tpl +++ b/src/views/admin/settings/general.tpl @@ -26,7 +26,7 @@
-
+
From 2c6f93efbd681e9813c26d191136635d29cb80ad Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 10 Sep 2015 10:43:42 -0400 Subject: [PATCH 016/117] closes #3583 --- src/groups/create.js | 103 +++++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 49 deletions(-) diff --git a/src/groups/create.js b/src/groups/create.js index d8eb4a0ab9..828de6056b 100644 --- a/src/groups/create.js +++ b/src/groups/create.js @@ -13,57 +13,62 @@ module.exports = function(Groups) { } var system = data.name === 'administrators' || data.name === 'registered-users' || Groups.isPrivilegeGroup(data.name); + var groupData; + var timestamp = data.timestamp || Date.now(); - meta.userOrGroupExists(data.name, function (err, exists) { - if (err) { - return callback(err); - } - - if (exists) { - return callback(new Error('[[error:group-already-exists]]')); - } - var timestamp = data.timestamp || Date.now(); - var memberCount = data.hasOwnProperty('ownerUid') ? 1 : 0; - var slug = utils.slugify(data.name); - var groupData = { - name: data.name, - slug: slug, - createtime: timestamp, - userTitle: data.name, - description: data.description || '', - memberCount: memberCount, - deleted: '0', - hidden: data.hidden || '0', - system: system ? '1' : '0', - private: data.private || '1' - }; - var tasks = [ - async.apply(db.sortedSetAdd, 'groups:createtime', timestamp, data.name), - async.apply(db.setObject, 'group:' + data.name, groupData) - ]; - - if (data.hasOwnProperty('ownerUid')) { - tasks.push(async.apply(db.setAdd, 'group:' + data.name + ':owners', data.ownerUid)); - tasks.push(async.apply(db.sortedSetAdd, 'group:' + data.name + ':members', timestamp, data.ownerUid)); - - groupData.ownerUid = data.ownerUid; - } - - if (!data.hidden && !system) { - tasks.push(async.apply(db.sortedSetAdd, 'groups:visible:createtime', timestamp, data.name)); - tasks.push(async.apply(db.sortedSetAdd, 'groups:visible:memberCount', memberCount, data.name)); - tasks.push(async.apply(db.sortedSetAdd, 'groups:visible:name', 0, data.name.toLowerCase() + ':' + data.name)); - } - - tasks.push(async.apply(db.setObjectField, 'groupslug:groupname', slug, data.name)); - - async.series(tasks, function(err) { - if (!err) { - plugins.fireHook('action:group.create', groupData); + async.waterfall([ + function (next) { + meta.userOrGroupExists(data.name, next); + }, + function (exists, next) { + if (exists) { + return next(new Error('[[error:group-already-exists]]')); } - callback(err, groupData); - }); - }); + var memberCount = data.hasOwnProperty('ownerUid') ? 1 : 0; + var slug = utils.slugify(data.name); + groupData = { + name: data.name, + slug: slug, + createtime: timestamp, + userTitle: data.name, + description: data.description || '', + memberCount: memberCount, + deleted: '0', + hidden: data.hidden || '0', + system: system ? '1' : '0', + private: data.private || '1' + }; + plugins.fireHook('filter:group.create', {group: groupData, data: data}, next); + }, + function (results, next) { + var tasks = [ + async.apply(db.sortedSetAdd, 'groups:createtime', groupData.createtime, groupData.name), + async.apply(db.setObject, 'group:' + groupData.name, groupData) + ]; + + if (data.hasOwnProperty('ownerUid')) { + tasks.push(async.apply(db.setAdd, 'group:' + groupData.name + ':owners', data.ownerUid)); + tasks.push(async.apply(db.sortedSetAdd, 'group:' + groupData.name + ':members', timestamp, data.ownerUid)); + + groupData.ownerUid = data.ownerUid; + } + + if (!data.hidden && !system) { + tasks.push(async.apply(db.sortedSetAdd, 'groups:visible:createtime', timestamp, groupData.name)); + tasks.push(async.apply(db.sortedSetAdd, 'groups:visible:memberCount', groupData.memberCount, groupData.name)); + tasks.push(async.apply(db.sortedSetAdd, 'groups:visible:name', 0, groupData.name.toLowerCase() + ':' + groupData.name)); + } + + tasks.push(async.apply(db.setObjectField, 'groupslug:groupname', groupData.slug, groupData.name)); + + async.series(tasks, next); + }, + function (results, next) { + plugins.fireHook('action:group.create', groupData); + next(null, groupData); + } + ], callback); + }; }; From 394fa87b393a40a1929fd860989e6547a5bd006a Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 10 Sep 2015 10:52:32 -0400 Subject: [PATCH 017/117] on account deletion modal, focus on text input --- public/src/client/account/edit.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/public/src/client/account/edit.js b/public/src/client/account/edit.js index c3de1ec9bf..aba7058b94 100644 --- a/public/src/client/account/edit.js +++ b/public/src/client/account/edit.js @@ -153,7 +153,7 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'], function handleAccountDelete() { $('#deleteAccountBtn').on('click', function() { translator.translate('[[user:delete_account_confirm]]', function(translated) { - bootbox.confirm(translated + '

', function(confirm) { + var modal = bootbox.confirm(translated + '

', function(confirm) { if (!confirm) { return; } @@ -169,6 +169,10 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'], }); } }); + + modal.on('shown.bs.modal', function() { + modal.find('input').focus(); + }); }); return false; }); From 265a16af1a5f95e8fe0493563a1507fe3a9a556b Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 10 Sep 2015 14:56:08 -0400 Subject: [PATCH 018/117] some old left over branding code --- src/meta/css.js | 2 -- src/socket.io/admin.js | 4 ---- 2 files changed, 6 deletions(-) diff --git a/src/meta/css.js b/src/meta/css.js index bc4ea92352..a93be08d09 100644 --- a/src/meta/css.js +++ b/src/meta/css.js @@ -18,8 +18,6 @@ module.exports = function(Meta) { Meta.css = {}; Meta.css.cache = undefined; Meta.css.acpCache = undefined; - Meta.css.branding = {}; - Meta.css.defaultBranding = {}; Meta.css.minify = function(callback) { callback = callback || function() {}; diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js index 88ccde7224..fcdc61a0ae 100644 --- a/src/socket.io/admin.js +++ b/src/socket.io/admin.js @@ -100,10 +100,6 @@ SocketAdmin.themes.set = function(socket, data, callback) { } }; -SocketAdmin.themes.updateBranding = function(socket, data, callback) { - meta.css.updateBranding(); -}; - SocketAdmin.plugins.toggleActive = function(socket, plugin_id, callback) { require('../posts/cache').reset(); plugins.toggleActive(plugin_id, callback); From af50ade0662eb0ca084576469f9a2f7702817918 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 10 Sep 2015 16:24:19 -0400 Subject: [PATCH 019/117] closes #3560 --- public/src/client/chats.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/client/chats.js b/public/src/client/chats.js index b5413a6de6..ed91a75da7 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -184,7 +184,7 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', }; Chats.sendMessage = function(toUid, inputEl) { - var msg = S(inputEl.val()).stripTags().s; + var msg = inputEl.val(); if (msg.length) { msg = msg +'\n'; socket.emit('modules.chats.send', { From 727ee3c557c74d17d05b550503cc9403579b7583 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 10 Sep 2015 16:28:01 -0400 Subject: [PATCH 020/117] up persona --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 91dc038584..93b92a2952 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "nodebb-plugin-spam-be-gone": "0.4.1", "nodebb-rewards-essentials": "0.0.5", "nodebb-theme-lavender": "1.0.54", - "nodebb-theme-persona": "2.1.25", + "nodebb-theme-persona": "2.1.26", "nodebb-theme-vanilla": "3.1.7", "nodebb-widget-essentials": "2.0.0", "npm": "^2.1.4", From 442c4be4ec227b300a5f19d1ed8da0c08665d858 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 10 Sep 2015 16:33:10 -0400 Subject: [PATCH 021/117] closes #3581 --- src/groups/create.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/groups/create.js b/src/groups/create.js index 828de6056b..836cfef035 100644 --- a/src/groups/create.js +++ b/src/groups/create.js @@ -7,16 +7,16 @@ var async = require('async'), db = require('../database'); module.exports = function(Groups) { - Groups.create = function(data, callback) { - if (data.name.length === 0) { - return callback(new Error('[[error:group-name-too-short]]')); - } + Groups.create = function(data, callback) { var system = data.name === 'administrators' || data.name === 'registered-users' || Groups.isPrivilegeGroup(data.name); var groupData; var timestamp = data.timestamp || Date.now(); async.waterfall([ + function (next) { + validateGroupName(data.name, next); + }, function (next) { meta.userOrGroupExists(data.name, next); }, @@ -71,4 +71,14 @@ module.exports = function(Groups) { ], callback); }; + + function validateGroupName(name, callback) { + if (!name) { + return callback(new Error('[[error:group-name-too-short]]')); + } + + if (name.indexOf('/') !== -1) { + return callback(new Error('[[error:invalid-group-name]]')); + } + } }; From 0bfc568ac34064df448c92e0263650c8e6d93785 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 10 Sep 2015 16:35:37 -0400 Subject: [PATCH 022/117] closes #3537 --- src/categories/create.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/categories/create.js b/src/categories/create.js index 73a2591714..fbd448407d 100644 --- a/src/categories/create.js +++ b/src/categories/create.js @@ -37,7 +37,7 @@ module.exports = function(Categories) { link: '', numRecentReplies: 1, class: ( data.class ? data.class : 'col-md-3 col-xs-6' ), - imageClass: 'auto' + imageClass: 'cover' }; plugins.fireHook('filter:category.create', {category: category, data: data}, next); From a1c407acd00080e462c37dd179ed502e21e338dd Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 10 Sep 2015 17:13:36 -0400 Subject: [PATCH 023/117] closes #3530 --- public/src/admin/manage/flags.js | 8 ++++---- public/src/client/account/favourites.js | 2 +- public/src/client/account/posts.js | 4 ++-- public/src/client/account/profile.js | 2 +- public/src/client/categories.js | 2 +- public/src/client/chats.js | 2 +- public/src/client/groups/details.js | 2 +- public/src/client/search.js | 4 ++-- public/src/client/topic/events.js | 2 +- public/src/modules/chat.js | 6 +++--- public/src/widgets.js | 4 ++-- 11 files changed, 19 insertions(+), 19 deletions(-) diff --git a/public/src/admin/manage/flags.js b/public/src/admin/manage/flags.js index 880094af20..5cbf55f7f1 100644 --- a/public/src/admin/manage/flags.js +++ b/public/src/admin/manage/flags.js @@ -2,15 +2,15 @@ /*global define, socket, app, admin, utils, bootbox, RELATIVE_PATH*/ define('admin/manage/flags', [ - 'forum/infinitescroll', - 'admin/modules/selectable', + 'forum/infinitescroll', + 'admin/modules/selectable', 'autocomplete' ], function(infinitescroll, selectable, autocomplete) { var Flags = {}; Flags.init = function() { - $('.post-container .content img').addClass('img-responsive'); + $('.post-container .content img:not(.not-responsive)').addClass('img-responsive'); var params = utils.params(); $('#flag-sort-by').val(params.sortBy); @@ -91,7 +91,7 @@ define('admin/manage/flags', [ infinitescroll.parseAndTranslate('admin/manage/flags', 'posts', {posts: data.posts}, function(html) { $('[data-next]').attr('data-next', data.next); $('.post-container').append(html); - html.find('img').addClass('img-responsive'); + html.find('img:not(.not-responsive)').addClass('img-responsive'); done(); }); } else { diff --git a/public/src/client/account/favourites.js b/public/src/client/account/favourites.js index fd5931cc49..9d732418c4 100644 --- a/public/src/client/account/favourites.js +++ b/public/src/client/account/favourites.js @@ -8,7 +8,7 @@ define('forum/account/favourites', ['forum/account/header', 'forum/account/posts Favourites.init = function() { header.init(); - $('[component="post/content"] img').addClass('img-responsive'); + $('[component="post/content"] img:not(.not-responsive)').addClass('img-responsive'); posts.handleInfiniteScroll('posts.loadMoreFavourites', 'account/favourites'); }; diff --git a/public/src/client/account/posts.js b/public/src/client/account/posts.js index 94dac96323..b5b04056e8 100644 --- a/public/src/client/account/posts.js +++ b/public/src/client/account/posts.js @@ -9,7 +9,7 @@ define('forum/account/posts', ['forum/account/header', 'forum/infinitescroll'], AccountPosts.init = function() { header.init(); - $('[component="post/content"] img').addClass('img-responsive'); + $('[component="post/content"] img:not(.not-responsive)').addClass('img-responsive'); AccountPosts.handleInfiniteScroll('posts.loadMoreUserPosts', 'account/posts'); }; @@ -43,7 +43,7 @@ define('forum/account/posts', ['forum/account/header', 'forum/infinitescroll'], function onPostsLoaded(posts, callback) { infinitescroll.parseAndTranslate(template, 'posts', {posts: posts}, function(html) { $('[component="posts"]').append(html); - html.find('img').addClass('img-responsive'); + html.find('img:not(.not-responsive)').addClass('img-responsive'); html.find('.timeago').timeago(); app.createUserTooltips(); utils.makeNumbersHumanReadable(html.find('.human-readable-number')); diff --git a/public/src/client/account/profile.js b/public/src/client/account/profile.js index 5e72605017..c369f9da16 100644 --- a/public/src/client/account/profile.js +++ b/public/src/client/account/profile.js @@ -44,7 +44,7 @@ define('forum/account/profile', ['forum/account/header', 'forum/infinitescroll', }; function processPage() { - $('[component="posts"] img, [component="aboutme"] img').addClass('img-responsive'); + $('[component="posts"] img:not(.not-responsive), [component="aboutme"] img:not(.not-responsive)').addClass('img-responsive'); } function updateButtons() { diff --git a/public/src/client/categories.js b/public/src/client/categories.js index 33db4ef784..32f6af67c3 100644 --- a/public/src/client/categories.js +++ b/public/src/client/categories.js @@ -62,7 +62,7 @@ define('forum/categories', ['components', 'translator'], function(components, tr templates.parse('categories', 'posts', {categories: {posts: posts}}, function(html) { translator.translate(html, function(translatedHTML) { translatedHTML = $(translatedHTML); - translatedHTML.find('img').addClass('img-responsive'); + translatedHTML.find('img:not(.not-responsive)').addClass('img-responsive'); translatedHTML.find('.timeago').timeago(); callback(translatedHTML); }); diff --git a/public/src/client/chats.js b/public/src/client/chats.js index ed91a75da7..4a0fdd942f 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -130,7 +130,7 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', chatContainer = $('.chat-content'); newMessage.appendTo(chatContainer); newMessage.find('.timeago').timeago(); - newMessage.find('img:not(".chat-user-image")').addClass('img-responsive'); + newMessage.find('img:not(.not-responsive)').addClass('img-responsive'); Chats.scrollToBottom($('.expanded-chat .chat-content')); } diff --git a/public/src/client/groups/details.js b/public/src/client/groups/details.js index ae6c46e03c..f5fb69e401 100644 --- a/public/src/client/groups/details.js +++ b/public/src/client/groups/details.js @@ -24,7 +24,7 @@ define('forum/groups/details', ['iconSelect', 'components', 'forum/infinitescrol handleMemberInfiniteScroll(); handleMemberInvitations(); - components.get('groups/activity').find('.content img').addClass('img-responsive'); + components.get('groups/activity').find('.content img:not(.not-responsive)').addClass('img-responsive'); detailsPage.on('click', '[data-action]', function() { var btnEl = $(this), diff --git a/public/src/client/search.js b/public/src/client/search.js index 5633884ff5..c7330f1ab8 100644 --- a/public/src/client/search.js +++ b/public/src/client/search.js @@ -125,12 +125,12 @@ define('forum/search', ['search', 'autocomplete'], function(searchModule, autoco var result = $(this); var text = result.html().replace(regex, '$1'); - result.html(text).find('img').addClass('img-responsive').each(function() { + result.html(text).find('img:not(.not-responsive)').addClass('img-responsive').each(function() { $(this).attr('src', $(this).attr('src').replace(/([\s\S]*?)<\/strong>/gi, '$1')); }); result.find('a').each(function() { - $(this).attr('href', $(this).attr('href').replace(/([\s\S]*?)<\/strong>/gi, '$1')); + $(this).attr('href', $(this).attr('href').replace(/([\s\S]*?)<\/strong>/gi, '$1')); }); }); } catch(e) { diff --git a/public/src/client/topic/events.js b/public/src/client/topic/events.js index 2d62781ee6..200a362b69 100644 --- a/public/src/client/topic/events.js +++ b/public/src/client/topic/events.js @@ -110,7 +110,7 @@ define('forum/topic/events', [ editedPostEl.fadeOut(250, function() { editedPostEl.html(data.post.content); - editedPostEl.find('img').addClass('img-responsive'); + editedPostEl.find('img:not(.not-responsive)').addClass('img-responsive'); app.replaceSelfLinks(editedPostEl.find('a')); editedPostEl.fadeIn(250); }); diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index 4c934b19b8..2d364b0f8f 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -241,7 +241,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra chatModal.find('.chat-content').css('height', module.calculateChatListHeight(chatModal)); }); - + chatModal.draggable({ start:function() { module.bringModalToTop(chatModal); @@ -265,7 +265,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra $(window).one('action:ajaxify.end', function() { components.get('chat/input').val(text); }); - + ajaxify.go('chats/' + utils.slugify(data.username)); module.close(chatModal); } @@ -471,7 +471,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra Chats.parseMessage(data, function(html) { var message = $(html); - message.find('img:not(".chat-user-image")').addClass('img-responsive'); + message.find('img:not(.not-responsive)').addClass('img-responsive'); message.find('.timeago').timeago(); message.appendTo(chatContent); Chats.scrollToBottom(chatContent); diff --git a/public/src/widgets.js b/public/src/widgets.js index 56a7927e47..18d593ad1f 100644 --- a/public/src/widgets.js +++ b/public/src/widgets.js @@ -18,7 +18,7 @@ if (template.match(/^admin/)) { return callback(); } - + var widgetLocations = ['sidebar', 'footer', 'header'], numLocations; $('#content [widget-area]').each(function() { @@ -74,7 +74,7 @@ } var widgetAreas = $('#content [widget-area]'); - widgetAreas.find('img:not(.user-img)').addClass('img-responsive'); + widgetAreas.find('img:not(.not-responsive)').addClass('img-responsive'); widgetAreas.find('.timeago').timeago(); widgetAreas.find('img[title].teaser-pic,img[title].user-img').each(function() { $(this).tooltip({ From e21ac298d7e2a1486a60731064fe0167238f3303 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 10 Sep 2015 17:26:42 -0400 Subject: [PATCH 024/117] up deps --- package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 93b92a2952..7b9486ea1d 100644 --- a/package.json +++ b/package.json @@ -40,18 +40,18 @@ "mmmagic": "^0.3.13", "morgan": "^1.3.2", "nconf": "~0.7.1", - "nodebb-plugin-composer-default": "1.0.13", + "nodebb-plugin-composer-default": "1.0.14", "nodebb-plugin-dbsearch": "0.2.16", - "nodebb-plugin-emoji-extended": "0.4.9", + "nodebb-plugin-emoji-extended": "0.4.11", "nodebb-plugin-markdown": "4.0.5", "nodebb-plugin-mentions": "1.0.1", "nodebb-plugin-soundpack-default": "0.1.4", - "nodebb-plugin-spam-be-gone": "0.4.1", + "nodebb-plugin-spam-be-gone": "0.4.2", "nodebb-rewards-essentials": "0.0.5", "nodebb-theme-lavender": "1.0.54", - "nodebb-theme-persona": "2.1.26", + "nodebb-theme-persona": "2.1.27", "nodebb-theme-vanilla": "3.1.7", - "nodebb-widget-essentials": "2.0.0", + "nodebb-widget-essentials": "2.0.1", "npm": "^2.1.4", "passport": "^0.3.0", "passport-local": "1.0.0", From 19eaa051c23626529695a3f30f7871fea51cde61 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 10 Sep 2015 18:13:26 -0400 Subject: [PATCH 025/117] closes #3469 --- src/install.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/install.js b/src/install.js index b8526b6521..1b2fcdc97f 100644 --- a/src/install.js +++ b/src/install.js @@ -347,16 +347,20 @@ function createAdmin(callback) { winston.warn("Passwords did not match, please try again"); return retryPassword(results); } - + var adminUid; async.waterfall([ function(next) { User.create({username: results.username, password: results.password, email: results.email}, next); }, function(uid, next) { + adminUid = uid; Groups.join('administrators', uid, next); }, function(next) { Groups.show('administrators', next); + }, + function(next) { + Groups.ownership.grant(adminUid, 'administrators', next); } ], function(err) { if (err) { From f7aef56e668d73121aaaac96fecebfed6f21b74b Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 10 Sep 2015 20:33:08 -0400 Subject: [PATCH 026/117] closes #3529 --- src/topics/create.js | 16 ++++++++++++---- src/user/posts.js | 7 +++++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/topics/create.js b/src/topics/create.js index 3c584b3dbc..c4ba5a2911 100644 --- a/src/topics/create.js +++ b/src/topics/create.js @@ -121,7 +121,7 @@ module.exports = function(Topics) { return next(new Error('[[error:guest-handle-invalid]]')); } - user.isReadyToPost(data.uid, next); + user.isReadyToPost(data.uid, data.cid, next); }, function(next) { plugins.fireHook('filter:topic.post', data, next); @@ -184,22 +184,30 @@ module.exports = function(Topics) { content = data.content, postData; + var cid; async.waterfall([ function(next) { + Topics.getTopicField(tid, 'cid', next); + }, + function(_cid, next) { + cid = _cid; async.parallel({ exists: async.apply(Topics.exists, tid), locked: async.apply(Topics.isLocked, tid), canReply: async.apply(privileges.topics.can, 'topics:reply', tid, uid), - isAdmin: async.apply(user.isAdministrator, uid) + isAdmin: async.apply(user.isAdministrator, uid), + isModerator: async.apply(user.isModerator, uid, cid) }, next); }, function(results, next) { if (!results.exists) { return next(new Error('[[error:no-topic]]')); } - if (results.locked && !results.isAdmin) { + + if (results.locked && !results.isAdmin && !results.isModerator) { return next(new Error('[[error:topic-locked]]')); } + if (!results.canReply) { return next(new Error('[[error:no-privileges]]')); } @@ -208,7 +216,7 @@ module.exports = function(Topics) { return next(new Error('[[error:guest-handle-invalid]]')); } - user.isReadyToPost(uid, next); + user.isReadyToPost(uid, cid, next); }, function(next) { plugins.fireHook('filter:topic.reply', data, next); diff --git a/src/user/posts.js b/src/user/posts.js index c7d8e230be..895b5dbc86 100644 --- a/src/user/posts.js +++ b/src/user/posts.js @@ -6,7 +6,7 @@ var async = require('async'), module.exports = function(User) { - User.isReadyToPost = function(uid, callback) { + User.isReadyToPost = function(uid, cid, callback) { if (parseInt(uid, 10) === 0) { return callback(); } @@ -20,6 +20,9 @@ module.exports = function(User) { }, isAdmin: function(next) { User.isAdministrator(uid, next); + }, + isModerator: function(next) { + User.isModerator(uid, cid, next); } }, function(err, results) { if (err) { @@ -30,7 +33,7 @@ module.exports = function(User) { return callback(new Error('[[error:no-user]]')); } - if (results.isAdmin) { + if (results.isAdmin || results.isModerator) { return callback(); } From a4d7022e687bcad5039c922d9ac26f8bc72c2c56 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 12:51:10 -0400 Subject: [PATCH 027/117] closes #2958 --- src/user/settings.js | 29 +++++++++------ src/views/admin/settings/user.tpl | 60 +++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 10 deletions(-) diff --git a/src/user/settings.js b/src/user/settings.js index 5af9f567c1..03922ade45 100644 --- a/src/user/settings.js +++ b/src/user/settings.js @@ -60,27 +60,36 @@ module.exports = function(User) { settings.showemail = parseInt(settings.showemail, 10) === 1; settings.showfullname = parseInt(settings.showfullname, 10) === 1; - settings.openOutgoingLinksInNewTab = parseInt(settings.openOutgoingLinksInNewTab, 10) === 1; - settings.dailyDigestFreq = settings.dailyDigestFreq || 'off'; - settings.usePagination = (settings.usePagination === null || settings.usePagination === undefined) ? parseInt(meta.config.usePagination, 10) === 1 : parseInt(settings.usePagination, 10) === 1; + settings.openOutgoingLinksInNewTab = parseInt(getSetting(settings, 'openOutgoingLinksInNewTab', 0), 10) === 1; + settings.dailyDigestFreq = getSetting(settings, 'dailyDigestFreq', 'off'); + settings.usePagination = parseInt(getSetting(settings, 'usePagination', 0), 10) === 1; settings.topicsPerPage = Math.min(settings.topicsPerPage ? parseInt(settings.topicsPerPage, 10) : defaultTopicsPerPage, defaultTopicsPerPage); settings.postsPerPage = Math.min(settings.postsPerPage ? parseInt(settings.postsPerPage, 10) : defaultPostsPerPage, defaultPostsPerPage); settings.notificationSounds = parseInt(settings.notificationSounds, 10) === 1; settings.userLang = settings.userLang || meta.config.defaultLang || 'en_GB'; - settings.topicPostSort = settings.topicPostSort || meta.config.topicPostSort || 'oldest_to_newest'; - settings.categoryTopicSort = settings.categoryTopicSort || meta.config.categoryTopicSort || 'newest_to_oldest'; - settings.followTopicsOnCreate = (settings.followTopicsOnCreate === null || settings.followTopicsOnCreate === undefined) ? true : parseInt(settings.followTopicsOnCreate, 10) === 1; - settings.followTopicsOnReply = parseInt(settings.followTopicsOnReply, 10) === 1; - settings.sendChatNotifications = parseInt(settings.sendChatNotifications, 10) === 1; - settings.sendPostNotifications = parseInt(settings.sendPostNotifications, 10) === 1; + settings.topicPostSort = getSetting(settings, 'topicPostSort', 'oldest_to_newest'); + settings.categoryTopicSort = getSetting(settings, 'categoryTopicSort', 'newest_to_oldest'); + settings.followTopicsOnCreate = parseInt(getSetting(settings, 'followTopicsOnCreate', 1), 10) === 1; + settings.followTopicsOnReply = parseInt(getSetting(settings, 'followTopicsOnReply', 0), 10) === 1; + settings.sendChatNotifications = parseInt(getSetting(settings, 'sendChatNotifications', 0), 10) === 1; + settings.sendPostNotifications = parseInt(getSetting(settings, 'sendPostNotifications', 0), 10) === 1; settings.restrictChat = parseInt(settings.restrictChat, 10) === 1; - settings.topicSearchEnabled = parseInt(settings.topicSearchEnabled, 10) === 1; + settings.topicSearchEnabled = parseInt(getSetting(settings, 'topicSearchEnabled', 0), 10) === 1; settings.bootswatchSkin = settings.bootswatchSkin || 'default'; callback(null, settings); }); } + function getSetting(settings, key, base) { + if (settings[key] || settings[key] === 0) { + return settings[key]; + } else if (meta.config[key] || meta.config[key] === 0) { + return meta.config[key]; + } + return base; + } + User.saveSettings = function(uid, data, callback) { if (invalidPaginationSettings(data)) { return callback(new Error('[[error:invalid-pagination-value]]')); diff --git a/src/views/admin/settings/user.tpl b/src/views/admin/settings/user.tpl index b0bec269e3..5468a07b5d 100644 --- a/src/views/admin/settings/user.tpl +++ b/src/views/admin/settings/user.tpl @@ -195,4 +195,64 @@ +
+
Default User Settings
+
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ \ No newline at end of file From e6755d0adcfb518bbba18f49a4b40e98718f2de9 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 13:48:58 -0400 Subject: [PATCH 028/117] closes #3458 --- public/language/en_GB/user.json | 1 + public/src/client/account/edit.js | 18 +++++ public/src/client/account/settings.js | 2 +- src/controllers/accounts.js | 2 +- src/socket.io/user.js | 112 ++++++++++++++------------ 5 files changed, 81 insertions(+), 54 deletions(-) diff --git a/public/language/en_GB/user.json b/public/language/en_GB/user.json index b11db877cb..d7f1576548 100644 --- a/public/language/en_GB/user.json +++ b/public/language/en_GB/user.json @@ -56,6 +56,7 @@ "upload_picture": "Upload picture", "upload_a_picture": "Upload a picture", + "remove_uploaded_picture" : "Remove Uploaded Picture", "image_spec": "You may only upload PNG, JPG, or GIF files", "settings": "Settings", diff --git a/public/src/client/account/edit.js b/public/src/client/account/edit.js index aba7058b94..886069a40c 100644 --- a/public/src/client/account/edit.js +++ b/public/src/client/account/edit.js @@ -186,8 +186,16 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'], $('#user-uploaded-picture').attr('src', urlOnServer); updateHeader(urlOnServer); uploadedPicture = urlOnServer; + $('#removeUploadedPictureBtn').removeClass('hide'); } + function onRemoveComplete(urlOnServer) { + $('#user-current-picture').attr('src', urlOnServer); + $('#user-uploaded-picture').attr('src', ''); + updateHeader(urlOnServer); + uploadedPicture = ''; + $('#removeUploadedPictureBtn').addClass('hide'); + } $('#upload-picture-modal').on('hide', function() { $('#userPhotoInput').val(''); @@ -226,6 +234,16 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'], }); return false; }); + + $('#removeUploadedPictureBtn').on('click', function() { + socket.emit('user.removeUploadedPicture', {uid: ajaxify.data.theirid}, function(err, imageUrlOnServer) { + if (err) { + return app.alertError(err.message); + } + onRemoveComplete(imageUrlOnServer); + $('#change-picture-modal').modal('hide'); + }); + }) } function handleEmailConfirm() { diff --git a/public/src/client/account/settings.js b/public/src/client/account/settings.js index 23afc2f5c0..bf535d8ec0 100644 --- a/public/src/client/account/settings.js +++ b/public/src/client/account/settings.js @@ -45,7 +45,7 @@ define('forum/account/settings', ['forum/account/header'], function(header) { config[key] = newSettings[key]; } } - app.exposeConfigToTemplates(); + if (requireReload && parseInt(app.user.uid, 10) === parseInt(ajaxify.data.theirid, 10)) { app.alert({ id: 'setting-change', diff --git a/src/controllers/accounts.js b/src/controllers/accounts.js index 6a1d4f100e..d997c4b79a 100644 --- a/src/controllers/accounts.js +++ b/src/controllers/accounts.js @@ -493,7 +493,7 @@ accountsController.uploadPicture = function (req, res, next) { } ], function(err, image) { fs.unlink(userPhoto.path, function(err) { - winston.error('unable to delete picture', err); + winston.error('unable to delete picture ' + userPhoto.path, err); }); if (err) { return next(err); diff --git a/src/socket.io/user.js b/src/socket.io/user.js index cb8d80ba87..8946b3e03d 100644 --- a/src/socket.io/user.js +++ b/src/socket.io/user.js @@ -2,6 +2,7 @@ var async = require('async'), nconf = require('nconf'), + winston = require('winston'), user = require('../user'), groups = require('../groups'), topics = require('../topics'), @@ -166,6 +167,18 @@ SocketUser.changePassword = function(socket, data, callback) { }); }; +function isAdminOrSelf(socket, uid, callback) { + if (socket.uid === parseInt(uid, 10)) { + return callback(); + } + user.isAdministrator(socket.uid, function(err, isAdmin) { + if (err || !isAdmin) { + return callback(err || new Error('[[error:no-privileges]]')); + } + callback(); + }); +} + SocketUser.updateProfile = function(socket, data, callback) { function update(oldUserData) { function done(err, userData) { @@ -197,15 +210,10 @@ SocketUser.updateProfile = function(socket, data, callback) { callback(null, userData); } - if (socket.uid === parseInt(data.uid, 10)) { - return user.updateProfile(socket.uid, data, done); - } - - user.isAdministrator(socket.uid, function(err, isAdmin) { - if (err || !isAdmin) { - return callback(err || new Error('[[error:no-privileges]]')); + isAdminOrSelf(socket, data.uid, function(err) { + if (err) { + return callback(err); } - user.updateProfile(data.uid, data, done); }); } @@ -238,16 +246,6 @@ SocketUser.changePicture = function(socket, data, callback) { var type = data.type; - function changePicture(uid, callback) { - user.getUserField(uid, type, function(err, picture) { - if (err) { - return callback(err); - } - - user.setUserField(uid, 'picture', picture, callback); - }); - } - if (type === 'gravatar') { type = 'gravatarpicture'; } else if (type === 'uploaded') { @@ -256,41 +254,60 @@ SocketUser.changePicture = function(socket, data, callback) { return callback(new Error('[[error:invalid-image-type, ' + ['gravatar', 'uploadedpicture'].join(', ') + ']]')); } - if (socket.uid === parseInt(data.uid, 10)) { - return changePicture(socket.uid, callback); - } - - user.isAdministrator(socket.uid, function(err, isAdmin) { - if (err || !isAdmin) { - return callback(err || new Error('[[error:no-privileges]]')); + async.waterfall([ + function (next) { + isAdminOrSelf(socket, data.uid, next); + }, + function (next) { + user.getUserField(data.uid, type, next); + }, + function (picture, next) { + user.setUserField(data.uid, 'picture', picture, next); } - - changePicture(data.uid, callback); - }); + ], callback); }; SocketUser.uploadProfileImageFromUrl = function(socket, data, callback) { - function upload() { - user.uploadFromUrl(data.uid, data.url, function(err, uploadedImage) { - callback(err, uploadedImage ? uploadedImage.url : null); - }); - } - if (!socket.uid || !data.url || !data.uid) { return; } - if (parseInt(socket.uid, 10) === parseInt(data.uid, 10)) { - return upload(); + isAdminOrSelf(socket, data.uid, function(err) { + if (err) { + return callback(err); + } + user.uploadFromUrl(data.uid, data.url, function(err, uploadedImage) { + callback(err, uploadedImage ? uploadedImage.url : null); + }); + }); +}; + +SocketUser.removeUploadedPicture = function(socket, data, callback) { + if (!socket.uid || !data.uid) { + return; } - user.isAdministrator(socket.uid, function(err, isAdmin) { - if (err || !isAdmin) { - return callback(err || new Error('[[error:not-allowed]]')); + async.waterfall([ + function (next) { + isAdminOrSelf(socket, data.uid, next); + }, + function (next) { + user.getUserField(data.uid, 'uploadedpicture', next); + }, + function(uploadedPicture, next) { + if (!uploadedPicture.startsWith('http')) { + require('fs').unlink(uploadedPicture, function(err) { + if (err) { + winston.error(err); + } + }); + } + user.setUserField(data.uid, 'uploadedpicture', '', next); + }, + function(next) { + user.getUserField(data.uid, 'picture', next); } - - upload(); - }); + ], callback); }; SocketUser.follow = function(socket, data, callback) { @@ -346,19 +363,10 @@ SocketUser.saveSettings = function(socket, data, callback) { return callback(new Error('[[error:invalid-data]]')); } - if (socket.uid === parseInt(data.uid, 10)) { - return user.saveSettings(socket.uid, data.settings, callback); - } - - user.isAdministrator(socket.uid, function(err, isAdmin) { + isAdminOrSelf(socket, data.uid, function(err) { if (err) { return callback(err); } - - if (!isAdmin) { - return callback(new Error('[[error:no-privileges]]')); - } - user.saveSettings(data.uid, data.settings, callback); }); }; From ceba2fdc3c562d190f40b593ed5023d8112f5833 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 13:51:33 -0400 Subject: [PATCH 029/117] up themes --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 7b9486ea1d..cf2bda78e1 100644 --- a/package.json +++ b/package.json @@ -49,8 +49,8 @@ "nodebb-plugin-spam-be-gone": "0.4.2", "nodebb-rewards-essentials": "0.0.5", "nodebb-theme-lavender": "1.0.54", - "nodebb-theme-persona": "2.1.27", - "nodebb-theme-vanilla": "3.1.7", + "nodebb-theme-persona": "2.1.28", + "nodebb-theme-vanilla": "3.1.8", "nodebb-widget-essentials": "2.0.1", "npm": "^2.1.4", "passport": "^0.3.0", From bb4984a9aac43694691e8c0191fc2306fcbc06a4 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 13:54:00 -0400 Subject: [PATCH 030/117] closes #3428 --- public/language/en_GB/user.json | 1 + public/src/client/account/profile.js | 1 + 2 files changed, 2 insertions(+) diff --git a/public/language/en_GB/user.json b/public/language/en_GB/user.json index d7f1576548..b4d3dcaaf5 100644 --- a/public/language/en_GB/user.json +++ b/public/language/en_GB/user.json @@ -13,6 +13,7 @@ "delete_account": "Delete Account", "delete_account_confirm": "Are you sure you want to delete your account?
This action is irreversible and you will not be able to recover any of your data

Enter your username to confirm that you wish to destroy this account.", "delete_this_account_confirm": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data

", + "account-deleted": "Account deleted", "fullname": "Full Name", "website": "Website", diff --git a/public/src/client/account/profile.js b/public/src/client/account/profile.js index c369f9da16..5a7756cc93 100644 --- a/public/src/client/account/profile.js +++ b/public/src/client/account/profile.js @@ -154,6 +154,7 @@ define('forum/account/profile', ['forum/account/header', 'forum/infinitescroll', if (err) { return app.alertError(err.message); } + app.alertSuccess('[[user:account-deleted]]'); history.back(); }); }); From 2d175cc3fb75a6601514ecd01241e24e1e1dd708 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 14:32:44 -0400 Subject: [PATCH 031/117] closes #3466 --- public/src/admin/manage/tags.js | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/public/src/admin/manage/tags.js b/public/src/admin/manage/tags.js index 95133f9836..d85df5c266 100644 --- a/public/src/admin/manage/tags.js +++ b/public/src/admin/manage/tags.js @@ -10,7 +10,6 @@ define('admin/manage/tags', [ timeoutId = 0; Tags.init = function() { - handleColorPickers(); selectable.enable('.tag-management', '.tag-row'); handleSearch(); @@ -53,7 +52,7 @@ define('admin/manage/tags', [ var firstTag = $(tagsToModify[0]), title = tagsToModify.length > 1 ? 'Editing multiple tags' : 'Editing ' + firstTag.find('.tag-item').text() + ' tag'; - bootbox.dialog({ + var modal = bootbox.dialog({ title: title, message: firstTag.find('.tag-modal').html(), buttons: { @@ -79,9 +78,7 @@ define('admin/manage/tags', [ } }); - setTimeout(function() { - handleColorPickers(); - }, 500); + handleColorPickers(modal); }); } @@ -110,21 +107,13 @@ define('admin/manage/tags', [ }); } - function handleColorPickers() { + function handleColorPickers(modal) { function enableColorPicker(idx, inputEl) { - var $inputEl = $(inputEl), - previewEl = $inputEl.parents('.tag-row').find('.tag-item'); - - colorpicker.enable($inputEl, function(hsb, hex) { - if ($inputEl.attr('data-name') === 'bgColor') { - previewEl.css('background-color', '#' + hex); - } else if ($inputEl.attr('data-name') === 'color') { - previewEl.css('color', '#' + hex); - } - }); + var $inputEl = $(inputEl); + colorpicker.enable($inputEl); } - $('[data-name="bgColor"], [data-name="color"]').each(enableColorPicker); + modal.find('[data-name="bgColor"], [data-name="color"]').each(enableColorPicker); } function save(tag) { From 079ef74fe96186c94da4abd31858086773d078c8 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 14:38:03 -0400 Subject: [PATCH 032/117] up emoji --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cf2bda78e1..4fcde49607 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "nconf": "~0.7.1", "nodebb-plugin-composer-default": "1.0.14", "nodebb-plugin-dbsearch": "0.2.16", - "nodebb-plugin-emoji-extended": "0.4.11", + "nodebb-plugin-emoji-extended": "0.4.12", "nodebb-plugin-markdown": "4.0.5", "nodebb-plugin-mentions": "1.0.1", "nodebb-plugin-soundpack-default": "0.1.4", From 961efa3533f8b8382bcb624082c158506d26bf9c Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 14:48:44 -0400 Subject: [PATCH 033/117] closes #3512 --- public/src/client/topic/posts.js | 1 + 1 file changed, 1 insertion(+) diff --git a/public/src/client/topic/posts.js b/public/src/client/topic/posts.js index 6532ae648c..5875af699a 100644 --- a/public/src/client/topic/posts.js +++ b/public/src/client/topic/posts.js @@ -32,6 +32,7 @@ define('forum/topic/posts', [ if (html) { html.addClass('new'); } + navigator.scrollBottom(data.posts[0].index); }); }; From 4f766ac7b54f8a2b73ad07426312ec479b6e6848 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 11 Sep 2015 16:03:08 -0400 Subject: [PATCH 034/117] Updated cache buster logic Removed css and script busters in favour of a uuid/guid cache buster that is generated when the app starts. This change means that in the event your NodeBB crashes often, users will not be able to rely on their browser cache to serve an unchanged style/js file, but if that's the case, you've got bigger problems anyway. re: #3573 --- src/controllers/api.js | 2 -- src/meta/configs.js | 5 ++++- src/webserver.js | 18 ------------------ 3 files changed, 4 insertions(+), 21 deletions(-) diff --git a/src/controllers/api.js b/src/controllers/api.js index 9bbb6dc5d7..99571ea65f 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -70,8 +70,6 @@ apiController.getConfig = function(req, res, next) { config.environment = process.env.NODE_ENV; config.loggedIn = !!req.user; config['cache-buster'] = meta.config['cache-buster'] || ''; - config['script-buster'] = meta.js.hash || ''; - config['css-buster'] = meta.css.hash || ''; config.requireEmailConfirmation = parseInt(meta.config.requireEmailConfirmation, 10) === 1; config.topicPostSort = meta.config.topicPostSort || 'oldest_to_newest'; config.categoryTopicSort = meta.config.categoryTopicSort || 'newest_to_oldest'; diff --git a/src/meta/configs.js b/src/meta/configs.js index 65ffb45b11..b7b6a13b33 100644 --- a/src/meta/configs.js +++ b/src/meta/configs.js @@ -4,7 +4,8 @@ var winston = require('winston'), db = require('../database'), pubsub = require('../pubsub'), - nconf = require('nconf'); + nconf = require('nconf'), + utils = require('../../public/src/utils'); module.exports = function(Meta) { @@ -20,6 +21,8 @@ module.exports = function(Meta) { return callback(err); } + config['cache-buster'] = utils.generateUUID(); + Meta.config = config; callback(); }); diff --git a/src/webserver.js b/src/webserver.js index abeb367a90..75bccd6100 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -117,24 +117,6 @@ function cacheStaticFiles(callback) { app.enable('cache'); app.enable('minification'); - - // Configure cache-buster timestamp - require('child_process').exec('git describe --tags', { - cwd: path.join(__dirname, '../') - }, function(err, stdOut) { - if (!err) { - meta.config['cache-buster'] = stdOut.trim(); - callback(); - } else { - fs.stat(path.join(__dirname, '../package.json'), function(err, stats) { - if (err) { - return callback(err); - } - meta.config['cache-buster'] = new Date(stats.mtime).getTime(); - callback(); - }); - } - }); } function listen(callback) { From 160cf938020b9c336d87988b1881cfe3673f7ba6 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 11 Sep 2015 16:12:47 -0400 Subject: [PATCH 035/117] Fix regression caused by 4f766ac --- src/webserver.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/webserver.js b/src/webserver.js index 75bccd6100..9b6fb24e8c 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -117,6 +117,7 @@ function cacheStaticFiles(callback) { app.enable('cache'); app.enable('minification'); + callback(); } function listen(callback) { From a64cbbc6dfb469c7e128a5f8737f3e0fe2a5cc5f Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 16:18:32 -0400 Subject: [PATCH 036/117] #3573 --- app.js | 2 -- loader.js | 14 ++++---------- minifier.js | 14 ++------------ public/src/app.js | 17 ++++++----------- src/meta/css.js | 11 +---------- src/meta/js.js | 7 +------ src/socket.io/meta.js | 4 +--- 7 files changed, 15 insertions(+), 54 deletions(-) diff --git a/app.js b/app.js index 4c4f6854bb..292b599335 100644 --- a/app.js +++ b/app.js @@ -152,14 +152,12 @@ function start() { case 'js-propagate': meta.js.cache = message.cache; meta.js.map = message.map; - meta.js.hash = message.hash; emitter.emit('meta:js.compiled'); winston.verbose('[cluster] Client-side javascript and mapping propagated to worker %s', process.pid); break; case 'css-propagate': meta.css.cache = message.cache; meta.css.acpCache = message.acpCache; - meta.css.hash = message.hash; emitter.emit('meta:css.compiled'); winston.verbose('[cluster] Stylesheets propagated to worker %s', process.pid); break; diff --git a/loader.js b/loader.js index c79756f09e..96c78da4ee 100644 --- a/loader.js +++ b/loader.js @@ -90,8 +90,7 @@ Loader.addWorkerEvents = function(worker) { worker.send({ action: 'js-propagate', cache: Loader.js.cache, - map: Loader.js.map, - hash: Loader.js.hash + map: Loader.js.map }); } @@ -99,8 +98,7 @@ Loader.addWorkerEvents = function(worker) { worker.send({ action: 'css-propagate', cache: Loader.css.cache, - acpCache: Loader.css.acpCache, - hash: Loader.css.hash + acpCache: Loader.css.acpCache }); } @@ -117,25 +115,21 @@ Loader.addWorkerEvents = function(worker) { case 'js-propagate': Loader.js.cache = message.cache; Loader.js.map = message.map; - Loader.js.hash = message.hash; Loader.notifyWorkers({ action: 'js-propagate', cache: message.cache, - map: message.map, - hash: message.hash + map: message.map }, worker.pid); break; case 'css-propagate': Loader.css.cache = message.cache; Loader.css.acpCache = message.acpCache; - Loader.css.hash = message.hash; Loader.notifyWorkers({ action: 'css-propagate', cache: message.cache, - acpCache: message.acpCache, - hash: message.hash + acpCache: message.acpCache }, worker.pid); break; case 'templates:compiled': diff --git a/minifier.js b/minifier.js index 7289843b2f..621d61fa08 100644 --- a/minifier.js +++ b/minifier.js @@ -6,7 +6,7 @@ var uglifyjs = require('uglify-js'), fs = require('fs'), crypto = require('crypto'), utils = require('./public/src/utils'), - + Minifier = { js: {} }; @@ -47,17 +47,7 @@ function minifyScripts(scripts, callback) { var minified = uglifyjs.minify(scripts, { // outSourceMap: "nodebb.min.js.map", compress: false - }), - hasher = crypto.createHash('md5'), - hash; - - // Calculate js hash - hasher.update(minified.code, 'utf-8'); - hash = hasher.digest('hex'); - process.send({ - type: 'hash', - payload: hash.slice(0, 8) - }); + }); callback(minified.code/*, minified.map*/); } catch(err) { diff --git a/public/src/app.js b/public/src/app.js index a55dc9d2ab..5993ba9e69 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -146,19 +146,19 @@ app.cacheBuster = null; var modal = $(dialog.apply(this, arguments)[0]); translate(modal); return modal; - } + }; bootbox.prompt = function() { var modal = $(prompt.apply(this, arguments)[0]); translate(modal); return modal; - } + }; bootbox.confirm = function() { var modal = $(confirm.apply(this, arguments)[0]); translate(modal); return modal; - } + }; } function overrideTimeago() { @@ -563,14 +563,9 @@ app.cacheBuster = null; app.showEmailConfirmWarning(); socket.removeAllListeners('event:nodebb.ready'); - socket.on('event:nodebb.ready', function(cacheBusters) { - if ( - !app.cacheBusters || - app.cacheBusters.general !== cacheBusters.general || - app.cacheBusters.css !== cacheBusters.css || - app.cacheBusters.js !== cacheBusters.js - ) { - app.cacheBusters = cacheBusters; + socket.on('event:nodebb.ready', function(data) { + if (!app.cacheBusters || app.cacheBusters['cache-buster'] !== data['cache-buster']) { + app.cacheBusters = data; app.alert({ alert_id: 'forum_updated', diff --git a/src/meta/css.js b/src/meta/css.js index a93be08d09..f3257e624f 100644 --- a/src/meta/css.js +++ b/src/meta/css.js @@ -92,8 +92,7 @@ module.exports = function(Meta) { process.send({ action: 'css-propagate', cache: minified[0], - acpCache: minified[1], - hash: Meta.css.hash + acpCache: minified[1] }); } @@ -186,14 +185,6 @@ module.exports = function(Meta) { Meta.css[destination] = lessOutput.css; - if (destination === 'cache') { - // Calculate css buster - var hasher = crypto.createHash('md5'); - - hasher.update(lessOutput.css, 'utf-8'); - Meta.css.hash = hasher.digest('hex').slice(0, 8); - } - // Save the compiled CSS in public/ so things like nginx can serve it if (nconf.get('isPrimary') === 'true') { Meta.css.commitToFile(destination); diff --git a/src/meta/js.js b/src/meta/js.js index 21dc1c4cee..c703f5332e 100644 --- a/src/meta/js.js +++ b/src/meta/js.js @@ -18,7 +18,6 @@ module.exports = function(Meta) { Meta.js = { cache: '', map: '', - hash: +new Date(), scripts: { base: [ 'public/vendor/jquery/js/jquery.js', @@ -142,8 +141,7 @@ module.exports = function(Meta) { process.send({ action: 'js-propagate', cache: Meta.js.cache, - map: Meta.js.map, - hash: Meta.js.hash + map: Meta.js.map }); } @@ -161,9 +159,6 @@ module.exports = function(Meta) { Meta.js.map = message.sourceMap; onComplete(); break; - case 'hash': - Meta.js.hash = message.payload; - break; case 'error': winston.error('[meta/js] Could not compile client-side scripts! ' + message.payload.message); minifier.kill(); diff --git a/src/socket.io/meta.js b/src/socket.io/meta.js index 2b18044d33..abe8d461d0 100644 --- a/src/socket.io/meta.js +++ b/src/socket.io/meta.js @@ -29,9 +29,7 @@ SocketMeta.reconnected = function(socket, data, callback) { emitter.on('nodebb:ready', function() { websockets.server.sockets.emit('event:nodebb.ready', { - general: meta.config['cache-buster'], - css: meta.css.hash, - js: meta.js.hash + 'cache-buster': meta.config['cache-buster'] }); }); From 6334e1ae6c8aa5665c1bf858c5908b70d99cffe3 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 11 Sep 2015 16:20:11 -0400 Subject: [PATCH 037/117] updated theme minvers, and generating cache buster on reload too --- package.json | 6 +++--- src/meta.js | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 4fcde49607..144afd4a41 100644 --- a/package.json +++ b/package.json @@ -48,9 +48,9 @@ "nodebb-plugin-soundpack-default": "0.1.4", "nodebb-plugin-spam-be-gone": "0.4.2", "nodebb-rewards-essentials": "0.0.5", - "nodebb-theme-lavender": "1.0.54", - "nodebb-theme-persona": "2.1.28", - "nodebb-theme-vanilla": "3.1.8", + "nodebb-theme-lavender": "2.0.0", + "nodebb-theme-persona": "3.0.0", + "nodebb-theme-vanilla": "4.0.0", "nodebb-widget-essentials": "2.0.1", "npm": "^2.1.4", "passport": "^0.3.0", diff --git a/src/meta.js b/src/meta.js index a9b276c313..316d6349ce 100644 --- a/src/meta.js +++ b/src/meta.js @@ -10,7 +10,8 @@ var async = require('async'), groups = require('./groups'), emitter = require('./emitter'), pubsub = require('./pubsub'), - auth = require('./routes/authentication'); + auth = require('./routes/authentication'), + utils = require('../public/src/utils'); (function (Meta) { Meta.reloadRequired = false; @@ -63,6 +64,7 @@ var async = require('async'), async.apply(Meta.templates.compile), async.apply(auth.reloadRoutes), function(next) { + Meta.config['cache-buster'] = utils.generateUUID(); templates.flush(); next(); } From 765f0c11371408268b1342fc94dcf9f63b58733b Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 16:35:44 -0400 Subject: [PATCH 038/117] closes #3571 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 144afd4a41..9493dca3f5 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "mime": "^1.3.4", "minimist": "^1.1.1", "mkdirp": "~0.5.0", - "mmmagic": "^0.3.13", + "mmmagic": "^0.4.0", "morgan": "^1.3.2", "nconf": "~0.7.1", "nodebb-plugin-composer-default": "1.0.14", From 3ea8d092aa9d3c452e56c3c165cceee01f105006 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 16:46:07 -0400 Subject: [PATCH 039/117] check for defaultTheme on install --- src/install.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/install.js b/src/install.js index 1b2fcdc97f..7915f2e5bf 100644 --- a/src/install.js +++ b/src/install.js @@ -287,7 +287,7 @@ function enableDefaultTheme(next) { process.stdout.write('Enabling default theme: Persona\n'); meta.themes.set({ type: 'local', - id: 'nodebb-theme-persona' + id: nconf.get('defaultTheme') || 'nodebb-theme-persona' }, next); }); } From 459e51862a48eba6dce55c336259a87ff770ef9b Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 17:13:44 -0400 Subject: [PATCH 040/117] closes #3553 --- public/src/app.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/public/src/app.js b/public/src/app.js index 5993ba9e69..2d0b44e804 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -434,11 +434,15 @@ app.cacheBuster = null; searchFields = $("#search-fields"), searchInput = $('#search-fields input'); + $('#advanced-search').on('mousedown', function() { + ajaxify.go('/search'); + }); + $('#search-form').on('submit', dismissSearch); searchInput.on('blur', dismissSearch); function dismissSearch(){ - searchFields.hide(); + searchFields.addClass('hide'); searchButton.show(); } @@ -469,7 +473,7 @@ app.cacheBuster = null; }; app.prepareSearch = function() { - $("#search-fields").removeClass('hide').show(); + $("#search-fields").removeClass('hide'); $("#search-button").hide(); $('#search-fields input').focus(); }; From 95b709c8066b863ac8b0408572d86df5ff501a70 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 18:36:19 -0400 Subject: [PATCH 041/117] dont crash if data.responseJSON doesnt exist --- public/src/ajaxify.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index 4412d185b1..a2239dfbb2 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -110,13 +110,14 @@ $(document).ready(function() { textStatus = err.textStatus; if (data) { - data.responseJSON.config = config; var status = parseInt(data.status, 10); if (status === 403 || status === 404 || status === 500 || status === 502 || status === 503) { if (status === 502) { status = 500; } - + if (data.responseJSON) { + data.responseJSON.config = config; + } $('#footer, #content').removeClass('hide').addClass('ajaxifying'); return renderTemplate(url, status.toString(), data.responseJSON, callback); } else if (status === 401) { From 929091d4d3a8054059b504553ad31b7de75898bc Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 18:48:29 -0400 Subject: [PATCH 042/117] default theme --- src/install.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/install.js b/src/install.js index 7915f2e5bf..9fa6a1cba0 100644 --- a/src/install.js +++ b/src/install.js @@ -283,11 +283,11 @@ function enableDefaultTheme(next) { process.stdout.write('Previous theme detected, skipping enabling default theme\n'); return next(err); } - - process.stdout.write('Enabling default theme: Persona\n'); + var defaultTheme = nconf.get('defaultTheme') || 'nodebb-theme-persona'; + process.stdout.write('Enabling default theme: ' + defaultTheme + '\n'); meta.themes.set({ type: 'local', - id: nconf.get('defaultTheme') || 'nodebb-theme-persona' + id: defaultTheme }, next); }); } From a6c8cd50e97c5761fd07b19980ec46339840004a Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 19:04:00 -0400 Subject: [PATCH 043/117] fix group creation regression --- src/groups/create.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/groups/create.js b/src/groups/create.js index 836cfef035..3eeed9a893 100644 --- a/src/groups/create.js +++ b/src/groups/create.js @@ -80,5 +80,7 @@ module.exports = function(Groups) { if (name.indexOf('/') !== -1) { return callback(new Error('[[error:invalid-group-name]]')); } + + callback(); } }; From 9a12d677eebfceff6ae1682bf7b116e980b58a0c Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 19:23:15 -0400 Subject: [PATCH 044/117] up themes --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 9493dca3f5..c5be9f8bab 100644 --- a/package.json +++ b/package.json @@ -49,8 +49,8 @@ "nodebb-plugin-spam-be-gone": "0.4.2", "nodebb-rewards-essentials": "0.0.5", "nodebb-theme-lavender": "2.0.0", - "nodebb-theme-persona": "3.0.0", - "nodebb-theme-vanilla": "4.0.0", + "nodebb-theme-persona": "3.0.1", + "nodebb-theme-vanilla": "4.0.1", "nodebb-widget-essentials": "2.0.1", "npm": "^2.1.4", "passport": "^0.3.0", From 775e0532ebef27cd5710f10fb7792711cef095ea Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 19:35:02 -0400 Subject: [PATCH 045/117] fix search selector --- public/src/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/app.js b/public/src/app.js index 2d0b44e804..1bf68c6aca 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -434,7 +434,7 @@ app.cacheBuster = null; searchFields = $("#search-fields"), searchInput = $('#search-fields input'); - $('#advanced-search').on('mousedown', function() { + $('#search-form .advanced-search-link').on('mousedown', function() { ajaxify.go('/search'); }); From 498f12d4dda882325a6a426c37050edabf5a2bfd Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 19:37:42 -0400 Subject: [PATCH 046/117] up themes --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index c5be9f8bab..8b988be205 100644 --- a/package.json +++ b/package.json @@ -49,8 +49,8 @@ "nodebb-plugin-spam-be-gone": "0.4.2", "nodebb-rewards-essentials": "0.0.5", "nodebb-theme-lavender": "2.0.0", - "nodebb-theme-persona": "3.0.1", - "nodebb-theme-vanilla": "4.0.1", + "nodebb-theme-persona": "3.0.2", + "nodebb-theme-vanilla": "4.0.2", "nodebb-widget-essentials": "2.0.1", "npm": "^2.1.4", "passport": "^0.3.0", From dd5a8e8a88ca2f6c05abb8c3b481b69d7a11eba8 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 20:36:31 -0400 Subject: [PATCH 047/117] closes #3568 --- public/src/variables.js | 1 + src/middleware/middleware.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/public/src/variables.js b/public/src/variables.js index 52674df429..6b443caa5d 100644 --- a/public/src/variables.js +++ b/public/src/variables.js @@ -9,6 +9,7 @@ var dataEl = $('#content [ajaxify-data]'); if (dataEl.length) { ajaxify.data = JSON.parse(decodeURIComponent(dataEl.attr('ajaxify-data'))); + dataEl.remove(); } }; }(ajaxify || {})); diff --git a/src/middleware/middleware.js b/src/middleware/middleware.js index cc77ccd8db..c381acea20 100644 --- a/src/middleware/middleware.js +++ b/src/middleware/middleware.js @@ -320,7 +320,6 @@ middleware.processRender = function(req, res, next) { self.send(str); }; - options = options || {}; if ('function' === typeof options) { @@ -346,13 +345,14 @@ middleware.processRender = function(req, res, next) { return res.json(options); } + var ajaxifyData = encodeURIComponent(JSON.stringify(options)); render.call(self, template, options, function(err, str) { if (err) { winston.error(err); return fn(err); } - str = str + ''; + str = str + ''; str = (res.locals.postHeader ? res.locals.postHeader : '') + str + (res.locals.preFooter ? res.locals.preFooter : ''); if (res.locals.footer) { From 9361ff07729bb6524752079fb5b4feb173d7d458 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 22:50:09 -0400 Subject: [PATCH 048/117] closes #3587 --- public/src/ajaxify.js | 2 +- src/controllers/helpers.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index a2239dfbb2..d1d7ecb4be 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -124,7 +124,7 @@ $(document).ready(function() { app.alertError('[[global:please_log_in]]'); app.previousUrl = url; return ajaxify.go('login'); - } else if (status === 302) { + } else if (status === 302 || status === 308) { if (data.responseJSON.external) { window.location.href = data.responseJSON.external; } else if (typeof data.responseJSON === 'string') { diff --git a/src/controllers/helpers.js b/src/controllers/helpers.js index 5486bcf009..ce1938ca30 100644 --- a/src/controllers/helpers.js +++ b/src/controllers/helpers.js @@ -39,7 +39,7 @@ helpers.notAllowed = function(req, res, error) { helpers.redirect = function(res, url) { if (res.locals.isAPI) { - res.status(302).json(url); + res.status(308).json(url); } else { res.redirect(nconf.get('relative_path') + url); } From 734a45ae0364a186156c724089c019a3a95e0176 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 22:51:25 -0400 Subject: [PATCH 049/117] dont log error if its redirect --- src/routes/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/index.js b/src/routes/index.js index a59b50a5e8..bd5be4f429 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -208,12 +208,12 @@ function handleErrors(app, middleware) { return res.sendStatus(403); } - winston.error(req.path + '\n', err.stack); - if (parseInt(err.status, 10) === 302 && err.path) { return res.locals.isAPI ? res.status(302).json(err.path) : res.redirect(err.path); } + winston.error(req.path + '\n', err.stack); + res.status(err.status || 500); if (res.locals.isAPI) { From 6c40d2e42472485b81d7c2967a1040edda789193 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 23:14:03 -0400 Subject: [PATCH 050/117] #3475 --- src/controllers/users.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/controllers/users.js b/src/controllers/users.js index a03f457913..679a5328d1 100644 --- a/src/controllers/users.js +++ b/src/controllers/users.js @@ -50,7 +50,8 @@ usersController.getOnlineUsers = function(req, res, next) { users: results.users, anonymousUserCount: websockets.getOnlineAnonCount(), defaultGravatar: user.createGravatarURLFromEmail(''), - title: '[[pages:users/online]]' + title: '[[pages:users/online]]', + breadcrumbs: helpers.buildBreadcrumbs([{text: '[[pages:users/online]]'}]) }; render(req, res, userData, next); @@ -75,6 +76,12 @@ usersController.getUsers = function(set, start, stop, req, res, next) { 'users:reputation': '[[pages:users/sort-reputation]]', 'users:joindate': '[[pages:users/latest]]' }; + + var setToCrumbs = { + 'users:postcount': '[[users:top_posters]]', + 'users:reputation': '[[users:most_reputation]]', + 'users:joindate': '[[pages:users/latest]]' + }; usersController.getUsersAndCount(set, req.uid, start, stop, function(err, data) { if (err) { return next(err); @@ -85,7 +92,8 @@ usersController.getUsers = function(set, start, stop, req, res, next) { loadmore_display: data.count > (stop - start + 1) ? 'block' : 'hide', users: data.users, pagination: pagination.create(1, pageCount), - title: setToTitles[set] || '[[pages:users/latest]]' + title: setToTitles[set] || '[[pages:users/latest]]', + breadcrumbs: helpers.buildBreadcrumbs([{text: setToCrumbs[set]}]) }; userData['route_' + set] = true; render(req, res, userData, next); @@ -127,7 +135,8 @@ usersController.getUsersForSearch = function(req, res, next) { search_display: 'block', loadmore_display: 'hidden', users: data.users, - title: '[[pages:users/search]]' + title: '[[pages:users/search]]', + breadcrumbs: helpers.buildBreadcrumbs([{text: '[[pages:users/search]]'}]) }; render(req, res, userData, next); @@ -203,7 +212,7 @@ usersController.getMap = function(req, res, next) { } }); - res.render('usersMap', {rooms: data, title: '[[pages:users/map]]'}); + res.render('usersMap', {rooms: data, title: '[[pages:users/map]]', breadcrumbs: helpers.buildBreadcrumbs([{text: '[[pages:users/map]]'}])}); }); }; @@ -212,6 +221,7 @@ function render(req, res, data, next) { if (err) { return next(err); } + data.templateData.inviteOnly = meta.config.registrationType === 'invite-only'; res.render('users', data.templateData); }); From bf70f3764e558f76975c158fefadf2c8139e90e4 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 23:24:16 -0400 Subject: [PATCH 051/117] #3475 --- src/controllers/accounts.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/controllers/accounts.js b/src/controllers/accounts.js index d997c4b79a..c3537f578a 100644 --- a/src/controllers/accounts.js +++ b/src/controllers/accounts.js @@ -510,7 +510,8 @@ accountsController.getNotifications = function(req, res, next) { } res.render('notifications', { notifications: notifications, - title: '[[pages:notifications]]' + title: '[[pages:notifications]]', + breadcrumbs: helpers.buildBreadcrumbs([{text: '[[pages:notifications]]'}]) }); }); }; @@ -550,7 +551,8 @@ accountsController.getChats = function(req, res, callback) { nextStart: results.recentChats.nextStart, contacts: results.contacts, allowed: true, - title: '[[pages:chats]]' + title: '[[pages:chats]]', + breadcrumbs: helpers.buildBreadcrumbs([{text: '[[pages:chats]]'}]) }); } @@ -584,7 +586,8 @@ accountsController.getChats = function(req, res, callback) { meta: data.toUser, messages: data.messages, allowed: data.allowed, - title: '[[pages:chat, ' + data.toUser.username + ']]' + title: '[[pages:chat, ' + data.toUser.username + ']]', + breadcrumbs: helpers.buildBreadcrumbs([{text: '[[pages:chats]]', url: nconf.get('relative_path') + '/chats'}, {text: data.toUser.username}]) }); }); }); From cb9b51e8a5e050677783fd54da20358392e8adfd Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 11 Sep 2015 23:57:46 -0400 Subject: [PATCH 052/117] closes #3475 --- src/controllers/accounts.js | 19 +++++++++++++------ src/controllers/categories.js | 3 +++ src/controllers/groups.js | 9 +++++++++ src/controllers/popular.js | 5 ++++- src/controllers/recent.js | 4 +++- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/controllers/accounts.js b/src/controllers/accounts.js index c3537f578a..2aa1bfcfe5 100644 --- a/src/controllers/accounts.js +++ b/src/controllers/accounts.js @@ -175,9 +175,11 @@ accountsController.getAccount = function(req, res, next) { userData.posts = results.posts.posts.filter(function (p) { return p && parseInt(p.deleted, 10) !== 1; }); + userData.aboutme = results.aboutme; userData.nextStart = results.posts.nextStart; userData.isFollowing = results.isFollowing; + userData.breadcrumbs = helpers.buildBreadcrumbs([{text: userData.username}]); if (!userData.profileviews) { userData.profileviews = 1; @@ -224,25 +226,26 @@ function getFollow(tpl, name, req, res, callback) { userData.users = users; userData.nextStart = 50; userData.title = '[[pages:' + tpl + ', ' + userData.username + ']]'; + userData.breadcrumbs = helpers.buildBreadcrumbs([{text: userData.username, url: '/user/' + userData.userslug}, {text: '[[user:' + name + ']]'}]); res.render(tpl, userData); }); } accountsController.getFavourites = function(req, res, next) { - getFromUserSet('account/favourites', 'favourites', posts.getPostSummariesFromSet, 'posts', req, res, next); + getFromUserSet('account/favourites', 'favourites', '[[user:favourites]]', posts.getPostSummariesFromSet, 'posts', req, res, next); }; accountsController.getPosts = function(req, res, next) { - getFromUserSet('account/posts', 'posts', posts.getPostSummariesFromSet, 'posts', req, res, next); + getFromUserSet('account/posts', 'posts', '[[global:posts]]', posts.getPostSummariesFromSet, 'posts', req, res, next); }; accountsController.getWatchedTopics = function(req, res, next) { - getFromUserSet('account/watched', 'followed_tids', topics.getTopicsFromSet, 'topics', req, res, next); + getFromUserSet('account/watched', 'followed_tids', '[[user:watched]]',topics.getTopicsFromSet, 'topics', req, res, next); }; accountsController.getTopics = function(req, res, next) { - getFromUserSet('account/topics', 'topics', topics.getTopicsFromSet, 'topics', req, res, next); + getFromUserSet('account/topics', 'topics', '[[global:topics]]', topics.getTopicsFromSet, 'topics', req, res, next); }; accountsController.getGroups = function(req, res, next) { @@ -259,12 +262,13 @@ accountsController.getGroups = function(req, res, next) { userData.groups = groupsData[0]; userData.groups.forEach(groups.escapeGroupData); userData.title = '[[pages:account/groups, ' + userData.username + ']]'; + userData.breadcrumbs = helpers.buildBreadcrumbs([{text: userData.username, url: '/user/' + userData.userslug}, {text: '[[global:header.groups]]'}]); res.render('account/groups', userData); }); }); }; -function getFromUserSet(tpl, set, method, type, req, res, next) { +function getFromUserSet(tpl, set, crumb, method, type, req, res, next) { async.parallel({ settings: function(next) { user.getSettings(req.uid, next); @@ -310,6 +314,7 @@ function getFromUserSet(tpl, set, method, type, req, res, next) { userData.pagination = pagination.create(page, pageCount); userData.title = '[[pages:' + tpl + ', ' + userData.username + ']]'; + userData.breadcrumbs = helpers.buildBreadcrumbs([{text: userData.username, url: '/user/' + userData.userslug}, {text: crumb}]); res.render(tpl, userData); }); @@ -371,6 +376,7 @@ accountsController.accountEdit = function(req, res, callback) { userData.hasPassword = !!password; userData.title = '[[pages:account/edit, ' + userData.username + ']]'; + userData.breadcrumbs = helpers.buildBreadcrumbs([{text: userData.username, url: '/user/' + userData.userslug}, {text: '[[user:edit]]'}]); res.render('account/edit', userData); }); }; @@ -457,6 +463,7 @@ accountsController.accountSettings = function(req, res, callback) { userData.disableCustomUserSkins = parseInt(meta.config.disableCustomUserSkins, 10) === 1; userData.title = '[[pages:account/settings]]'; + userData.breadcrumbs = helpers.buildBreadcrumbs([{text: userData.username, url: '/user/' + userData.userslug}, {text: '[[user:settings]]'}]); res.render('account/settings', userData); }); @@ -587,7 +594,7 @@ accountsController.getChats = function(req, res, callback) { messages: data.messages, allowed: data.allowed, title: '[[pages:chat, ' + data.toUser.username + ']]', - breadcrumbs: helpers.buildBreadcrumbs([{text: '[[pages:chats]]', url: nconf.get('relative_path') + '/chats'}, {text: data.toUser.username}]) + breadcrumbs: helpers.buildBreadcrumbs([{text: '[[pages:chats]]', url: '/chats'}, {text: data.toUser.username}]) }); }); }); diff --git a/src/controllers/categories.js b/src/controllers/categories.js index b770598311..438e970c99 100644 --- a/src/controllers/categories.js +++ b/src/controllers/categories.js @@ -66,6 +66,9 @@ categoriesController.list = function(req, res, next) { } data.title = '[[pages:categories]]'; + if (req.path.startsWith('/api/categories') || req.path.startsWith('/categories')) { + data.breadcrumbs = helpers.buildBreadcrumbs([{text: data.title}]); + } plugins.fireHook('filter:categories.build', {req: req, res: res, templateData: data}, function(err, data) { if (err) { diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 2d4f97bc89..72150cdf79 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -17,6 +17,7 @@ groupsController.list = function(req, res, next) { return next(err); } data.title = '[[pages:groups]]'; + data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[pages:groups]]'}]); res.render('groups/list', data); }); }; @@ -90,6 +91,7 @@ groupsController.details = function(req, res, callback) { } results.title = '[[pages:group, ' + results.group.displayName + ']]'; + results.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[pages:groups]]', url: '/groups' }, {text: results.group.slug}]); res.render('groups/details', results); }); }); @@ -108,10 +110,17 @@ groupsController.members = function(req, res, next) { return next(err); } + var breadcrumbs = helpers.buildBreadcrumbs([ + {text: '[[pages:groups]]', url: '/groups' }, + {text: req.params.slug, url: '/groups/' + req.params.slug}, + {text: '[[groups:details.members]]'} + ]); + res.render('groups/members', { users: users, nextStart: 50, loadmore_display: users.length > 50 ? 'block' : 'hide', + breadcrumbs: breadcrumbs }); }); }; diff --git a/src/controllers/popular.js b/src/controllers/popular.js index 9a39af45f6..6f80254f47 100644 --- a/src/controllers/popular.js +++ b/src/controllers/popular.js @@ -36,10 +36,13 @@ popularController.get = function(req, res, next) { topics: topics, 'feeds:disableRSS': parseInt(meta.config['feeds:disableRSS'], 10) === 1, rssFeedUrl: nconf.get('relative_path') + '/popular/' + (req.params.term || 'daily') + '.rss', - breadcrumbs: helpers.buildBreadcrumbs([{text: '[[global:header.popular]]'}]), title: '[[pages:popular-' + term + ']]' }; + if (req.path.startsWith('/api/popular') || req.path.startsWith('/popular')) { + data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[global:header.popular]]'}]); + } + if (!req.uid) { anonCache[term] = data; lastUpdateTime = Date.now(); diff --git a/src/controllers/recent.js b/src/controllers/recent.js index a7f0cfca8a..93cd2a9d09 100644 --- a/src/controllers/recent.js +++ b/src/controllers/recent.js @@ -20,8 +20,10 @@ recentController.get = function(req, res, next) { data['feeds:disableRSS'] = parseInt(meta.config['feeds:disableRSS'], 10) === 1; data.rssFeedUrl = nconf.get('relative_path') + '/recent.rss'; - data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[recent:title]]'}]); data.title = '[[pages:recent]]'; + if (req.path.startsWith('/api/recent') || req.path.startsWith('/recent')) { + data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[recent:title]]'}]); + } plugins.fireHook('filter:recent.build', {req: req, res: res, templateData: data}, function(err, data) { if (err) { From 7373f3bbce25ac1137a606fe131760c02d9eb4dd Mon Sep 17 00:00:00 2001 From: barisusakli Date: Sat, 12 Sep 2015 00:02:21 -0400 Subject: [PATCH 053/117] up themes --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 8b988be205..ed41a7bdf7 100644 --- a/package.json +++ b/package.json @@ -49,8 +49,8 @@ "nodebb-plugin-spam-be-gone": "0.4.2", "nodebb-rewards-essentials": "0.0.5", "nodebb-theme-lavender": "2.0.0", - "nodebb-theme-persona": "3.0.2", - "nodebb-theme-vanilla": "4.0.2", + "nodebb-theme-persona": "3.0.3", + "nodebb-theme-vanilla": "4.0.3", "nodebb-widget-essentials": "2.0.1", "npm": "^2.1.4", "passport": "^0.3.0", From 240a619a7268179b0d070058d7b4d8ea546008d0 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Sat, 12 Sep 2015 00:13:25 -0400 Subject: [PATCH 054/117] pretty group breadcrumbs --- src/controllers/groups.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 72150cdf79..e7602f85e9 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -2,6 +2,7 @@ var async = require('async'), nconf = require('nconf'), + validator = require('validator'), db = require('../database'), meta = require('../meta'), groups = require('../groups'), @@ -91,18 +92,20 @@ groupsController.details = function(req, res, callback) { } results.title = '[[pages:group, ' + results.group.displayName + ']]'; - results.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[pages:groups]]', url: '/groups' }, {text: results.group.slug}]); + results.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[pages:groups]]', url: '/groups' }, {text: results.group.displayName}]); res.render('groups/details', results); }); }); }; groupsController.members = function(req, res, next) { + var groupName; async.waterfall([ function(next) { groups.getGroupNameByGroupSlug(req.params.slug, next); }, - function(groupName, next) { + function(_groupName, next) { + groupName = _groupName; user.getUsersFromSet('group:' + groupName + ':members', req.uid, 0, 49, next); }, ], function(err, users) { @@ -112,7 +115,7 @@ groupsController.members = function(req, res, next) { var breadcrumbs = helpers.buildBreadcrumbs([ {text: '[[pages:groups]]', url: '/groups' }, - {text: req.params.slug, url: '/groups/' + req.params.slug}, + {text: validator.escape(groupName), url: '/groups/' + req.params.slug}, {text: '[[groups:details.members]]'} ]); From 915a992448e9f533977d4d99e243789e286f7244 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Sat, 12 Sep 2015 14:42:08 -0400 Subject: [PATCH 055/117] closes #3592 --- src/groups/ownership.js | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/groups/ownership.js b/src/groups/ownership.js index 4af6126aef..aa085f8213 100644 --- a/src/groups/ownership.js +++ b/src/groups/ownership.js @@ -1,6 +1,8 @@ 'use strict'; -var db = require('../database'); +var async = require('async'), + db = require('../database'), + plugins = require('../plugins'); module.exports = function(Groups) { @@ -23,22 +25,35 @@ module.exports = function(Groups) { Groups.ownership.grant = function(toUid, groupName, callback) { // Note: No ownership checking is done here on purpose! - db.setAdd('group:' + groupName + ':owners', toUid, callback); + async.waterfall([ + function(next) { + db.setAdd('group:' + groupName + ':owners', toUid, next); + }, + function(next) { + plugins.fireHook('action:group.grantOwnership', {uid: toUid, groupName: groupName}); + next(); + } + ], callback); }; Groups.ownership.rescind = function(toUid, groupName, callback) { // Note: No ownership checking is done here on purpose! // If the owners set only contains one member, error out! - db.setCount('group:' + groupName + ':owners', function(err, numOwners) { - if (err) { - return callback(err); + async.waterfall([ + function (next) { + db.setCount('group:' + groupName + ':owners', next); + }, + function (numOwners, next) { + if (numOwners <= 1) { + return next(new Error('[[error:group-needs-owner]]')); + } + db.setRemove('group:' + groupName + ':owners', toUid, next); + }, + function (next) { + plugins.fireHook('action:group.rescindOwnership', {uid: toUid, groupName: groupName}); + next(); } - if (numOwners <= 1) { - return callback(new Error('[[error:group-needs-owner]]')); - } - - db.setRemove('group:' + groupName + ':owners', toUid, callback); - }); + ], callback); }; }; From e3ca91e325a3f9bb6c85fa72d1d17369b8ccb937 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Sat, 12 Sep 2015 15:14:49 -0400 Subject: [PATCH 056/117] #3591 fixes item 1 --- public/src/admin/extend/plugins.js | 23 +++++++++++++++-------- src/plugins/install.js | 4 ++-- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/public/src/admin/extend/plugins.js b/public/src/admin/extend/plugins.js index 5944f98f91..c886a56c75 100644 --- a/public/src/admin/extend/plugins.js +++ b/public/src/admin/extend/plugins.js @@ -38,6 +38,8 @@ define('admin/extend/plugins', function() { }); pluginsList.on('click', 'button[data-action="toggleInstall"]', function() { + var btn = $(this); + btn.attr('disabled', true); pluginID = $(this).parents('li').attr('data-plugin-id'); if ($(this).attr('data-installed') === '1') { @@ -49,6 +51,8 @@ define('admin/extend/plugins', function() { bootbox.confirm('

NodeBB could not reach the package manager, proceed with installation of latest version?

Server returned (' + err.status + '): ' + err.responseText + '
', function(confirm) { if (confirm) { Plugins.toggleInstall(pluginID, 'latest'); + } else { + btn.removeAttr('disabled'); } }); return; @@ -58,9 +62,15 @@ define('admin/extend/plugins', function() { if (payload.version !== 'latest') { Plugins.toggleInstall(pluginID, payload.version); } else if (payload.version === 'latest') { - confirmInstall(pluginID, function() { - Plugins.toggleInstall(pluginID, 'latest'); + confirmInstall(pluginID, function(confirm) { + if (confirm) { + Plugins.toggleInstall(pluginID, 'latest'); + } else { + btn.removeAttr('disabled'); + } }); + } else { + btn.removeAttr('disabled'); } }); }); @@ -137,9 +147,7 @@ define('admin/extend/plugins', function() { '

In the event that NodeBB cannot boot properly:

' + '
$ ./nodebb reset plugin="' + pluginID + '"
' + '

Continue installation of latest version of this plugin?

', function(confirm) { - if (confirm) { - callback(); - } + callback(confirm); }); } @@ -176,15 +184,14 @@ define('admin/extend/plugins', function() { Plugins.toggleInstall = function(pluginID, version, callback) { var btn = $('li[data-plugin-id="' + pluginID + '"] button[data-action="toggleInstall"]'); var activateBtn = btn.siblings('[data-action="toggleActive"]'); - btn.html(btn.html() + 'ing') - .attr('disabled', true) - .find('i').attr('class', 'fa fa-refresh fa-spin'); + btn.find('i').attr('class', 'fa fa-refresh fa-spin'); socket.emit('admin.plugins.toggleInstall', { id: pluginID, version: version }, function(err, pluginData) { if (err) { + btn.removeAttr('disabled'); return app.alertError(err.message); } diff --git a/src/plugins/install.js b/src/plugins/install.js index 693e90c866..cdc8743b69 100644 --- a/src/plugins/install.js +++ b/src/plugins/install.js @@ -44,8 +44,8 @@ module.exports = function(Plugins) { if (err) { return next(err); } - db.sortedSetAdd('plugins:active', count, id, next); - }); + db.sortedSetAdd('plugins:active', count, id, next); + }); } }, function(next) { From d998fe2dd55b191adaa7362079c842933d87c1ae Mon Sep 17 00:00:00 2001 From: yariplus Date: Sat, 12 Sep 2015 17:08:12 -0400 Subject: [PATCH 057/117] Add Custom and Category homepages. --- public/src/admin/general/homepage.js | 23 +++++++++++ src/controllers/admin.js | 62 +++++++++++++++++++++------- src/controllers/index.js | 6 +-- src/views/admin/general/homepage.tpl | 5 +++ 4 files changed, 79 insertions(+), 17 deletions(-) create mode 100644 public/src/admin/general/homepage.js diff --git a/public/src/admin/general/homepage.js b/public/src/admin/general/homepage.js new file mode 100644 index 0000000000..96a6aac0eb --- /dev/null +++ b/public/src/admin/general/homepage.js @@ -0,0 +1,23 @@ +"use strict"; +/*global define*/ + +define('admin/general/homepage', ['admin/settings'], function(Settings) { + + function toggleCustomRoute() { + if ($('[data-field="homePageRoute"]').val()) { + $('#homePageCustom').hide(); + }else{ + $('#homePageCustom').show(); + } + } + + var Homepage = {}; + + Homepage.init = function() { + $('[data-field="homePageRoute"]').on('change', toggleCustomRoute); + + toggleCustomRoute(); + }; + + return Homepage; +}); diff --git a/src/controllers/admin.js b/src/controllers/admin.js index 776b6e9362..24927574b4 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -348,21 +348,55 @@ adminController.navigation.get = function(req, res, next) { }; adminController.homepage.get = function(req, res, next) { - plugins.fireHook('filter:homepage.get', {routes: [ - { - route: 'categories', - name: 'Categories' - }, - { - route: 'recent', - name: 'Recent' - }, - { - route: 'popular', - name: 'Popular' + async.parallel({ + categoryData: function(next) { + async.waterfall([ + function(next) { + db.getSortedSetRange('cid:0:children', 0, -1, next); + }, + function(cids, next) { + privileges.categories.filterCids('find', cids, 0, next); + }, + function(cids, next) { + categories.getMultipleCategoryFields(cids, ['name', 'slug'], next); + }, + function(categoryData, next) { + async.map(categoryData, function(category, next) { + var route = 'category/' + category.slug, + hook = 'action:homepage.get:' + route; + + next(null, { + route: route, + name: 'Category: ' + category.name + }); + }, next); + } + ], next); } - ]}, function(err, data) { - res.render('admin/general/homepage', data); + }, function(err, results) { + if (err || !results || !results.categoryData) results = {categoryData:[]}; + + plugins.fireHook('filter:homepage.get', {routes: [ + { + route: 'categories', + name: 'Categories' + }, + { + route: 'recent', + name: 'Recent' + }, + { + route: 'popular', + name: 'Popular' + } + ].concat(results.categoryData)}, function(err, data) { + data.routes.push({ + route: '', + name: 'Custom' + }); + + res.render('admin/general/homepage', data); + }); }); }; diff --git a/src/controllers/index.js b/src/controllers/index.js index 53ae658937..d4d321657b 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -33,20 +33,20 @@ var Controllers = { Controllers.home = function(req, res, next) { - var route = meta.config.homePageRoute || 'categories', + var route = meta.config.homePageRoute || meta.config.homePageCustom || 'categories', hook = 'action:homepage.get:' + route; if (plugins.hasListeners(hook)) { plugins.fireHook(hook, {req: req, res: res, next: next}); } else { - if (route === 'categories') { + if (route === 'categories' || route === '/') { Controllers.categories.list(req, res, next); } else if (route === 'recent') { Controllers.recent.get(req, res, next); } else if (route === 'popular') { Controllers.popular.get(req, res, next); } else { - next(); + res.redirect(route); } } }; diff --git a/src/views/admin/general/homepage.tpl b/src/views/admin/general/homepage.tpl index 812b31b077..5148d79da9 100644 --- a/src/views/admin/general/homepage.tpl +++ b/src/views/admin/general/homepage.tpl @@ -12,6 +12,11 @@ +
+ From beb2b38cb76128be25decba00077c7b3a12897f0 Mon Sep 17 00:00:00 2001 From: yariplus Date: Sat, 12 Sep 2015 18:08:41 -0400 Subject: [PATCH 058/117] mistake --- src/controllers/admin.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/controllers/admin.js b/src/controllers/admin.js index 24927574b4..f40ce3c84c 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -362,8 +362,7 @@ adminController.homepage.get = function(req, res, next) { }, function(categoryData, next) { async.map(categoryData, function(category, next) { - var route = 'category/' + category.slug, - hook = 'action:homepage.get:' + route; + var route = 'category/' + category.slug; next(null, { route: route, From a469ca2972f76c813ee5d6c1bee9e4c196a79945 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Sat, 12 Sep 2015 19:21:01 -0400 Subject: [PATCH 059/117] notification id change --- src/topics/follow.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/topics/follow.js b/src/topics/follow.js index 2dd3e434f4..1b0ce4d3dd 100644 --- a/src/topics/follow.js +++ b/src/topics/follow.js @@ -133,13 +133,14 @@ module.exports = function(Topics) { bodyShort: '[[notifications:user_posted_to, ' + postData.user.username + ', ' + title + ']]', bodyLong: postData.content, pid: postData.pid, - nid: 'tid:' + postData.topic.tid + ':pid:' + postData.pid + ':uid:' + exceptUid, + nid: 'new_post:tid:' + postData.topic.tid + ':pid:' + postData.pid + ':uid:' + exceptUid, tid: postData.topic.tid, from: exceptUid }, function(err, notification) { if (err) { return next(err); } + if (notification) { notifications.push(notification, followers); } From 183528f4c9cbaaa4cd1cbc57748ecf6ea2e57ae1 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Sat, 12 Sep 2015 19:22:57 -0400 Subject: [PATCH 060/117] up mentions --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ed41a7bdf7..e7d1b269b9 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "nodebb-plugin-dbsearch": "0.2.16", "nodebb-plugin-emoji-extended": "0.4.12", "nodebb-plugin-markdown": "4.0.5", - "nodebb-plugin-mentions": "1.0.1", + "nodebb-plugin-mentions": "1.0.2", "nodebb-plugin-soundpack-default": "0.1.4", "nodebb-plugin-spam-be-gone": "0.4.2", "nodebb-rewards-essentials": "0.0.5", From 0c1c42d36223b4dcaf47387386afce976f77e777 Mon Sep 17 00:00:00 2001 From: yariplus Date: Sat, 12 Sep 2015 19:41:19 -0400 Subject: [PATCH 061/117] parallel not needed --- src/controllers/admin.js | 46 ++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/src/controllers/admin.js b/src/controllers/admin.js index f40ce3c84c..7d1e55d8c2 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -348,32 +348,28 @@ adminController.navigation.get = function(req, res, next) { }; adminController.homepage.get = function(req, res, next) { - async.parallel({ - categoryData: function(next) { - async.waterfall([ - function(next) { - db.getSortedSetRange('cid:0:children', 0, -1, next); - }, - function(cids, next) { - privileges.categories.filterCids('find', cids, 0, next); - }, - function(cids, next) { - categories.getMultipleCategoryFields(cids, ['name', 'slug'], next); - }, - function(categoryData, next) { - async.map(categoryData, function(category, next) { - var route = 'category/' + category.slug; + async.waterfall([ + function(next) { + db.getSortedSetRange('cid:0:children', 0, -1, next); + }, + function(cids, next) { + privileges.categories.filterCids('find', cids, 0, next); + }, + function(cids, next) { + categories.getMultipleCategoryFields(cids, ['name', 'slug'], next); + }, + function(categoryData, next) { + async.map(categoryData, function(category, next) { + var route = 'category/' + category.slug; - next(null, { - route: route, - name: 'Category: ' + category.name - }); - }, next); - } - ], next); + next(null, { + route: route, + name: 'Category: ' + category.name + }); + }, next); } - }, function(err, results) { - if (err || !results || !results.categoryData) results = {categoryData:[]}; + ], function(err, categoryData) { + if (err || !categoryData) categoryData = []; plugins.fireHook('filter:homepage.get', {routes: [ { @@ -388,7 +384,7 @@ adminController.homepage.get = function(req, res, next) { route: 'popular', name: 'Popular' } - ].concat(results.categoryData)}, function(err, data) { + ].concat(categoryData)}, function(err, data) { data.routes.push({ route: '', name: 'Custom' From 08e4f14971284d1a1b9fe0b84ed7234d44b44e68 Mon Sep 17 00:00:00 2001 From: yariplus Date: Sat, 12 Sep 2015 20:51:49 -0400 Subject: [PATCH 062/117] fix unneeded async --- src/controllers/admin.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/controllers/admin.js b/src/controllers/admin.js index 7d1e55d8c2..07c7f9f1da 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -359,14 +359,13 @@ adminController.homepage.get = function(req, res, next) { categories.getMultipleCategoryFields(cids, ['name', 'slug'], next); }, function(categoryData, next) { - async.map(categoryData, function(category, next) { - var route = 'category/' + category.slug; - - next(null, { - route: route, + categoryData = categoryData.map(function(category) { + return { + route: 'category/' + category.slug, name: 'Category: ' + category.name - }); - }, next); + }; + }); + next(null, categoryData); } ], function(err, categoryData) { if (err || !categoryData) categoryData = []; From b4bcfb83a7a0777d37ce3a99240f8bd4a47e9bc9 Mon Sep 17 00:00:00 2001 From: Mega Date: Sun, 13 Sep 2015 09:26:34 +0300 Subject: [PATCH 063/117] Fix: When a user changes his online status, all [component="user/status"] elements on a page are changing. The same bug appears in: /user/* /users/* /topic/* /chats/* Cold load: http://i.imgur.com/OL2njmf.jpg After change my status: http://i.imgur.com/J6I8lOC.jpg --- public/src/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/app.js b/public/src/app.js index 1bf68c6aca..282e76718f 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -485,7 +485,7 @@ app.cacheBuster = null; if(err) { return app.alertError(err.message); } - $('[component="user/status"]') + $('[data-uid="' + app.user.uid + '"] [component="user/status"], [component="header/profilelink"] [component="user/status"]') .removeClass('away online dnd offline') .addClass(status); From b383c17df2969358cde67907992f1dd50503771a Mon Sep 17 00:00:00 2001 From: Mega Date: Sun, 13 Sep 2015 11:38:32 +0300 Subject: [PATCH 064/117] Resolve #3599 --- public/src/client/account/header.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/client/account/header.js b/public/src/client/account/header.js index 511f3be5a9..226f6ac3de 100644 --- a/public/src/client/account/header.js +++ b/public/src/client/account/header.js @@ -19,7 +19,7 @@ define('forum/account/header', function() { $('.account-sub-links li').removeClass('active').each(function() { var href = $(this).find('a').attr('href'); - if (href === window.location.pathname) { + if (decodeURIComponent(href) === decodeURIComponent(window.location.pathname)) { $(this).addClass('active'); return false; } From 896aadada7aaa1310f1452581e610a1149f682c3 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Sun, 13 Sep 2015 15:14:29 -0400 Subject: [PATCH 065/117] closes #3597 --- public/src/client/topic/posts.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/public/src/client/topic/posts.js b/public/src/client/topic/posts.js index 5875af699a..dc9825f1dc 100644 --- a/public/src/client/topic/posts.js +++ b/public/src/client/topic/posts.js @@ -18,6 +18,10 @@ define('forum/topic/posts', [ return; } + if (!data || !data.posts || !data.posts.length) { + return; + } + if (config.usePagination) { return onNewPostPagination(data); } @@ -32,13 +36,20 @@ define('forum/topic/posts', [ if (html) { html.addClass('new'); } - navigator.scrollBottom(data.posts[0].index); + scrollToPostIfSelf(data.posts[0]); }); }; + function scrollToPostIfSelf(post) { + var isSelfPost = parseInt(post.uid, 10) === parseInt(app.user.uid, 10); + if (isSelfPost) { + navigator.scrollBottom(post.index); + } + } + function onNewPostPagination(data) { function scrollToPost() { - navigator.scrollBottom(data.posts[0].index); + scrollToPostIfSelf(data.posts[0]); } var posts = data.posts; From ad685a01473994e1d185abe00693bd40742ced2b Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 14 Sep 2015 13:19:21 -0400 Subject: [PATCH 066/117] fix function in loops --- public/src/modules/chat.js | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index 2d364b0f8f..7555a380e5 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -152,8 +152,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra return; } - for(var x = 0; x') .attr('data-uid', userObj.uid) .html(''+ @@ -162,16 +161,15 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra userObj.username + '') .appendTo(chatsListEl); - (function(userObj) { - dropdownEl.click(function() { - if (!ajaxify.currentPage.match(/^chats\//)) { - app.openChat(userObj.username, userObj.uid); - } else { - ajaxify.go('chats/' + utils.slugify(userObj.username)); - } - }); - })(userObj); - } + + dropdownEl.click(function() { + if (!ajaxify.currentPage.match(/^chats\//)) { + app.openChat(userObj.username, userObj.uid); + } else { + ajaxify.go('chats/' + utils.slugify(userObj.username)); + } + }); + }); }); }; @@ -490,8 +488,11 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra module.sync = function() { socket.emit('modules.chats.sync', function(err, users) { - for(var x=0,numUsers=users.length,user;x Date: Mon, 14 Sep 2015 13:36:02 -0400 Subject: [PATCH 067/117] dont download images to local, pass to upload plugin --- src/user/picture.js | 47 +++++++++++++++------------------------------ 1 file changed, 15 insertions(+), 32 deletions(-) diff --git a/src/user/picture.js b/src/user/picture.js index 68b3d90431..2a934bfc7e 100644 --- a/src/user/picture.js +++ b/src/user/picture.js @@ -90,28 +90,10 @@ module.exports = function(User) { }; User.uploadFromUrl = function(uid, url, callback) { - var extension = url.substring(url.lastIndexOf('.') + 1); - if (['png', 'jpeg', 'jpg', 'gif'].indexOf(extension) == -1) { - return callback('[[error:invalid-image-extension]]'); + if (!plugins.hasListeners('filter:uploadImage')) { + return callback(new Error('[[error:no-plugin]]')); } - var filename = 'uid_' + uid + '_tmp-image.' + extension; - downloadFromUrl(url, filename, function(err, downloadedImage) { - if (err) { - return callback(err); - } - User.uploadPicture(uid, downloadedImage, function(err, image) { - fs.unlink(filename, function(err) { - if (err) { - winston.error(err); - } - }); - callback(err, image); - }); - }); - }; - - function downloadFromUrl(url, filename, callback) { request.head(url, function(err, res, body) { if (err) { return callback(err); @@ -121,21 +103,22 @@ module.exports = function(User) { var type = res.headers['content-type']; var extension = mime.extension(type); + if (['png', 'jpeg', 'jpg', 'gif'].indexOf(extension) === -1) { + return callback(new Error('[[error:invalid-image-extension]]')); + } + if (size > uploadSize * 1024) { return callback(new Error('[[error:file-too-big, ' + uploadSize + ']]')); } - request.get(url) - .on('error', function(err) { - winston.error(err); - }) - .pipe(fs.createWriteStream(filename)) - .on('close', function(err) { - if (err) { - return callback(err); - } - callback(null, {path: filename, size: size, type: type, name: filename + '.' + extension}); - }); + var picture = {url: url, name: ''}; + plugins.fireHook('filter:uploadImage', {image: picture, uid: uid}, function(err, image) { + if (err) { + return callback(err); + } + User.setUserFields(uid, {uploadedpicture: image.url, picture: image.url}); + callback(null, image); + }); }); - } + }; }; \ No newline at end of file From 2d0224d42a184c87a6e5d93e8dca663fee9b9af6 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 14 Sep 2015 14:51:53 -0400 Subject: [PATCH 068/117] closes #3588 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e7d1b269b9..75c47e12a2 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "nodebb-plugin-spam-be-gone": "0.4.2", "nodebb-rewards-essentials": "0.0.5", "nodebb-theme-lavender": "2.0.0", - "nodebb-theme-persona": "3.0.3", + "nodebb-theme-persona": "3.0.4", "nodebb-theme-vanilla": "4.0.3", "nodebb-widget-essentials": "2.0.1", "npm": "^2.1.4", From ff83305c6a56821dc76f1ca9220c8142c8b18827 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 14 Sep 2015 15:01:35 -0400 Subject: [PATCH 069/117] closes #3593 --- src/messaging.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/messaging.js b/src/messaging.js index ab27543f96..72346d52c8 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -255,20 +255,16 @@ var db = require('./database'), user.getMultipleUserFields(uids, ['uid', 'username', 'picture', 'status'] , next); }, teasers: function(next) { - var teasers = []; - async.each(uids, function(fromuid, next) { + async.map(uids, function(fromuid, next) { Messaging.getMessages({ fromuid: fromuid, touid: uid, isNew: false, count: 1 }, function(err, teaser) { - teasers[uids.indexOf(fromuid)] = teaser[0]; - next(err); + next(err, teaser[0]); }); - }, function(err) { - next(err, teasers); - }); + }, next); } }, function(err, results) { if (err) { From 64f0d338638c628fb40786a53e3a46ae154add4a Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 14 Sep 2015 15:01:57 -0400 Subject: [PATCH 070/117] up'd persona --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 75c47e12a2..0230332f6f 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "nodebb-plugin-spam-be-gone": "0.4.2", "nodebb-rewards-essentials": "0.0.5", "nodebb-theme-lavender": "2.0.0", - "nodebb-theme-persona": "3.0.4", + "nodebb-theme-persona": "3.0.5", "nodebb-theme-vanilla": "4.0.3", "nodebb-widget-essentials": "2.0.1", "npm": "^2.1.4", From 5ff2eccbd202c64da5e3e85528488aa6a7ff287d Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 14 Sep 2015 15:10:25 -0400 Subject: [PATCH 071/117] #3593 --- src/messaging.js | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/messaging.js b/src/messaging.js index 72346d52c8..81a0a5fe4c 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -274,6 +274,9 @@ var db = require('./database'), results.users.forEach(function(user, index) { if (user && !parseInt(user.uid, 10)) { Messaging.markRead(uid, uids[index]); + user.unread = results.unread[index]; + user.status = sockets.isUserOnline(user.uid) ? user.status : 'offline'; + user.teaser = results.teasers[index]; } }); @@ -281,18 +284,6 @@ var db = require('./database'), return user && parseInt(user.uid, 10); }); - if (!results.users.length) { - return callback(null, {users: [], nextStart: stop + 1}); - } - - results.users.forEach(function(user, index) { - if (user) { - user.unread = results.unread[index]; - user.status = sockets.isUserOnline(user.uid) ? user.status : 'offline'; - user.teaser = results.teasers[index]; - } - }); - callback(null, {users: results.users, nextStart: stop + 1}); }); }); From 2516be620bb6546caa2d3c2c4656e9a33f227eef Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 14 Sep 2015 15:10:10 -0400 Subject: [PATCH 072/117] Groups.getGroupsAndMembers --- src/groups.js | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/groups.js b/src/groups.js index 24ee5a9fd8..5405a9b115 100644 --- a/src/groups.js +++ b/src/groups.js @@ -80,28 +80,7 @@ var async = require('async'), }); } - async.parallel({ - groups: function(next) { - Groups.getGroupsData(groupNames, next); - }, - members: function(next) { - Groups.getMemberUsers(groupNames, 0, 3, next); - } - }, function (err, data) { - if (err) { - return callback(err); - } - data.groups.forEach(function(group, index) { - if (!group) { - return; - } - Groups.escapeGroupData(group); - group.members = data.members[index] || []; - group.truncated = group.memberCount > data.members.length; - }); - - callback(null, data.groups); - }); + Groups.getGroupsAndMembers(groupNames, callback); } }; @@ -109,6 +88,31 @@ var async = require('async'), db.getSortedSetRevRange(set, start, stop, callback); }; + Groups.getGroupsAndMembers = function(groupNames, callback) { + async.parallel({ + groups: function(next) { + Groups.getGroupsData(groupNames, next); + }, + members: function(next) { + Groups.getMemberUsers(groupNames, 0, 3, next); + } + }, function (err, data) { + if (err) { + return callback(err); + } + data.groups.forEach(function(group, index) { + if (!group) { + return; + } + Groups.escapeGroupData(group); + group.members = data.members[index] || []; + group.truncated = group.memberCount > data.members.length; + }); + + callback(null, data.groups); + }); + }; + Groups.get = function(groupName, options, callback) { if (!groupName) { return callback(new Error('[[error:invalid-group]]')); From ed358f4bf7bae12587e498ef64e48c857946b541 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 14 Sep 2015 15:10:53 -0400 Subject: [PATCH 073/117] closes #3589 --- src/groups.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/groups.js b/src/groups.js index 5405a9b115..5e5a19b984 100644 --- a/src/groups.js +++ b/src/groups.js @@ -444,7 +444,7 @@ var async = require('async'), } }); - Groups.getGroupsData(memberOf, next); + Groups.getGroupsAndMembers(memberOf, next); }); }, next); } From b660eec67f38c4b5e58d577cbe8ef73b70fa4ed2 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 14 Sep 2015 15:29:26 -0400 Subject: [PATCH 074/117] closes #3608 inb4 all admin routes are publically visible --- src/routes/index.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/routes/index.js b/src/routes/index.js index bd5be4f429..dfda79e76d 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -100,7 +100,8 @@ module.exports = function(app, middleware) { var router = express.Router(), pluginRouter = express.Router(), authRouter = express.Router(), - relativePath = nconf.get('relative_path'); + relativePath = nconf.get('relative_path'), + ensureLoggedIn = require('connect-ensure-login'); pluginRouter.render = function() { app.render.apply(app, arguments); @@ -112,11 +113,9 @@ module.exports = function(app, middleware) { app.use(middleware.maintenanceMode); - app.all(relativePath + '/api/?*', middleware.prepareAPI); - app.all(relativePath + '/api/admin/?*', middleware.isAdmin); - - var ensureLoggedIn = require('connect-ensure-login'); - app.all(relativePath + '/admin/?*', ensureLoggedIn.ensureLoggedIn(nconf.get('relative_path') + '/login?local=1'), middleware.applyCSRF, middleware.isAdmin); + app.all(relativePath + '(/api|/api/*?)', middleware.prepareAPI); + app.all(relativePath + '(/api/admin|/api/admin/*?)', middleware.isAdmin); + app.all(relativePath + '(/admin|/admin/*?)', ensureLoggedIn.ensureLoggedIn(nconf.get('relative_path') + '/login?local=1'), middleware.applyCSRF, middleware.isAdmin); adminRoutes(router, middleware, controllers); metaRoutes(router, middleware, controllers); From 30d087cbd31b48980cd2f6b080b3bfdc0794ff3f Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 14 Sep 2015 15:50:02 -0400 Subject: [PATCH 075/117] closes #3607 --- public/src/client/reset_code.js | 2 +- src/controllers/authentication.js | 6 +----- src/user/create.js | 13 ++++++++++++- src/user/reset.js | 3 +++ 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/public/src/client/reset_code.js b/public/src/client/reset_code.js index abbc4582db..678edf987a 100644 --- a/public/src/client/reset_code.js +++ b/public/src/client/reset_code.js @@ -13,7 +13,7 @@ define('forum/reset_code', function() { noticeEl = $('#notice'); resetEl.on('click', function() { - if (password.val().length < 6) { + if (password.val().length < config.minimumPasswordLength) { app.alertError('[[reset_password:password_too_short]]'); } else if (password.val() !== repeat.val()) { app.alertError('[[reset_password:passwords_do_not_match]]'); diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js index beb319b04c..043776eda2 100644 --- a/src/controllers/authentication.js +++ b/src/controllers/authentication.js @@ -52,11 +52,7 @@ authenticationController.register = function(req, res, next) { return next(new Error('[[error:username-too-long')); } - if (!userData.password || userData.password.length < meta.config.minimumPasswordLength) { - return next(new Error('[[user:change_password_error_length]]')); - } - - next(); + user.isPasswordValid(userData.password, next); }, function(next) { plugins.fireHook('filter:register.check', {req: req, res: res, userData: userData}, next); diff --git a/src/user/create.js b/src/user/create.js index 1a6e2a40e8..ed04c2cd75 100644 --- a/src/user/create.js +++ b/src/user/create.js @@ -157,7 +157,7 @@ module.exports = function(User) { }, passwordValid: function(next) { if (userData.password) { - next(!utils.isPasswordValid(userData.password) ? new Error('[[error:invalid-password]]') : null); + User.isPasswordValid(userData.password, next); } else { next(); } @@ -179,6 +179,17 @@ module.exports = function(User) { }); }; + User.isPasswordValid = function(password, callback) { + if (!password || !utils.isPasswordValid(password)) { + return callback(new Error('[[error:invalid-password]]')); + } + + if (password.length < meta.config.minimumPasswordLength) { + return callback(new Error('[[user:change_password_error_length]]')); + } + callback(); + }; + function renameUsername(userData, callback) { meta.userOrGroupExists(userData.userslug, function(err, exists) { if (err || !exists) { diff --git a/src/user/reset.js b/src/user/reset.js index e9a5e46c1a..26acc5ecbc 100644 --- a/src/user/reset.js +++ b/src/user/reset.js @@ -77,6 +77,9 @@ var async = require('async'), UserReset.commit = function(code, password, callback) { var uid; async.waterfall([ + function(next) { + user.isPasswordValid(password, next); + }, function(next) { UserReset.validate(code, next); }, From af19bd8bd240d7cf19860ce109ee32406c89d1e9 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 14 Sep 2015 15:52:48 -0400 Subject: [PATCH 076/117] msgs on reset --- public/src/client/reset.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/src/client/reset.js b/public/src/client/reset.js index fcf4098b68..ef6856555a 100644 --- a/public/src/client/reset.js +++ b/public/src/client/reset.js @@ -16,13 +16,13 @@ define('forum/reset', function() { return app.alertError(err.message); } - errorEl.addClass('hide').hide(); - successEl.removeClass('hide').show(); + errorEl.addClass('hide'); + successEl.removeClass('hide'); inputEl.val(''); }); } else { - successEl.addClass('hide').hide(); - errorEl.removeClass('hide').show(); + successEl.addClass('hide'); + errorEl.removeClass('hide'); } return false; }); From e90ee46cd1c5f8db2fdb9fe259ed643b289add89 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 14 Sep 2015 16:20:56 -0400 Subject: [PATCH 077/117] closes #3603 --- src/user/settings.js | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/user/settings.js b/src/user/settings.js index 03922ade45..17e524bcb7 100644 --- a/src/user/settings.js +++ b/src/user/settings.js @@ -99,27 +99,32 @@ module.exports = function(User) { plugins.fireHook('action:user.saveSettings', {uid: uid, settings: data}); + var settings = { + showemail: data.showemail, + showfullname: data.showfullname, + openOutgoingLinksInNewTab: data.openOutgoingLinksInNewTab, + dailyDigestFreq: data.dailyDigestFreq || 'off', + usePagination: data.usePagination, + topicsPerPage: Math.min(data.topicsPerPage, parseInt(meta.config.topicsPerPage, 10) || 20), + postsPerPage: Math.min(data.postsPerPage, parseInt(meta.config.postsPerPage, 10) || 20), + notificationSounds: data.notificationSounds, + userLang: data.userLang || meta.config.defaultLang, + followTopicsOnCreate: data.followTopicsOnCreate, + followTopicsOnReply: data.followTopicsOnReply, + sendChatNotifications: data.sendChatNotifications, + sendPostNotifications: data.sendPostNotifications, + restrictChat: data.restrictChat, + topicSearchEnabled: data.topicSearchEnabled, + groupTitle: data.groupTitle + }; + + if (data.bootswatchSkin) { + settings.bootswatchSkin = data.bootswatchSkin; + } + async.waterfall([ function(next) { - db.setObject('user:' + uid + ':settings', { - showemail: data.showemail, - showfullname: data.showfullname, - openOutgoingLinksInNewTab: data.openOutgoingLinksInNewTab, - dailyDigestFreq: data.dailyDigestFreq || 'off', - usePagination: data.usePagination, - topicsPerPage: Math.min(data.topicsPerPage, parseInt(meta.config.topicsPerPage, 10) || 20), - postsPerPage: Math.min(data.postsPerPage, parseInt(meta.config.postsPerPage, 10) || 20), - notificationSounds: data.notificationSounds, - userLang: data.userLang || meta.config.defaultLang, - followTopicsOnCreate: data.followTopicsOnCreate, - followTopicsOnReply: data.followTopicsOnReply, - sendChatNotifications: data.sendChatNotifications, - sendPostNotifications: data.sendPostNotifications, - restrictChat: data.restrictChat, - topicSearchEnabled: data.topicSearchEnabled, - bootswatchSkin: data.bootswatchSkin, - groupTitle: data.groupTitle - }, next); + db.setObject('user:' + uid + ':settings', settings, next); }, function(next) { updateDigestSetting(uid, data.dailyDigestFreq, next); From 45c446dac0a14ab64714312a544e8387ecfb4af7 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 14 Sep 2015 16:22:36 -0400 Subject: [PATCH 078/117] new hook filter:email.send --- src/emailer.js | 95 +++++++++++++++++++++++++++++--------------------- 1 file changed, 55 insertions(+), 40 deletions(-) diff --git a/src/emailer.js b/src/emailer.js index 14c683d640..b42af0d372 100644 --- a/src/emailer.js +++ b/src/emailer.js @@ -45,49 +45,64 @@ var fs = require('fs'), }; Emailer.sendToEmail = function(template, email, language, params, callback) { - callback = callback || function() {}; - async.parallel({ - html: function(next) { - app.render('emails/' + template, params, next); - }, - plaintext: function(next) { - app.render('emails/' + template + '_plaintext', params, next); - } - }, function(err, results) { - if (err) { - winston.error('[emailer] Error sending digest : ' + err.stack); - return callback(err); - } - async.map([results.html, results.plaintext, params.subject], function(raw, next) { - translator.translate(raw, language || meta.config.defaultLang || 'en_GB', function(translated) { - next(undefined, translated); - }); - }, function(err, translated) { - if (err) { - return callback(err); - } - - if (Plugins.hasListeners('action:email.send')) { - Plugins.fireHook('action:email.send', { - to: email, - from: meta.config['email:from'] || 'no-reply@localhost.lan', - from_name: meta.config['email:from_name'] || 'NodeBB', - subject: translated[2], - html: translated[0], - plaintext: translated[1], - template: template, - uid: params.uid, - pid: params.pid, - fromUid: params.fromUid + function renderAndTranslate(tpl, params, callback) { + async.waterfal([ + function (next) { + app.render(template, params, next); + }, + function (html, next) { + translator.translate(html, lang, function(translated) { + next(null, translated); }); - callback(); - } else { - winston.warn('[emailer] No active email plugin found!'); - callback(); } - }); - }); + ], callback); + } + callback = callback || function() {}; + + if (!Plugins.hasListeners('action:email.send')) { + winston.warn('[emailer] No active email plugin found!'); + return callback(); + } + + var lang = language || meta.config.defaultLang || 'en_GB'; + + async.waterfall([ + function (next) { + async.parallel({ + html: function(next) { + renderAndTranslate('emails/' + template, params, next); + }, + plaintext: function(next) { + renderAndTranslate('emails/' + template + '_plaintext', params, next); + }, + subject: function(next) { + translator.translate(params.subject, lang, function(translated) { + next(null, translated); + }); + } + }, next); + }, + function (results, next) { + var data = { + to: email, + from: meta.config['email:from'] || 'no-reply@localhost.lan', + from_name: meta.config['email:from_name'] || 'NodeBB', + subject: results.subject, + html: results.html, + plaintext: results.plaintext, + template: template, + uid: params.uid, + pid: params.pid, + fromUid: params.fromUid + }; + Plugins.fireHook('filter:email.send', data, next); + }, + function (data, next) { + Plugins.fireHook('action:email.send', data); + next(); + } + ], callback); }; From 5388ddf791c979915c55a19db75fbb64ad319177 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 14 Sep 2015 17:00:41 -0400 Subject: [PATCH 079/117] removed posttools edit --- src/postTools.js | 4 ---- src/socket.io/posts.js | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/postTools.js b/src/postTools.js index d702274abf..d84d2a0da4 100644 --- a/src/postTools.js +++ b/src/postTools.js @@ -8,10 +8,6 @@ var async = require('async'), (function(PostTools) { - PostTools.edit = function(data, callback) { - posts.edit(data, callback); - }; - PostTools.delete = function(uid, pid, callback) { togglePostDelete(uid, pid, true, callback); }; diff --git a/src/socket.io/posts.js b/src/socket.io/posts.js index 62c218b195..e302dda0ff 100644 --- a/src/socket.io/posts.js +++ b/src/socket.io/posts.js @@ -294,7 +294,7 @@ SocketPosts.edit = function(socket, data, callback) { return callback(new Error('[[error:content-too-long, ' + meta.config.maximumPostLength + ']]')); } - postTools.edit({ + posts.edit({ uid: socket.uid, handle: data.handle, pid: data.pid, From 8070de79624eb97cbeb111e82733231b3c9a715a Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 14 Sep 2015 17:11:58 -0400 Subject: [PATCH 080/117] closes #3461 --- src/controllers/topics.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/controllers/topics.js b/src/controllers/topics.js index c1798d5bb4..93ab192823 100644 --- a/src/controllers/topics.js +++ b/src/controllers/topics.js @@ -81,7 +81,8 @@ topicsController.get = function(req, res, callback) { set = 'tid:' + tid + ':posts:votes'; } - var postIndex = 0; + var postIndex = 0, + index = 0; req.params.post_index = parseInt(req.params.post_index, 10) || 0; if (reverse && req.params.post_index === 1) { @@ -94,7 +95,6 @@ topicsController.get = function(req, res, callback) { postIndex = Math.max(0, (req.params.post_index || 1) - Math.ceil(settings.postsPerPage / 2)); } } else if (!req.query.page) { - var index = 0; if (reverse) { index = Math.max(0, postCount - (req.params.post_index || postCount)); } else { @@ -118,6 +118,7 @@ topicsController.get = function(req, res, callback) { topicData.pageCount = pageCount; topicData.currentPage = page; + topicData.postIndex = postIndex || index || req.params.post_index; if (page > 1) { topicData.posts.splice(0, 1); @@ -147,10 +148,11 @@ topicsController.get = function(req, res, callback) { }); }, function (topicData, next) { - var description = ''; + var description = '', + idx = topicData.postIndex; - if (topicData.posts[0] && topicData.posts[0].content) { - description = S(topicData.posts[0].content).stripTags().decodeHTMLEntities().s; + if (topicData.posts[idx] && topicData.posts[idx].content) { + description = S(topicData.posts[idx].content).stripTags().decodeHTMLEntities().s; } if (description.length > 255) { @@ -163,8 +165,8 @@ topicsController.get = function(req, res, callback) { var ogImageUrl = ''; if (topicData.thumb) { ogImageUrl = topicData.thumb; - } else if(topicData.posts.length && topicData.posts[0] && topicData.posts[0].user && topicData.posts[0].user.picture){ - ogImageUrl = topicData.posts[0].user.picture; + } else if(topicData.posts.length && topicData.posts[idx] && topicData.posts[idx].user && topicData.posts[idx].user.picture){ + ogImageUrl = topicData.posts[idx].user.picture; } else if(meta.config['brand:logo']) { ogImageUrl = meta.config['brand:logo']; } else { From 4aad5b86894a5179ef7f73ac17b3a2170219f60e Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 14 Sep 2015 17:30:03 -0400 Subject: [PATCH 081/117] moving chat settings out of post --- src/views/admin/partials/menu.tpl | 7 ++++--- src/views/admin/settings/chat.tpl | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 src/views/admin/settings/chat.tpl diff --git a/src/views/admin/partials/menu.tpl b/src/views/admin/partials/menu.tpl index 4c483ad1ba..55ca6f5839 100644 --- a/src/views/admin/partials/menu.tpl +++ b/src/views/admin/partials/menu.tpl @@ -18,7 +18,7 @@
  • Users
  • Registration Queue
  • Groups
  • -
  • Flags
  • +
  • Flags
  • @@ -32,6 +32,7 @@
  • Group
  • Guests
  • Post
  • +
  • Chat
  • Pagination
  • Tags
  • Notifications
  • @@ -108,8 +109,8 @@

    - - + +
    • Group
    • Guests
    • Post
    • +
    • Chat
    • Pagination
    • Tags
    • Notifications
    • diff --git a/src/views/admin/settings/chat.tpl b/src/views/admin/settings/chat.tpl index 66b1361c70..4238ecbb63 100644 --- a/src/views/admin/settings/chat.tpl +++ b/src/views/admin/settings/chat.tpl @@ -10,7 +10,7 @@
      - +
      diff --git a/src/views/admin/settings/post.tpl b/src/views/admin/settings/post.tpl index 8697b81f4b..b669021204 100644 --- a/src/views/admin/settings/post.tpl +++ b/src/views/admin/settings/post.tpl @@ -114,15 +114,6 @@ -
      -
      Chat Settings
      -
      -
      - Chat Message Inbox Size
      -
      -
      -
      -
      Upload Settings
      From 0cac0692b6ecebd1518035b59ee65d9e02e7e504 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 14 Sep 2015 17:45:02 -0400 Subject: [PATCH 083/117] closes #3532 --- public/src/client/chats.js | 4 ++-- public/src/modules/chat.js | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/public/src/client/chats.js b/public/src/client/chats.js index 4a0fdd942f..0a14f266dd 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -186,7 +186,8 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', Chats.sendMessage = function(toUid, inputEl) { var msg = inputEl.val(); if (msg.length) { - msg = msg +'\n'; + inputEl.val(''); + msg = msg + '\n'; socket.emit('modules.chats.send', { touid:toUid, message:msg @@ -198,7 +199,6 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', return app.alertError(err.message); } - inputEl.val(''); sounds.play('chat-outgoing'); Chats.notifyTyping(toUid, false); }); diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index 7555a380e5..11e0e63c25 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -455,6 +455,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra chatModal.find('#chat-message-send-btn').off('click').on('click', function(e){ Chats.sendMessage(chatModal.attr('touid'), input); + input.focus(); return false; }); } From 22d6ceab59b8073870db4750e02e33ffc544f281 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 14 Sep 2015 17:55:15 -0400 Subject: [PATCH 084/117] closes #2427 --- public/src/client/chats.js | 36 +++++++++++++++++++----------------- public/src/modules/chat.js | 1 + src/controllers/api.js | 1 + 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/public/src/client/chats.js b/public/src/client/chats.js index 0a14f266dd..4766efa6d5 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -185,24 +185,26 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', Chats.sendMessage = function(toUid, inputEl) { var msg = inputEl.val(); - if (msg.length) { - inputEl.val(''); - msg = msg + '\n'; - socket.emit('modules.chats.send', { - touid:toUid, - message:msg - }, function(err) { - if (err) { - if (err.message === '[[error:email-not-confirmed-chat]]') { - return app.showEmailConfirmWarning(err); - } - return app.alertError(err.message); - } - - sounds.play('chat-outgoing'); - Chats.notifyTyping(toUid, false); - }); + if (msg.length > config.maximumChatMessageLength) { + return app.alertError('[[error:chat-message-too-long]]'); } + + inputEl.val(''); + msg = msg + '\n'; + socket.emit('modules.chats.send', { + touid:toUid, + message:msg + }, function(err) { + if (err) { + if (err.message === '[[error:email-not-confirmed-chat]]') { + return app.showEmailConfirmWarning(err); + } + return app.alertError(err.message); + } + + sounds.play('chat-outgoing'); + Chats.notifyTyping(toUid, false); + }); }; Chats.scrollToBottom = function(containerEl) { diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index 11e0e63c25..254ffc0b35 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -440,6 +440,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra input.off('keypress').on('keypress', function(e) { if (e.which === 13 && !e.shiftKey) { Chats.sendMessage(chatModal.attr('touid'), input); + return false; } }); diff --git a/src/controllers/api.js b/src/controllers/api.js index 99571ea65f..4e141bbec4 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -54,6 +54,7 @@ apiController.getConfig = function(req, res, next) { config.usePagination = parseInt(meta.config.usePagination, 10) === 1; config.disableSocialButtons = parseInt(meta.config.disableSocialButtons, 10) === 1; config.disableChat = parseInt(meta.config.disableChat, 10) === 1; + config.maximumChatMessageLength = parseInt(meta.config.maximumChatMessageLength, 10); config.maxReconnectionAttempts = meta.config.maxReconnectionAttempts || 5; config.reconnectionDelay = meta.config.reconnectionDelay || 1500; config.minimumTagsPerTopic = meta.config.minimumTagsPerTopic || 0; From b099aaf61f359ed6e2b4bb47d83f1d2aad4ffdf2 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 14 Sep 2015 21:01:13 -0400 Subject: [PATCH 085/117] fix typo in emailer --- src/emailer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emailer.js b/src/emailer.js index b42af0d372..1b408e0f44 100644 --- a/src/emailer.js +++ b/src/emailer.js @@ -46,7 +46,7 @@ var fs = require('fs'), Emailer.sendToEmail = function(template, email, language, params, callback) { function renderAndTranslate(tpl, params, callback) { - async.waterfal([ + async.waterfall([ function (next) { app.render(template, params, next); }, From 501bfbfc47bde64343f629ed330e799eced39b0e Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 14 Sep 2015 21:04:56 -0400 Subject: [PATCH 086/117] closes #3611 --- public/src/modules/search.js | 13 +++++--- src/posts.js | 24 ++++--------- src/socket.io/posts.js | 9 +++-- src/topics/create.js | 65 +++++++++++++++++++++++------------- src/topics/follow.js | 3 ++ 5 files changed, 65 insertions(+), 49 deletions(-) diff --git a/public/src/modules/search.js b/public/src/modules/search.js index 81b98e9ab3..f88f62b066 100644 --- a/public/src/modules/search.js +++ b/public/src/modules/search.js @@ -1,5 +1,5 @@ "use strict"; -/* globals socket, ajaxify, app, define */ +/* globals socket, ajaxify, app, define, config */ define('search', ['navigator', 'translator'], function(nav, translator) { @@ -77,7 +77,7 @@ define('search', ['navigator', 'translator'], function(nav, translator) { term: term }, function(err, pids) { if (err) { - return callback(err); + return app.alertError(err.message); } if (Array.isArray(pids)) { @@ -126,8 +126,13 @@ define('search', ['navigator', 'translator'], function(nav, translator) { if (Search.current.results.length > 0) { topicSearchEl.find('.count').html((index+1) + ' / ' + Search.current.results.length); topicSearchEl.find('.prev, .next').removeAttr('disabled'); - socket.emit('posts.getPidIndex', Search.current.results[index], function(err, postIndex) { - nav.scrollToPost(postIndex-1, true); // why -1? Ask @barisusakli + var data = { + pid: Search.current.results[index], + tid: Search.current.tid, + topicPostSort: config.topicPostSort + }; + socket.emit('posts.getPidIndex', data, function(err, postIndex) { + nav.scrollToPost(postIndex, true); }); } else { translator.translate('[[search:no-matches]]', function(text) { diff --git a/src/posts.js b/src/posts.js index 82be31fea0..24d3371c23 100644 --- a/src/posts.js +++ b/src/posts.js @@ -168,25 +168,13 @@ var async = require('async'), }); }; - Posts.getPidIndex = function(pid, uid, callback) { - async.parallel({ - settings: function(next) { - user.getSettings(uid, next); - }, - tid: function(next) { - Posts.getPostField(pid, 'tid', next); + Posts.getPidIndex = function(pid, tid, topicPostSort, callback) { + var set = topicPostSort === 'most_votes' ? 'tid:' + tid + ':posts:votes' : 'tid:' + tid + ':posts'; + db.sortedSetRank(set, pid, function(err, index) { + if (!utils.isNumber(index)) { + return callback(err, 0); } - }, function(err, results) { - if(err) { - return callback(err); - } - var set = results.settings.topicPostSort === 'most_votes' ? 'tid:' + results.tid + ':posts:votes' : 'tid:' + results.tid + ':posts'; - db.sortedSetRank(set, pid, function(err, index) { - if (!utils.isNumber(index)) { - return callback(err, 1); - } - callback(err, parseInt(index, 10) + 2); - }); + callback(err, parseInt(index, 10) + 1); }); }; diff --git a/src/socket.io/posts.js b/src/socket.io/posts.js index e302dda0ff..d6af25a71a 100644 --- a/src/socket.io/posts.js +++ b/src/socket.io/posts.js @@ -579,7 +579,7 @@ function loadMorePosts(set, uid, data, callback) { } SocketPosts.getRecentPosts = function(socket, data, callback) { - if(!data || !data.count) { + if (!data || !data.count) { return callback(new Error('[[error:invalid-data]]')); } @@ -590,8 +590,11 @@ SocketPosts.getCategory = function(socket, pid, callback) { posts.getCidByPid(pid, callback); }; -SocketPosts.getPidIndex = function(socket, pid, callback) { - posts.getPidIndex(pid, socket.uid, callback); +SocketPosts.getPidIndex = function(socket, data, callback) { + if (!data) { + return callback(new Error('[[error:invalid-data]]')); + } + posts.getPidIndex(data.pid, data.tid, data.topicPostSort, callback); }; module.exports = SocketPosts; diff --git a/src/topics/create.js b/src/topics/create.js index c4ba5a2911..995ddc1550 100644 --- a/src/topics/create.js +++ b/src/topics/create.js @@ -131,7 +131,10 @@ module.exports = function(Topics) { Topics.create({uid: data.uid, title: data.title, cid: data.cid, thumb: data.thumb, tags: data.tags, timestamp: data.timestamp}, next); }, function(tid, next) { - Topics.reply({uid: data.uid, tid: tid, handle: data.handle, content: data.content, timestamp: data.timestamp, req: data.req}, next); + posts.create({uid: data.uid, tid: tid, handle: data.handle, content: data.content, timestamp: data.timestamp, ip: data.req ? data.req.ip : null}, next); + }, + function(postData, next) { + onNewPost(postData, data, next); }, function(postData, next) { async.parallel({ @@ -163,6 +166,7 @@ module.exports = function(Topics) { data.topicData = data.topicData[0]; data.topicData.unreplied = 1; data.topicData.mainPost = data.postData; + data.postData.index = 0; plugins.fireHook('action:topic.post', data.topicData); @@ -232,8 +236,40 @@ module.exports = function(Topics) { function(next) { posts.create({uid: uid, tid: tid, handle: data.handle, content: content, toPid: data.toPid, timestamp: data.timestamp, ip: data.req ? data.req.ip : null}, next); }, - function(data, next) { - postData = data; + function(_postData, next) { + postData = _postData; + onNewPost(postData, data, next); + }, + function(postData, next) { + user.getSettings(uid, next); + }, + function(settings, next) { + if (settings.followTopicsOnReply) { + Topics.follow(postData.tid, uid); + } + + posts.getPidIndex(postData.pid, postData.tid, settings.topicPostSort, next); + }, + function(postIndex, next) { + postData.index = postIndex; + + if (parseInt(uid, 10)) { + Topics.notifyFollowers(postData, uid); + user.setUserField(uid, 'lastonline', Date.now()); + } + + plugins.fireHook('action:topic.reply', postData); + + next(null, postData); + } + ], callback); + }; + + function onNewPost(postData, data, callback) { + var tid = postData.tid; + var uid = postData.uid; + async.waterfall([ + function(next) { Topics.markAsUnreadForAll(tid, next); }, function(next) { @@ -247,12 +283,6 @@ module.exports = function(Topics) { topicInfo: function(next) { Topics.getTopicFields(tid, ['tid', 'title', 'slug', 'cid', 'postcount'], next); }, - settings: function(next) { - user.getSettings(uid, next); - }, - postIndex: function(next) { - posts.getPidIndex(postData.pid, uid, next); - }, content: function(next) { posts.parsePost(postData, next); } @@ -267,31 +297,18 @@ module.exports = function(Topics) { postData.user.username = validator.escape(data.handle); } - if (results.settings.followTopicsOnReply) { - Topics.follow(postData.tid, uid); - } - postData.index = results.postIndex - 1; postData.favourited = false; postData.votes = 0; postData.display_moderator_tools = true; postData.display_move_tools = true; postData.selfPost = false; postData.relativeTime = utils.toISOString(postData.timestamp); - - if (parseInt(uid, 10) && data.req) { - Topics.notifyFollowers(postData, uid); - user.setUserField(uid, 'lastonline', Date.now()); - } - - if (postData.index > 0) { - plugins.fireHook('action:topic.reply', postData); - } - postData.topic.title = validator.escape(postData.topic.title); + next(null, postData); } ], callback); - }; + } function checkTitleLength(title, callback) { if (!title || title.length < parseInt(meta.config.minimumTitleLength, 10)) { diff --git a/src/topics/follow.js b/src/topics/follow.js index 1b0ce4d3dd..16d54f04fb 100644 --- a/src/topics/follow.js +++ b/src/topics/follow.js @@ -46,6 +46,9 @@ module.exports = function(Topics) { Topics.follow = function(tid, uid, callback) { callback = callback || function() {}; + if (!parseInt(uid, 10)) { + return callback(); + } async.waterfall([ function (next) { Topics.exists(tid, next); From bb47761f6a97d13f350ae67dd9d2c2943c8f15c8 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 14 Sep 2015 21:11:59 -0400 Subject: [PATCH 087/117] moved disable chat button --- src/views/admin/settings/chat.tpl | 8 ++++++++ src/views/admin/settings/general.tpl | 7 +------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/views/admin/settings/chat.tpl b/src/views/admin/settings/chat.tpl index 4238ecbb63..828bf339a0 100644 --- a/src/views/admin/settings/chat.tpl +++ b/src/views/admin/settings/chat.tpl @@ -4,6 +4,14 @@
      Chat Settings
      +
      +
      + +
      +
      Chat Message Inbox Size
      diff --git a/src/views/admin/settings/general.tpl b/src/views/admin/settings/general.tpl index 871c690900..dda578a529 100644 --- a/src/views/admin/settings/general.tpl +++ b/src/views/admin/settings/general.tpl @@ -90,12 +90,7 @@ Disable social buttons
      -
      - -
      +
      From b17feb3b6c8667a4d5b09f82968137e9ee24f4b7 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 14 Sep 2015 22:15:11 -0400 Subject: [PATCH 088/117] nested categories on search --- public/src/client/search.js | 40 +++++++++++++++++++-- src/controllers/search.js | 72 +++++++++++++++---------------------- 2 files changed, 66 insertions(+), 46 deletions(-) diff --git a/public/src/client/search.js b/public/src/client/search.js index c7330f1ab8..adee1adaee 100644 --- a/public/src/client/search.js +++ b/public/src/client/search.js @@ -80,9 +80,11 @@ define('forum/search', ['search', 'autocomplete'], function(searchModule, autoco $('#posted-by-user').val(params.by); } - if (params.categories) { - $('#posted-in-categories').val(params.categories); - } + loadCategories(function() { + if (params.categories) { + $('#posted-in-categories').val(params.categories); + } + }); if (params.searchChildren) { $('#search-children').prop('checked', true); @@ -112,6 +114,38 @@ define('forum/search', ['search', 'autocomplete'], function(searchModule, autoco } } + function loadCategories(callback) { + var listEl = $('#posted-in-categories'); + + socket.emit('categories.getCategoriesByPrivilege', 'read', function(err, categories) { + if (err) { + return app.alertError(err.message); + } + + categories = categories.filter(function(category) { + return !category.link && !parseInt(category.parentCid, 10); + }); + + categories.forEach(function(category) { + recursive(category, listEl, ''); + }); + listEl.attr('size', listEl.find('option').length); + callback(); + }); + } + + function recursive(category, listEl, level) { + if (category.link) { + return; + } + var bullet = level ? '• ' : ''; + $('').appendTo(listEl); + + category.children.forEach(function(child) { + recursive(child, listEl, '    ' + level); + }); + } + function highlightMatches(searchQuery) { if (!searchQuery) { return; diff --git a/src/controllers/search.js b/src/controllers/search.js index 5da395c2b8..874cbea1a2 100644 --- a/src/controllers/search.js +++ b/src/controllers/search.js @@ -15,60 +15,46 @@ searchController.search = function(req, res, next) { return next(); } - var breadcrumbs = helpers.buildBreadcrumbs([{text: '[[global:search]]'}]); + req.params.term = validator.escape(req.params.term); + var page = Math.max(1, parseInt(req.query.page, 10)) || 1; + if (req.query.categories && !Array.isArray(req.query.categories)) { + req.query.categories = [req.query.categories]; + } - categories.getCategoriesByPrivilege('categories:cid', req.uid, 'read', function(err, categories) { + var data = { + query: req.params.term, + searchIn: req.query.in || 'posts', + postedBy: req.query.by, + categories: req.query.categories, + searchChildren: req.query.searchChildren, + replies: req.query.replies, + repliesFilter: req.query.repliesFilter, + timeRange: req.query.timeRange, + timeFilter: req.query.timeFilter, + sortBy: req.query.sortBy, + sortDirection: req.query.sortDirection, + page: page, + uid: req.uid + }; + + search.search(data, function(err, results) { if (err) { return next(err); } - categories = categories.filter(function(category) { - return category && !category.link; - }); + results.pagination = pagination.create(page, results.pageCount, req.query); + results.showAsPosts = !req.query.showAs || req.query.showAs === 'posts'; + results.showAsTopics = req.query.showAs === 'topics'; + results.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[global:search]]'}]); + results.expandSearch = !req.params.term; - req.params.term = validator.escape(req.params.term); - var page = Math.max(1, parseInt(req.query.page, 10)) || 1; - if (req.query.categories && !Array.isArray(req.query.categories)) { - req.query.categories = [req.query.categories]; - } - - var data = { - query: req.params.term, - searchIn: req.query.in || 'posts', - postedBy: req.query.by, - categories: req.query.categories, - searchChildren: req.query.searchChildren, - replies: req.query.replies, - repliesFilter: req.query.repliesFilter, - timeRange: req.query.timeRange, - timeFilter: req.query.timeFilter, - sortBy: req.query.sortBy, - sortDirection: req.query.sortDirection, - page: page, - uid: req.uid - }; - - search.search(data, function(err, results) { + plugins.fireHook('filter:search.build', {data: data, results: results}, function(err, data) { if (err) { return next(err); } - - results.pagination = pagination.create(page, results.pageCount, req.query); - results.showAsPosts = !req.query.showAs || req.query.showAs === 'posts'; - results.showAsTopics = req.query.showAs === 'topics'; - results.breadcrumbs = breadcrumbs; - results.categories = categories; - results.expandSearch = !req.params.term; - - plugins.fireHook('filter:search.build', {data: data, results: results}, function(err, data) { - if (err) { - return next(err); - } - res.render('search', data.results); - }); + res.render('search', data.results); }); }); }; - module.exports = searchController; From b22cb128a92ca440b715fcbfd7733f4c3c0a4653 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 14 Sep 2015 22:16:32 -0400 Subject: [PATCH 089/117] up persona --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0230332f6f..e7493401d4 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "nodebb-plugin-spam-be-gone": "0.4.2", "nodebb-rewards-essentials": "0.0.5", "nodebb-theme-lavender": "2.0.0", - "nodebb-theme-persona": "3.0.5", + "nodebb-theme-persona": "3.0.6", "nodebb-theme-vanilla": "4.0.3", "nodebb-widget-essentials": "2.0.1", "npm": "^2.1.4", From 17a40b716e04b11ddcc48663996ebc51c650c4e7 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 14 Sep 2015 22:26:29 -0400 Subject: [PATCH 090/117] fix topic move notification --- public/src/client/topic/move.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/src/client/topic/move.js b/public/src/client/topic/move.js index 648f9f3b27..4c932d8a9b 100644 --- a/public/src/client/topic/move.js +++ b/public/src/client/topic/move.js @@ -18,7 +18,7 @@ define('forum/topic/move', function() { Move.moveAll = tids ? false : true; modal.on('shown.bs.modal', onMoveModalShown); - $('#move-confirm').hide(); + $('#move-confirm').addClass('hide'); if (Move.moveAll || (tids && tids.length > 1)) { modal.find('.modal-header h3').translateText('[[topic:move_topics]]'); @@ -52,7 +52,7 @@ define('forum/topic/move', function() { function selectCategory(category) { modal.find('#confirm-category-name').html(category.html()); - $('#move-confirm').show(); + $('#move-confirm').removeClass('hide'); targetCid = category.attr('data-cid'); targetCategoryLabel = category.html(); From cc6e556c77219277ce3e8b5dff1355061432bfbe Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 14 Sep 2015 22:28:55 -0400 Subject: [PATCH 091/117] up persona vanilla --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index e7493401d4..a3e725bb61 100644 --- a/package.json +++ b/package.json @@ -49,8 +49,8 @@ "nodebb-plugin-spam-be-gone": "0.4.2", "nodebb-rewards-essentials": "0.0.5", "nodebb-theme-lavender": "2.0.0", - "nodebb-theme-persona": "3.0.6", - "nodebb-theme-vanilla": "4.0.3", + "nodebb-theme-persona": "3.0.7", + "nodebb-theme-vanilla": "4.0.4", "nodebb-widget-essentials": "2.0.1", "npm": "^2.1.4", "passport": "^0.3.0", From a5e9394d9392f19c12b8f17b03775955c18d90ca Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 14 Sep 2015 22:41:15 -0400 Subject: [PATCH 092/117] closes #3612 --- public/src/client/topic/events.js | 8 +++++--- src/socket.io/topics.js | 16 +++++++++------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/public/src/client/topic/events.js b/public/src/client/topic/events.js index 200a362b69..99564cd50c 100644 --- a/public/src/client/topic/events.js +++ b/public/src/client/topic/events.js @@ -82,12 +82,14 @@ define('forum/topic/events', [ } function onTopicPurged(data) { - ajaxify.go('category/' + ajaxify.data.cid); + if (ajaxify.data.category && ajaxify.data.category.slug) { + ajaxify.go('category/' + ajaxify.data.category.slug, null, true); + } } function onTopicMoved(data) { - if (data && data.tid > 0) { - ajaxify.go('topic/' + data.tid); + if (data && data.slug) { + ajaxify.go('topic/' + data.slug, null, true); } } diff --git a/src/socket.io/topics.js b/src/socket.io/topics.js index e609629ef0..d99fada364 100644 --- a/src/socket.io/topics.js +++ b/src/socket.io/topics.js @@ -322,7 +322,7 @@ SocketTopics.move = function(socket, data, callback) { } async.eachLimit(data.tids, 10, function(tid, next) { - var oldCid; + var topicData; async.waterfall([ function(next) { privileges.topics.canMove(tid, socket.uid, next); @@ -334,10 +334,10 @@ SocketTopics.move = function(socket, data, callback) { next(); }, function(next) { - topics.getTopicField(tid, 'cid', next); + topics.getTopicFields(tid, ['cid', 'slug'], next); }, - function(cid, next) { - oldCid = cid; + function(_topicData, next) { + topicData = _topicData; threadTools.move(tid, data.cid, socket.uid, next); } ], function(err) { @@ -346,11 +346,13 @@ SocketTopics.move = function(socket, data, callback) { } websockets.in('topic_' + tid).emit('event:topic_moved', { - tid: tid + tid: tid, + slug: topicData.slug }); - websockets.in('category_' + oldCid).emit('event:topic_moved', { - tid: tid + websockets.in('category_' + topicData.cid).emit('event:topic_moved', { + tid: tid, + slug: topicData.slug }); SocketTopics.sendNotificationToTopicOwner(tid, socket.uid, 'notifications:moved_your_topic'); From d01736a726d87c400420032bfdc8e3064e7dcbb1 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 14 Sep 2015 22:43:09 -0400 Subject: [PATCH 093/117] cleanup --- src/socket.io/topics.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/socket.io/topics.js b/src/socket.io/topics.js index d99fada364..1137422913 100644 --- a/src/socket.io/topics.js +++ b/src/socket.io/topics.js @@ -338,6 +338,7 @@ SocketTopics.move = function(socket, data, callback) { }, function(_topicData, next) { topicData = _topicData; + topicData.tid = tid; threadTools.move(tid, data.cid, socket.uid, next); } ], function(err) { @@ -345,15 +346,9 @@ SocketTopics.move = function(socket, data, callback) { return next(err); } - websockets.in('topic_' + tid).emit('event:topic_moved', { - tid: tid, - slug: topicData.slug - }); + websockets.in('topic_' + tid).emit('event:topic_moved', topicData); - websockets.in('category_' + topicData.cid).emit('event:topic_moved', { - tid: tid, - slug: topicData.slug - }); + websockets.in('category_' + topicData.cid).emit('event:topic_moved', topicData); SocketTopics.sendNotificationToTopicOwner(tid, socket.uid, 'notifications:moved_your_topic'); From 6fc267858e121e5dc90e00d14545b616e59ac56d Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 14 Sep 2015 22:58:02 -0400 Subject: [PATCH 094/117] fix missing callback --- src/topics.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/topics.js b/src/topics.js index fd9e19ca24..fc7c1f49e9 100644 --- a/src/topics.js +++ b/src/topics.js @@ -286,9 +286,9 @@ var async = require('async'), } } - Topics.addPostData(posts, uid, callback); + Topics.addPostData(posts, uid, next); } - ]); + ], callback); } Topics.getMainPost = function(tid, uid, callback) { From 3a6871fd65e614982728dfb0a1292f0bf29b3c80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 15 Sep 2015 00:54:48 -0400 Subject: [PATCH 095/117] display all categories in custom home --- src/controllers/admin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/admin.js b/src/controllers/admin.js index 07c7f9f1da..bf9b0f0087 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -350,7 +350,7 @@ adminController.navigation.get = function(req, res, next) { adminController.homepage.get = function(req, res, next) { async.waterfall([ function(next) { - db.getSortedSetRange('cid:0:children', 0, -1, next); + db.getSortedSetRange('categories:cid', 0, -1, next); }, function(cids, next) { privileges.categories.filterCids('find', cids, 0, next); From e9692a3e1f058c4efdee6fb046a4c2c73cb7d47b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 15 Sep 2015 12:14:01 -0400 Subject: [PATCH 096/117] fix emailer --- src/emailer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emailer.js b/src/emailer.js index 1b408e0f44..221c693dab 100644 --- a/src/emailer.js +++ b/src/emailer.js @@ -48,7 +48,7 @@ var fs = require('fs'), function renderAndTranslate(tpl, params, callback) { async.waterfall([ function (next) { - app.render(template, params, next); + app.render(tpl, params, next); }, function (html, next) { translator.translate(html, lang, function(translated) { From 6d8134618fdf0ba500800aafdec25619b15d78d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 15 Sep 2015 12:29:24 -0400 Subject: [PATCH 097/117] fix chats --- public/src/client/chats.js | 9 ++++++--- src/controllers/api.js | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/public/src/client/chats.js b/public/src/client/chats.js index 4766efa6d5..997efa6650 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -189,11 +189,14 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', return app.alertError('[[error:chat-message-too-long]]'); } + if (!msg.length) { + return; + } + inputEl.val(''); - msg = msg + '\n'; socket.emit('modules.chats.send', { - touid:toUid, - message:msg + touid: toUid, + message: msg }, function(err) { if (err) { if (err.message === '[[error:email-not-confirmed-chat]]') { diff --git a/src/controllers/api.js b/src/controllers/api.js index 4e141bbec4..5af74d2ed6 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -54,7 +54,7 @@ apiController.getConfig = function(req, res, next) { config.usePagination = parseInt(meta.config.usePagination, 10) === 1; config.disableSocialButtons = parseInt(meta.config.disableSocialButtons, 10) === 1; config.disableChat = parseInt(meta.config.disableChat, 10) === 1; - config.maximumChatMessageLength = parseInt(meta.config.maximumChatMessageLength, 10); + config.maximumChatMessageLength = parseInt(meta.config.maximumChatMessageLength, 10) || 1000; config.maxReconnectionAttempts = meta.config.maxReconnectionAttempts || 5; config.reconnectionDelay = meta.config.reconnectionDelay || 1500; config.minimumTagsPerTopic = meta.config.minimumTagsPerTopic || 0; From 4acb31839ed573831a86e584536603abec948eb7 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 15 Sep 2015 12:52:43 -0400 Subject: [PATCH 098/117] nodebb/nodebb#3615 --- src/categories/topics.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/categories/topics.js b/src/categories/topics.js index 9da0957c8b..ef2c69e068 100644 --- a/src/categories/topics.js +++ b/src/categories/topics.js @@ -53,6 +53,7 @@ module.exports = function(Categories) { topic.title = '[[topic:topic_is_deleted]]'; topic.slug = topic.tid; topic.teaser = null; + topic.noAnchor = true; } }); From 5b08538264846717b350df8c5ce1d44a0cd56c50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 15 Sep 2015 12:58:19 -0400 Subject: [PATCH 099/117] closes #3614 --- public/src/admin/manage/category.js | 8 ++++++++ src/categories.js | 3 +++ src/categories/update.js | 3 +++ 3 files changed, 14 insertions(+) diff --git a/public/src/admin/manage/category.js b/public/src/admin/manage/category.js index 25832bcac6..c16809e44f 100644 --- a/public/src/admin/manage/category.js +++ b/public/src/admin/manage/category.js @@ -234,6 +234,14 @@ define('admin/manage/category', [ Category.launchParentSelector = function() { socket.emit('categories.get', function(err, categories) { + if (err) { + return app.alertError(err.message); + } + + categories = categories.filter(function(category) { + return category && parseInt(category.cid, 10) !== parseInt(ajaxify.data.category.cid, 10); + }); + templates.parse('partials/category_list', { categories: categories }, function(html) { diff --git a/src/categories.js b/src/categories.js index 0626de3349..a7cd08eebe 100644 --- a/src/categories.js +++ b/src/categories.js @@ -333,6 +333,9 @@ var async = require('async'), privileges.categories.filterCids('find', children, uid, next); }, function (children, next) { + children = children.filter(function(cid) { + return parseInt(category.cid, 10) !== parseInt(cid, 10); + }); if (!children.length) { category.children = []; return callback(); diff --git a/src/categories/update.js b/src/categories/update.js index 036b405c96..9ad8acf61e 100644 --- a/src/categories/update.js +++ b/src/categories/update.js @@ -68,6 +68,9 @@ module.exports = function(Categories) { } function updateParent(cid, newParent, callback) { + if (parseInt(cid, 10) === parseInt(newParent, 10)) { + return callback(new Error('[[error:cant-set-self-as-parent]]')); + } Categories.getCategoryField(cid, 'parentCid', function(err, oldParent) { if (err) { return callback(err); From d6b0f6b831da588e4aff854c2760a316918cf487 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 15 Sep 2015 13:09:43 -0400 Subject: [PATCH 100/117] updated theme minvers --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index a3e725bb61..58291eae2c 100644 --- a/package.json +++ b/package.json @@ -48,9 +48,9 @@ "nodebb-plugin-soundpack-default": "0.1.4", "nodebb-plugin-spam-be-gone": "0.4.2", "nodebb-rewards-essentials": "0.0.5", - "nodebb-theme-lavender": "2.0.0", - "nodebb-theme-persona": "3.0.7", - "nodebb-theme-vanilla": "4.0.4", + "nodebb-theme-lavender": "2.0.1", + "nodebb-theme-persona": "3.0.9", + "nodebb-theme-vanilla": "4.0.5", "nodebb-widget-essentials": "2.0.1", "npm": "^2.1.4", "passport": "^0.3.0", From e62bd2ab9e8c4b6f0aa1a435c427e2d6223e9990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 15 Sep 2015 13:13:07 -0400 Subject: [PATCH 101/117] fix chat status unread etc. --- src/messaging.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/messaging.js b/src/messaging.js index 6b0e2318bf..f3020ed8f5 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -280,7 +280,7 @@ var db = require('./database'), } results.users.forEach(function(user, index) { - if (user && !parseInt(user.uid, 10)) { + if (user && parseInt(user.uid, 10)) { Messaging.markRead(uid, uids[index]); user.unread = results.unread[index]; user.status = sockets.isUserOnline(user.uid) ? user.status : 'offline'; From 1405a10cbee3cd8823bf09440ca65fc6e0543c03 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 15 Sep 2015 13:51:52 -0400 Subject: [PATCH 102/117] Updated persona minver --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 58291eae2c..d666ff89ce 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "nodebb-plugin-spam-be-gone": "0.4.2", "nodebb-rewards-essentials": "0.0.5", "nodebb-theme-lavender": "2.0.1", - "nodebb-theme-persona": "3.0.9", + "nodebb-theme-persona": "3.0.11", "nodebb-theme-vanilla": "4.0.5", "nodebb-widget-essentials": "2.0.1", "npm": "^2.1.4", From 9454e5a73afbf30b635958b7d10a8e472ee4e956 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 15 Sep 2015 14:06:15 -0400 Subject: [PATCH 103/117] latest translations --- public/language/de/global.json | 2 +- public/language/de/modules.json | 6 +-- public/language/de/notifications.json | 2 +- public/language/de/pages.json | 52 +++++++++++++------------- public/language/de/recent.json | 2 +- public/language/de/user.json | 4 +- public/language/en_US/pages.json | 2 +- public/language/en_US/user.json | 2 +- public/language/es/global.json | 4 +- public/language/es/modules.json | 6 +-- public/language/es/notifications.json | 2 +- public/language/es/pages.json | 52 +++++++++++++------------- public/language/es/user.json | 2 +- public/language/fr/modules.json | 2 +- public/language/it/global.json | 4 +- public/language/it/modules.json | 6 +-- public/language/it/notifications.json | 2 +- public/language/it/pages.json | 54 +++++++++++++-------------- public/language/it/user.json | 2 +- public/language/nl/global.json | 4 +- public/language/nl/modules.json | 6 +-- public/language/nl/notifications.json | 2 +- public/language/nl/pages.json | 52 +++++++++++++------------- public/language/nl/user.json | 2 +- public/language/tr/global.json | 4 +- public/language/tr/modules.json | 8 ++-- public/language/tr/notifications.json | 2 +- public/language/tr/pages.json | 54 +++++++++++++-------------- public/language/tr/user.json | 2 +- 29 files changed, 172 insertions(+), 172 deletions(-) diff --git a/public/language/de/global.json b/public/language/de/global.json index 8002e59ead..2cb475d5eb 100644 --- a/public/language/de/global.json +++ b/public/language/de/global.json @@ -68,7 +68,7 @@ "recentposts": "Aktuelle Beiträge", "recentips": "Zuletzt eingeloggte IPs", "away": "Abwesend", - "dnd": "Do not disturb", + "dnd": "Nicht stören", "invisible": "Unsichtbar", "offline": "Offline", "email": "E-Mail", diff --git a/public/language/de/modules.json b/public/language/de/modules.json index c872442e56..5d6e544127 100644 --- a/public/language/de/modules.json +++ b/public/language/de/modules.json @@ -5,7 +5,7 @@ "chat.no_active": "Du hast keine aktiven Chats.", "chat.user_typing": "%1 tippt gerade ...", "chat.user_has_messaged_you": "%1 hat dir geschrieben.", - "chat.see_all": "See all chats", + "chat.see_all": "Alle Diagramme anzeigen", "chat.no-messages": "Bitte wähle einen Empfänger, um den jeweiligen Nachrichtenverlauf anzuzeigen.", "chat.recent-chats": "Aktuelle Chats", "chat.contacts": "Kontakte", @@ -24,6 +24,6 @@ "composer.submit_and_lock": "Einreichen und Sperren", "composer.toggle_dropdown": "Menu aus-/einblenden", "bootbox.ok": "OK", - "bootbox.cancel": "Cancel", - "bootbox.confirm": "Confirm" + "bootbox.cancel": "Abbrechen", + "bootbox.confirm": "Bestätigen" } \ No newline at end of file diff --git a/public/language/de/notifications.json b/public/language/de/notifications.json index 3ba55b59a2..ff2d5e316d 100644 --- a/public/language/de/notifications.json +++ b/public/language/de/notifications.json @@ -1,7 +1,7 @@ { "title": "Benachrichtigungen", "no_notifs": "Du hast keine neuen Benachrichtigungen", - "see_all": "See all notifications", + "see_all": "Alle Benachrichtigungen anzeigen", "mark_all_read": "Alle Benachrichtigungen als gelesen markieren", "back_to_home": "Zurück zu %1", "outgoing_link": "Externer Link", diff --git a/public/language/de/pages.json b/public/language/de/pages.json index 239414954d..014968761a 100644 --- a/public/language/de/pages.json +++ b/public/language/de/pages.json @@ -1,37 +1,37 @@ { "home": "Home", "unread": "Ungelesene Themen", - "popular-day": "Popular topics today", - "popular-week": "Popular topics this week", - "popular-month": "Popular topics this month", - "popular-alltime": "All time popular topics", + "popular-day": "Beliebte Themen von Heute", + "popular-week": "Beliebte Themen dieser Woche", + "popular-month": "Beliebte Themen dieses Monats", + "popular-alltime": "Beliebteste Themen", "recent": "Neueste Themen", - "users/online": "Online Users", - "users/latest": "Latest Users", - "users/sort-posts": "Users with the most posts", - "users/sort-reputation": "Users with the most reputation", - "users/map": "User Map", - "users/search": "User Search", + "users/online": "Benutzer online", + "users/latest": "Neuste Benutzer", + "users/sort-posts": "Benutzer mit den meisten Beiträgen", + "users/sort-reputation": "Benutzer mit der besten Reputation", + "users/map": "Benutzer Karte", + "users/search": "Benutzer Suche", "notifications": "Benachrichtigungen", "tags": "Markierungen", "tag": "Themen markiert unter \"%1\"", - "register": "Register an account", - "login": "Login to your account", - "reset": "Reset your account password", - "categories": "Categories", - "groups": "Groups", - "group": "%1 group", + "register": "Einen Benutzer erstellen", + "login": "Einloggen", + "reset": "Passwort zurücksetzen", + "categories": "Kategorien", + "groups": "Gruppen", + "group": "%1's Gruppen", "chats": "Chats", - "chat": "Chatting with %1", - "account/edit": "Editing \"%1\"", - "account/following": "People %1 follows", - "account/followers": "People who follow %1", - "account/posts": "Posts made by %1", - "account/topics": "Topics created by %1", - "account/groups": "%1's Groups", - "account/favourites": "%1's Favourite Posts", - "account/settings": "User Settings", - "account/watched": "Topics watched by %1", + "chat": "Chatte mit %1", + "account/edit": "Bearbeite %1", + "account/following": "Nutzer, die %1 folgt", + "account/followers": "Nutzer, die %1 folgen", + "account/posts": "Beiträge von %1", + "account/topics": "Themen verfasst von %1", + "account/groups": "%1's Gruppen", + "account/favourites": "Von %1 favorisierte Beiträge", + "account/settings": "Benutzer-Einstellungen", + "account/watched": "Themen angeschaut von %1", "maintenance.text": "%1 befindet sich derzeit in der Wartung. Bitte komm später wieder.", "maintenance.messageIntro": "Zusätzlich hat der Administrator diese Nachricht hinterlassen:" } \ No newline at end of file diff --git a/public/language/de/recent.json b/public/language/de/recent.json index e64846b674..d9fa50b996 100644 --- a/public/language/de/recent.json +++ b/public/language/de/recent.json @@ -15,5 +15,5 @@ "there-are-new-topics-and-new-posts": "Es gibt %1 neue Themen und %2 neue Beiträge.", "there-is-a-new-post": "Es gibt einen neuen Beitrag.", "there-are-new-posts": "Es gibt %1 neue Beiträge.", - "click-here-to-reload": "Hier klicken um zu aktualisieren." + "click-here-to-reload": "Zum aktualisieren hier klicken." } \ No newline at end of file diff --git a/public/language/de/user.json b/public/language/de/user.json index 7d5ba0bbd4..d75cc50af6 100644 --- a/public/language/de/user.json +++ b/public/language/de/user.json @@ -65,7 +65,7 @@ "digest_monthly": "Monatlich", "send_chat_notifications": "Sende eine E-Mail, wenn eine neue Chat-Nachricht eingeht und ich nicht online bin", "send_post_notifications": "Sende eine E-Mail wenn auf Themen die ich abonniert habe geantwortet wird", - "settings-require-reload": "Manche Einstellungsänderung benötigt ein aktualisieren. Drücke hier um die Seite neu zu laden.", + "settings-require-reload": "Einige Einstellungsänderung benötigen eine Aktualisierung. Hier klicken um die Seite neu zu laden.", "has_no_follower": "Dieser User hat noch keine Follower.", "follows_no_one": "Dieser User folgt noch niemandem :(", "has_no_posts": "Dieser Nutzer hat noch nichts gepostet.", @@ -85,5 +85,5 @@ "follow_topics_you_create": "Themen folgen, die du erstellst", "grouptitle": "Wähle den anzuzeigenden Gruppen Titel aus", "no-group-title": "Kein Gruppentitel", - "select-skin": "Select a Skin" + "select-skin": "Einen Skin auswählen" } \ No newline at end of file diff --git a/public/language/en_US/pages.json b/public/language/en_US/pages.json index ebf02ba2c9..88b2ed946e 100644 --- a/public/language/en_US/pages.json +++ b/public/language/en_US/pages.json @@ -29,7 +29,7 @@ "account/posts": "Posts made by %1", "account/topics": "Topics created by %1", "account/groups": "%1's Groups", - "account/favourites": "%1's Favourite Posts", + "account/favourites": "%1's Favorite Posts", "account/settings": "User Settings", "account/watched": "Topics watched by %1", "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", diff --git a/public/language/en_US/user.json b/public/language/en_US/user.json index e06df9e525..bf28a9bbf5 100644 --- a/public/language/en_US/user.json +++ b/public/language/en_US/user.json @@ -80,7 +80,7 @@ "browsing": "Browsing Settings", "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Enable In-Topic Searching", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behavior and allow you to search through the entire topic, instead of what is only shown on screen", "follow_topics_you_reply_to": "Follow topics that you reply to", "follow_topics_you_create": "Follow topics you create", "grouptitle": "Select the group title you would like to display", diff --git a/public/language/es/global.json b/public/language/es/global.json index 138161bb35..ca995941b2 100644 --- a/public/language/es/global.json +++ b/public/language/es/global.json @@ -33,7 +33,7 @@ "header.notifications": "Notificaciones", "header.search": "Buscar", "header.profile": "Perfil", - "header.navigation": "Navigation", + "header.navigation": "Navegación", "notifications.loading": "Cargando notificaciones", "chats.loading": "Cargando chats", "motd.welcome": "Bienvenido a NodeBB, la plataforma de debate del el futuro.", @@ -68,7 +68,7 @@ "recentposts": "Publicaciones recientes", "recentips": "IP's conectadas recientemente", "away": "Ausente", - "dnd": "Do not disturb", + "dnd": "No molestar", "invisible": "Invisible", "offline": "Desconectado", "email": "Email", diff --git a/public/language/es/modules.json b/public/language/es/modules.json index 7597bce0c3..7b3e15e5c3 100644 --- a/public/language/es/modules.json +++ b/public/language/es/modules.json @@ -5,7 +5,7 @@ "chat.no_active": "No tiene conversaciones activas.", "chat.user_typing": "%1 está escribiendo...", "chat.user_has_messaged_you": "%1 te ha enviado un mensaje.", - "chat.see_all": "See all chats", + "chat.see_all": "Ver todos los chats", "chat.no-messages": "Por favor, selecciona un contacto para ver el historial de mensajes", "chat.recent-chats": "Chats recientes", "chat.contacts": "Contactos", @@ -24,6 +24,6 @@ "composer.submit_and_lock": "Enviar y Bloquear", "composer.toggle_dropdown": "Alternar desplegable", "bootbox.ok": "OK", - "bootbox.cancel": "Cancel", - "bootbox.confirm": "Confirm" + "bootbox.cancel": "Cancelar", + "bootbox.confirm": "Confirmar" } \ No newline at end of file diff --git a/public/language/es/notifications.json b/public/language/es/notifications.json index 62d03601a7..5258be8a9a 100644 --- a/public/language/es/notifications.json +++ b/public/language/es/notifications.json @@ -1,7 +1,7 @@ { "title": "Notificaciones", "no_notifs": "No tienes nuevas notificaciones", - "see_all": "See all notifications", + "see_all": "Ver todas las notificaciones", "mark_all_read": "Marcar todas las notificaciones como leídas", "back_to_home": "Volver a %1", "outgoing_link": "Enlace externo", diff --git a/public/language/es/pages.json b/public/language/es/pages.json index 2e43b8021b..1e1f34addc 100644 --- a/public/language/es/pages.json +++ b/public/language/es/pages.json @@ -1,37 +1,37 @@ { "home": "Inicio", "unread": "Temas no leídos", - "popular-day": "Popular topics today", - "popular-week": "Popular topics this week", - "popular-month": "Popular topics this month", - "popular-alltime": "All time popular topics", + "popular-day": "Temas populares hoy", + "popular-week": "Temas populares de la semana", + "popular-month": "Temas populares del mes", + "popular-alltime": "Temas populares de siempre", "recent": "Temas recientes", - "users/online": "Online Users", - "users/latest": "Latest Users", - "users/sort-posts": "Users with the most posts", - "users/sort-reputation": "Users with the most reputation", - "users/map": "User Map", - "users/search": "User Search", + "users/online": "Conectados", + "users/latest": "Últimos usuarios", + "users/sort-posts": "Top por mensajes", + "users/sort-reputation": "Más reputados", + "users/map": "Actividad", + "users/search": "Buscar", "notifications": "Notificaciones", "tags": "Etiquetas", "tag": "Temas etiquetados en \"%1\"", - "register": "Register an account", - "login": "Login to your account", - "reset": "Reset your account password", - "categories": "Categories", - "groups": "Groups", - "group": "%1 group", + "register": "Registrar una cuenta", + "login": "Acceder a tu cuenta", + "reset": "Restablecer contraseña", + "categories": "Categorías", + "groups": "Grupos", + "group": "Grupo de %1", "chats": "Chats", - "chat": "Chatting with %1", - "account/edit": "Editing \"%1\"", - "account/following": "People %1 follows", - "account/followers": "People who follow %1", - "account/posts": "Posts made by %1", - "account/topics": "Topics created by %1", - "account/groups": "%1's Groups", - "account/favourites": "%1's Favourite Posts", - "account/settings": "User Settings", - "account/watched": "Topics watched by %1", + "chat": "Chateando con %1", + "account/edit": "Editando \"%1\"", + "account/following": "Gente que sigue %1", + "account/followers": "Seguidores de %1", + "account/posts": "Publicados por %1", + "account/topics": "Temas creados por %1", + "account/groups": "Grupos de %1", + "account/favourites": "Favoritos de %1", + "account/settings": "Preferencias", + "account/watched": "Temas seguidos por %1", "maintenance.text": "%1 está en mantenimiento actualmente. Por favor vuelva en otro momento.", "maintenance.messageIntro": "Adicionalmente, la administración ha dejado este mensaje:" } \ No newline at end of file diff --git a/public/language/es/user.json b/public/language/es/user.json index 2a795889e4..60b5bb29b7 100644 --- a/public/language/es/user.json +++ b/public/language/es/user.json @@ -85,5 +85,5 @@ "follow_topics_you_create": "Seguir publicaciones que creas", "grouptitle": "Selecciona el título del grupo que deseas visualizar", "no-group-title": "Sin título de grupo", - "select-skin": "Select a Skin" + "select-skin": "Seleccionar una plantilla" } \ No newline at end of file diff --git a/public/language/fr/modules.json b/public/language/fr/modules.json index cfb05ef8c2..6adb73b460 100644 --- a/public/language/fr/modules.json +++ b/public/language/fr/modules.json @@ -21,7 +21,7 @@ "composer.user_said_in": "%1 a dit dans %2 :", "composer.user_said": "%1 a dit :", "composer.discard": "Êtes-vous sûr de bien vouloir supprimer ce message ?", - "composer.submit_and_lock": "Soumettre et Verrouiller", + "composer.submit_and_lock": "Envoyer et verrouiller", "composer.toggle_dropdown": "Afficher/masquer le menu", "bootbox.ok": "OK", "bootbox.cancel": "Annuler", diff --git a/public/language/it/global.json b/public/language/it/global.json index 320a7f9498..4d9ee25eee 100644 --- a/public/language/it/global.json +++ b/public/language/it/global.json @@ -33,7 +33,7 @@ "header.notifications": "Notifiche", "header.search": "Cerca", "header.profile": "Profilo", - "header.navigation": "Navigation", + "header.navigation": "Navigazione", "notifications.loading": "Caricamento delle Notifiche", "chats.loading": "Caricamento Messaggi", "motd.welcome": "Benvenuti in NodeBB, la piattaforma di discussione del futuro.", @@ -68,7 +68,7 @@ "recentposts": "Post Recenti", "recentips": "Indirizzi IP Recentemente Loggati", "away": "Non disponibile", - "dnd": "Do not disturb", + "dnd": "Non disturbare", "invisible": "Invisibile", "offline": "Non in linea", "email": "Email", diff --git a/public/language/it/modules.json b/public/language/it/modules.json index a9ca9051a0..5d9b4a8e1d 100644 --- a/public/language/it/modules.json +++ b/public/language/it/modules.json @@ -5,7 +5,7 @@ "chat.no_active": "Non hai chat attive.", "chat.user_typing": "%1 sta scrivendo...", "chat.user_has_messaged_you": "%1 ti ha scritto.", - "chat.see_all": "See all chats", + "chat.see_all": "Vedi tutte le chat", "chat.no-messages": "Si prega di selezionare un destinatario per vedere la cronologia dei messaggi", "chat.recent-chats": "Chat Recenti", "chat.contacts": "Contatti", @@ -24,6 +24,6 @@ "composer.submit_and_lock": "Invia e Blocca", "composer.toggle_dropdown": "Mostra/Nascondi menu a discesa", "bootbox.ok": "OK", - "bootbox.cancel": "Cancel", - "bootbox.confirm": "Confirm" + "bootbox.cancel": "Annulla", + "bootbox.confirm": "Conferma" } \ No newline at end of file diff --git a/public/language/it/notifications.json b/public/language/it/notifications.json index b6052d0e92..031859daf3 100644 --- a/public/language/it/notifications.json +++ b/public/language/it/notifications.json @@ -1,7 +1,7 @@ { "title": "Notifiche", "no_notifs": "Non hai nuove notifiche", - "see_all": "See all notifications", + "see_all": "Vedi tutte le notifiche", "mark_all_read": "Segna tutte le notifiche come già lette", "back_to_home": "Indietro a %1", "outgoing_link": "Link in uscita", diff --git a/public/language/it/pages.json b/public/language/it/pages.json index 210feb51fb..fc632584c2 100644 --- a/public/language/it/pages.json +++ b/public/language/it/pages.json @@ -1,37 +1,37 @@ { "home": "Home", "unread": "Discussioni non lette", - "popular-day": "Popular topics today", - "popular-week": "Popular topics this week", - "popular-month": "Popular topics this month", - "popular-alltime": "All time popular topics", + "popular-day": "Discussioni popolari oggi", + "popular-week": "Discussioni popolari questa settimana", + "popular-month": "Discussioni popolari questo mese", + "popular-alltime": "Discussioni più popolari di sempre", "recent": "Discussioni Recenti", - "users/online": "Online Users", - "users/latest": "Latest Users", - "users/sort-posts": "Users with the most posts", - "users/sort-reputation": "Users with the most reputation", - "users/map": "User Map", - "users/search": "User Search", + "users/online": "Utenti Online", + "users/latest": "Ultimi Utenti", + "users/sort-posts": "Utenti maggiori contributori", + "users/sort-reputation": "Utenti con la reputazione più alta", + "users/map": "Mappa Utenti", + "users/search": "Ricerca Utenti", "notifications": "Notifiche", "tags": "Tags", "tag": "Discussioni taggate \"%1\"", - "register": "Register an account", - "login": "Login to your account", - "reset": "Reset your account password", - "categories": "Categories", - "groups": "Groups", - "group": "%1 group", - "chats": "Chats", - "chat": "Chatting with %1", - "account/edit": "Editing \"%1\"", - "account/following": "People %1 follows", - "account/followers": "People who follow %1", - "account/posts": "Posts made by %1", - "account/topics": "Topics created by %1", - "account/groups": "%1's Groups", - "account/favourites": "%1's Favourite Posts", - "account/settings": "User Settings", - "account/watched": "Topics watched by %1", + "register": "Registrati", + "login": "Autenticati", + "reset": "Resetta password", + "categories": "Categorie", + "groups": "Gruppi", + "group": "Gruppo %1", + "chats": "Chat", + "chat": "In chat con %1", + "account/edit": "Modificando \"%1\"", + "account/following": "Persone seguite da %1", + "account/followers": "Persone che seguono %1", + "account/posts": "Post creati da %1", + "account/topics": "Discussioni create da %1", + "account/groups": "Gruppi di %1", + "account/favourites": "Post Favoriti da %1", + "account/settings": "Impostazioni Utente", + "account/watched": "Discussioni osservate da %1", "maintenance.text": "%1 è attualmente in manutenzione. Per favore ritorna più tardi.", "maintenance.messageIntro": "Inoltre, l'amministratore ha lasciato questo messaggio:" } \ No newline at end of file diff --git a/public/language/it/user.json b/public/language/it/user.json index efdfafa7fe..e4aa957973 100644 --- a/public/language/it/user.json +++ b/public/language/it/user.json @@ -85,5 +85,5 @@ "follow_topics_you_create": "Segui le discussioni che hai iniziato", "grouptitle": "Seleziona il titolo del gruppo che vorresti vedere", "no-group-title": "Nessun titolo al gruppo", - "select-skin": "Select a Skin" + "select-skin": "Seleziona uno Skin" } \ No newline at end of file diff --git a/public/language/nl/global.json b/public/language/nl/global.json index 8cdd5286e3..3da3a88d9f 100644 --- a/public/language/nl/global.json +++ b/public/language/nl/global.json @@ -33,7 +33,7 @@ "header.notifications": "Notificaties", "header.search": "Zoeken", "header.profile": "Profiel", - "header.navigation": "Navigation", + "header.navigation": "Navigatie", "notifications.loading": "Notificaties laden", "chats.loading": "Chats laden", "motd.welcome": "Welkom bij NodeBB, het discussie platform van de toekomst.", @@ -68,7 +68,7 @@ "recentposts": "Recente berichten", "recentips": "IP-adressen van recente gebruikers", "away": "Afwezig", - "dnd": "Do not disturb", + "dnd": "Niet Storen", "invisible": "Onzichtbaar", "offline": "Offline", "email": "E-mailadres", diff --git a/public/language/nl/modules.json b/public/language/nl/modules.json index 538c5c9436..2324798d39 100644 --- a/public/language/nl/modules.json +++ b/public/language/nl/modules.json @@ -5,7 +5,7 @@ "chat.no_active": "Er zijn geen actieve chats.", "chat.user_typing": "%1 is aan het typen ...", "chat.user_has_messaged_you": "%1 heeft een bericht gestuurd", - "chat.see_all": "See all chats", + "chat.see_all": "Laat alle chats zien", "chat.no-messages": "Selecteer een ontvanger om de chatgeschiedenis in te zien", "chat.recent-chats": "Recent gevoerde gesprekken", "chat.contacts": "Contacten", @@ -24,6 +24,6 @@ "composer.submit_and_lock": "Plaats een bericht en vergrendel direct het onderwerp", "composer.toggle_dropdown": "Keuzelijst schakelen", "bootbox.ok": "OK", - "bootbox.cancel": "Cancel", - "bootbox.confirm": "Confirm" + "bootbox.cancel": "Anuleren", + "bootbox.confirm": "Bevestig" } \ No newline at end of file diff --git a/public/language/nl/notifications.json b/public/language/nl/notifications.json index a53316b83e..d8ee5b7287 100644 --- a/public/language/nl/notifications.json +++ b/public/language/nl/notifications.json @@ -1,7 +1,7 @@ { "title": "Notificaties", "no_notifs": "Je hebt geen nieuwe notificaties", - "see_all": "See all notifications", + "see_all": "Laat alle notificaties zien", "mark_all_read": "Markeer alles als gelezen", "back_to_home": "Terug naar %1", "outgoing_link": "Uitgaande Link", diff --git a/public/language/nl/pages.json b/public/language/nl/pages.json index bc944d347e..220ea1bc3b 100644 --- a/public/language/nl/pages.json +++ b/public/language/nl/pages.json @@ -1,37 +1,37 @@ { "home": "Home", "unread": "Ongelezen onderwerpen", - "popular-day": "Popular topics today", - "popular-week": "Popular topics this week", - "popular-month": "Popular topics this month", - "popular-alltime": "All time popular topics", + "popular-day": "De populaire onderwerpen van vandaag", + "popular-week": "De populaire onderwerpen van deze week", + "popular-month": "De populaire onderwerpen van deze maand", + "popular-alltime": "De populaire onderwerpen", "recent": "Recente onderwerpen", - "users/online": "Online Users", - "users/latest": "Latest Users", - "users/sort-posts": "Users with the most posts", - "users/sort-reputation": "Users with the most reputation", - "users/map": "User Map", - "users/search": "User Search", + "users/online": "Online Gebruikers", + "users/latest": "Meest recente gebruikers", + "users/sort-posts": "Gebruikers met de meeste berichten", + "users/sort-reputation": "Gebruikers met de meeste reputatie", + "users/map": "Gebruikersmap", + "users/search": "Zoek Gebruiker", "notifications": "Notificaties", "tags": "Tags", "tag": "Onderwerpen geplaatst onder \"%1\"", - "register": "Register an account", - "login": "Login to your account", - "reset": "Reset your account password", - "categories": "Categories", - "groups": "Groups", - "group": "%1 group", + "register": "Registeer een gebruikersaccount", + "login": "Login met u gebruikersaccount in", + "reset": "Gebruikerswachtwoord opnieuw instellen", + "categories": "Categorieën", + "groups": "Groepen", + "group": "%1's groep", "chats": "Chats", - "chat": "Chatting with %1", - "account/edit": "Editing \"%1\"", - "account/following": "People %1 follows", - "account/followers": "People who follow %1", - "account/posts": "Posts made by %1", - "account/topics": "Topics created by %1", - "account/groups": "%1's Groups", - "account/favourites": "%1's Favourite Posts", - "account/settings": "User Settings", - "account/watched": "Topics watched by %1", + "chat": "Chatten met %1", + "account/edit": "\"%1\" aanpassen", + "account/following": "Door %1 gevolgd", + "account/followers": "Die %1 volgen", + "account/posts": "Berichten geplaatst door %1", + "account/topics": "Onderwerpen begonnen door %1", + "account/groups": "%1's groepen", + "account/favourites": "Favoriete berichten van %1", + "account/settings": "Gebruikersinstellingen", + "account/watched": "Berichten die door %1 bekeken worden", "maintenance.text": "%1 is momenteel in onderhoud. Excuses voor het ongemak en probeer het later nog eens", "maintenance.messageIntro": "Daarnaast heeft de beheerder het volgende bericht achtergelaten:" } \ No newline at end of file diff --git a/public/language/nl/user.json b/public/language/nl/user.json index 343498373a..456cbe6e0a 100644 --- a/public/language/nl/user.json +++ b/public/language/nl/user.json @@ -85,5 +85,5 @@ "follow_topics_you_create": "Volg de onderwerpen waarvan ik de oorspronkelijke auteur ben", "grouptitle": "Selecteer de groepstitel voor weergave", "no-group-title": "Geen groepstitel", - "select-skin": "Select a Skin" + "select-skin": "Selecteer een uiterlijk" } \ No newline at end of file diff --git a/public/language/tr/global.json b/public/language/tr/global.json index d6da9c3e05..bcc82ef7c1 100644 --- a/public/language/tr/global.json +++ b/public/language/tr/global.json @@ -33,7 +33,7 @@ "header.notifications": "Bildirimler", "header.search": "Arama", "header.profile": "Profil", - "header.navigation": "Navigation", + "header.navigation": "Navigasyon", "notifications.loading": "Bildirimler Yükleniyor", "chats.loading": "Sohbetler Yükleniyor", "motd.welcome": "NodeBB, geleceğin tartışma platformuna hoş geldiniz.", @@ -68,7 +68,7 @@ "recentposts": "Güncel İletiler", "recentips": "Güncel giriş yapilan IP adresleri", "away": "Dışarıda", - "dnd": "Do not disturb", + "dnd": "Rahatsız etme", "invisible": "Görünmez", "offline": "Çevrimdışı", "email": "E-posta", diff --git a/public/language/tr/modules.json b/public/language/tr/modules.json index 310c7824e3..bd46d890be 100644 --- a/public/language/tr/modules.json +++ b/public/language/tr/modules.json @@ -5,7 +5,7 @@ "chat.no_active": "Aktif sohbet mevcut değil", "chat.user_typing": "%1 yazıyor ...", "chat.user_has_messaged_you": "%1 size bir mesaj gönderdi.", - "chat.see_all": "See all chats", + "chat.see_all": "Bütün sohbetleri gör", "chat.no-messages": "Lütfen sohbet geçmişini görmek için bir kontak seçin", "chat.recent-chats": "Güncel Sohbetler", "chat.contacts": "Kontaklar", @@ -23,7 +23,7 @@ "composer.discard": "Bu iletiyi iptal etmek istediğinizden eminmisiniz?", "composer.submit_and_lock": "Gönder ve Kitle", "composer.toggle_dropdown": "Menü aç", - "bootbox.ok": "OK", - "bootbox.cancel": "Cancel", - "bootbox.confirm": "Confirm" + "bootbox.ok": "Tamam", + "bootbox.cancel": "İptal", + "bootbox.confirm": "Onayla" } \ No newline at end of file diff --git a/public/language/tr/notifications.json b/public/language/tr/notifications.json index 0ccd12db98..d498a13dbe 100644 --- a/public/language/tr/notifications.json +++ b/public/language/tr/notifications.json @@ -1,7 +1,7 @@ { "title": "Bildirimler", "no_notifs": "Yeni bildirimleriniz yok", - "see_all": "See all notifications", + "see_all": "Bütün bildirimleri gör", "mark_all_read": "Okunmuş bütün bildirimleri işaretle", "back_to_home": "Geri dön %1", "outgoing_link": "Harici Link", diff --git a/public/language/tr/pages.json b/public/language/tr/pages.json index 8a4da32fbe..451dd4de9b 100644 --- a/public/language/tr/pages.json +++ b/public/language/tr/pages.json @@ -1,37 +1,37 @@ { "home": "Ana Sayfa", "unread": "Okunmayan Başlıklar", - "popular-day": "Popular topics today", - "popular-week": "Popular topics this week", - "popular-month": "Popular topics this month", - "popular-alltime": "All time popular topics", + "popular-day": "Bugünkü popüler başlıklar", + "popular-week": "Bu haftaki popüler başlıklar", + "popular-month": "Bu ayki popüler başlıklar", + "popular-alltime": "En popüler başlıklar", "recent": "Güncel Konular", - "users/online": "Online Users", - "users/latest": "Latest Users", - "users/sort-posts": "Users with the most posts", - "users/sort-reputation": "Users with the most reputation", - "users/map": "User Map", - "users/search": "User Search", + "users/online": "Çevrimiçi Kullanıcılar", + "users/latest": "En Yeni Kullanıcılar", + "users/sort-posts": "En çok ileti gönderen kullanıcılar", + "users/sort-reputation": "En çok saygınlığı olan kullanıcılar", + "users/map": "Kullanıcı Haritası", + "users/search": "Kullanıcı Ara", "notifications": "Bildirimler", "tags": "Etiketler", "tag": "“%1“ ile etiketlenmiş konular", - "register": "Register an account", - "login": "Login to your account", - "reset": "Reset your account password", - "categories": "Categories", - "groups": "Groups", - "group": "%1 group", - "chats": "Chats", - "chat": "Chatting with %1", - "account/edit": "Editing \"%1\"", - "account/following": "People %1 follows", - "account/followers": "People who follow %1", - "account/posts": "Posts made by %1", - "account/topics": "Topics created by %1", - "account/groups": "%1's Groups", - "account/favourites": "%1's Favourite Posts", - "account/settings": "User Settings", - "account/watched": "Topics watched by %1", + "register": "Bir hesap aç", + "login": "Hesabına giriş yap", + "reset": "Hesap şifreni baştan yarat", + "categories": "Kategoriler", + "groups": "Gruplar", + "group": "%1 grubu", + "chats": "Sohbetler", + "chat": "%1 ile sohbet", + "account/edit": "\"%1\" düzenleniyor", + "account/following": "%1 tarafından takip edilenler", + "account/followers": "%1 takip edenler", + "account/posts": "%1 tarafından gönderilen iletiler", + "account/topics": "%1 tarafından gönderilen başlıklar", + "account/groups": "%1 Kişisine Ait Gruplar", + "account/favourites": "%1'in Favori İletileri", + "account/settings": "Kullanıcı Ayarları", + "account/watched": "%1 tarafından izlenen konular", "maintenance.text": "%1 şu anda bakımda. Lütfen bir süre sonra tekrar deneyin.", "maintenance.messageIntro": "Ayrıca, yönetici şu mesaji bıraktı:" } \ No newline at end of file diff --git a/public/language/tr/user.json b/public/language/tr/user.json index 51e995af0a..fc1acecbb9 100644 --- a/public/language/tr/user.json +++ b/public/language/tr/user.json @@ -85,5 +85,5 @@ "follow_topics_you_create": "Kendi konularımı takip et", "grouptitle": "Göstermek istediğiniz gurup başlığını seçin", "no-group-title": "Grup başlığı yok", - "select-skin": "Select a Skin" + "select-skin": "Bir deri şeçin" } \ No newline at end of file From 6c10c846a177be78735bef4ce8a78e68bc0f0fda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 15 Sep 2015 15:19:42 -0400 Subject: [PATCH 104/117] up vanilla --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d666ff89ce..80097f9e06 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "nodebb-rewards-essentials": "0.0.5", "nodebb-theme-lavender": "2.0.1", "nodebb-theme-persona": "3.0.11", - "nodebb-theme-vanilla": "4.0.5", + "nodebb-theme-vanilla": "4.0.6", "nodebb-widget-essentials": "2.0.1", "npm": "^2.1.4", "passport": "^0.3.0", From 76178e32328c1b7ec9c960b3268532880eaa1464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 15 Sep 2015 15:56:19 -0400 Subject: [PATCH 105/117] move admin categories controller --- src/controllers/admin.js | 29 +-------------------- src/controllers/admin/categories.js | 40 +++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 28 deletions(-) create mode 100644 src/controllers/admin/categories.js diff --git a/src/controllers/admin.js b/src/controllers/admin.js index bf9b0f0087..023df9c6b1 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -19,7 +19,7 @@ var async = require('async'), var adminController = { - categories: {}, + categories: require('./admin/categories'), tags: {}, flags: {}, topics: {}, @@ -142,33 +142,6 @@ function getGlobalField(field, callback) { }); } -adminController.categories.get = function(req, res, next) { - async.parallel({ - category: async.apply(categories.getCategories, [req.params.category_id], req.user.uid), - privileges: async.apply(privileges.categories.list, req.params.category_id) - }, function(err, data) { - if (err) { - return next(err); - } - - plugins.fireHook('filter:admin.category.get', {req: req, res: res, category: data.category[0], privileges: data.privileges}, function(err, data) { - if (err) { - return next(err); - } - - res.render('admin/manage/category', { - category: data.category, - privileges: data.privileges - }); - }); - }); -}; - -adminController.categories.getAll = function(req, res, next) { - //Categories list will be rendered on client side with recursion, etc. - res.render('admin/manage/categories', {}); -}; - adminController.tags.get = function(req, res, next) { topics.getTags(0, 199, function(err, tags) { if (err) { diff --git a/src/controllers/admin/categories.js b/src/controllers/admin/categories.js new file mode 100644 index 0000000000..99bda3285b --- /dev/null +++ b/src/controllers/admin/categories.js @@ -0,0 +1,40 @@ +"use strict"; + +var async = require('async'), + + categories = require('../../categories'), + privileges = require('../../privileges'), + plugins = require('../../plugins'); + + +var categoriesController = {}; + +categoriesController.get = function(req, res, next) { + async.parallel({ + category: async.apply(categories.getCategories, [req.params.category_id], req.user.uid), + privileges: async.apply(privileges.categories.list, req.params.category_id) + }, function(err, data) { + if (err) { + return next(err); + } + + plugins.fireHook('filter:admin.category.get', {req: req, res: res, category: data.category[0], privileges: data.privileges}, function(err, data) { + if (err) { + return next(err); + } + + res.render('admin/manage/category', { + category: data.category, + privileges: data.privileges + }); + }); + }); +}; + +categoriesController.getAll = function(req, res, next) { + //Categories list will be rendered on client side with recursion, etc. + res.render('admin/manage/categories', {}); +}; + + +module.exports = categoriesController; From 51cdd0800c919c80baca6d3fbe852ce74dc63a58 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 15 Sep 2015 17:19:03 -0400 Subject: [PATCH 106/117] simplified calculatePostIndices --- src/topics.js | 10 +++------- src/topics/posts.js | 20 ++++++-------------- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/src/topics.js b/src/topics.js index fc7c1f49e9..45b066b3e1 100644 --- a/src/topics.js +++ b/src/topics.js @@ -274,17 +274,13 @@ var async = require('async'), if (!posts.length) { return next(null, []); } - + var replies = posts; if (topic.mainPid) { posts[0].index = 0; + replies = posts.slice(1); } - var indices = Topics.calculatePostIndices(start, stop, topic.postcount, reverse); - for (var i=1; i Date: Tue, 15 Sep 2015 18:21:17 -0400 Subject: [PATCH 107/117] privilege fixes --- src/privileges/categories.js | 11 +++ src/privileges/topics.js | 15 ++-- src/socket.io/topics.js | 45 +++++------- src/threadTools.js | 131 +++++++++++++++++++++++------------ 4 files changed, 128 insertions(+), 74 deletions(-) diff --git a/src/privileges/categories.js b/src/privileges/categories.js index feb3c53bc6..4ae73274bc 100644 --- a/src/privileges/categories.js +++ b/src/privileges/categories.js @@ -183,6 +183,17 @@ module.exports = function(privileges) { }); }; + privileges.categories.isAdminOrMod = function(cid, uid, callback) { + helpers.some([ + function (next) { + user.isModerator(uid, cid, next); + }, + function (next) { + user.isAdministrator(uid, next); + } + ], callback); + }; + privileges.categories.can = function(privilege, cid, uid, callback) { if (!cid) { return callback(null, false); diff --git a/src/privileges/topics.js b/src/privileges/topics.js index c61764fe8e..da5a61271b 100644 --- a/src/privileges/topics.js +++ b/src/privileges/topics.js @@ -2,6 +2,7 @@ 'use strict'; var async = require('async'), + winston = require('winston'), db = require('../database'), topics = require('../topics'), @@ -170,21 +171,27 @@ module.exports = function(privileges) { }; privileges.topics.canEdit = function(tid, uid, callback) { + winston.warn('[deprecated] please use privileges.topics.isOwnerOrAdminOrMod'); + privileges.topics.isOwnerOrAdminOrMod(tid, uid, callback); + }; + + privileges.topics.isOwnerOrAdminOrMod = function(tid, uid, callback) { helpers.some([ function(next) { topics.isOwner(tid, uid, next); }, function(next) { - isAdminOrMod(tid, uid, next); + privileges.topics.isAdminOrMod(tid, uid, next); } ], callback); }; privileges.topics.canMove = function(tid, uid, callback) { - isAdminOrMod(tid, uid, callback); + winston.warn('[deprecated] please use privileges.topics.isAdminOrMod'); + privileges.topics.isAdminOrMod(tid, uid, callback); }; - function isAdminOrMod(tid, uid, callback) { + privileges.topics.isAdminOrMod = function(tid, uid, callback) { helpers.some([ function(next) { topics.getTopicField(tid, 'cid', function(err, cid) { @@ -198,5 +205,5 @@ module.exports = function(privileges) { user.isAdministrator(uid, next); } ], callback); - } + }; }; diff --git a/src/socket.io/topics.js b/src/socket.io/topics.js index 1137422913..5d9fbed84f 100644 --- a/src/socket.io/topics.js +++ b/src/socket.io/topics.js @@ -234,42 +234,33 @@ SocketTopics.doTopicAction = function(action, event, socket, data, callback) { if (!socket.uid) { return; } - if(!data || !Array.isArray(data.tids) || !data.cid) { + + if (!data || !Array.isArray(data.tids) || !data.cid) { return callback(new Error('[[error:invalid-tid]]')); } + if (typeof threadTools[action] !== 'function') { + return callback(); + } + async.each(data.tids, function(tid, next) { - privileges.topics.canEdit(tid, socket.uid, function(err, canEdit) { + threadTools[action](tid, socket.uid, function(err, data) { if (err) { return next(err); } - if (!canEdit) { - return next(new Error('[[error:no-privileges]]')); + emitToTopicAndCategory(event, data); + + if (action === 'delete' || action === 'restore' || action === 'purge') { + events.log({ + type: 'topic-' + action, + uid: socket.uid, + ip: socket.ip, + tid: tid + }); } - if (typeof threadTools[action] !== 'function') { - return next(); - } - - threadTools[action](tid, socket.uid, function(err, data) { - if (err) { - return next(err); - } - - emitToTopicAndCategory(event, data); - - if (action === 'delete' || action === 'restore' || action === 'purge') { - events.log({ - type: 'topic-' + action, - uid: socket.uid, - ip: socket.ip, - tid: tid - }); - } - - next(); - }); + next(); }); }, callback); }; @@ -325,7 +316,7 @@ SocketTopics.move = function(socket, data, callback) { var topicData; async.waterfall([ function(next) { - privileges.topics.canMove(tid, socket.uid, next); + privileges.topics.isAdminOrMod(tid, socket.uid, next); }, function(canMove, next) { if (!canMove) { diff --git a/src/threadTools.js b/src/threadTools.js index 9e3bb2e53d..bbf78e1054 100644 --- a/src/threadTools.js +++ b/src/threadTools.js @@ -7,7 +7,8 @@ var async = require('async'), categories = require('./categories'), posts = require('./posts'), plugins = require('./plugins'), - batch = require('./batch'); + batch = require('./batch'), + privileges = require('./privileges'); (function(ThreadTools) { @@ -21,21 +22,29 @@ var async = require('async'), }; function toggleDelete(tid, uid, isDelete, callback) { - topics.getTopicFields(tid, ['tid', 'cid', 'uid', 'deleted', 'title', 'mainPid'], function(err, topicData) { - if (err) { - return callback(err); - } - - if (parseInt(topicData.deleted, 10) === 1 && isDelete) { - return callback(new Error('[[error:topic-already-deleted]]')); - } else if(parseInt(topicData.deleted, 10) !== 1 && !isDelete) { - return callback(new Error('[[error:topic-already-restored]]')); - } - - topics[isDelete ? 'delete' : 'restore'](tid, function(err) { - if (err) { - return callback(err); + var topicData; + async.waterfall([ + function (next) { + privileges.topics.isOwnerOrAdminOrMod(tid, uid, next); + }, + function (isOwnerOrAdminOrMod, next) { + if (!isOwnerOrAdminOrMod) { + return next(new Error('[[error:no-privileges]]')); } + topics.getTopicFields(tid, ['tid', 'cid', 'uid', 'deleted', 'title', 'mainPid'], next); + }, + function (_topicData, next) { + topicData = _topicData; + + if (parseInt(topicData.deleted, 10) === 1 && isDelete) { + return callback(new Error('[[error:topic-already-deleted]]')); + } else if(parseInt(topicData.deleted, 10) !== 1 && !isDelete) { + return callback(new Error('[[error:topic-already-restored]]')); + } + + topics[isDelete ? 'delete' : 'restore'](tid, next); + }, + function (next) { topicData.deleted = isDelete ? 1 : 0; if (isDelete) { @@ -52,8 +61,8 @@ var async = require('async'), }; callback(null, data); - }); - }); + } + ], callback); } ThreadTools.purge = function(tid, uid, callback) { @@ -66,21 +75,29 @@ var async = require('async'), if (!exists) { return callback(); } + privileges.topics.isOwnerOrAdminOrMod(tid, uid, next); + }, + function (isOwnerOrAdminOrMod, next) { + if (!isOwnerOrAdminOrMod) { + return next(new Error('[[error:no-privileges]]')); + } + + topics.getTopicFields(tid, ['mainPid', 'cid'], next); + }, + function (_topic, next) { + topic = _topic; + batch.processSortedSet('tid:' + tid + ':posts', function(pids, next) { async.eachLimit(pids, 10, posts.purge, next); }, {alwaysStartAt: 0}, next); }, - function(next) { - topics.getTopicFields(tid, ['mainPid', 'cid'], next); - }, - function(_topic, next) { - topic = _topic; + function (next) { posts.purge(topic.mainPid, next); }, - function(next) { + function (next) { topics.purge(tid, next); }, - function(next) { + function (next) { next(null, {tid: tid, cid: topic.cid, uid: uid}); } ], callback); @@ -96,24 +113,40 @@ var async = require('async'), function toggleLock(tid, uid, lock, callback) { callback = callback || function() {}; - topics.getTopicField(tid, 'cid', function(err, cid) { - if (err) { - return callback(err); + + var cid; + + async.waterfall([ + function (next) { + topics.getTopicField(tid, 'cid', next); + }, + function (_cid, next) { + cid = _cid; + if (!cid) { + return next(new Error('[[error:no-topic]]')); + } + privileges.categories.isAdminOrMod(cid, uid, next); + }, + function (isAdminOrMod, next) { + if (!isAdminOrMod) { + return next(new Error('[[error:no-privileges]]')); + } + + topics.setTopicField(tid, 'locked', lock ? 1 : 0, next); + }, + function (next) { + var data = { + tid: tid, + isLocked: lock, + uid: uid, + cid: cid + }; + + plugins.fireHook('action:topic.lock', data); + + next(null, data); } - - topics.setTopicField(tid, 'locked', lock ? 1 : 0); - - var data = { - tid: tid, - isLocked: lock, - uid: uid, - cid: cid - }; - - plugins.fireHook('action:topic.lock', data); - - callback(null, data); - }); + ], callback); } ThreadTools.pin = function(tid, uid, callback) { @@ -127,11 +160,23 @@ var async = require('async'), function togglePin(tid, uid, pin, callback) { var topicData; async.waterfall([ - function(next) { + function (next) { + topics.exists(tid, next); + }, + function (exists, next) { + if (!exists) { + return callback(); + } topics.getTopicFields(tid, ['cid', 'lastposttime'], next); }, - function(_topicData, next) { + function (_topicData, next) { topicData = _topicData; + privileges.categories.isAdminOrMod(_topicData.cid, uid, next); + }, + function(isAdminOrMod, next) { + if (!isAdminOrMod) { + return next(new Error('[[error:no-privileges]]')); + } async.parallel([ async.apply(topics.setTopicField, tid, 'pinned', pin ? 1 : 0), async.apply(db.sortedSetAdd, 'cid:' + topicData.cid + ':tids', pin ? Math.pow(2, 53) : topicData.lastposttime, tid) From ae1043543d955efc45691280548066655c91d22b Mon Sep 17 00:00:00 2001 From: psychobunny Date: Tue, 15 Sep 2015 18:34:31 -0400 Subject: [PATCH 108/117] closes #3585 --- src/controllers/accounts.js | 2 ++ src/socket.io/user.js | 4 ++++ src/views/admin/settings/user.tpl | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/src/controllers/accounts.js b/src/controllers/accounts.js index 2aa1bfcfe5..8e2ee930c2 100644 --- a/src/controllers/accounts.js +++ b/src/controllers/accounts.js @@ -374,6 +374,8 @@ accountsController.accountEdit = function(req, res, callback) { return callback(err); } + userData['username:disableEdit'] = parseInt(meta.config['username:disableEdit'], 10) === 1; + userData.hasPassword = !!password; userData.title = '[[pages:account/edit, ' + userData.username + ']]'; userData.breadcrumbs = helpers.buildBreadcrumbs([{text: userData.username, url: '/user/' + userData.userslug}, {text: '[[user:edit]]'}]); diff --git a/src/socket.io/user.js b/src/socket.io/user.js index 8946b3e03d..9d7a229120 100644 --- a/src/socket.io/user.js +++ b/src/socket.io/user.js @@ -231,6 +231,10 @@ SocketUser.updateProfile = function(socket, data, callback) { return callback(err); } + if (parseInt(meta.config['username:disableEdit'], 10) === 1) { + data.username = oldUserData.username; + } + update(oldUserData, callback); }); }; diff --git a/src/views/admin/settings/user.tpl b/src/views/admin/settings/user.tpl index 5468a07b5d..f5cc3884ee 100644 --- a/src/views/admin/settings/user.tpl +++ b/src/views/admin/settings/user.tpl @@ -44,6 +44,12 @@
      Account Settings
      +
      + +