From 66b6e12aa338382f0c4b347b87c17b603ff17108 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 30 Jan 2015 12:21:00 -0500 Subject: [PATCH 001/142] added grunt deps --- package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index c46f966b43..500543de16 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,10 @@ "xregexp": "~2.0.0" }, "devDependencies": { - "mocha": "~1.13.0" + "mocha": "~1.13.0", + "grunt": "~0.4.5", + "grunt-supervisor": "^0.2.4", + "grunt-express-server": "^0.4.19" }, "bugs": { "url": "https://github.com/NodeBB/NodeBB/issues" From f5ef3cf86634a9709f81a3059a30526bb14a3f15 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 30 Jan 2015 13:06:04 -0500 Subject: [PATCH 002/142] Gruntfile.js --- Gruntfile.js | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Gruntfile.js diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000000..19b0af68d0 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,53 @@ +module.exports = function(grunt) { + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + execute: { + dev: { + src: 'a.js', + options: { + args: ['arg'] + } + }, + minifyJS: { + src: 'app.js', + options: { + args: ['from-file'] + } + }, + minifyCSS: { + src: 'app.js', + options: { + args: ['from-file'] + } + }, + }, + watch: { + clientScripts: { + files: ['public/src/**/*.js'], + tasks: ['execute:minifyJS'], + options: { + spawn: false + } + }, + serverScripts: { + files: ['*.js', 'src/**/*.js', 'node_modules/**/*.js'], + tasks: ['execute:minifyCSS'], + options: { + spawn: false + } + }, + less: { + files: ['**/*.less'], + tasks: ['execute:minifyCSS'], + options: { + spawn: false + } + } + } + }); + + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-execute'); + + grunt.registerTask('default', ['watch', 'execute:minifyCSS']); +}; \ No newline at end of file From 7639bff0fef697e6b326b7a6c4eb6d8b80db888e Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 30 Jan 2015 13:06:17 -0500 Subject: [PATCH 003/142] updated package.json --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 500543de16..c8583eaf64 100644 --- a/package.json +++ b/package.json @@ -69,8 +69,8 @@ "devDependencies": { "mocha": "~1.13.0", "grunt": "~0.4.5", - "grunt-supervisor": "^0.2.4", - "grunt-express-server": "^0.4.19" + "grunt-contrib-watch": "^0.6.1", + "grunt-execute": "^0.2.2" }, "bugs": { "url": "https://github.com/NodeBB/NodeBB/issues" From df0e92471499645239800b511fd0ea5e62aa1d96 Mon Sep 17 00:00:00 2001 From: Mega Date: Mon, 2 Feb 2015 03:27:25 +0300 Subject: [PATCH 004/142] Change argument in action:posts.loaded --- public/src/client/home.js | 2 +- public/src/client/topic/posts.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/src/client/home.js b/public/src/client/home.js index 23049af741..f3b4e65acc 100644 --- a/public/src/client/home.js +++ b/public/src/client/home.js @@ -59,7 +59,7 @@ define('forum/home', function() { recentPosts.last().remove(); } - $(window).trigger('action:posts.loaded'); + $(window).trigger('action:posts.loaded', {pids: [post.pid]}); }); } diff --git a/public/src/client/topic/posts.js b/public/src/client/topic/posts.js index ec566d9812..1f49c6fdd0 100644 --- a/public/src/client/topic/posts.js +++ b/public/src/client/topic/posts.js @@ -130,7 +130,7 @@ define('forum/topic/posts', [ pids.push(data.posts[i].pid); } - $(window).trigger('action:posts.loaded', pids); + $(window).trigger('action:posts.loaded', {pids: pids}); onNewPostsLoaded(html, pids); callback(true); }); From a63c67993579ffec60a89cce61010c0457e77cc9 Mon Sep 17 00:00:00 2001 From: acardinale Date: Sun, 8 Feb 2015 11:58:01 +0100 Subject: [PATCH 005/142] Add hook "filter:topic.build" Same behavior as "filter:register.build" --- src/controllers/topics.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/controllers/topics.js b/src/controllers/topics.js index 498a3d81db..4c7708c2ba 100644 --- a/src/controllers/topics.js +++ b/src/controllers/topics.js @@ -266,7 +266,14 @@ topicsController.get = function(req, res, next) { }); topics.increaseViewCount(tid); - res.render('topic', data); + + plugins.fireHook('filter:topic.build', {req: req, res: res, templateData: data}, function(err, data) { + if (err && process.env === 'development') { + winston.warn(JSON.stringify(err)); + return next(err); + } + res.render('topic', data.templateData); + }); }); }; From 612acc41ba310d78d74c38daeef03adc32886988 Mon Sep 17 00:00:00 2001 From: acardinale Date: Sun, 8 Feb 2015 12:05:26 +0100 Subject: [PATCH 006/142] Add hook "filter:category.build" Same behavior as "filter:register.build" --- src/controllers/categories.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/controllers/categories.js b/src/controllers/categories.js index 25690a6810..7ea49453f5 100644 --- a/src/controllers/categories.js +++ b/src/controllers/categories.js @@ -266,7 +266,13 @@ categoriesController.get = function(req, res, next) { res.locals.linkTags.push(rel); }); - res.render('category', data); + plugins.fireHook('filter:category.build', {req: req, res: res, templateData: data}, function(err, data) { + if (err && process.env === 'development') { + winston.warn(JSON.stringify(err)); + return next(err); + } + res.render('category', data.templateData); + }); }); }; From c3f0f527578e629f5e237a70aa32795f448b3642 Mon Sep 17 00:00:00 2001 From: yariplus Date: Sun, 15 Feb 2015 01:53:18 -0500 Subject: [PATCH 007/142] Fix select and select multiple loading/saving on widget ACP. --- public/src/admin/extend/widgets.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/public/src/admin/extend/widgets.js b/public/src/admin/extend/widgets.js index 9632bbfa68..43f70de05a 100644 --- a/public/src/admin/extend/widgets.js +++ b/public/src/admin/extend/widgets.js @@ -80,7 +80,14 @@ define('admin/extend/widgets', function() { for (var d in data) { if (data.hasOwnProperty(d)) { if (data[d].name) { - widgetData[data[d].name] = data[d].value; + if (widgetData[data[d].name]) { + if(!Array.isArray(widgetData[data[d].name])) { + widgetData[data[d].name] = [ widgetData[data[d].name] ]; + } + widgetData[data[d].name].push(data[d].value); + }else{ + widgetData[data[d].name] = data[d].value; + } } } } @@ -162,7 +169,7 @@ define('admin/extend/widgets', function() { title.text(title.text() + ' - ' + data.title); } - widget.find('input, textarea').each(function() { + widget.find('input, textarea, select').each(function() { var input = $(this), value = data[input.attr('name')]; From 16c5c18165d75c9030f9b361a4d25ff0d0153497 Mon Sep 17 00:00:00 2001 From: Mega Date: Mon, 16 Feb 2015 10:43:56 +0300 Subject: [PATCH 008/142] Update arguments in action:posts.loaded --- public/src/client/home.js | 2 +- public/src/client/topic/posts.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/src/client/home.js b/public/src/client/home.js index f3b4e65acc..9b1a1862cb 100644 --- a/public/src/client/home.js +++ b/public/src/client/home.js @@ -59,7 +59,7 @@ define('forum/home', function() { recentPosts.last().remove(); } - $(window).trigger('action:posts.loaded', {pids: [post.pid]}); + $(window).trigger('action:posts.loaded', {posts: [post]}); }); } diff --git a/public/src/client/topic/posts.js b/public/src/client/topic/posts.js index 1f49c6fdd0..8137cdb8ab 100644 --- a/public/src/client/topic/posts.js +++ b/public/src/client/topic/posts.js @@ -130,7 +130,7 @@ define('forum/topic/posts', [ pids.push(data.posts[i].pid); } - $(window).trigger('action:posts.loaded', {pids: pids}); + $(window).trigger('action:posts.loaded', {posts: data.posts}); onNewPostsLoaded(html, pids); callback(true); }); From bd553eb05d6fe34ddc00ea47768bc3c7b1c09034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 17 Feb 2015 13:25:13 -0500 Subject: [PATCH 009/142] closes #2002 --- public/src/client/{home.js => categories.js} | 27 ++++++++++---------- src/controllers/categories.js | 4 +-- src/controllers/index.js | 6 +++-- src/controllers/templates.js | 3 ++- src/views/admin/settings/general.tpl | 2 +- src/views/config.json | 2 +- 6 files changed, 23 insertions(+), 21 deletions(-) rename public/src/client/{home.js => categories.js} (70%) diff --git a/public/src/client/home.js b/public/src/client/categories.js similarity index 70% rename from public/src/client/home.js rename to public/src/client/categories.js index 9b1a1862cb..6840f69e89 100644 --- a/public/src/client/home.js +++ b/public/src/client/categories.js @@ -2,35 +2,34 @@ /* globals define, socket, app, templates, translator, ajaxify*/ -define('forum/home', function() { - var home = {}; +define('forum/categories', function() { + var categories = {}; $(window).on('action:ajaxify.start', function(ev, data) { - if (data.tpl_url !== 'home') { - socket.removeListener('event:new_post', home.onNewPost); + if (data.tpl_url !== 'categories') { + socket.removeListener('event:new_post', categories.onNewPost); } }); + categories.init = function() { + app.enterRoom('categories'); - home.init = function() { - app.enterRoom('home'); + socket.removeListener('event:new_post', categories.onNewPost); + socket.on('event:new_post', categories.onNewPost); - socket.removeListener('event:new_post', home.onNewPost); - socket.on('event:new_post', home.onNewPost); - - $('.home .category-header').tooltip({ + $('.category-header').tooltip({ placement: 'bottom' }); }; - home.onNewPost = function(data) { + categories.onNewPost = function(data) { if (data && data.posts && data.posts.length && data.posts[0].topic) { renderNewPost(data.posts[0].topic.cid, data.posts[0]); } }; function renderNewPost(cid, post) { - var category = $('.home .category-item[data-cid="' + cid + '"]'); + var category = $('.category-item[data-cid="' + cid + '"]'); if (!category.length) { return; } @@ -64,7 +63,7 @@ define('forum/home', function() { } function parseAndTranslate(posts, callback) { - templates.parse('home', 'posts', {categories: {posts: posts}}, function(html) { + templates.parse('categories', 'posts', {categories: {posts: posts}}, function(html) { translator.translate(html, function(translatedHTML) { translatedHTML = $(translatedHTML); translatedHTML.find('img').addClass('img-responsive'); @@ -74,5 +73,5 @@ define('forum/home', function() { }); } - return home; + return categories; }); diff --git a/src/controllers/categories.js b/src/controllers/categories.js index 9ef288272c..187653268d 100644 --- a/src/controllers/categories.js +++ b/src/controllers/categories.js @@ -154,8 +154,8 @@ categoriesController.list = function(req, res, next) { if (err) { return next(err); } - // TODO: template should be called categories.tpl - res.render('home', data); + + res.render('categories', data); }); }; diff --git a/src/controllers/index.js b/src/controllers/index.js index 805ba52d20..c8511c7c61 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -30,13 +30,15 @@ var Controllers = { Controllers.home = function(req, res, next) { - var route = meta.config.homePageRoute || 'home'; - if (route === 'home') { + var route = meta.config.homePageRoute || 'categories'; + if (route === 'categories') { return Controllers.categories.list(req, res, next); } else if (route === 'recent') { Controllers.categories.recent(req, res, next); } else if (route === 'popular') { Controllers.categories.popular(req, res, next); + } else { + next(); } }; diff --git a/src/controllers/templates.js b/src/controllers/templates.js index 0d686a1455..7cbe6728e9 100644 --- a/src/controllers/templates.js +++ b/src/controllers/templates.js @@ -24,7 +24,8 @@ templatesController.getTemplatesListing = function(req, res, next) { readConfigFile(next); }, function(config, next) { - config.custom_mapping['^/?$'] = meta.config.homePageRoute || 'home'; + console.log(meta.config.homePageRoute); + config.custom_mapping['^/?$'] = meta.config.homePageRoute || 'categories'; plugins.fireHook('filter:templates.get_config', config, next); } diff --git a/src/views/admin/settings/general.tpl b/src/views/admin/settings/general.tpl index 66a27dee61..b403835994 100644 --- a/src/views/admin/settings/general.tpl +++ b/src/views/admin/settings/general.tpl @@ -34,7 +34,7 @@
diff --git a/src/views/config.json b/src/views/config.json index 26ae7b692c..f8a04fa620 100644 --- a/src/views/config.json +++ b/src/views/config.json @@ -1,6 +1,6 @@ { "custom_mapping": { - "^\/?$": "home", + "^\/?$": "categories", "^admin?$": "admin/general/dashboard", "^users/sort-posts": "users", "^users/latest": "users", From 8f6b577bad5e82ed54ec3147b9522146112b12aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 17 Feb 2015 14:50:08 -0500 Subject: [PATCH 010/142] Update package.json --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index a7907757e8..b6a70902e4 100644 --- a/package.json +++ b/package.json @@ -42,9 +42,9 @@ "nodebb-plugin-mentions": "^0.9.0", "nodebb-plugin-soundpack-default": "~0.1.1", "nodebb-plugin-spam-be-gone": "^0.4.0", - "nodebb-theme-lavender": "^1.0.0", - "nodebb-theme-vanilla": "^1.0.0", - "nodebb-widget-essentials": "~0.2.0", + "nodebb-theme-lavender": "^1.0.4", + "nodebb-theme-vanilla": "^1.0.5", + "nodebb-widget-essentials": "~0.2.11", "npm": "^2.1.4", "passport": "^0.2.1", "passport-local": "1.0.0", From 0611b7e1eac8e25228bcd58fd78ffdbb5fde5787 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Tue, 17 Feb 2015 15:12:31 -0500 Subject: [PATCH 011/142] closes #2733 --- src/controllers/admin/uploads.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/admin/uploads.js b/src/controllers/admin/uploads.js index 9ba3ad049c..81a98ed151 100644 --- a/src/controllers/admin/uploads.js +++ b/src/controllers/admin/uploads.js @@ -30,7 +30,7 @@ uploadsController.uploadFavicon = function(req, res, next) { var uploadedFile = req.files.files[0]; var allowedTypes = ['image/x-icon', 'image/vnd.microsoft.icon']; - if (validateUpload(res, req, next, uploadedFile, allowedTypes)) { + if (validateUpload(req, res, next, uploadedFile, allowedTypes)) { file.saveFileToLocal('favicon.ico', 'files', uploadedFile.path, function(err, image) { fs.unlink(uploadedFile.path); if (err) { @@ -63,7 +63,7 @@ function validateUpload(req, res, next, uploadedFile, allowedTypes) { if (allowedTypes.indexOf(uploadedFile.type) === -1) { fs.unlink(uploadedFile.path); - next(new Error('[[error:invalid-image-type, ' + allowedTypes.join(', ') + ']]')); + res.json({error: '[[error:invalid-image-type, ' + allowedTypes.join(', ') + ']]'}); return false; } From 276cd51836fbba54187f948fab31ec18279e3f97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 17 Feb 2015 15:13:58 -0500 Subject: [PATCH 012/142] closes #2467 --- public/language/en_GB/notifications.json | 1 + public/src/client/notifications.js | 2 +- public/src/modules/notifications.js | 79 ++++++++++-------------- src/controllers/templates.js | 1 - src/socket.io/modules.js | 10 --- src/socket.io/notifications.js | 10 ++- 6 files changed, 45 insertions(+), 58 deletions(-) diff --git a/public/language/en_GB/notifications.json b/public/language/en_GB/notifications.json index 54c926a597..96aef60183 100644 --- a/public/language/en_GB/notifications.json +++ b/public/language/en_GB/notifications.json @@ -2,6 +2,7 @@ "title": "Notifications", "no_notifs": "You have no new notifications", "see_all": "See all Notifications", + "mark_all_read": "Mark all notifications read", "back_to_home": "Back to %1", "outgoing_link": "Outgoing Link", diff --git a/public/src/client/notifications.js b/public/src/client/notifications.js index b0fbb4619f..024fe6715c 100644 --- a/public/src/client/notifications.js +++ b/public/src/client/notifications.js @@ -10,7 +10,7 @@ define('forum/notifications', function() { $('span.timeago').timeago(); $('.notifications .delete').on('click', function() { - socket.emit('notifications.deleteAll', function(err) { + socket.emit('notifications.markAllRead', function(err) { if (err) { return app.alertError(err.message); } diff --git a/public/src/modules/notifications.js b/public/src/modules/notifications.js index 319d88041f..8ce107f20a 100644 --- a/public/src/modules/notifications.js +++ b/public/src/modules/notifications.js @@ -2,7 +2,6 @@ /* globals define, socket, translator, utils, config, app, ajaxify, Tinycon*/ - define('notifications', ['sounds'], function(sound) { var Notifications = {}; @@ -14,54 +13,44 @@ define('notifications', ['sounds'], function(sound) { notifTrigger.on('click', function(e) { e.preventDefault(); - if (!notifContainer.hasClass('open')) { - - socket.emit('notifications.get', null, function(err, data) { - - function createNotification(notification, callback) { - if (notification.image) { - image = ''; - } else { - image = ''; - } - - return '
  • ' + image + '' + $.timeago(new Date(parseInt(notification.datetime, 10))) + '' + notification.bodyShort + '
  • '; - } - - var x, html = ''; - - // Switch to shorthand - translator.toggleTimeagoShorthand(); - - if (!err && (data.read.length + data.unread.length) > 0) { - var image = ''; - for (x = 0; x < data.unread.length; x++) { - html += createNotification(data.unread[x]); - } - - for (x = 0; x < data.read.length; x++) { - html += createNotification(data.read[x]); - } - } else { - html += '
  • [[notifications:no_notifs]]
  • '; - } - - // Switch back to original timeago strings - translator.toggleTimeagoShorthand(); - - html += ''; + if (notifContainer.hasClass('open')) { + return; + } + socket.emit('notifications.get', null, function(err, data) { + if (err) { + return app.alertError(err.message); + } + + var notifs = data.unread.concat(data.read); + + translator.toggleTimeagoShorthand(); + for(var i=0; i Date: Tue, 17 Feb 2015 16:01:49 -0500 Subject: [PATCH 013/142] querying via qs now --- src/plugins.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins.js b/src/plugins.js index 730377c673..cc8220f84f 100644 --- a/src/plugins.js +++ b/src/plugins.js @@ -171,7 +171,7 @@ var fs = require('fs'), Plugins.getAll = function(callback) { var request = require('request'); - request((nconf.get('registry') || 'https://packages.nodebb.org') + '/api/v1/plugins/' + pkg.version, function(err, res, body) { + request((nconf.get('registry') || 'https://packages.nodebb.org') + '/api/v1/plugins?version=' + pkg.version, function(err, res, body) { var plugins = []; try { From df7c48c47479437e0a16d741208814ad90842e7f Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 17 Feb 2015 16:09:25 -0500 Subject: [PATCH 014/142] restoring latest plugin version in plugins ACP --- src/plugins.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins.js b/src/plugins.js index cc8220f84f..b33fe46ac7 100644 --- a/src/plugins.js +++ b/src/plugins.js @@ -187,8 +187,7 @@ var fs = require('fs'), plugins[i].installed = false; plugins[i].active = false; plugins[i].url = plugins[i].url ? plugins[i].url : plugins[i].repository ? plugins[i].repository.url : ''; - plugins[i].latest = getLatestVersion(plugins[i].versions); - // plugins[i].latest = plugins[i].latest; + plugins[i].latest = plugins[i].latest; pluginMap[plugins[i].name] = plugins[i]; } From 150d33eb92af05924d8c62d09c675994b7c84546 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 17 Feb 2015 17:55:15 -0500 Subject: [PATCH 015/142] upgrade script to rename widgets:home.tpl to widgets:categories.tpl --- src/upgrade.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/upgrade.js b/src/upgrade.js index 52347bcb40..977854ab01 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -21,7 +21,7 @@ var db = require('./database'), schemaDate, thisSchemaDate, // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema - latestSchema = Date.UTC(2015, 1, 8); + latestSchema = Date.UTC(2015, 1, 17); Upgrade.check = function(callback) { db.get('schemaDate', function(err, value) { @@ -827,6 +827,25 @@ Upgrade.upgrade = function(callback) { winston.info('[2015/02/08] Clearing reset tokens skipped'); next(); } + }, + function(next) { + thisSchemaDate = Date.UTC(2015, 1, 17); + if (schemaDate < thisSchemaDate) { + updatesMade = true; + winston.info('[2015/02/17] renaming home.tpl to categories.tpl'); + + db.rename('widgets:home.tpl', 'widgets:categories.tpl', function(err) { + if (err) { + return next(err); + } + + winston.info('[2015/02/17] renaming home.tpl to categories.tpl done'); + Upgrade.update(thisSchemaDate, next); + }); + } else { + winston.info('[2015/02/17] renaming home.tpl to categories.tpl skipped'); + next(); + } } // Add new schema updates here From 0096d74cfad73b56c98a38bef840e773ab44d09c Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 17 Feb 2015 18:11:30 -0500 Subject: [PATCH 016/142] user password reset test --- src/user/reset.js | 2 +- tests/user.js | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/user/reset.js b/src/user/reset.js index 60ff820dbf..e010a1448e 100644 --- a/src/user/reset.js +++ b/src/user/reset.js @@ -51,7 +51,7 @@ var async = require('async'), template: 'reset', uid: uid }); - callback(); + callback(null, reset_code); }); }); }; diff --git a/tests/user.js b/tests/user.js index 466379c6e8..5c2f5bfd62 100644 --- a/tests/user.js +++ b/tests/user.js @@ -14,7 +14,8 @@ var assert = require('assert'), var User = require('../src/user'), Topics = require('../src/topics'), Categories = require('../src/categories'), - Meta = require('../src/meta'); + Meta = require('../src/meta'), + Password = require('../src/password'); describe('User', function() { var userData, @@ -193,6 +194,37 @@ describe('User', function() { }); }); + describe('passwordReset', function() { + var uid; + before(function(done) { + User.create({username: 'resetuser', password: '123456', email: 'reset@me.com'}, function(err, newUid) { + assert.ifError(err); + uid = newUid; + done(); + }); + }); + + it('should create a new reset code and reset password', function(done) { + User.reset.send('reset@me.com', function(err, code) { + assert.ifError(err); + assert(code); + + User.reset.commit(code, 'newpassword', function(err) { + assert.ifError(err); + + db.getObjectField('user:' + uid, 'password', function(err, newPassword) { + assert.ifError(err); + Password.compare('newpassword', newPassword, function(err, match) { + assert.ifError(err); + assert(match); + done(); + }); + }); + }); + }); + }); + }); + after(function() { db.flushdb(); }); From c305cd8220212519e8ee450deebb6ba138b0f20a Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 17 Feb 2015 18:14:29 -0500 Subject: [PATCH 017/142] removed commented out code --- src/user/reset.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/user/reset.js b/src/user/reset.js index e010a1448e..069ab4dd62 100644 --- a/src/user/reset.js +++ b/src/user/reset.js @@ -21,7 +21,6 @@ var async = require('async'), } db.sortedSetScore('reset:issueDate', code, function(err, issueDate) { - // db.getObjectField('reset:expiry', code, function(err, expiry) { if (err) { return callback(err); } From fbcc7c4bc374a5a0f98c342a720fcc1e88557b67 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 17 Feb 2015 18:52:54 -0500 Subject: [PATCH 018/142] closes #2634 --- public/src/client/pagination.js | 12 +++++------- public/src/client/topic/posts.js | 13 ++++++++++--- src/topics/create.js | 2 +- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/public/src/client/pagination.js b/public/src/client/pagination.js index 3d306e9f65..e2df0a6ebe 100644 --- a/public/src/client/pagination.js +++ b/public/src/client/pagination.js @@ -20,16 +20,14 @@ define('forum/pagination', function() { }; pagination.loadPage = function(page, callback) { + callback = callback || function() {}; page = parseInt(page, 10); - if(!utils.isNumber(page) || page < 1 || page > pagination.pageCount) { + if (!utils.isNumber(page) || page < 1 || page > pagination.pageCount) { + callback(false); return false; } - - ajaxify.go(window.location.pathname.slice(1) + '?page=' + page, function() { - if (typeof callback === 'function') { - callback(); - } - }); + var url = window.location.pathname.slice(1).split('/').slice(0, 3).join('/') + '?page=' + page; + ajaxify.go(url, callback); return true; }; diff --git a/public/src/client/topic/posts.js b/public/src/client/topic/posts.js index 7d3933b8de..d95a015c9e 100644 --- a/public/src/client/topic/posts.js +++ b/public/src/client/topic/posts.js @@ -31,11 +31,18 @@ define('forum/topic/posts', [ }; function onNewPostPagination(data) { + function scrollToPost() { + navigator.scrollBottom(data.posts[0].index); + } + var posts = data.posts; + + pagination.pageCount = Math.max(1, Math.ceil((posts[0].topic.postcount - 1) / config.postsPerPage)); + if (pagination.currentPage === pagination.pageCount) { - createNewPosts(data); - } else if(data.posts && data.posts.length && parseInt(data.posts[0].uid, 10) === parseInt(app.user.uid, 10)) { - pagination.loadPage(pagination.pageCount); + createNewPosts(data, scrollToPost); + } else if (parseInt(posts[0].uid, 10) === parseInt(app.user.uid, 10)) { + pagination.loadPage(pagination.pageCount, scrollToPost); } } diff --git a/src/topics/create.js b/src/topics/create.js index 2eb882da80..1e9336fedc 100644 --- a/src/topics/create.js +++ b/src/topics/create.js @@ -243,7 +243,7 @@ module.exports = function(Topics) { posts.getUserInfoForPosts([postData.uid], uid, next); }, topicInfo: function(next) { - Topics.getTopicFields(tid, ['tid', 'title', 'slug', 'cid'], next); + Topics.getTopicFields(tid, ['tid', 'title', 'slug', 'cid', 'postcount'], next); }, settings: function(next) { user.getSettings(uid, next); From 75657605ad27972cbcf605db4acf8ba3bb75bfec Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 17 Feb 2015 18:57:24 -0500 Subject: [PATCH 019/142] fix typo in search --- public/src/client/search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/client/search.js b/public/src/client/search.js index a8924ce8f9..95f49b60f8 100644 --- a/public/src/client/search.js +++ b/public/src/client/search.js @@ -44,7 +44,7 @@ define('forum/search', ['search'], function(searchModule) { in: form.find('#search-in').val() }; - if (searchData.in === 'posts' || searchData.in === 'titlespost' || searchData.in === 'titles') { + if (searchData.in === 'posts' || searchData.in === 'titlesposts' || searchData.in === 'titles') { searchData.by = form.find('#posted-by-user').val(); searchData.categories = form.find('#posted-in-categories').val(); searchData.searchChildren = form.find('#search-children').is(':checked'); From 540facaaab2757ea6b30535d9e25c5253e7c0e24 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Tue, 17 Feb 2015 19:18:55 -0500 Subject: [PATCH 020/142] try #2 (I give up, grunt-watch seems so unpredictable... on windows anyways) --- Gruntfile.js | 61 +++++++++++++----------- bcrypt.js | 1 + package.json | 4 +- public/less/admin/advanced/database.less | 19 +++++++- public/src/overrides.js | 1 + src/webserver.js | 4 +- 6 files changed, 57 insertions(+), 33 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 19b0af68d0..4db400bf83 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,53 +1,56 @@ module.exports = function(grunt) { grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), - execute: { + express: { dev: { - src: 'a.js', options: { - args: ['arg'] + script: 'app.js', + node_env: 'development', + args: ['dev', 'from-file'], + output: "NodeBB Ready" } }, minifyJS: { - src: 'app.js', options: { - args: ['from-file'] + script: 'app.js', + node_env: 'development', + args: ['dev', 'minify-js'], + output: "NodeBB Ready" } }, - minifyCSS: { - src: 'app.js', + compileLESS: { options: { - args: ['from-file'] + script: 'app.js', + node_env: 'development', + args: ['dev', 'compile-less'], + output: "NodeBB Ready" } - }, + } }, - watch: { - clientScripts: { - files: ['public/src/**/*.js'], - tasks: ['execute:minifyJS'], - options: { - spawn: false - } - }, - serverScripts: { - files: ['*.js', 'src/**/*.js', 'node_modules/**/*.js'], - tasks: ['execute:minifyCSS'], - options: { - spawn: false + less: { + development: { + files: { + "public/bin/manifest.css": "source/manifest.less" } - }, - less: { - files: ['**/*.less'], - tasks: ['execute:minifyCSS'], + } + }, + watch: { + compileLESS: { + files: "**/*.less", + tasks: ['express:compileLESS'], options: { - spawn: false + livereload: true, } } } }); grunt.loadNpmTasks('grunt-contrib-watch'); - grunt.loadNpmTasks('grunt-execute'); + grunt.loadNpmTasks('grunt-express-server'); + + grunt.registerTask('default', ['express:dev', 'watch']); - grunt.registerTask('default', ['watch', 'execute:minifyCSS']); + grunt.event.on('watch', function(action, filepath, target) { + grunt.log.writeln(target + ': ' + filepath + ' has ' + action); + }); }; \ No newline at end of file diff --git a/bcrypt.js b/bcrypt.js index 7611ab61a9..1445999a8a 100644 --- a/bcrypt.js +++ b/bcrypt.js @@ -13,6 +13,7 @@ process.on('message', function(msg) { } }); + function hashPassword(password, rounds) { async.waterfall([ function(next) { diff --git a/package.json b/package.json index 6d3525778c..cf4289efbf 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,9 @@ "mocha": "~1.13.0", "grunt": "~0.4.5", "grunt-contrib-watch": "^0.6.1", - "grunt-execute": "^0.2.2" + "grunt-execute": "^0.2.2", + "grunt-express-server": "^0.4.19", + "grunt-contrib-less": "^1.0.0" }, "bugs": { "url": "https://github.com/NodeBB/NodeBB/issues" diff --git a/public/less/admin/advanced/database.less b/public/less/admin/advanced/database.less index c81ef613cc..3799ced20a 100644 --- a/public/less/admin/advanced/database.less +++ b/public/less/admin/advanced/database.less @@ -3,4 +3,21 @@ display:inline-block; width:220px; } -} \ No newline at end of file +} + + + + + + + + + + + + + + + + + diff --git a/public/src/overrides.js b/public/src/overrides.js index 978dea0720..913a5abf3f 100644 --- a/public/src/overrides.js +++ b/public/src/overrides.js @@ -1,5 +1,6 @@ 'use strict'; + if ('undefined' !== typeof window) { (function ($, undefined) { diff --git a/src/webserver.js b/src/webserver.js index 01dee2b91c..5512c84e40 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -39,8 +39,8 @@ if(nconf.get('ssl')) { // Preparation dependent on plugins plugins.ready(function() { async.parallel([ - async.apply(!nconf.get('from-file') ? meta.js.minify : meta.js.getFromFile, app.enabled('minification')), - async.apply(!nconf.get('from-file') ? meta.css.minify : meta.css.getFromFile), + async.apply((!nconf.get('minify-js') && !nconf.get('from-file')) ? meta.js.minify : meta.js.getFromFile, app.enabled('minification')), + async.apply((!nconf.get('compile-less') && !nconf.get('from-file')) ? meta.css.minify : meta.css.getFromFile), async.apply(meta.sounds.init) ]); }); From 3bcd8aefb0490a4faa59bc154fd9e3173698b3b7 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 17 Feb 2015 22:22:06 -0500 Subject: [PATCH 021/142] deleteObjectFields method cleaned up user reset --- src/database/mongo/hash.js | 13 +++- src/database/redis/hash.js | 6 ++ src/user/reset.js | 132 +++++++++++++++++++++---------------- tests/database/hash.js | 18 ++++- 4 files changed, 108 insertions(+), 61 deletions(-) diff --git a/src/database/mongo/hash.js b/src/database/mongo/hash.js index f40a756600..5c5970f1d9 100644 --- a/src/database/mongo/hash.js +++ b/src/database/mongo/hash.js @@ -200,13 +200,20 @@ module.exports = function(db, module) { }; module.deleteObjectField = function(key, field, callback) { + module.deleteObjectFields(key, [field], callback); + }; + + module.deleteObjectFields = function(key, fields, callback) { callback = callback || helpers.noop; - if (!key || !field) { + if (!key || !Array.isArray(fields) || !fields.length) { return callback(); } var data = {}; - field = helpers.fieldToString(field); - data[field] = ''; + fields.forEach(function(field) { + field = helpers.fieldToString(field); + data[field] = ''; + }); + db.collection('objects').update({_key: key}, {$unset : data}, function(err, res) { callback(err); }); diff --git a/src/database/redis/hash.js b/src/database/redis/hash.js index 92ae7017c0..ce79d0bc6f 100644 --- a/src/database/redis/hash.js +++ b/src/database/redis/hash.js @@ -94,6 +94,12 @@ module.exports = function(redisClient, module) { }); }; + module.deleteObjectFields = function(key, fields, callback) { + helpers.multiKeyValues(redisClient, 'hdel', key, fields, function(err, results) { + callback(err); + }); + }; + module.incrObjectField = function(key, field, callback) { redisClient.hincrby(key, field, 1, callback); }; diff --git a/src/user/reset.js b/src/user/reset.js index 069ab4dd62..7434c4fc30 100644 --- a/src/user/reset.js +++ b/src/user/reset.js @@ -10,91 +10,109 @@ var async = require('async'), db = require('../database'), meta = require('../meta'), - events = require('../events'), emailer = require('../emailer'); (function(UserReset) { - UserReset.validate = function(code, callback) { - db.getObjectField('reset:uid', code, function(err, uid) { - if (err || !uid) { - return callback(err, false); - } + var twoHours = 7200000; - db.sortedSetScore('reset:issueDate', code, function(err, issueDate) { - if (err) { - return callback(err); + UserReset.validate = function(code, callback) { + async.waterfall([ + function(next) { + db.getObjectField('reset:uid', code, next); + }, + function(uid, next) { + if (!uid) { + return callback(null, false); } - - callback(null, parseInt(issueDate, 10) > (Date.now() - (1000*60*120))); - }); - }); + db.sortedSetScore('reset:issueDate', code, next); + }, + function(issueDate, next) { + next(null, parseInt(issueDate, 10) > Date.now() - twoHours); + } + ], callback); }; UserReset.send = function(email, callback) { - user.getUidByEmail(email, function(err, uid) { - if (err || !uid) { - return callback(err || new Error('[[error:invalid-email]]')); - } - - var reset_code = utils.generateUUID(); - db.setObjectField('reset:uid', reset_code, uid); - db.sortedSetAdd('reset:issueDate', Date.now(), reset_code); - - var reset_link = nconf.get('url') + '/reset/' + reset_code; + var reset_code = utils.generateUUID(); + var uid; + async.waterfall([ + function(next) { + user.getUidByEmail(email, next); + }, + function(_uid, next) { + if (!_uid) { + return next(new Error('[[error:invalid-email]]')); + } - translator.translate('[[email:password-reset-requested, ' + (meta.config.title || 'NodeBB') + ']]', meta.config.defaultLang, function(subject) { + uid = _uid; + async.parallel([ + async.apply(db.setObjectField, 'reset:uid', reset_code, uid), + async.apply(db.sortedSetAdd, 'reset:issueDate', Date.now(), reset_code) + ], next); + }, + function(results, next) { + translator.translate('[[email:password-reset-requested, ' + (meta.config.title || 'NodeBB') + ']]', meta.config.defaultLang, function(subject) { + next(null, subject); + }); + }, + function(subject, next) { + var reset_link = nconf.get('url') + '/reset/' + reset_code; emailer.send('reset', uid, { site_title: (meta.config.title || 'NodeBB'), reset_link: reset_link, subject: subject, template: 'reset', uid: uid - }); - callback(null, reset_code); - }); - }); + }, next); + }, + function(next) { + next(null, reset_code); + } + ], callback); }; UserReset.commit = function(code, password, callback) { - UserReset.validate(code, function(err, validated) { - if(err) { - return callback(err); - } - - if (!validated) { - return callback(new Error('[[error:reset-code-not-valid]]')); - } - - db.getObjectField('reset:uid', code, function(err, uid) { - if (err) { - return callback(err); + var uid; + async.waterfall([ + function(next) { + UserReset.validate(code, next); + }, + function(validated, next) { + if (!validated) { + return next(new Error('[[error:reset-code-not-valid]]')); + } + db.getObjectField('reset:uid', code, next); + }, + function(_uid, next) { + uid = _uid; + if (!uid) { + return next(new Error('[[error:reset-code-not-valid]]')); } - user.hashPassword(password, function(err, hash) { - if (err) { - return callback(err); - } - user.setUserField(uid, 'password', hash); - - db.deleteObjectField('reset:uid', code); - db.sortedSetRemove('reset:issueDate', code); - - user.auth.resetLockout(uid, callback); - }); - }); - }); + user.hashPassword(password, next); + }, + function(hash, next) { + async.parallel([ + async.apply(user.setUserField, uid, 'password', hash), + async.apply(db.deleteObjectField, 'reset:uid', code), + async.apply(db.sortedSetRemove, 'reset:issueDate', code), + async.apply(user.auth.resetLockout, uid) + ], next); + } + ], callback); }; UserReset.clean = function(callback) { - // Locate all codes that have expired, and remove them from the set/hash async.waterfall([ - async.apply(db.getSortedSetRangeByScore, 'reset:issueDate', 0, -1, -1, +new Date()-(1000*60*120)), + async.apply(db.getSortedSetRangeByScore, 'reset:issueDate', 0, -1, 0, Date.now() - twoHours), function(tokens, next) { - if (!tokens.length) { return next(); } + if (!tokens.length) { + return next(); + } winston.verbose('[UserReset.clean] Removing ' + tokens.length + ' reset tokens from database'); async.parallel([ - async.apply(db.deleteObjectField, 'reset:uid', tokens), + async.apply(db.deleteObjectFields, 'reset:uid', tokens), async.apply(db.sortedSetRemove, 'reset:issueDate', tokens) ], next); } diff --git a/tests/database/hash.js b/tests/database/hash.js index 5d1d002c3b..5e294f3c4b 100644 --- a/tests/database/hash.js +++ b/tests/database/hash.js @@ -263,7 +263,7 @@ describe('Hash methods', function() { describe('deleteObjectField()', function() { before(function(done) { - db.setObject('testObject10', {foo: 'bar', delete: 'this'}, done); + db.setObject('testObject10', {foo: 'bar', delete: 'this', delete1: 'this', delete2: 'this'}, done); }); it('should delete an objects field', function(done) { @@ -277,6 +277,22 @@ describe('Hash methods', function() { }); }); }); + + it('should delete multiple fields of the object', function(done) { + db.deleteObjectFields('testObject10', ['delete1', 'delete2'], function(err) { + assert.ifError(err); + assert.equal(arguments.length, 1); + async.parallel({ + delete1: async.apply(db.isObjectField, 'testObject10', 'delete1'), + delete2: async.apply(db.isObjectField, 'testObject10', 'delete2') + }, function(err, results) { + assert.ifError(err); + assert.equal(results.delete1, false); + assert.equal(results.delete2, false); + done(); + }); + }); + }); }); describe('incrObjectField()', function() { From 31a44a9982c9293dc9d8f2a2c7667e2ec7b28924 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 18 Feb 2015 00:04:55 -0500 Subject: [PATCH 022/142] dont use fa icon --- public/src/modules/topicSelect.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/modules/topicSelect.js b/public/src/modules/topicSelect.js index 842cf7f8b6..75f32f80be 100644 --- a/public/src/modules/topicSelect.js +++ b/public/src/modules/topicSelect.js @@ -23,7 +23,7 @@ define('topicSelect', function() { return false; } - var isSelected = select.hasClass('fa-check-square-o'); + var isSelected = select.parents('[data-tid]').hasClass('selected'); toggleSelect(select, !isSelected); lastSelected = select; if (typeof onSelect === 'function') { From 60efeb8561e3d0dc06ba30fe2439c85bbcb5c314 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 18 Feb 2015 07:28:53 -0500 Subject: [PATCH 023/142] fix nid --- public/src/modules/notifications.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/public/src/modules/notifications.js b/public/src/modules/notifications.js index 8ce107f20a..4d9fffcde0 100644 --- a/public/src/modules/notifications.js +++ b/public/src/modules/notifications.js @@ -21,7 +21,7 @@ define('notifications', ['sounds'], function(sound) { if (err) { return app.alertError(err.message); } - + var notifs = data.unread.concat(data.read); translator.toggleTimeagoShorthand(); @@ -29,14 +29,15 @@ define('notifications', ['sounds'], function(sound) { notifs[i].timeago = $.timeago(new Date(parseInt(notifs[i].datetime, 10))); } translator.toggleTimeagoShorthand(); - + templates.parse('partials/notifications_list', {notifications: notifs}, function(html) { notifList.translateHtml(html); }); - }); + }); }); notifList.on('click', '[data-nid]', function() { + var nid = $(this).attr('data-nid'); socket.emit('notifications.markRead', nid, function(err) { if (err) { app.alertError(err.message); From 0dca86f255440e28d01c8fdd5c64fd1490c2f031 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 18 Feb 2015 07:36:49 -0500 Subject: [PATCH 024/142] fix tests --- src/database/mongo/hash.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/database/mongo/hash.js b/src/database/mongo/hash.js index 5c5970f1d9..800bd6525c 100644 --- a/src/database/mongo/hash.js +++ b/src/database/mongo/hash.js @@ -210,8 +210,10 @@ module.exports = function(db, module) { } var data = {}; fields.forEach(function(field) { - field = helpers.fieldToString(field); - data[field] = ''; + if (field) { + field = helpers.fieldToString(field); + data[field] = ''; + } }); db.collection('objects').update({_key: key}, {$unset : data}, function(err, res) { From d577371a51adefe89179185c364d325bba3a2692 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 18 Feb 2015 07:41:35 -0500 Subject: [PATCH 025/142] fix test --- src/database/mongo/hash.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/database/mongo/hash.js b/src/database/mongo/hash.js index 800bd6525c..da1d2483d9 100644 --- a/src/database/mongo/hash.js +++ b/src/database/mongo/hash.js @@ -208,12 +208,15 @@ module.exports = function(db, module) { if (!key || !Array.isArray(fields) || !fields.length) { return callback(); } + fields = fields.filter(Boolean); + if (!fields.length) { + return callback(); + } + var data = {}; fields.forEach(function(field) { - if (field) { - field = helpers.fieldToString(field); - data[field] = ''; - } + field = helpers.fieldToString(field); + data[field] = ''; }); db.collection('objects').update({_key: key}, {$unset : data}, function(err, res) { From 8975a0962ea9a229004f52769dc7ce8380c33e44 Mon Sep 17 00:00:00 2001 From: acardinale Date: Wed, 18 Feb 2015 15:26:05 +0100 Subject: [PATCH 026/142] Update topics.js --- src/controllers/topics.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/controllers/topics.js b/src/controllers/topics.js index 4c7708c2ba..1d756b4189 100644 --- a/src/controllers/topics.js +++ b/src/controllers/topics.js @@ -268,8 +268,7 @@ topicsController.get = function(req, res, next) { topics.increaseViewCount(tid); plugins.fireHook('filter:topic.build', {req: req, res: res, templateData: data}, function(err, data) { - if (err && process.env === 'development') { - winston.warn(JSON.stringify(err)); + if (err) { return next(err); } res.render('topic', data.templateData); From 6607dfc4c9947a4201813f8e71520b79b322e2ad Mon Sep 17 00:00:00 2001 From: acardinale Date: Wed, 18 Feb 2015 15:27:14 +0100 Subject: [PATCH 027/142] Update categories.js --- src/controllers/categories.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/controllers/categories.js b/src/controllers/categories.js index 7ea49453f5..64c5d8c61a 100644 --- a/src/controllers/categories.js +++ b/src/controllers/categories.js @@ -267,8 +267,7 @@ categoriesController.get = function(req, res, next) { }); plugins.fireHook('filter:category.build', {req: req, res: res, templateData: data}, function(err, data) { - if (err && process.env === 'development') { - winston.warn(JSON.stringify(err)); + if (err) { return next(err); } res.render('category', data.templateData); From 502e806f88acefe4a7d85658771c0eadd53c6595 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 18 Feb 2015 15:33:21 -0500 Subject: [PATCH 028/142] fixed #2732 -- group creation now using language keys, and no longer succeeds if a group by the name of 'guest' is created. --- public/src/admin/manage/groups.js | 20 ++++++--------- public/src/utils.js | 5 ++++ src/groups.js | 41 +++++++++++++++++-------------- 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/public/src/admin/manage/groups.js b/public/src/admin/manage/groups.js index b2ae0c0afb..ed8cc59683 100644 --- a/public/src/admin/manage/groups.js +++ b/public/src/admin/manage/groups.js @@ -1,5 +1,5 @@ "use strict"; -/*global define, templates, socket, ajaxify, app, admin, bootbox*/ +/*global define, templates, socket, ajaxify, app, admin, bootbox, utils, config, translator */ define('admin/manage/groups', [ 'iconSelect', @@ -50,19 +50,13 @@ define('admin/manage/groups', [ socket.emit('admin.groups.create', submitObj, function(err) { if (err) { - switch (err) { - case 'group-exists': - errorText = 'Please choose another name

    There seems to be a group with this name already.

    '; - break; - case 'name-too-short': - errorText = 'Please specify a group name

    A group name is required for administrative purposes.

    '; - break; - default: - errorText = 'Uh-Oh

    There was a problem creating your group. Please try again later!

    '; - break; + if (err.hasOwnProperty('message') && utils.hasLanguageKey(err.message)) { + translator.translate(err.message, config.defaultLang, function(translated) { + createModalError.html(translated).removeClass('hide'); + }); + } else { + createModalError.html('Uh-Oh

    There was a problem creating your group. Please try again later!

    ').removeClass('hide'); } - - createModalError.html(errorText).removeClass('hide'); } else { createModalError.addClass('hide'); createGroupName.val(''); diff --git a/public/src/utils.js b/public/src/utils.js index 3b82573c9b..e0fef6151b 100644 --- a/public/src/utils.js +++ b/public/src/utils.js @@ -71,6 +71,7 @@ trimTrailingDash: /-$/g, trimLeadingDash: /^-/g, isLatin: /^[\w]+$/, + languageKeyRegex: /\[\[[\w]+:.+\]\]/, //http://dense13.com/blog/2009/05/03/converting-string-to-slug-javascript/ slugify: function(str) { @@ -108,6 +109,10 @@ return !isNaN(parseFloat(n)) && isFinite(n); }, + hasLanguageKey: function(input) { + return utils.languageKeyRegex.test(input); + }, + // shallow objects merge merge: function() { var result = {}, obj, keys; diff --git a/src/groups.js b/src/groups.js index f82e452b96..2c0015d75a 100644 --- a/src/groups.js +++ b/src/groups.js @@ -43,20 +43,14 @@ var async = require('async'), return groups; } }, - getEphemeralGroup: function(groupName, options, callback) { - Groups.exists(groupName, function(err, exists) { - if (!err && exists) { - Groups.get.apply(null, arguments); - } else { - callback(null, { - name: groupName, - description: '', - deleted: '0', - hidden: '0', - system: '1' - }); - } - }); + getEphemeralGroup: function(groupName) { + return { + name: groupName, + description: '', + deleted: '0', + hidden: '0', + system: '1' + }; }, removeEphemeralGroups: function(groups) { var x = groups.length; @@ -95,6 +89,9 @@ var async = require('async'), }; Groups.get = function(groupName, options, callback) { + if (!arguments[0]) { + console.log(new Error.stack); + } var truncated = false, numUsers; @@ -103,7 +100,7 @@ var async = require('async'), if (ephemeralGroups.indexOf(groupName) === -1) { db.getObject('group:' + groupName, next); } else { - internals.getEphemeralGroup(groupName, options, next); + next(null, internals.getEphemeralGroup(groupName)); } }, users: function (next) { @@ -400,6 +397,11 @@ var async = require('async'), return utils.slugify(groupName); }); async.parallel([ + function(next) { + callback(null, slugs.map(function(slug) { + return ephemeralGroups.indexOf(slug) !== -1; + })); + }, async.apply(db.isObjectFields, 'groupslug:groupname', slugs), async.apply(db.isSortedSetMembers, 'groups:createtime', name) ], function(err, results) { @@ -407,17 +409,20 @@ var async = require('async'), return callback(err); } - callback(null, results.map(function(pair) { - return pair[0] || pair[1]; + callback(null, results.map(function(result) { + return result[0] || result[1] || result[2]; })); }); } else { var slug = utils.slugify(name); async.parallel([ + function(next) { + next(null, ephemeralGroups.indexOf(slug) !== -1); + }, async.apply(db.isObjectField, 'groupslug:groupname', slug), async.apply(db.isSortedSetMember, 'groups:createtime', name) ], function(err, results) { - callback(err, !err ? (results[0] || results[1]) : null); + callback(err, !err ? (results[0] || results[1] || results[2]) : null); }); } }; From 6a69fcd4cd9e062d4365dbdeec802fa1ad7b19b4 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 18 Feb 2015 15:41:44 -0500 Subject: [PATCH 029/142] fixes #2665 --- public/src/admin/appearance/skins.js | 3 ++- src/views/admin/partials/theme_list.tpl | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/public/src/admin/appearance/skins.js b/public/src/admin/appearance/skins.js index 8fd9208c7e..df21cb5b0a 100644 --- a/public/src/admin/appearance/skins.js +++ b/public/src/admin/appearance/skins.js @@ -58,7 +58,8 @@ define('admin/appearance/skins', function() { url: theme.preview, css: theme.cssCdn }; - }) + }), + showRevert: true }, function(html) { themeContainer.html(html); }); diff --git a/src/views/admin/partials/theme_list.tpl b/src/views/admin/partials/theme_list.tpl index 5eb085bce4..cc3d0243fb 100644 --- a/src/views/admin/partials/theme_list.tpl +++ b/src/views/admin/partials/theme_list.tpl @@ -16,10 +16,12 @@
    +
  • No Skin

    Remove applied skin and revert back to the base colours

    -
  • \ No newline at end of file + + \ No newline at end of file From 1337924afbc04f0b863d046900584d0ab1476fd5 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Wed, 18 Feb 2015 16:04:40 -0500 Subject: [PATCH 030/142] test commit - testing docgen --- tests/.jshintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/.jshintrc b/tests/.jshintrc index 3bc176b7f6..7658a260ad 100644 --- a/tests/.jshintrc +++ b/tests/.jshintrc @@ -6,4 +6,4 @@ "it": false, "describe": false } -} \ No newline at end of file +} From eb88d2fbd89dfb31350525a880d85315b3a21950 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Wed, 18 Feb 2015 16:19:23 -0500 Subject: [PATCH 031/142] testing docgen again --- tests/.jshintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/.jshintrc b/tests/.jshintrc index 7658a260ad..3bc176b7f6 100644 --- a/tests/.jshintrc +++ b/tests/.jshintrc @@ -6,4 +6,4 @@ "it": false, "describe": false } -} +} \ No newline at end of file From aa94dafac16810ab6763dc6271e8e79747adbd7a Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 18 Feb 2015 21:09:33 -0500 Subject: [PATCH 032/142] closes #725 --- package.json | 2 + public/language/en_GB/error.json | 2 +- public/src/modules/composer/uploads.js | 3 +- public/src/utils.js | 65 +++++++++++++------------- src/controllers/accounts.js | 34 ++++++-------- src/controllers/uploads.js | 63 +++++++++++++++---------- src/file.js | 43 ++++++++++++++++- src/views/admin/settings/post.tpl | 5 +- 8 files changed, 133 insertions(+), 84 deletions(-) diff --git a/package.json b/package.json index b6a70902e4..8561984d0d 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,9 @@ "heapdump": "^0.3.0", "less": "^2.0.0", "logrotate-stream": "^0.2.3", + "mime": "^1.3.4", "mkdirp": "~0.5.0", + "mmmagic": "^0.3.13", "morgan": "^1.3.2", "nconf": "~0.7.1", "nodebb-plugin-dbsearch": "^0.1.0", diff --git a/public/language/en_GB/error.json b/public/language/en_GB/error.json index bf4e7229fd..8c2fadce8d 100644 --- a/public/language/en_GB/error.json +++ b/public/language/en_GB/error.json @@ -64,6 +64,7 @@ "invalid-image-type": "Invalid image type. Allowed types are: %1", "invalid-image-extension": "Invalid image extension", + "invalid-file-type": "Invalid file type. Allowed types are: %1", "group-name-too-short": "Group name too short", "group-already-exists": "Group already exists", @@ -80,7 +81,6 @@ "topic-thumbnails-are-disabled": "Topic thumbnails are disabled.", "invalid-file": "Invalid File", "uploads-are-disabled": "Uploads are disabled", - "upload-error": "Upload Error : %1", "signature-too-long" : "Sorry, your signature cannot be longer than %1 characters.", diff --git a/public/src/modules/composer/uploads.js b/public/src/modules/composer/uploads.js index e7bc473cb5..2a6833c604 100644 --- a/public/src/modules/composer/uploads.js +++ b/public/src/modules/composer/uploads.js @@ -316,8 +316,7 @@ define('composer/uploads', ['composer/preview', 'csrf'], function(preview, csrf) function onUploadError(xhr) { xhr = maybeParse(xhr); - - app.alertError('[[error:upload-error, ' + xhr.responseText + ']]'); + app.alertError(xhr.responseText); } return uploads; diff --git a/public/src/utils.js b/public/src/utils.js index e0fef6151b..9c46e3fb6a 100644 --- a/public/src/utils.js +++ b/public/src/utils.js @@ -130,38 +130,39 @@ return ('' + path).split('.').pop(); }, - fileMimeType: (function () { - // we only care about images, for now - var map = { - "bmp": "image/bmp", - "cmx": "image/x-cmx", - "cod": "image/cis-cod", - "gif": "image/gif", - "ico": "image/x-icon", - "ief": "image/ief", - "jfif": "image/pipeg", - "jpe": "image/jpeg", - "jpeg": "image/jpeg", - "jpg": "image/jpeg", - "pbm": "image/x-portable-bitmap", - "pgm": "image/x-portable-graymap", - "pnm": "image/x-portable-anymap", - "ppm": "image/x-portable-pixmap", - "ras": "image/x-cmu-raster", - "rgb": "image/x-rgb", - "svg": "image/svg+xml", - "tif": "image/tiff", - "tiff": "image/tiff", - "xbm": "image/x-xbitmap", - "xpm": "image/x-xpixmap", - "xwd": "image/x-xwindowdump" - }; - - return function (path) { - var extension = utils.fileExtension(path); - return map[extension] || '*'; - }; - })(), + extensionMimeTypeMap: { + "bmp": "image/bmp", + "cmx": "image/x-cmx", + "cod": "image/cis-cod", + "gif": "image/gif", + "ico": "image/x-icon", + "ief": "image/ief", + "jfif": "image/pipeg", + "jpe": "image/jpeg", + "jpeg": "image/jpeg", + "jpg": "image/jpeg", + "png": "image/png", + "pbm": "image/x-portable-bitmap", + "pgm": "image/x-portable-graymap", + "pnm": "image/x-portable-anymap", + "ppm": "image/x-portable-pixmap", + "ras": "image/x-cmu-raster", + "rgb": "image/x-rgb", + "svg": "image/svg+xml", + "tif": "image/tiff", + "tiff": "image/tiff", + "xbm": "image/x-xbitmap", + "xpm": "image/x-xpixmap", + "xwd": "image/x-xwindowdump" + }, + + fileMimeType: function (path) { + utils.extensionToMimeType(utils.fileExtension(path)); + }, + + extensionToMimeType: function(extension) { + return utils.extensionMimeTypeMap[extension] || '*'; + }, isRelativeUrl: function(url) { var firstChar = url.slice(0, 1); diff --git a/src/controllers/accounts.js b/src/controllers/accounts.js index 13055ca852..600c6a2347 100644 --- a/src/controllers/accounts.js +++ b/src/controllers/accounts.js @@ -376,33 +376,26 @@ accountsController.accountSettings = function(req, res, next) { accountsController.uploadPicture = function (req, res, next) { var userPhoto = req.files.files[0]; var uploadSize = parseInt(meta.config.maximumProfileImageSize, 10) || 256; - - if (userPhoto.size > uploadSize * 1024) { - fs.unlink(userPhoto.path); - return next(new Error('[[error:file-too-big, ' + uploadSize + ']]')); - } - - var allowedTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif']; - if (allowedTypes.indexOf(userPhoto.type) === -1) { - fs.unlink(userPhoto.path); - return next(new Error('[[error:invalid-image-type, ' + allowedTypes.join(', ') + ']]')); - } - var extension = path.extname(userPhoto.name); - if (!extension) { - fs.unlink(userPhoto.path); - return next(new Error('[[error:invalid-image-extension]]')); - } - var updateUid = req.user ? req.user.uid : 0; var imageDimension = parseInt(meta.config.profileImageDimension, 10) || 128; + var convertToPNG = parseInt(meta.config['profile:convertProfileImageToPNG'], 10) === 1; async.waterfall([ + function(next) { + next(userPhoto.size > uploadSize * 1024 ? new Error('[[error:file-too-big, ' + uploadSize + ']]') : null); + }, + function(next) { + next(!extension ? new Error('[[error:invalid-image-extension]]') : null); + }, + function(next) { + file.isFileTypeAllowed(userPhoto.path, ['png', 'jpeg', 'jpg', 'gif'], next); + }, function(next) { image.resizeImage(userPhoto.path, extension, imageDimension, imageDimension, next); }, function(next) { - if (parseInt(meta.config['profile:convertProfileImageToPNG'], 10) === 1) { + if (convertToPNG) { image.convertImageToPng(userPhoto.path, extension, next); } else { next(); @@ -412,7 +405,7 @@ accountsController.uploadPicture = function (req, res, next) { user.getUidByUserslug(req.params.userslug, next); }, function(uid, next) { - if(parseInt(updateUid, 10) === parseInt(uid, 10)) { + if (parseInt(updateUid, 10) === parseInt(uid, 10)) { return next(); } @@ -450,7 +443,6 @@ accountsController.uploadPicture = function (req, res, next) { return plugins.fireHook('filter:uploadImage', {image: userPhoto, uid: updateUid}, done); } - var convertToPNG = parseInt(meta.config['profile:convertProfileImageToPNG'], 10) === 1; var filename = updateUid + '-profileimg' + (convertToPNG ? '.png' : extension); user.getUserField(updateUid, 'uploadedpicture', function (err, oldpicture) { @@ -524,7 +516,7 @@ accountsController.getChats = function(req, res, next) { if (!toUid || parseInt(toUid, 10) === parseInt(req.user.uid, 10)) { return helpers.notFound(req, res); } - + async.parallel({ toUser: async.apply(user.getUserFields, toUid, ['uid', 'username']), messages: async.apply(messaging.getMessages, req.user.uid, toUid, 'recent', false), diff --git a/src/controllers/uploads.js b/src/controllers/uploads.js index b7e590b6bf..dc0db0591d 100644 --- a/src/controllers/uploads.js +++ b/src/controllers/uploads.js @@ -7,6 +7,7 @@ var uploadsController = {}, async = require('async'), meta = require('../meta'), + file = require('../file'), plugins = require('../plugins'), utils = require('../../public/src/utils'), image = require('../image'); @@ -42,12 +43,18 @@ uploadsController.upload = function(req, res, filesIterator, next) { }; uploadsController.uploadPost = function(req, res, next) { - uploadsController.upload(req, res, function(file, next) { - if (file.type.match(/image./)) { - uploadImage(req.user.uid, file, next); - } else { - uploadFile(req.user.uid, file, next); - } + uploadsController.upload(req, res, function(uploadedFile, next) { + file.isFileTypeAllowed(uploadedFile.path, file.allowedExtensions(), function(err) { + if (err) { + return next(err); + } + + if (uploadedFile.type.match(/image./)) { + uploadImage(req.user.uid, uploadedFile, next); + } else { + uploadFile(req.user.uid, uploadedFile, next); + } + }); }, next); }; @@ -57,18 +64,24 @@ uploadsController.uploadThumb = function(req, res, next) { return next(new Error('[[error:topic-thumbnails-are-disabled]]')); } - uploadsController.upload(req, res, function(file, next) { - if(file.type.match(/image./)) { - var size = meta.config.topicThumbSize || 120; - image.resizeImage(file.path, path.extname(file.name), size, size, function(err) { - if (err) { - return next(err); - } - uploadImage(req.user.uid, file, next); - }); - } else { - next(new Error('[[error:invalid-file]]')); - } + uploadsController.upload(req, res, function(uploadedFile, next) { + file.isFileTypeAllowed(uploadedFile.path, file.allowedExtensions(), function(err) { + if (err) { + return next(err); + } + + if (uploadedFile.type.match(/image./)) { + var size = meta.config.topicThumbSize || 120; + image.resizeImage(uploadedFile.path, path.extname(uploadedFile.name), size, size, function(err) { + if (err) { + return next(err); + } + uploadImage(req.user.uid, uploadedFile, next); + }); + } else { + next(new Error('[[error:invalid-file]]')); + } + }); }, next); }; @@ -88,32 +101,32 @@ function uploadImage(uid, image, callback) { } } -function uploadFile(uid, file, callback) { +function uploadFile(uid, uploadedFile, callback) { if (plugins.hasListeners('filter:uploadFile')) { - return plugins.fireHook('filter:uploadFile', {file: file, uid: uid}, callback); + return plugins.fireHook('filter:uploadFile', {file: uploadedFile, uid: uid}, callback); } if (parseInt(meta.config.allowFileUploads, 10) !== 1) { return callback(new Error('[[error:uploads-are-disabled]]')); } - if (!file) { + if (!uploadedFile) { return callback(new Error('[[error:invalid-file]]')); } - if (file.size > parseInt(meta.config.maximumFileSize, 10) * 1024) { + if (uploadedFile.size > parseInt(meta.config.maximumFileSize, 10) * 1024) { return callback(new Error('[[error:file-too-big, ' + meta.config.maximumFileSize + ']]')); } - var filename = 'upload-' + utils.generateUUID() + path.extname(file.name); - require('../file').saveFileToLocal(filename, 'files', file.path, function(err, upload) { + var filename = 'upload-' + utils.generateUUID() + path.extname(uploadedFile.name); + file.saveFileToLocal(filename, 'files', uploadedFile.path, function(err, upload) { if (err) { return callback(err); } callback(null, { url: upload.url, - name: file.name + name: uploadedFile.name }); }); } diff --git a/src/file.js b/src/file.js index 9433dfb40b..a3b594f326 100644 --- a/src/file.js +++ b/src/file.js @@ -3,7 +3,12 @@ var fs = require('fs'), nconf = require('nconf'), path = require('path'), - winston = require('winston'); + winston = require('winston'), + mmmagic = require('mmmagic'), + Magic = mmmagic.Magic, + mime = require('mime'), + + meta= require('./meta'); var file = {}; @@ -11,7 +16,7 @@ file.saveFileToLocal = function(filename, folder, tempPath, callback) { var uploadPath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), folder, filename); - winston.info('Saving file '+ filename +' to : ' + uploadPath); + winston.verbose('Saving file '+ filename +' to : ' + uploadPath); var is = fs.createReadStream(tempPath); var os = fs.createWriteStream(uploadPath); @@ -30,4 +35,38 @@ file.saveFileToLocal = function(filename, folder, tempPath, callback) { is.pipe(os); }; +file.isFileTypeAllowed = function(path, allowedExtensions, callback) { + if (!Array.isArray(allowedExtensions) || !allowedExtensions.length) { + return callback(); + } + + allowedExtensions = allowedExtensions.filter(Boolean).map(function(extension) { + return extension.trim(); + }); + + var magic = new Magic(mmmagic.MAGIC_MIME_TYPE); + magic.detectFile(path, function(err, mimeType) { + if (err) { + return callback(err); + } + + var uploadedFileExtension = mime.extension(mimeType); + + if (allowedExtensions.indexOf(uploadedFileExtension) === -1) { + return callback(new Error('[[error:invalid-file-type, ' + allowedExtensions.join('-') + ']]')); + } + + callback(); + }); +}; + +file.allowedExtensions = function() { + var allowedExtensions = (meta.config.allowedFileExtensions || '').trim(); + if (!allowedExtensions) { + return []; + } + allowedExtensions = allowedExtensions.split(','); + return allowedExtensions; +}; + module.exports = file; \ No newline at end of file diff --git a/src/views/admin/settings/post.tpl b/src/views/admin/settings/post.tpl index 3cb55eac0e..98b99f3973 100644 --- a/src/views/admin/settings/post.tpl +++ b/src/views/admin/settings/post.tpl @@ -119,7 +119,10 @@ Allow users to upload topic thumbnails - Topic Thumb Size
    + Topic Thumb Size

    + + Allowed file types, (ie png, pdf, zip). Leave empty to allow all.

    + From 364ed641ddc2ad5a4806c8af6f1683966ed24b98 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 18 Feb 2015 21:27:09 -0500 Subject: [PATCH 033/142] truncateUserList on search --- public/src/client/groups/list.js | 1 + 1 file changed, 1 insertion(+) diff --git a/public/src/client/groups/list.js b/public/src/client/groups/list.js index d0b4c69b50..2fe5caab95 100644 --- a/public/src/client/groups/list.js +++ b/public/src/client/groups/list.js @@ -47,6 +47,7 @@ define('forum/groups/list', function() { query: queryEl.val(), options: { expand: true, + truncateUserList: true, sort: sortEl.val() } }, function(err, groups) { From 268981f6dba5ed0f5951cd7e9721c507fc1174a0 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 18 Feb 2015 23:00:29 -0500 Subject: [PATCH 034/142] latest translations --- public/language/de/groups.json | 2 +- public/language/fr/groups.json | 4 +- public/language/hu/pages.json | 2 +- public/language/hu/user.json | 6 +-- public/language/pl/email.json | 10 ++-- public/language/pl/error.json | 16 +++--- public/language/pl/global.json | 12 ++--- public/language/pl/groups.json | 30 +++++------ public/language/pl/pages.json | 2 +- public/language/pl/recent.json | 18 +++---- public/language/pl/reset_password.json | 4 +- public/language/pl/search.json | 64 +++++++++++------------ public/language/pl/topic.json | 4 +- public/language/pl/user.json | 10 ++-- public/language/pl/users.json | 8 +-- public/language/pt_BR/email.json | 6 +-- public/language/pt_BR/error.json | 2 +- public/language/pt_BR/groups.json | 4 +- public/language/pt_BR/pages.json | 2 +- public/language/pt_BR/reset_password.json | 4 +- public/language/pt_BR/search.json | 56 ++++++++++---------- public/language/pt_BR/users.json | 2 +- public/language/ru/groups.json | 6 +-- public/language/zh_CN/email.json | 10 ++-- public/language/zh_CN/error.json | 16 +++--- public/language/zh_CN/global.json | 12 ++--- public/language/zh_CN/groups.json | 30 +++++------ public/language/zh_CN/pages.json | 2 +- public/language/zh_CN/recent.json | 4 +- public/language/zh_CN/reset_password.json | 4 +- public/language/zh_CN/search.json | 64 +++++++++++------------ public/language/zh_CN/topic.json | 4 +- public/language/zh_CN/user.json | 10 ++-- public/language/zh_CN/users.json | 8 +-- 34 files changed, 219 insertions(+), 219 deletions(-) diff --git a/public/language/de/groups.json b/public/language/de/groups.json index 375e09d9b5..92b805dd6a 100644 --- a/public/language/de/groups.json +++ b/public/language/de/groups.json @@ -15,7 +15,7 @@ "details.latest_posts": "Aktuelle Beiträge", "details.private": "Private Gruppe", "details.public": "Öffentliche Gruppe", - "details.grant": "Grant/Rescind Ownership", + "details.grant": "Gewähre/Widerrufe Besitz", "details.kick": "Kick", "details.owner_options": "Gruppenadministration", "event.updated": "Gruppendetails wurden aktualisiert", diff --git a/public/language/fr/groups.json b/public/language/fr/groups.json index d8c81570df..abbca27470 100644 --- a/public/language/fr/groups.json +++ b/public/language/fr/groups.json @@ -15,8 +15,8 @@ "details.latest_posts": "Derniers messages", "details.private": "Groupe Privé", "details.public": "Groupe Public", - "details.grant": "Grant/Rescind Ownership", - "details.kick": "Kick", + "details.grant": "Accorder/Résilier Propriétaire", + "details.kick": "Retirer", "details.owner_options": "Administration du Groupe", "event.updated": "Les détails du groupe ont été mis à jour", "event.deleted": "Le groupe é%1\" a été supprimé" diff --git a/public/language/hu/pages.json b/public/language/hu/pages.json index f8d1c20484..53b98e8562 100644 --- a/public/language/hu/pages.json +++ b/public/language/hu/pages.json @@ -11,7 +11,7 @@ "user.followers": "Tagok akik követik %1 -t", "user.posts": "Hozzászólások által %1", "user.topics": "%1 által létrehozott témák", - "user.groups": "%1's Groups", + "user.groups": "%1's csoport", "user.favourites": "%1 Kedvenc Hozzászólásai", "user.settings": "Felhasználói Beállítások", "maintenance.text": "%1 jelenleg karbantartás alatt van. Kérlek nézz vissza késöbb!", diff --git a/public/language/hu/user.json b/public/language/hu/user.json index c673eaee36..bacce74415 100644 --- a/public/language/hu/user.json +++ b/public/language/hu/user.json @@ -2,8 +2,8 @@ "banned": "Kitíltva", "offline": "Nem elérhető", "username": "Felhasználónév", - "joindate": "Join Date", - "postcount": "Post Count", + "joindate": "Regisztráció dátum", + "postcount": "Bejegyzés megtekintés", "email": "E-mail", "confirm_email": "E-mail megerősítése", "delete_account": "Fiók törlése", @@ -18,7 +18,7 @@ "profile_views": "Megtekintések", "reputation": "Hírnév", "favourites": "Kedvencek", - "watched": "Watched", + "watched": "Megtekintve", "followers": "Követők", "following": "Követve", "signature": "Aláírás", diff --git a/public/language/pl/email.json b/public/language/pl/email.json index d1f327d90a..da76caf73c 100644 --- a/public/language/pl/email.json +++ b/public/language/pl/email.json @@ -9,9 +9,9 @@ "reset.text1": "Otrzymaliśmy żądanie przywrócenia Twojego hasła. Jeśli nie żądałeś przywrócenia hasła, zignoruj ten e-mail.", "reset.text2": "Aby przywrócić swoje hasło, skorzystaj z poniższego linku:", "reset.cta": "Kliknij tu, by przywrócić swoje hasło", - "reset.notify.subject": "Password successfully changed", - "reset.notify.text1": "We are notifying you that on %1, your password was changed successfully.", - "reset.notify.text2": "If you did not authorise this, please notify an administrator immediately.", + "reset.notify.subject": "Hasło pomyślnie zmienione", + "reset.notify.text1": "Informujemy, że %1, twoje hasło zostało pomyślnie zmienione.", + "reset.notify.text2": "Jeśli nie wyraziłeś na to zgody, proszę niezwłocznie poinformuj administratora.", "digest.notifications": "Masz nowe powiadomienia od %1:", "digest.latest_topics": "Ostatnie tematy z %1", "digest.cta": "Kliknij, by odwiedzić %1", @@ -20,8 +20,8 @@ "notif.chat.subject": "Nowa wiadomość czatu od %1", "notif.chat.cta": "Kliknij tutaj, by kontynuować konwersację", "notif.chat.unsub.info": "To powiadomienie o czacie zostało Ci wysłane zgodnie z ustawieniami Twojego konta.", - "notif.post.cta": "Click here to read the full topic", - "notif.post.unsub.info": "This post notification was sent to you due to your subscription settings.", + "notif.post.cta": "Kliknij tutaj, aby przeczytać cały wątek.", + "notif.post.unsub.info": "To powiadomienie o poście zostało Ci wysłane zgodnie z ustawieniami Twojego konta.", "test.text1": "To jest e-mail testowy, aby sprawdzić, czy poprawnie skonfigurowałeś e-mailer w swoim NodeBB.", "unsub.cta": "Kliknij tutaj, by zmienić te ustawienia", "closing": "Dziękujemy!" diff --git a/public/language/pl/error.json b/public/language/pl/error.json index 119a9ca553..eaebbb6fcd 100644 --- a/public/language/pl/error.json +++ b/public/language/pl/error.json @@ -18,7 +18,7 @@ "username-taken": "Login zajęty.", "email-taken": "E-mail zajęty.", "email-not-confirmed": "Twój email nie został jeszcze potwierdzony. Proszę kliknąć tutaj by go potwierdzić.", - "email-not-confirmed-chat": "You are unable to chat until your email is confirmed", + "email-not-confirmed-chat": "Nie możesz rozmawiać do czasu, gdy twój email zostanie potwierdzony.", "username-too-short": "Nazwa użytkownika za krótka.", "username-too-long": "Zbyt długa nazwa użytkownika", "user-banned": "Użytkownik zbanowany", @@ -35,7 +35,7 @@ "topic-locked": "Temat zamknięty", "still-uploading": "Poczekaj na pełne załadowanie", "content-too-short": "Proszę wpisać dłuższy post. Posty powinny zawierać co najmniej %1 znaków.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", + "content-too-long": "Proszę wpisać krótszy post. Posty nie mogą zawierać więcej niż %1 znaków.", "title-too-short": "Proszę podać dłuższy tytuł. Tytuły powinny zawierać co najmniej %1 znaków.", "title-too-long": "Wpisz krótszy tytuł, nie może być dłuższy niż %1 znaków.", "too-many-posts": "Możesz wysyłać posty co %1 sekund - proszę poczekać", @@ -45,13 +45,13 @@ "already-favourited": "Już polubiłeś ten post", "already-unfavourited": "Już przestałeś lubić ten post", "cant-ban-other-admins": "Nie możesz zbanować innych adminów!", - "invalid-image-type": "Invalid image type. Allowed types are: %1", - "invalid-image-extension": "Invalid image extension", + "invalid-image-type": "Błędny typ pliku. Dozwolone typy to: %1", + "invalid-image-extension": "Błędne rozszerzenie pliku", "group-name-too-short": "Nazwa grupy za krótka", "group-already-exists": "Grupa już istnieje", "group-name-change-not-allowed": "Nie można zmieniać nazwy tej grupy.", - "group-already-member": "You are already part of this group", - "group-needs-owner": "This group requires at least one owner", + "group-already-member": "Już należysz do tej grupy", + "group-needs-owner": "Ta grupa musi mieć przynajmniej jednego właściciela", "post-already-deleted": "Ten post został już skasowany", "post-already-restored": "Ten post został już przywrócony", "topic-already-deleted": "Ten temat został już skasowany", @@ -63,12 +63,12 @@ "signature-too-long": "Przepraszamy, ale podpis nie może być dłuższy niż %1 znaków.", "cant-chat-with-yourself": "Nie możesz rozmawiać ze sobą", "chat-restricted": "Ten użytkownik ograniczył swoje czaty. Musi Cię śledzić, zanim będziesz mógł z nim czatować.", - "too-many-messages": "You have sent too many messages, please wait awhile.", + "too-many-messages": "Wysłałeś zbyt wiele wiadomości, proszę poczekaj chwilę.", "reputation-system-disabled": "System reputacji został wyłączony", "downvoting-disabled": "Ocena postów jest wyłączona", "not-enough-reputation-to-downvote": "Masz za mało reputacji by ocenić ten post.", "not-enough-reputation-to-flag": "Nie masz dość reputacji, by flagować ten post", "reload-failed": "NodeBB napotkał problem w czasie ładowania \"%1\". Forum będzie nadal dostarczać zasoby dostępne w kliencie, jednak powinieneś cofnąć ostatnią akcję.", "registration-error": "Błąd rejestracji", - "parse-error": "Something went wrong while parsing server response" + "parse-error": "Coś poszło nie tak podczas parsingu odpowiedzi serwera" } \ No newline at end of file diff --git a/public/language/pl/global.json b/public/language/pl/global.json index a0c76caf7f..a12b45bcdf 100644 --- a/public/language/pl/global.json +++ b/public/language/pl/global.json @@ -3,10 +3,10 @@ "search": "Szukaj", "buttons.close": "Zamknij", "403.title": "Dostęp zabroniony", - "403.message": "You seem to have stumbled upon a page that you do not have access to.", - "403.login": "Perhaps you should try logging in?", + "403.message": "Wygląda na to, że trafiłeś na stronę, do której nie masz dostępu.", + "403.login": "Może powinieneś się zalogować?", "404.title": "Nie znaleziono", - "404.message": "You seem to have stumbled upon a page that does not exist. Return to the home page.", + "404.message": "Wygląda na to, że trafiłeś na stronę, która nie istnieje. Wróć do strony głównej.", "500.title": "Błąd wewnętrzny", "500.message": "Ups! Coś poszło nie tak.", "register": "Zarejestruj się", @@ -27,7 +27,7 @@ "header.tags": "Tagi", "header.popular": "Popularne", "header.users": "Użytkownicy", - "header.groups": "Groups", + "header.groups": "Grupy", "header.chats": "Rozmowy", "header.notifications": "Powiadomienia", "header.search": "Szukaj", @@ -75,7 +75,7 @@ "updated.title": "Forum zaktualizowane", "updated.message": "To forum zostało zaktualizowane do najnowszej wersji. Kliknij tutaj by odświeżyć stronę", "privacy": "Prywatność", - "follow": "Follow", - "unfollow": "Unfollow", + "follow": "Obserwuj", + "unfollow": "Przestań śledzić", "delete_all": "Usuń wszystko" } \ No newline at end of file diff --git a/public/language/pl/groups.json b/public/language/pl/groups.json index 0cd285b938..ca279d4b90 100644 --- a/public/language/pl/groups.json +++ b/public/language/pl/groups.json @@ -1,23 +1,23 @@ { "groups": "Grupy", "view_group": "Obejrzyj grupę", - "owner": "Group Owner", - "new_group": "Create New Group", - "no_groups_found": "There are no groups to see", - "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", - "cover-change": "Change", - "cover-save": "Save", - "cover-saving": "Saving", + "owner": "Właściciel grupy", + "new_group": "Stwórz nową grupę", + "no_groups_found": "Brak grup do wyświetlenia", + "cover-instructions": "Przeciągnij i upuść zdjęcie, ustaw w odpowiedniej pozycji i kliknij Zapisz", + "cover-change": "Zmień", + "cover-save": "Zapisz", + "cover-saving": "Zapisuję", "details.title": "Szczegóły grupy", "details.members": "Lista członków", - "details.pending": "Pending Members", + "details.pending": "Członkowie oczekujący", "details.has_no_posts": "Członkowie tej grupy nie napisali żadnych postów.", "details.latest_posts": "Ostatnie posty", - "details.private": "Private Group", - "details.public": "Public Group", - "details.grant": "Grant/Rescind Ownership", - "details.kick": "Kick", - "details.owner_options": "Group Administration", - "event.updated": "Group details have been updated", - "event.deleted": "The group \"%1\" has been deleted" + "details.private": "Grupa prywatna", + "details.public": "Grupa publiczna", + "details.grant": "Nadaj/Cofnij prawa Właściciela", + "details.kick": "Wykop", + "details.owner_options": "Administracja grupy", + "event.updated": "Dane grupy zostały zaktualizowane", + "event.deleted": "Grupa \"%1\" została skasowana" } \ No newline at end of file diff --git a/public/language/pl/pages.json b/public/language/pl/pages.json index 4aad8f6c6d..7f71ba388a 100644 --- a/public/language/pl/pages.json +++ b/public/language/pl/pages.json @@ -11,7 +11,7 @@ "user.followers": "Obserwujący %1", "user.posts": "Posty napisane przez %1", "user.topics": "Wątki stworzone przez %1", - "user.groups": "%1's Groups", + "user.groups": "Grupy %1", "user.favourites": "Ulubione posty %1", "user.settings": "Ustawienia użytkownika", "maintenance.text": "Obecnie trwają prace konserwacyjne nad %1. Proszę wrócić później.", diff --git a/public/language/pl/recent.json b/public/language/pl/recent.json index 382c9d8095..3a5ed0fa33 100644 --- a/public/language/pl/recent.json +++ b/public/language/pl/recent.json @@ -6,13 +6,13 @@ "year": "Rok", "alltime": "Od początku", "no_recent_topics": "Brak ostatnich wątków.", - "there-is-a-new-topic": "There is a new topic.", - "there-is-a-new-topic-and-a-new-post": "There is a new topic and a new post.", - "there-is-a-new-topic-and-new-posts": "There is a new topic and %1 new posts.", - "there-are-new-topics": "There are %1 new topics.", - "there-are-new-topics-and-a-new-post": "There are %1 new topics and a new post.", - "there-are-new-topics-and-new-posts": "There are %1 new topics and %2 new posts.", - "there-is-a-new-post": "There is a new post.", - "there-are-new-posts": "There are %1 new posts.", - "click-here-to-reload": "Click here to reload." + "there-is-a-new-topic": "Masz nowy wątek.", + "there-is-a-new-topic-and-a-new-post": "Masz nowy wątek i nowy post.", + "there-is-a-new-topic-and-new-posts": "Masz nowy wątek i %1 nowych postów.", + "there-are-new-topics": "Masz %1 nowych wątków.", + "there-are-new-topics-and-a-new-post": "Masz %1 nowych wątków i nowy post.", + "there-are-new-topics-and-new-posts": "Masz %1 nowych wątków i %2 nowych postów.", + "there-is-a-new-post": "Masz nowy post.", + "there-are-new-posts": "Masz %1 nowych postów.", + "click-here-to-reload": "Kliknij tutaj, aby przeładować." } \ No newline at end of file diff --git a/public/language/pl/reset_password.json b/public/language/pl/reset_password.json index 99b7518a97..57b58f35e3 100644 --- a/public/language/pl/reset_password.json +++ b/public/language/pl/reset_password.json @@ -11,6 +11,6 @@ "enter_email_address": "Wpisz swój adres e-mail", "password_reset_sent": "Instrukcje zostały wysłane", "invalid_email": "Niepoprawny adres e-mail.", - "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "password_too_short": "Wprowadzone hasło jest zbyt krótkie, proszę wybierz inne hasło.", + "passwords_do_not_match": "Wprowadzone hasła nie pasują do siebie" } \ No newline at end of file diff --git a/public/language/pl/search.json b/public/language/pl/search.json index c8775e8c66..6f2afda91d 100644 --- a/public/language/pl/search.json +++ b/public/language/pl/search.json @@ -1,35 +1,35 @@ { "results_matching": "%1 wyników pasujących do \"%2\", (%3 sekund)", - "no-matches": "No matches found", - "in": "In", - "by": "By", - "titles": "Titles", - "titles-posts": "Titles and Posts", - "posted-by": "Posted by", - "in-categories": "In Categories", - "search-child-categories": "Search child categories", - "reply-count": "Reply Count", - "at-least": "At least", - "at-most": "At most", - "post-time": "Post time", - "newer-than": "Newer than", - "older-than": "Older than", - "any-date": "Any date", - "yesterday": "Yesterday", - "one-week": "One week", - "two-weeks": "Two weeks", - "one-month": "One month", - "three-months": "Three months", - "six-months": "Six months", - "one-year": "One year", - "sort-by": "Sort by", - "last-reply-time": "Last reply time", - "topic-title": "Topic title", - "number-of-replies": "Number of replies", - "number-of-views": "Number of views", - "topic-start-date": "Topic start date", - "username": "Username", - "category": "Category", - "descending": "In descending order", - "ascending": "In ascending order" + "no-matches": "Nie znaleziono pasujących wyników", + "in": "W", + "by": "Przez", + "titles": "Tytuły", + "titles-posts": "Tytuły i posty", + "posted-by": "Napisane przez", + "in-categories": "W kategoriach", + "search-child-categories": "Przeszukaj podkategorie", + "reply-count": "Ilość odpowiedzi", + "at-least": "Przynajmniej", + "at-most": "Co najwyżej", + "post-time": "Napisano", + "newer-than": "Nowsze niż", + "older-than": "Starsze niż", + "any-date": "Kiedykolwiek", + "yesterday": "Wczoraj", + "one-week": "Jeden tydzień", + "two-weeks": "Dwa tygodnie", + "one-month": "Jeden miesiąc", + "three-months": "Trzy miesiące", + "six-months": "Sześć miesięcy", + "one-year": "Jeden rok", + "sort-by": "Sortuj po", + "last-reply-time": "Odpowiedziano ostatnio", + "topic-title": "Tytuł wątku", + "number-of-replies": "Ilość odpowiedzi", + "number-of-views": "Ilość wyświetleń", + "topic-start-date": "Data utworzenia wątku", + "username": "Nazwa użytkownika", + "category": "Kategoria", + "descending": "W kolejności malejącej", + "ascending": "W kolejności rosnącej" } \ No newline at end of file diff --git a/public/language/pl/topic.json b/public/language/pl/topic.json index e5d3a154ae..354da37ef3 100644 --- a/public/language/pl/topic.json +++ b/public/language/pl/topic.json @@ -74,7 +74,7 @@ "fork_no_pids": "Nie zaznaczyłeś żadnych postów!", "fork_success": "Udało się skopiować wątek. Kliknij tutaj, aby do niego przejść.", "composer.title_placeholder": "Wpisz tytuł wątku tutaj", - "composer.handle_placeholder": "Name", + "composer.handle_placeholder": "Nazwa", "composer.discard": "Odrzuć", "composer.submit": "Wyślij", "composer.replying_to": "Odpowiadanie na %1", @@ -94,5 +94,5 @@ "oldest_to_newest": "Najpierw najstarsze", "newest_to_oldest": "Najpierw najnowsze", "most_votes": "Najwięcej głosów", - "most_posts": "Most posts" + "most_posts": "Najwięcej postów" } \ No newline at end of file diff --git a/public/language/pl/user.json b/public/language/pl/user.json index b031c36d5a..e28e3f637f 100644 --- a/public/language/pl/user.json +++ b/public/language/pl/user.json @@ -2,8 +2,8 @@ "banned": "Zbanowany", "offline": "Offline", "username": "Nazwa użytkownika", - "joindate": "Join Date", - "postcount": "Post Count", + "joindate": "Data rejestracji", + "postcount": "Liczba postów", "email": "Adres e-mail", "confirm_email": "Potwierdź e-mail", "delete_account": "Skasuj konto", @@ -18,7 +18,7 @@ "profile_views": "wyświetleń", "reputation": "reputacji", "favourites": "Ulubione", - "watched": "Watched", + "watched": "Obserwowane", "followers": "Obserwujących", "following": "Obserwowanych", "signature": "Sygnatura", @@ -59,12 +59,12 @@ "digest_weekly": "Co tydzień", "digest_monthly": "Co miesiąc", "send_chat_notifications": "Wyślij e-maila, jeśli dostanę nową wiadomość, a nie jestem on-line", - "send_post_notifications": "Send an email when replies are made to topics I am subscribed to", + "send_post_notifications": "Wyślij e-maila, kiedy wątki, które subskrybuję otrzymają odpowiedź", "has_no_follower": "Ten użytkownik nie ma jeszcze żadnych obserwujących", "follows_no_one": "Użytkownik jeszcze nikogo nie obsweruje.", "has_no_posts": "Użytkownik nie napisał jeszcze żadnych postów.", "has_no_topics": "Ten użytkownik nie napisał jeszcze żadnego wątku.", - "has_no_watched_topics": "This user didn't watch any topics yet.", + "has_no_watched_topics": "Ten użytkownik nie obserwował jeszcze żadnego wątku.", "email_hidden": "Adres e-mail ukryty", "hidden": "ukryty", "paginate_description": "Użyj klasycznego trybu paginacji zamiast nieskończonego przewijania.", diff --git a/public/language/pl/users.json b/public/language/pl/users.json index 2df9600a30..b41b99309a 100644 --- a/public/language/pl/users.json +++ b/public/language/pl/users.json @@ -5,8 +5,8 @@ "search": "Szukaj", "enter_username": "Wpisz nazwę użytkownika", "load_more": "Więcej", - "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", - "filter-by": "Filter By", - "online-only": "Online only", - "picture-only": "Picture only" + "users-found-search-took": "Znaleziono %1 użytkownik(ów). Szukanie zajęło %2 sek.", + "filter-by": "Filtruj", + "online-only": "Tylko dostępny", + "picture-only": "Tylko ze zdjęciem" } \ No newline at end of file diff --git a/public/language/pt_BR/email.json b/public/language/pt_BR/email.json index 250bf39788..cca995c0a5 100644 --- a/public/language/pt_BR/email.json +++ b/public/language/pt_BR/email.json @@ -9,9 +9,9 @@ "reset.text1": "Nós recebemos um pedido para reconfigurar sua senha, possivelmente porque você a esqueceu. Se este não é o caso, por favor ignore este email.", "reset.text2": "Para continuar com a reconfiguração de senha, por favor clique no seguinte link:", "reset.cta": "Clique aqui para reconfigurar sua senha", - "reset.notify.subject": "Password successfully changed", - "reset.notify.text1": "We are notifying you that on %1, your password was changed successfully.", - "reset.notify.text2": "If you did not authorise this, please notify an administrator immediately.", + "reset.notify.subject": "Senha alterada com sucesso", + "reset.notify.text1": "Nós estamos notificando você que em %1, sua senha foi alterada com sucesso.", + "reset.notify.text2": "Se você não autorizou isso, por favor notifique um administrador imediatamente.", "digest.notifications": "Você tem notificações não lidas de %1:", "digest.latest_topics": "Últimos tópicos de %1", "digest.cta": "Clique aqui para visitar %1", diff --git a/public/language/pt_BR/error.json b/public/language/pt_BR/error.json index 51d9f13502..e0ad9b1e67 100644 --- a/public/language/pt_BR/error.json +++ b/public/language/pt_BR/error.json @@ -35,7 +35,7 @@ "topic-locked": "Tópico Trancado", "still-uploading": "Aguarde a conclusão dos uploads.", "content-too-short": "Por favor digite um post mais longo. Posts devem conter no mínimo %1 caracteres.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", + "content-too-long": "Por favor entre com um post mais curto. Posts não podem ser maiores do que %1 caracteres.", "title-too-short": "Por favor digite um título mais longo. Títulos devem conter no mínimo %1 caracteres.", "title-too-long": "Por favor entre com um título mais curto; Títulos não podem ser maiores que %1 caracteres.", "too-many-posts": "Você pode postar apenas uma vez a cada %1 segundos - por favor aguarde antes de postar novamente", diff --git a/public/language/pt_BR/groups.json b/public/language/pt_BR/groups.json index 4095f4c3ce..f3a19ea8ef 100644 --- a/public/language/pt_BR/groups.json +++ b/public/language/pt_BR/groups.json @@ -15,8 +15,8 @@ "details.latest_posts": "Últimos Posts", "details.private": "Grupo Privado", "details.public": "Grupo Público.", - "details.grant": "Grant/Rescind Ownership", - "details.kick": "Kick", + "details.grant": "Conceder/Retomar a Posse", + "details.kick": "Chutar", "details.owner_options": "Administração do Grupo", "event.updated": "Os detalhes do grupo foram atualizados", "event.deleted": "O grupo \"%1\" foi deletado" diff --git a/public/language/pt_BR/pages.json b/public/language/pt_BR/pages.json index ae42c35f0f..415f50c0fd 100644 --- a/public/language/pt_BR/pages.json +++ b/public/language/pt_BR/pages.json @@ -11,7 +11,7 @@ "user.followers": "Pessoas que Seguem %1", "user.posts": "Posts feitos por %1", "user.topics": "Tópicos criados por %1", - "user.groups": "%1's Groups", + "user.groups": "%1's Grupos", "user.favourites": "Posts Favoritos de %1", "user.settings": "Configurações de Usuário", "maintenance.text": "%1 está atualmente sob manutenção. Por favor retorne em outro momento.", diff --git a/public/language/pt_BR/reset_password.json b/public/language/pt_BR/reset_password.json index bf96416678..3aa6f341b4 100644 --- a/public/language/pt_BR/reset_password.json +++ b/public/language/pt_BR/reset_password.json @@ -11,6 +11,6 @@ "enter_email_address": "Digite seu Email", "password_reset_sent": "Reconfiguração de Senha Enviada", "invalid_email": "Email Inválido / Email não existe!", - "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "password_too_short": "A senha entrada é muito curta, por favor escolha uma senha diferente.", + "passwords_do_not_match": "As duas senhas que você digitou não combinam." } \ No newline at end of file diff --git a/public/language/pt_BR/search.json b/public/language/pt_BR/search.json index 9f10c0970b..f1b47b737e 100644 --- a/public/language/pt_BR/search.json +++ b/public/language/pt_BR/search.json @@ -3,33 +3,33 @@ "no-matches": "Nenhum resultado encontrado", "in": "Em", "by": "Por", - "titles": "Titles", - "titles-posts": "Titles and Posts", + "titles": "Títulos", + "titles-posts": "Títulos e Posts", "posted-by": "Postado por", - "in-categories": "In Categories", - "search-child-categories": "Search child categories", - "reply-count": "Reply Count", - "at-least": "At least", - "at-most": "At most", - "post-time": "Post time", - "newer-than": "Newer than", - "older-than": "Older than", - "any-date": "Any date", - "yesterday": "Yesterday", - "one-week": "One week", - "two-weeks": "Two weeks", - "one-month": "One month", - "three-months": "Three months", - "six-months": "Six months", - "one-year": "One year", - "sort-by": "Sort by", - "last-reply-time": "Last reply time", - "topic-title": "Topic title", - "number-of-replies": "Number of replies", - "number-of-views": "Number of views", - "topic-start-date": "Topic start date", - "username": "Username", - "category": "Category", - "descending": "In descending order", - "ascending": "In ascending order" + "in-categories": "Nas Categorias", + "search-child-categories": "Buscar categorias filhas", + "reply-count": "Contagem de Respostas", + "at-least": "No mínimo", + "at-most": "No máximo", + "post-time": "Hora da Postagem", + "newer-than": "Mais novo que", + "older-than": "Mais velho que", + "any-date": "Qualquer data", + "yesterday": "Ontem", + "one-week": "Uma semana", + "two-weeks": "Duas semanas", + "one-month": "Um mês", + "three-months": "Três meses", + "six-months": "Seis meses", + "one-year": "Um ano", + "sort-by": "Ordenar por", + "last-reply-time": "Tempo da última resposta", + "topic-title": "Título do tópico", + "number-of-replies": "Número de respostas", + "number-of-views": "Número de visualizações", + "topic-start-date": "Data do início do tópico", + "username": "Nome de usuário", + "category": "Categoria", + "descending": "Em ordem descendente", + "ascending": "Em ordem ascendente" } \ No newline at end of file diff --git a/public/language/pt_BR/users.json b/public/language/pt_BR/users.json index 32edc568f5..cc49b0fe57 100644 --- a/public/language/pt_BR/users.json +++ b/public/language/pt_BR/users.json @@ -5,7 +5,7 @@ "search": "Procurar", "enter_username": "Digite um nome de usuário para procurar", "load_more": "Carregar Mais", - "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", + "users-found-search-took": "%1 usuário(s) encontrado(s)! A busca levou %2 segundos.", "filter-by": "Filtrar Por", "online-only": "Apenas Online", "picture-only": "Apenas foto" diff --git a/public/language/ru/groups.json b/public/language/ru/groups.json index 4b86076056..20a4c0b01c 100644 --- a/public/language/ru/groups.json +++ b/public/language/ru/groups.json @@ -1,7 +1,7 @@ { "groups": "Группы", "view_group": "Просмотр группы", - "owner": "Владелец группы", + "owner": "Администратор группы", "new_group": "Создать группу", "no_groups_found": "There are no groups to see", "cover-instructions": "Перетяните сюда изображение, переместите на нужную позицию и нажмите Сохранить", @@ -15,8 +15,8 @@ "details.latest_posts": "Последние записи", "details.private": "Приватная группа", "details.public": "Открытая группа", - "details.grant": "Grant/Rescind Ownership", - "details.kick": "Kick", + "details.grant": "Выдать/забрать администратора", + "details.kick": "Исключить", "details.owner_options": "Настройки группы", "event.updated": "Настройки группы обновлены", "event.deleted": "Группа \"%1\" удалена" diff --git a/public/language/zh_CN/email.json b/public/language/zh_CN/email.json index e81b810a9a..9f0316ce3b 100644 --- a/public/language/zh_CN/email.json +++ b/public/language/zh_CN/email.json @@ -9,9 +9,9 @@ "reset.text1": "我们收到了重置您帐户密码的申请,可能是因为您遗忘了密码。如果不是,请忽略这封邮件。", "reset.text2": "如需继续重置密码,请点击下面的链接:", "reset.cta": "点击这里重置您的密码", - "reset.notify.subject": "Password successfully changed", - "reset.notify.text1": "We are notifying you that on %1, your password was changed successfully.", - "reset.notify.text2": "If you did not authorise this, please notify an administrator immediately.", + "reset.notify.subject": "更改密码成功", + "reset.notify.text1": "我们注意到你在 %1 上,成功修改了你的密码。", + "reset.notify.text2": "如果你没有授权此操作,请立即联系管理员。", "digest.notifications": "您有来自 %1 的未读通知:", "digest.latest_topics": "来自 %1 的最新主题", "digest.cta": "点击这里访问 %1", @@ -20,8 +20,8 @@ "notif.chat.subject": "收到来自 %1 的新聊天消息", "notif.chat.cta": "点击这里恢复会话", "notif.chat.unsub.info": "根据您的订阅设置,为您发送此聊天提醒。", - "notif.post.cta": "Click here to read the full topic", - "notif.post.unsub.info": "This post notification was sent to you due to your subscription settings.", + "notif.post.cta": "点击这里阅读全主题。", + "notif.post.unsub.info": "根据您的订阅设置,为您发送此回帖提醒。", "test.text1": "这是一封测试邮件,用来验证 NodeBB 的邮件配置是否设置正确。", "unsub.cta": "点击这里修改这些设置", "closing": "谢谢!" diff --git a/public/language/zh_CN/error.json b/public/language/zh_CN/error.json index cfcb33aee8..2511027169 100644 --- a/public/language/zh_CN/error.json +++ b/public/language/zh_CN/error.json @@ -18,7 +18,7 @@ "username-taken": "用户名已被占用", "email-taken": "电子邮箱已被占用", "email-not-confirmed": "您的电子邮箱尚未确认,请点击这里确认您的电子邮箱。", - "email-not-confirmed-chat": "You are unable to chat until your email is confirmed", + "email-not-confirmed-chat": "在确认您的邮箱之前,您不能使用聊天功能", "username-too-short": "用户名太短", "username-too-long": "用户名太长", "user-banned": "用户已禁止", @@ -35,7 +35,7 @@ "topic-locked": "主题已锁定", "still-uploading": "请等待上传完成", "content-too-short": "请再输入一些内容,帖子至少要有 %1 个字符。", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", + "content-too-long": "请输入更短的发帖。发帖字数不能超过 %1 个字符。", "title-too-short": "请再输入一些内容,标题至少要有 %1 个字符。", "title-too-long": "请输入更短的标题。不超过 %1 字。", "too-many-posts": "发帖间隔至少要 %1 秒 - 请稍候再发帖", @@ -45,13 +45,13 @@ "already-favourited": "您已收藏该帖", "already-unfavourited": "您已取消收藏此帖", "cant-ban-other-admins": "您不能禁止其他管理员!", - "invalid-image-type": "Invalid image type. Allowed types are: %1", - "invalid-image-extension": "Invalid image extension", + "invalid-image-type": "无效的图像类型。允许的类型有:%1", + "invalid-image-extension": "无效的图像扩展", "group-name-too-short": "用户组名称太短", "group-already-exists": "用户组已存在", "group-name-change-not-allowed": "不允许更改用户组名称", - "group-already-member": "You are already part of this group", - "group-needs-owner": "This group requires at least one owner", + "group-already-member": "您已是此小组成员", + "group-needs-owner": "此小组需要至少一名组长", "post-already-deleted": "此帖子已被删除", "post-already-restored": "此帖已经恢复", "topic-already-deleted": "此主题已被删除", @@ -63,12 +63,12 @@ "signature-too-long": "抱歉,您的签名不能超过 %1 个字符。", "cant-chat-with-yourself": "您不能和自己聊天!", "chat-restricted": "此用户限制了他的聊天消息。必须他先关注您,您才能和他聊天。", - "too-many-messages": "You have sent too many messages, please wait awhile.", + "too-many-messages": "您发送了太多消息,请稍等片刻。", "reputation-system-disabled": "威望系统已禁用。", "downvoting-disabled": "反对功能已禁用", "not-enough-reputation-to-downvote": "您还没有足够的威望为此帖扣分", "not-enough-reputation-to-flag": "您没有足够的威望标记此帖", "reload-failed": "NodeBB 重新加载时遇到问题: \"%1\"。NodeBB 会继续给已存在的客户端组件服务,虽然您应该撤销在重新加载前执行的操作。", "registration-error": "注册错误", - "parse-error": "Something went wrong while parsing server response" + "parse-error": "解析服务器响应时出错" } \ No newline at end of file diff --git a/public/language/zh_CN/global.json b/public/language/zh_CN/global.json index d8b6b8ea01..ede930cbbf 100644 --- a/public/language/zh_CN/global.json +++ b/public/language/zh_CN/global.json @@ -3,10 +3,10 @@ "search": "搜索", "buttons.close": "关闭", "403.title": "禁止访问", - "403.message": "You seem to have stumbled upon a page that you do not have access to.", - "403.login": "Perhaps you should try logging in?", + "403.message": "您遇到了没有权限访问的页面。", + "403.login": "或许您应该 试试登入?", "404.title": "未找到", - "404.message": "You seem to have stumbled upon a page that does not exist. Return to the home page.", + "404.message": "您遇到了不存在的页面。返回首页。", "500.title": "内部错误。", "500.message": "哎呀!看来是哪里出错了!", "register": "注册", @@ -27,7 +27,7 @@ "header.tags": "话题", "header.popular": "热门", "header.users": "会员", - "header.groups": "Groups", + "header.groups": "小组", "header.chats": "聊天", "header.notifications": "通知", "header.search": "搜索", @@ -75,7 +75,7 @@ "updated.title": "论坛已更新", "updated.message": "论坛已更新到最新版本。点这里刷新页面。", "privacy": "隐私", - "follow": "Follow", - "unfollow": "Unfollow", + "follow": "关注", + "unfollow": "取消关注", "delete_all": "全部删除" } \ No newline at end of file diff --git a/public/language/zh_CN/groups.json b/public/language/zh_CN/groups.json index 5a4bdd5b1f..bd0abbbf67 100644 --- a/public/language/zh_CN/groups.json +++ b/public/language/zh_CN/groups.json @@ -1,23 +1,23 @@ { "groups": "用户组", "view_group": "查看用户组", - "owner": "Group Owner", - "new_group": "Create New Group", - "no_groups_found": "There are no groups to see", - "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", - "cover-change": "Change", - "cover-save": "Save", - "cover-saving": "Saving", + "owner": "用户组组长", + "new_group": "创建新用户组", + "no_groups_found": "还没有用户组", + "cover-instructions": "拖放照片,拖动位置,然后点击 保存", + "cover-change": "变更", + "cover-save": "保存", + "cover-saving": "正在保存", "details.title": "用户组详情", "details.members": "会员列表", - "details.pending": "Pending Members", + "details.pending": "预备成员", "details.has_no_posts": "此用户组的会员尚未发表任何帖子。", "details.latest_posts": "最新帖子", - "details.private": "Private Group", - "details.public": "Public Group", - "details.grant": "Grant/Rescind Ownership", - "details.kick": "Kick", - "details.owner_options": "Group Administration", - "event.updated": "Group details have been updated", - "event.deleted": "The group \"%1\" has been deleted" + "details.private": "私有组", + "details.public": "公共组", + "details.grant": "授予/取消所有权", + "details.kick": "踢", + "details.owner_options": "用户组管理", + "event.updated": "用户组信息已更新", + "event.deleted": "用户组 \"%1\" 已被删除" } \ No newline at end of file diff --git a/public/language/zh_CN/pages.json b/public/language/zh_CN/pages.json index 36d10c1cb3..6fde75dfe5 100644 --- a/public/language/zh_CN/pages.json +++ b/public/language/zh_CN/pages.json @@ -11,7 +11,7 @@ "user.followers": "关注 %1 的人", "user.posts": "%1 发布的帖子", "user.topics": "%1 创建的主题", - "user.groups": "%1's Groups", + "user.groups": "%1 的用户组", "user.favourites": "%1 收藏的帖子", "user.settings": "用户设置", "maintenance.text": "%1 正在进行维护。请稍后再来。", diff --git a/public/language/zh_CN/recent.json b/public/language/zh_CN/recent.json index d3fea3da46..8aa1e96ab2 100644 --- a/public/language/zh_CN/recent.json +++ b/public/language/zh_CN/recent.json @@ -6,7 +6,7 @@ "year": "年度热帖榜", "alltime": "总热帖榜", "no_recent_topics": "暂无主题。", - "there-is-a-new-topic": "There is a new topic.", + "there-is-a-new-topic": "这是个新主题。", "there-is-a-new-topic-and-a-new-post": "There is a new topic and a new post.", "there-is-a-new-topic-and-new-posts": "There is a new topic and %1 new posts.", "there-are-new-topics": "There are %1 new topics.", @@ -14,5 +14,5 @@ "there-are-new-topics-and-new-posts": "There are %1 new topics and %2 new posts.", "there-is-a-new-post": "There is a new post.", "there-are-new-posts": "There are %1 new posts.", - "click-here-to-reload": "Click here to reload." + "click-here-to-reload": "点击这里重新加载" } \ No newline at end of file diff --git a/public/language/zh_CN/reset_password.json b/public/language/zh_CN/reset_password.json index ca294f9dc7..9b0ca29dbe 100644 --- a/public/language/zh_CN/reset_password.json +++ b/public/language/zh_CN/reset_password.json @@ -11,6 +11,6 @@ "enter_email_address": "输入邮箱地址", "password_reset_sent": "密码重置邮件已发送。", "invalid_email": "无效的电子邮箱/电子邮箱不存在!", - "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "password_too_short": "密码太短,请选择其他密码。", + "passwords_do_not_match": "您输入两个密码不一致。" } \ No newline at end of file diff --git a/public/language/zh_CN/search.json b/public/language/zh_CN/search.json index 25cc3605e5..1b1096b667 100644 --- a/public/language/zh_CN/search.json +++ b/public/language/zh_CN/search.json @@ -1,35 +1,35 @@ { "results_matching": "共 %1 条结果匹配 \"%2\",(耗时 %3 秒)", - "no-matches": "No matches found", - "in": "In", - "by": "By", - "titles": "Titles", - "titles-posts": "Titles and Posts", - "posted-by": "Posted by", - "in-categories": "In Categories", - "search-child-categories": "Search child categories", - "reply-count": "Reply Count", - "at-least": "At least", - "at-most": "At most", - "post-time": "Post time", - "newer-than": "Newer than", - "older-than": "Older than", - "any-date": "Any date", - "yesterday": "Yesterday", - "one-week": "One week", - "two-weeks": "Two weeks", - "one-month": "One month", - "three-months": "Three months", - "six-months": "Six months", - "one-year": "One year", - "sort-by": "Sort by", - "last-reply-time": "Last reply time", - "topic-title": "Topic title", - "number-of-replies": "Number of replies", - "number-of-views": "Number of views", - "topic-start-date": "Topic start date", - "username": "Username", - "category": "Category", - "descending": "In descending order", - "ascending": "In ascending order" + "no-matches": "无匹配结果", + "in": "在", + "by": "-", + "titles": "标题", + "titles-posts": "标题和回帖", + "posted-by": "发表", + "in-categories": "在版面", + "search-child-categories": "搜索子版面", + "reply-count": "回复数", + "at-least": "至少", + "at-most": "至多", + "post-time": "发帖时间", + "newer-than": "晚于", + "older-than": "早于", + "any-date": "任何日期", + "yesterday": "昨天", + "one-week": "一周", + "two-weeks": "两周", + "one-month": "一个月", + "three-months": "三个月", + "six-months": "六个月", + "one-year": "一年", + "sort-by": "排序", + "last-reply-time": "最后回复时间", + "topic-title": "主题标题", + "number-of-replies": "回帖数", + "number-of-views": "查看数", + "topic-start-date": "主题开始日期", + "username": "用户名", + "category": "版面", + "descending": "逆序", + "ascending": "顺序" } \ No newline at end of file diff --git a/public/language/zh_CN/topic.json b/public/language/zh_CN/topic.json index 8dc93ec5f5..0cd68a3250 100644 --- a/public/language/zh_CN/topic.json +++ b/public/language/zh_CN/topic.json @@ -74,7 +74,7 @@ "fork_no_pids": "未选中帖子!", "fork_success": "成功分割主题! 点这里跳转到分割后的主题。", "composer.title_placeholder": "在此输入您主题的标题...", - "composer.handle_placeholder": "Name", + "composer.handle_placeholder": "姓名", "composer.discard": "撤销", "composer.submit": "提交", "composer.replying_to": "正在回复 %1", @@ -94,5 +94,5 @@ "oldest_to_newest": "从旧到新", "newest_to_oldest": "从新到旧", "most_votes": "最多投票", - "most_posts": "Most posts" + "most_posts": "最多发表" } \ No newline at end of file diff --git a/public/language/zh_CN/user.json b/public/language/zh_CN/user.json index 9379b69e38..6c360a4d4e 100644 --- a/public/language/zh_CN/user.json +++ b/public/language/zh_CN/user.json @@ -2,8 +2,8 @@ "banned": "禁止", "offline": "离线", "username": "用户名", - "joindate": "Join Date", - "postcount": "Post Count", + "joindate": "注册日期", + "postcount": "发帖数", "email": "电子邮件", "confirm_email": "确认电子邮箱", "delete_account": "删除帐号", @@ -18,7 +18,7 @@ "profile_views": "资料浏览", "reputation": "威望", "favourites": "收藏的帖子", - "watched": "Watched", + "watched": "已订阅", "followers": "粉丝", "following": "关注", "signature": "签名档", @@ -59,12 +59,12 @@ "digest_weekly": "每周", "digest_monthly": "每月", "send_chat_notifications": "当我不在线,并受到新的聊天消息时给我发邮件", - "send_post_notifications": "Send an email when replies are made to topics I am subscribed to", + "send_post_notifications": "我订阅的主题有回复时发送邮件", "has_no_follower": "此用户还没有粉丝 :(", "follows_no_one": "此用户尚未关注任何人 :(", "has_no_posts": "此用户尚未发布任何帖子。", "has_no_topics": "此用户还未发布任何主题。", - "has_no_watched_topics": "This user didn't watch any topics yet.", + "has_no_watched_topics": "此用户还未订阅任何主题", "email_hidden": "电子邮箱已隐藏", "hidden": "隐藏", "paginate_description": "分页展示主题和帖子,替代滚动展示。", diff --git a/public/language/zh_CN/users.json b/public/language/zh_CN/users.json index f56a4fb88e..ef7d6dd15b 100644 --- a/public/language/zh_CN/users.json +++ b/public/language/zh_CN/users.json @@ -5,8 +5,8 @@ "search": "搜索", "enter_username": "输入用户名搜索", "load_more": "加载更多", - "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", - "filter-by": "Filter By", - "online-only": "Online only", - "picture-only": "Picture only" + "users-found-search-took": "找到 %1 位用户!搜索耗时 %2 毫秒。", + "filter-by": "过滤选项", + "online-only": "只看在线", + "picture-only": "只看图片" } \ No newline at end of file From 145f7b3809063524bbbb3450f950980e8ac937fb Mon Sep 17 00:00:00 2001 From: yariplus Date: Thu, 19 Feb 2015 06:31:06 -0500 Subject: [PATCH 035/142] Fix #2684 ACP navbar on mobile. --- public/less/admin/admin.less | 69 +++++++++++++++++++------------ src/views/admin/header.tpl | 79 ++++++++++++++++++++---------------- 2 files changed, 86 insertions(+), 62 deletions(-) diff --git a/public/less/admin/admin.less b/public/less/admin/admin.less index ee9a87b605..36626df136 100644 --- a/public/less/admin/admin.less +++ b/public/less/admin/admin.less @@ -183,15 +183,18 @@ .box-header-font } - #user_dropdown { - padding: 6px; - - img { - width: 30px; - height: 30px; - vertical-align: -88%; - margin-right: 5px; - } + #user_label { + a { + padding-top: 13px; + padding-bottom: 13px; + + img { + width: 24px; + height: 24px; + border-radius: 50%; + border: 1px solid #454; + } + } } .icon-container { @@ -213,24 +216,36 @@ } } - .navbar { - padding: 0 5px; - - .nav-home a, .nav-home a:hover { - width: 30px; - height: 30px; - padding: 5px; - text-align: center; - margin-top: 10px; - background: #DDD; - - i { - color: black; - font-size: 17px; - } - } - - } + .navbar-static-top, .navbar-fixed-top { + box-shadow: 0px -3px 12px rgba(0, 0, 0, 0.5); + } + + .navbar-header > .navbar-toggle { + margin-right: 8px; + } + + .navbar-nav { + margin-top: 0; + margin-bottom: 0; + + >li { + >a { + padding-top: 15px; + padding-bottom: 15px; + } + >a:hover, >a:focus { + color:#333; + background-color:#b2cdf0 + } + >#reconnect { + color: #afb1b1; + } + >#reconnect:focus, >#reconnect:hover { + color: #afb1b1; + background-color: transparent; + } + } + } #acp-search { input { diff --git a/src/views/admin/header.tpl b/src/views/admin/header.tpl index 9bd680936a..7a5d658eb2 100644 --- a/src/views/admin/header.tpl +++ b/src/views/admin/header.tpl @@ -56,46 +56,55 @@ - +
    + +
    + + +
    From bdcf090aaaf9635f97c6aab2ea6a19c72ab36bb1 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 19 Feb 2015 14:20:13 -0500 Subject: [PATCH 041/142] init rewards system --- src/controllers/admin.js | 9 ++++++ src/rewards/admin.js | 50 ++++++++++++++++++++++++++++++ src/rewards/index.js | 7 +++++ src/routes/admin.js | 1 + src/views/admin/extend/rewards.tpl | 27 ++++++++++++++++ src/views/admin/partials/menu.tpl | 1 + 6 files changed, 95 insertions(+) create mode 100644 src/rewards/admin.js create mode 100644 src/rewards/index.js create mode 100644 src/views/admin/extend/rewards.tpl diff --git a/src/controllers/admin.js b/src/controllers/admin.js index f03b7fdb88..b5123da2e8 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -304,6 +304,15 @@ adminController.extend.widgets = function(req, res, next) { }); }; +adminController.extend.rewards = function(req, res, next) { + require('../rewards/admin').get(function(err, data) { + if (err) { + return next(err); + } + + res.render('admin/extend/rewards', data); + }); +}; adminController.groups.get = function(req, res, next) { groups.list({ diff --git a/src/rewards/admin.js b/src/rewards/admin.js new file mode 100644 index 0000000000..b2864fbbd7 --- /dev/null +++ b/src/rewards/admin.js @@ -0,0 +1,50 @@ +"use strict"; + +var rewards = {}; + + +rewards.get = function(callback) { + callback({ + conditions: ["Reputation", "Post Count", "Last Logged in Time"], + conditionals: [">", ">=", "<", "<=", "is string:"], + rewards: [ + { + "rewardID": 0, + "name": "Add to Group", + "inputs": [ + { + "type": "select", + "name": "groupname", + "values": ["Group 1", "Group 2", "Group 3"], + } + ], + "disabled": 0 + }, + { + "rewardID": 0, + "name": "Send alert message", + "inputs": [ + { + "type": "text", + "name": "title", + }, + { + "type": "text", + "name": "message", + } + ], + "disabled": 0 + } + ] + }) +}; + +function getConditions() { + +} + +function getRewards() { + +} + +module.exports = rewards; \ No newline at end of file diff --git a/src/rewards/index.js b/src/rewards/index.js new file mode 100644 index 0000000000..ba68fae92c --- /dev/null +++ b/src/rewards/index.js @@ -0,0 +1,7 @@ +"use strict"; + +var rewards = {}; + + + +module.exports = rewards; \ No newline at end of file diff --git a/src/routes/admin.js b/src/routes/admin.js index 1b9efcef58..5175059493 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -67,6 +67,7 @@ function addRoutes(router, middleware, controllers) { router.get('/extend/plugins', controllers.admin.plugins.get); router.get('/extend/widgets', controllers.admin.extend.widgets); + router.get('/extend/rewards', controllers.admin.extend.rewards); router.get('/advanced/database', controllers.admin.database.get); router.get('/advanced/events', controllers.admin.events.get); diff --git a/src/views/admin/extend/rewards.tpl b/src/views/admin/extend/rewards.tpl new file mode 100644 index 0000000000..329d64ea2e --- /dev/null +++ b/src/views/admin/extend/rewards.tpl @@ -0,0 +1,27 @@ +
    +
    +
    +
    Rewards
    +
    +
    +
      + +
    • + +
    • + +
    +
    +
    +
    +
    + +
    +
    +
    Save Settings
    +
    + +
    +
    +
    +
    \ No newline at end of file diff --git a/src/views/admin/partials/menu.tpl b/src/views/admin/partials/menu.tpl index 29f049fa03..597173be2e 100644 --- a/src/views/admin/partials/menu.tpl +++ b/src/views/admin/partials/menu.tpl @@ -47,6 +47,7 @@
  • Plugins
  • Widgets
  • +
  • Rewards
  • From 58094d2b3b3a20a2d5503d9d19f917b92c2e3a85 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 19 Feb 2015 17:07:01 -0500 Subject: [PATCH 053/142] removed console logs, and requiring markdown 1.0.0 plugin --- package.json | 2 +- public/src/modules/composer.js | 1 - public/src/modules/composer/formatting.js | 3 --- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/package.json b/package.json index 8561984d0d..f8bfb660ee 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "nconf": "~0.7.1", "nodebb-plugin-dbsearch": "^0.1.0", "nodebb-plugin-emoji-extended": "^0.4.1-4", - "nodebb-plugin-markdown": "^0.8.0", + "nodebb-plugin-markdown": "^1.0.0", "nodebb-plugin-mentions": "^0.9.0", "nodebb-plugin-soundpack-default": "~0.1.1", "nodebb-plugin-spam-be-gone": "^0.4.0", diff --git a/public/src/modules/composer.js b/public/src/modules/composer.js index 26f4e4a8f2..84c4c4e9dd 100644 --- a/public/src/modules/composer.js +++ b/public/src/modules/composer.js @@ -241,7 +241,6 @@ define('composer', [ handle: composer.posts[post_uuid] ? composer.posts[post_uuid].handle || '' : undefined, formatting: composer.formatting }; - console.log(composer.formatting); parseAndTranslate(template, data, function(composerTemplate) { if ($('#cmp-uuid-' + post_uuid).length) { diff --git a/public/src/modules/composer/formatting.js b/public/src/modules/composer/formatting.js index 56edd38815..03fb05730e 100644 --- a/public/src/modules/composer/formatting.js +++ b/public/src/modules/composer/formatting.js @@ -46,9 +46,6 @@ define('composer/formatting', ['composer/controls', 'composer/preview'], functio postContainer.on('click', '.formatting-bar span', function () { var format = $(this).attr('data-format'), textarea = $(this).parents('.composer').find('textarea')[0]; - console.log('handling', format); - - console.log(formattingDispatchTable); if(formattingDispatchTable.hasOwnProperty(format)){ formattingDispatchTable[format](textarea, textarea.selectionStart, textarea.selectionEnd); From 4f53bd59be4fd13155d22a4596f09aabd7e35a6c Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 19 Feb 2015 17:24:58 -0500 Subject: [PATCH 054/142] rewards - got custom inputs loading --- public/src/admin/extend/rewards.js | 69 +++++++++++++++++++++++++++--- src/rewards/admin.js | 22 ++++++++-- src/views/admin/extend/rewards.tpl | 5 ++- 3 files changed, 84 insertions(+), 12 deletions(-) diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index 2c31124032..7fb619c752 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -1,5 +1,5 @@ "use strict"; -/* global define, app, socket, bootbox */ +/* global define, app, ajaxify, socket, templates, bootbox */ define('admin/extend/rewards', function() { var rewards = {}; @@ -9,17 +9,72 @@ define('admin/extend/rewards', function() { active; rewards.init = function() { - //available = JSON.parse($('#rewards').val()); - //active = JSON.parse($('#active').val()); - - $(window).on('action:ajaxify.end', function() { + available = JSON.parse(ajaxify.variables.get('rewards')); + active = JSON.parse(ajaxify.variables.get('active')); + $('[data-selected]').each(function() { - console.log($(this).attr('data-selected')); - $(this).val($(this).attr('data-selected')); + select($(this)); + }).on('change', function() { + update($(this)); }); }); }; + function select(el) { + el.val(el.attr('data-selected')); + switch (el.attr('name')) { + case 'reward': + selectReward(el); + break; + } + } + + function update(el) { + el.attr('data-selected', el.val()); + switch (el.attr('name')) { + case 'reward': + selectReward(el); + break; + } + } + + function selectReward(el) { + var div = el.parent().find('.inputs'), + inputs, + html = ''; + + for (var reward in available) { + if (available.hasOwnProperty(reward)) { + if (parseInt(available[reward].rewardID, 10) === parseInt(el.attr('data-selected'), 10)) { + inputs = available[reward].inputs; + break; + } + } + } + + if (!inputs) { + app.alertError('Illegal reward - no inputs found! ' + el.attr('data-selected')); + } + + inputs.forEach(function(input) { + html += ''; + }); + + div.html(html); + } + return rewards; }); \ No newline at end of file diff --git a/src/rewards/admin.js b/src/rewards/admin.js index ebc2b95a95..c7fc90cf8d 100644 --- a/src/rewards/admin.js +++ b/src/rewards/admin.js @@ -37,8 +37,8 @@ rewards.get = function(callback) { "conditional": "lesserorequalthan" }, { - "name": "is string:", - "conditional": "isstring" + "name": "string:", + "conditional": "string" } ], active: [ @@ -65,7 +65,21 @@ rewards.get = function(callback) { { "type": "select", "name": "groupname", - "values": ["Group 1", "Group 2", "Group 3"], + "label": "Group Name:", + "values": [ + { + "name": "Group 1", + "value": "group1" + }, + { + "name": "Group 2", + "value": "group2" + }, + { + "name": "Group 3", + "value": "group3" + } + ], } ] }, @@ -76,10 +90,12 @@ rewards.get = function(callback) { { "type": "text", "name": "title", + "label": "Title:" }, { "type": "text", "name": "message", + "label": "Message:" } ] } diff --git a/src/views/admin/extend/rewards.tpl b/src/views/admin/extend/rewards.tpl index 7ff08995b9..f94cf347c8 100644 --- a/src/views/admin/extend/rewards.tpl +++ b/src/views/admin/extend/rewards.tpl @@ -26,12 +26,13 @@ +
    - - + + From d9bd7a400de83a3f9f011bfe6f0364f65c538723 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 19 Feb 2015 17:29:44 -0500 Subject: [PATCH 055/142] minor styling --- public/src/admin/extend/rewards.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index 7fb619c752..0deba1328c 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -58,7 +58,7 @@ define('admin/extend/rewards', function() { } inputs.forEach(function(input) { - html += '
    '; }); div.html(html); From 82543bdeee0f004a6cde63ce684096fec871b140 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 19 Feb 2015 17:31:05 -0500 Subject: [PATCH 056/142] fixed #2744 --- public/src/admin/manage/users.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/admin/manage/users.js b/public/src/admin/manage/users.js index dad64e35af..d9eb888985 100644 --- a/public/src/admin/manage/users.js +++ b/public/src/admin/manage/users.js @@ -214,7 +214,7 @@ define('admin/manage/users', ['admin/modules/selectable'], function(selectable) } $('#create-modal').modal('hide'); $('#create-modal').on('hidden.bs.modal', function() { - ajaxify.go('admin/users'); + ajaxify.refresh(); }); app.alertSuccess('User created!'); }); From 9291ec64f9bb432cdd8a885368bc85ce1d2dd65b Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 19 Feb 2015 17:47:47 -0500 Subject: [PATCH 057/142] styling --- public/less/admin/admin.less | 1 + public/less/admin/extend/rewards.less | 17 +++++++++++ public/src/admin/extend/rewards.js | 2 +- src/views/admin/extend/rewards.tpl | 44 +++++++++++++++------------ 4 files changed, 44 insertions(+), 20 deletions(-) create mode 100644 public/less/admin/extend/rewards.less diff --git a/public/less/admin/admin.less b/public/less/admin/admin.less index ee9a87b605..c304ddd774 100644 --- a/public/less/admin/admin.less +++ b/public/less/admin/admin.less @@ -10,6 +10,7 @@ @import "./appearance/customise"; @import "./appearance/themes"; @import "./extend/plugins"; +@import "./extend/rewards"; @import "./advanced/database"; @import "./modules/alerts"; diff --git a/public/less/admin/extend/rewards.less b/public/less/admin/extend/rewards.less new file mode 100644 index 0000000000..997983ea81 --- /dev/null +++ b/public/less/admin/extend/rewards.less @@ -0,0 +1,17 @@ +#rewards { + .well { + vertical-align: top; + min-height: 100px; + } + + ul { + list-style-type: none; + padding: 0px; + margin: 0px; + + > li { + border-bottom: 1px solid #ddd; + margin-bottom: 20px; + } + } +} \ No newline at end of file diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index 0deba1328c..02725f49fb 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -40,7 +40,7 @@ define('admin/extend/rewards', function() { } function selectReward(el) { - var div = el.parent().find('.inputs'), + var div = el.parents('li').find('.inputs'), inputs, html = ''; diff --git a/src/views/admin/extend/rewards.tpl b/src/views/admin/extend/rewards.tpl index f94cf347c8..8c7b9e31c3 100644 --- a/src/views/admin/extend/rewards.tpl +++ b/src/views/admin/extend/rewards.tpl @@ -7,25 +7,31 @@
    • - If User's - - Is - - - Then - +
      +
      + +
      +
      +
      + + +
      +
      +
      + +
    • From 7457b22427cdf2e2459b7568cc5b3ee418710773 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 19 Feb 2015 17:48:01 -0500 Subject: [PATCH 058/142] added more api fields to rewards --- src/rewards/admin.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/rewards/admin.js b/src/rewards/admin.js index c7fc90cf8d..0da54efa7f 100644 --- a/src/rewards/admin.js +++ b/src/rewards/admin.js @@ -46,6 +46,10 @@ rewards.get = function(callback) { "rewardID": 1, "condition": "postcount", "conditional": "greaterthan", + "rewards": { + "title": "Here is a title", + "message": "here is a message" + }, "value": 100, "disabled": 0 }, @@ -53,6 +57,9 @@ rewards.get = function(callback) { "rewardID": 0, "condition": "lastLoggedIn", "conditional": "lesserthan", + "rewards": { + "groupname": "group2" + }, "value": 10, "disabled": 0 } From 756d03fa62d4d235571a2a14699275ff972f40f7 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 19 Feb 2015 18:14:38 -0500 Subject: [PATCH 059/142] closes #2745 generate paths on retrieval instead of notification creation also fix follow notification path --- src/messaging.js | 53 ++++++++++++++++++++------------------- src/notifications.js | 5 ++-- src/socket.io/user.js | 1 - src/user/notifications.js | 7 ++++++ 4 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/messaging.js b/src/messaging.js index 339ec7798c..86ecf7014d 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -320,33 +320,34 @@ var db = require('./database'), }; function sendNotifications(fromuid, touid, messageObj, callback) { - if (!websockets.isUserOnline(touid)) { - notifications.create({ - bodyShort: '[[notifications:new_message_from, ' + messageObj.fromUser.username + ']]', - bodyLong: messageObj.content, - path: nconf.get('relative_path') + '/chats/' + utils.slugify(messageObj.fromUser.username), - nid: 'chat_' + fromuid + '_' + touid, - from: fromuid - }, function(err, notification) { - if (!err && notification) { - notifications.push(notification, [touid], callback); - } - }); - - user.getSettings(messageObj.toUser.uid, function(err, settings) { - if (settings.sendChatNotifications && !parseInt(meta.config.disableEmailSubscriptions, 10)) { - emailer.send('notif_chat', touid, { - subject: '[[email:notif.chat.subject, ' + messageObj.fromUser.username + ']]', - username: messageObj.toUser.username, - summary: '[[notifications:new_message_from, ' + messageObj.fromUser.username + ']]', - message: messageObj, - site_title: meta.config.title || 'NodeBB', - url: nconf.get('url'), - fromUserslug: utils.slugify(messageObj.fromUser.username) - }); - } - }); + if (websockets.isUserOnline(touid)) { + return callback(); } + + notifications.create({ + bodyShort: '[[notifications:new_message_from, ' + messageObj.fromUser.username + ']]', + bodyLong: messageObj.content, + nid: 'chat_' + fromuid + '_' + touid, + from: fromuid + }, function(err, notification) { + if (!err && notification) { + notifications.push(notification, [touid], callback); + } + }); + + user.getSettings(messageObj.toUser.uid, function(err, settings) { + if (settings.sendChatNotifications && !parseInt(meta.config.disableEmailSubscriptions, 10)) { + emailer.send('notif_chat', touid, { + subject: '[[email:notif.chat.subject, ' + messageObj.fromUser.username + ']]', + username: messageObj.toUser.username, + summary: '[[notifications:new_message_from, ' + messageObj.fromUser.username + ']]', + message: messageObj, + site_title: meta.config.title || 'NodeBB', + url: nconf.get('url'), + fromUserslug: utils.slugify(messageObj.fromUser.username) + }); + } + }); } }(exports)); diff --git a/src/notifications.js b/src/notifications.js index 0008fb1f69..5beed15f9c 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -55,11 +55,12 @@ var async = require('async'), } if (notification.from && !notification.image) { - User.getUserField(notification.from, 'picture', function(err, picture) { + User.getUserFields(notification.from, ['username', 'userslug', 'picture'], function(err, userData) { if (err) { return next(err); } - notification.image = picture; + notification.image = userData.picture; + notification.user = userData; next(null, notification); }); return; diff --git a/src/socket.io/user.js b/src/socket.io/user.js index fd23405758..15aa0e2ba6 100644 --- a/src/socket.io/user.js +++ b/src/socket.io/user.js @@ -294,7 +294,6 @@ SocketUser.follow = function(socket, data, callback) { notifications.create({ bodyShort: '[[notifications:user_started_following_you, ' + userData.username + ']]', - path: nconf.get('relative_path') + '/user/' + userData.userslug, nid: 'follow:' + data.uid + ':uid:' + socket.uid, from: socket.uid }, function(err, notification) { diff --git a/src/user/notifications.js b/src/user/notifications.js index 523b9e4903..706c227097 100644 --- a/src/user/notifications.js +++ b/src/user/notifications.js @@ -124,6 +124,13 @@ var async = require('async'), } notification.path = pidToPaths[notification.pid] || notification.path || ''; + + if (notification.nid.startsWith('chat')) { + notification.path = nconf.get('relative_path') + '/chats/' + notification.user.userslug; + } else if (notification.nid.startsWith('follow')) { + notification.path = nconf.get('relative_path') + '/user/' + notification.user.userslug; + } + notification.datetimeISO = utils.toISOString(notification.datetime); return notification; }); From b1c37775c5fa548ba1234822b53f09300eb5d262 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 19 Feb 2015 18:22:21 -0500 Subject: [PATCH 060/142] more styling --- public/less/admin/extend/rewards.less | 4 ++++ src/rewards/admin.js | 4 ++-- src/views/admin/extend/rewards.tpl | 9 +++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/public/less/admin/extend/rewards.less b/public/less/admin/extend/rewards.less index 997983ea81..cf62159695 100644 --- a/public/less/admin/extend/rewards.less +++ b/public/less/admin/extend/rewards.less @@ -2,6 +2,10 @@ .well { vertical-align: top; min-height: 100px; + + &.pull-right { + min-height: 0px; + } } ul { diff --git a/src/rewards/admin.js b/src/rewards/admin.js index 0da54efa7f..32b1321a8f 100644 --- a/src/rewards/admin.js +++ b/src/rewards/admin.js @@ -51,7 +51,7 @@ rewards.get = function(callback) { "message": "here is a message" }, "value": 100, - "disabled": 0 + "disabled": false }, { "rewardID": 0, @@ -61,7 +61,7 @@ rewards.get = function(callback) { "groupname": "group2" }, "value": 10, - "disabled": 0 + "disabled": true } ], rewards: [ diff --git a/src/views/admin/extend/rewards.tpl b/src/views/admin/extend/rewards.tpl index 8c7b9e31c3..a45f319847 100644 --- a/src/views/admin/extend/rewards.tpl +++ b/src/views/admin/extend/rewards.tpl @@ -33,6 +33,15 @@
      +
      + + + + + + +
      +
    From 4145046e02374bb05e0dc526a26e1e8188799847 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 19 Feb 2015 18:46:36 -0500 Subject: [PATCH 061/142] rewards: got everything loading --- public/src/admin/extend/rewards.js | 15 ++++++++++++++- src/views/admin/extend/rewards.tpl | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index 02725f49fb..712089d401 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -40,7 +40,8 @@ define('admin/extend/rewards', function() { } function selectReward(el) { - var div = el.parents('li').find('.inputs'), + var parent = el.parents('[data-rewardID]'), + div = parent.find('.inputs'), inputs, html = ''; @@ -48,6 +49,7 @@ define('admin/extend/rewards', function() { if (available.hasOwnProperty(reward)) { if (parseInt(available[reward].rewardID, 10) === parseInt(el.attr('data-selected'), 10)) { inputs = available[reward].inputs; + parent.attr('data-rewardID', available[reward].rewardID); break; } } @@ -74,6 +76,17 @@ define('admin/extend/rewards', function() { }); div.html(html); + + $('[data-rewardID]').each(function(i) { + var div = $(this).find('.inputs'), + rewards = active[i].rewards; + + for (var reward in rewards) { + if (rewards.hasOwnProperty(reward)) { + div.find('[name="' + reward + '"]').val(rewards[reward]); + } + } + }); } return rewards; diff --git a/src/views/admin/extend/rewards.tpl b/src/views/admin/extend/rewards.tpl index a45f319847..d2deab96d1 100644 --- a/src/views/admin/extend/rewards.tpl +++ b/src/views/admin/extend/rewards.tpl @@ -6,7 +6,7 @@
      -
    • +

    • @@ -34,11 +34,11 @@
      - + - + - +
      From 029ae11ed56aaebf0864bbc6139d731f4cac1f19 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 19 Feb 2015 19:10:01 -0500 Subject: [PATCH 063/142] rewards - cleanup --- public/src/admin/extend/rewards.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index 0bf6fa35d9..b73ad877b0 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -20,19 +20,24 @@ define('admin/extend/rewards', function() { }); $('.delete').on('click', function() { - var id = $(this).parents('[data-index]'); + var parent = $(this).parents('[data-index]'), + id = parent.attr('data-index'); delete active[id]; // send delete api call + + parent.remove(); + return false; }); $('.toggle').on('click', function() { var btn = $(this), disabled = btn.html() === 'Disabled', - id = $(this).parents('[data-index]'); + id = $(this).parents('[data-index]').attr('data-index'); btn.toggleClass('btn-warning').toggleClass('btn-success').html(disabled ? 'Enabled' : 'Disabled'); // send disable api call + return false; }); }); }; @@ -93,6 +98,10 @@ define('admin/extend/rewards', function() { div.html(html); + populateInputs(); + } + + function populateInputs() { $('[data-rewardID]').each(function(i) { var div = $(this).find('.inputs'), rewards = active[i].rewards; From 88aa4276695baf626ccef35b94e1f8bd8de225ea Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 19 Feb 2015 19:33:33 -0500 Subject: [PATCH 064/142] updated upgrade script so that it does not rely on Groups.list, as the method has changed between v0.5.0 and v0.6.0. Also added setImmediate call so eachLimit doesn't explode --- src/upgrade.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/upgrade.js b/src/upgrade.js index 977854ab01..117b79848e 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -250,11 +250,24 @@ Upgrade.upgrade = function(callback) { }, next); } - Groups.list({showSystemGroups: true}, function(err, groups) { + async.waterfall([ + async.apply(db.getSetMembers, 'groups'), + function(groups, next) { + async.filter(groups, function(group, next) { + db.getObjectField('group:' + group, 'hidden', function(err, hidden) { + next(!parseInt(hidden, 10)); + }, next); + }, function(groups) { + next(null, groups); + }); + } + ], function(err, groups) { if (err) { return next(err); } + groups.push('administrators', 'registered-users'); + async.eachLimit(cids, 50, function(cid, next) { upgradePrivileges(cid, groups, next); }, next); @@ -445,7 +458,7 @@ Upgrade.upgrade = function(callback) { if (setting.dailyDigestFreq !== 'off') { db.sortedSetAdd('digest:' + setting.dailyDigestFreq + ':uids', now, setting.uid, next); } else { - next(false); + setImmediate(next); } }, function(err) { if (err) { From 4acf8cafee1bee1b6b6e57a69b698876069b33e7 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 19 Feb 2015 21:51:32 -0500 Subject: [PATCH 065/142] rewards: adding new rows --- public/src/admin/extend/rewards.js | 18 +++++++++++++++++- src/views/admin/extend/rewards.tpl | 5 +++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index b73ad877b0..b19f092a69 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -39,6 +39,8 @@ define('admin/extend/rewards', function() { // send disable api call return false; }); + + $('#new').on('click', newReward); }); }; @@ -77,7 +79,7 @@ define('admin/extend/rewards', function() { } if (!inputs) { - app.alertError('Illegal reward - no inputs found! ' + el.attr('data-selected')); + return app.alertError('Illegal reward - no inputs found! ' + el.attr('data-selected')); } inputs.forEach(function(input) { @@ -114,5 +116,19 @@ define('admin/extend/rewards', function() { }); } + function newReward() { + var ul = $('#active'), + li = $('#active li').last().clone(true); + + li.attr('data-index', parseInt(li.attr('data-index') + 1, 10)) + .attr('data-rewardID', ''); + + li.find('.inputs').html(''); + li.find('[name="reward"]').val(''); + + + ul.append(li); + } + return rewards; }); \ No newline at end of file diff --git a/src/views/admin/extend/rewards.tpl b/src/views/admin/extend/rewards.tpl index 8fc7cd2cb9..360d0b85ec 100644 --- a/src/views/admin/extend/rewards.tpl +++ b/src/views/admin/extend/rewards.tpl @@ -4,7 +4,7 @@
      Rewards
      -
        +
        • @@ -54,8 +54,9 @@
          -
          Save Settings
          +
          Rewards Control
          +
          From 2cc6f0c5076aeec94a8130f2773a527511f0a833 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 19 Feb 2015 21:56:50 -0500 Subject: [PATCH 066/142] fixed disable logic --- public/src/admin/extend/rewards.js | 6 +++--- src/views/admin/extend/rewards.tpl | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index b19f092a69..1445f2cce1 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -32,10 +32,10 @@ define('admin/extend/rewards', function() { $('.toggle').on('click', function() { var btn = $(this), - disabled = btn.html() === 'Disabled', + disabled = btn.html() === 'Enable', id = $(this).parents('[data-index]').attr('data-index'); - btn.toggleClass('btn-warning').toggleClass('btn-success').html(disabled ? 'Enabled' : 'Disabled'); + btn.toggleClass('btn-warning').toggleClass('btn-success').html(disabled ? 'Enable' : 'Disable'); // send disable api call return false; }); @@ -125,7 +125,7 @@ define('admin/extend/rewards', function() { li.find('.inputs').html(''); li.find('[name="reward"]').val(''); - + li.find('.toggle').removeClass('btn-warning').addClass('btn-success').html('Enable'); ul.append(li); } diff --git a/src/views/admin/extend/rewards.tpl b/src/views/admin/extend/rewards.tpl index 360d0b85ec..5730e4b750 100644 --- a/src/views/admin/extend/rewards.tpl +++ b/src/views/admin/extend/rewards.tpl @@ -36,9 +36,9 @@
          - + - +
          @@ -56,7 +56,7 @@
          Rewards Control
          -
          +
          From 46e0fad2d8b5022337a0f10cd692bfc7bf23cdf7 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 19 Feb 2015 22:06:52 -0500 Subject: [PATCH 067/142] rewards: code organization, getting ready to code saving/loading of active rewards --- src/rewards/admin.js | 222 +++++++++++++++++++++++-------------------- 1 file changed, 117 insertions(+), 105 deletions(-) diff --git a/src/rewards/admin.js b/src/rewards/admin.js index 32b1321a8f..3a9b0b0580 100644 --- a/src/rewards/admin.js +++ b/src/rewards/admin.js @@ -1,121 +1,133 @@ "use strict"; -var rewards = {}; +var rewards = {}, + async = require('async'), + plugins = require('../plugins'); + +var defaults = { + conditionals: [ + { + "name": ">", + "conditional": "greaterthan" + }, + { + "name": ">=", + "conditional": "greaterorequalthan" + }, + { + "name": "<", + "conditional": "lesserthan" + }, + { + "name": "<=", + "conditional": "lesserorequalthan" + }, + { + "name": "string:", + "conditional": "string" + } + ] +}; rewards.get = function(callback) { - callback(false, { - conditions: [ - { - "name": "Reputation", - "condition": "reputation" - }, - { - "name": "Post Count", - "condition": "postcount" - }, - { - "name": "Last Logged in Time", - "condition": "lastLoggedIn" - } - ], - conditionals: [ - { - "name": ">", - "conditional": "greaterthan" - }, - { - "name": ">=", - "conditional": "greaterorequalthan" - }, - { - "name": "<", - "conditional": "lesserthan" - }, - { - "name": "<=", - "conditional": "lesserorequalthan" - }, - { - "name": "string:", - "conditional": "string" - } - ], - active: [ - { - "rewardID": 1, - "condition": "postcount", - "conditional": "greaterthan", - "rewards": { - "title": "Here is a title", - "message": "here is a message" + async.parallel({ + active: getActiveRewards, + conditions: function(next) { + plugins.fireHook('filter:rewards.conditions', [ + { + "name": "Reputation", + "condition": "reputation" }, - "value": 100, - "disabled": false - }, - { - "rewardID": 0, - "condition": "lastLoggedIn", - "conditional": "lesserthan", - "rewards": { - "groupname": "group2" + { + "name": "Post Count", + "condition": "postcount" }, - "value": 10, - "disabled": true - } - ], - rewards: [ - { - "rewardID": 0, - "name": "Add to Group", - "inputs": [ - { - "type": "select", - "name": "groupname", - "label": "Group Name:", - "values": [ - { - "name": "Group 1", - "value": "group1" - }, - { - "name": "Group 2", - "value": "group2" - }, - { - "name": "Group 3", - "value": "group3" - } - ], - } - ] - }, - { - "rewardID": 1, - "name": "Send alert message", - "inputs": [ - { - "type": "text", - "name": "title", - "label": "Title:" - }, - { - "type": "text", - "name": "message", - "label": "Message:" - } - ] - } - ] - }); + { + "name": "Last Logged in Time", + "condition": "lastLoggedIn" + } + ], next); + }, + conditionals: function(next) { + plugins.fireHook('filter:rewards.conditionals', defaults.conditionals, next); + }, + rewards: function(next) { + plugins.fireHook('filter:rewards.rewards', [ + { + "rewardID": 0, + "name": "Add to Group", + "inputs": [ + { + "type": "select", + "name": "groupname", + "label": "Group Name:", + "values": [ + { + "name": "Group 1", + "value": "group1" + }, + { + "name": "Group 2", + "value": "group2" + }, + { + "name": "Group 3", + "value": "group3" + } + ], + } + ] + }, + { + "rewardID": 1, + "name": "Send alert message", + "inputs": [ + { + "type": "text", + "name": "title", + "label": "Title:" + }, + { + "type": "text", + "name": "message", + "label": "Message:" + } + ] + } + ], next); + } + }, callback); }; function getConditions() { } -function getRewards() { - +function getActiveRewards(callback) { + callback(false, [ + { + "rewardID": 1, + "condition": "postcount", + "conditional": "greaterthan", + "rewards": { + "title": "Here is a title", + "message": "here is a message" + }, + "value": 100, + "disabled": false + }, + { + "rewardID": 0, + "condition": "lastLoggedIn", + "conditional": "lesserthan", + "rewards": { + "groupname": "group2" + }, + "value": 10, + "disabled": true + } + ]); } module.exports = rewards; \ No newline at end of file From 91c02d4e29407dbff64d3de4fd7d3c75ae6bdd67 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 19 Feb 2015 22:19:04 -0500 Subject: [PATCH 068/142] ditching rewardID in favour of verbose id's --- public/src/admin/extend/rewards.js | 10 +++++----- src/rewards/admin.js | 8 ++++---- src/views/admin/extend/rewards.tpl | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index 1445f2cce1..9dae5a731b 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -63,16 +63,16 @@ define('admin/extend/rewards', function() { } function selectReward(el) { - var parent = el.parents('[data-rewardID]'), + var parent = el.parents('[data-id]'), div = parent.find('.inputs'), inputs, html = ''; for (var reward in available) { if (available.hasOwnProperty(reward)) { - if (parseInt(available[reward].rewardID, 10) === parseInt(el.attr('data-selected'), 10)) { + if (available[reward].id === el.attr('data-selected')) { inputs = available[reward].inputs; - parent.attr('data-rewardID', available[reward].rewardID); + parent.attr('data-id', available[reward].id); break; } } @@ -104,7 +104,7 @@ define('admin/extend/rewards', function() { } function populateInputs() { - $('[data-rewardID]').each(function(i) { + $('[data-id]').each(function(i) { var div = $(this).find('.inputs'), rewards = active[i].rewards; @@ -121,7 +121,7 @@ define('admin/extend/rewards', function() { li = $('#active li').last().clone(true); li.attr('data-index', parseInt(li.attr('data-index') + 1, 10)) - .attr('data-rewardID', ''); + .attr('data-id', ''); li.find('.inputs').html(''); li.find('[name="reward"]').val(''); diff --git a/src/rewards/admin.js b/src/rewards/admin.js index 3a9b0b0580..dfb70ae78f 100644 --- a/src/rewards/admin.js +++ b/src/rewards/admin.js @@ -55,7 +55,7 @@ rewards.get = function(callback) { rewards: function(next) { plugins.fireHook('filter:rewards.rewards', [ { - "rewardID": 0, + "id": "core:add-to-group", "name": "Add to Group", "inputs": [ { @@ -80,7 +80,7 @@ rewards.get = function(callback) { ] }, { - "rewardID": 1, + "id": "core:alert-user", "name": "Send alert message", "inputs": [ { @@ -107,7 +107,7 @@ function getConditions() { function getActiveRewards(callback) { callback(false, [ { - "rewardID": 1, + "id": "core:alert-user", "condition": "postcount", "conditional": "greaterthan", "rewards": { @@ -118,7 +118,7 @@ function getActiveRewards(callback) { "disabled": false }, { - "rewardID": 0, + "id": "core:add-to-group", "condition": "lastLoggedIn", "conditional": "lesserthan", "rewards": { diff --git a/src/views/admin/extend/rewards.tpl b/src/views/admin/extend/rewards.tpl index 5730e4b750..cc628cbf5b 100644 --- a/src/views/admin/extend/rewards.tpl +++ b/src/views/admin/extend/rewards.tpl @@ -6,7 +6,7 @@
            -
          • +

          • +
            From cd26f5d06db66d094a20221de62055462f22bd5b Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 19 Feb 2015 22:44:23 -0500 Subject: [PATCH 069/142] passing in values for saving rewards --- public/src/admin/extend/rewards.js | 26 ++++++++++++++++-- src/socket.io/admin.js | 1 + src/socket.io/admin/rewards.js | 13 +++++++++ src/views/admin/extend/rewards.tpl | 42 ++++++++++++++++-------------- 4 files changed, 61 insertions(+), 21 deletions(-) create mode 100644 src/socket.io/admin/rewards.js diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index 9dae5a731b..54177ea4c9 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -41,13 +41,14 @@ define('admin/extend/rewards', function() { }); $('#new').on('click', newReward); + $('#save').on('click', saveRewards); }); }; function select(el) { el.val(el.attr('data-selected')); switch (el.attr('name')) { - case 'reward': + case 'id': selectReward(el); break; } @@ -56,7 +57,7 @@ define('admin/extend/rewards', function() { function update(el) { el.attr('data-selected', el.val()); switch (el.attr('name')) { - case 'reward': + case 'id': selectReward(el); break; } @@ -130,5 +131,26 @@ define('admin/extend/rewards', function() { ul.append(li); } + function saveRewards() { + var activeRewards = []; + + $('#active li').each(function() { + var data = $(this).find('form.main').serializeArray(); + + data.rewards = $(this).find('form.rewards').serializeArray(); + data.disabled = $(this).find('.toggle').html() === 'Enable'; + + activeRewards.push(data); + }); + + socket.emit('admin.rewards.save', activeRewards, function(err) { + if (err) { + app.alertError(err.message); + } else { + app.alertSuccess('Successfully saved rewards'); + } + }); + } + return rewards; }); \ No newline at end of file diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js index a770af5c53..49b3744dbb 100644 --- a/src/socket.io/admin.js +++ b/src/socket.io/admin.js @@ -25,6 +25,7 @@ var async = require('async'), categories: require('./admin/categories'), groups: require('./admin/groups'), tags: require('./admin/tags'), + rewards: require('./admin/rewards'), themes: {}, plugins: {}, widgets: {}, diff --git a/src/socket.io/admin/rewards.js b/src/socket.io/admin/rewards.js new file mode 100644 index 0000000000..70ac1fecf5 --- /dev/null +++ b/src/socket.io/admin/rewards.js @@ -0,0 +1,13 @@ +"use strict"; + +var rewards = require('../../rewards'), + rewards = {}; + +rewards.save = function(socket, data, callback) { + console.log(data); + callback(new Error('derp')); + //callback(err ? err.message : null); +}; + + +module.exports = rewards; \ No newline at end of file diff --git a/src/views/admin/extend/rewards.tpl b/src/views/admin/extend/rewards.tpl index cc628cbf5b..af6dac1865 100644 --- a/src/views/admin/extend/rewards.tpl +++ b/src/views/admin/extend/rewards.tpl @@ -3,10 +3,10 @@
            Rewards
            - -
              - -
            • +
                + +
              • +

                - +

                -
                +
              • + +
                -
                - - - - - - -
                -
                - - -
              - + + +
              + + + + + + +
              +
              +
            • + +
            From 67b693cd3a708c336baf232b309d265fa833a564 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 19 Feb 2015 23:02:52 -0500 Subject: [PATCH 070/142] pass in expected format of active rewards --- public/src/admin/extend/rewards.js | 15 ++++++++++++--- src/rewards/admin.js | 6 +++++- src/views/admin/extend/rewards.tpl | 6 +++--- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index 54177ea4c9..77055fe32e 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -135,11 +135,20 @@ define('admin/extend/rewards', function() { var activeRewards = []; $('#active li').each(function() { - var data = $(this).find('form.main').serializeArray(); + var data = {rewards: {}}, + main = $(this).find('form.main').serializeArray(), + rewards = $(this).find('form.rewards').serializeArray(); + + main.forEach(function(obj) { + data[obj.name] = obj.value; + }); + + rewards.forEach(function(obj) { + data.rewards[obj.name] = obj.value; + }); - data.rewards = $(this).find('form.rewards').serializeArray(); data.disabled = $(this).find('.toggle').html() === 'Enable'; - + activeRewards.push(data); }); diff --git a/src/rewards/admin.js b/src/rewards/admin.js index dfb70ae78f..a279f44284 100644 --- a/src/rewards/admin.js +++ b/src/rewards/admin.js @@ -2,7 +2,8 @@ var rewards = {}, async = require('async'), - plugins = require('../plugins'); + plugins = require('../plugins'), + db = require('../database'); var defaults = { conditionals: [ @@ -29,6 +30,9 @@ var defaults = { ] }; +rewards.save = function(data, callback) { + +}; rewards.get = function(callback) { async.parallel({ diff --git a/src/views/admin/extend/rewards.tpl b/src/views/admin/extend/rewards.tpl index af6dac1865..9d060fe965 100644 --- a/src/views/admin/extend/rewards.tpl +++ b/src/views/admin/extend/rewards.tpl @@ -6,7 +6,7 @@
            • -
              +

              - -
              + +
              From e5115c0cf2e9a6cf15d09bcf70d535a372ca895a Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 19 Feb 2015 23:15:37 -0500 Subject: [PATCH 071/142] differentiate between rid and id --- public/src/admin/extend/rewards.js | 23 ++++++++++++----------- src/rewards/admin.js | 16 +++++++++++----- src/views/admin/extend/rewards.tpl | 6 +++--- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index 77055fe32e..b9e837159d 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -20,8 +20,8 @@ define('admin/extend/rewards', function() { }); $('.delete').on('click', function() { - var parent = $(this).parents('[data-index]'), - id = parent.attr('data-index'); + var parent = $(this).parents('[data-id]'), + id = parent.attr('data-id'); delete active[id]; // send delete api call @@ -33,7 +33,7 @@ define('admin/extend/rewards', function() { $('.toggle').on('click', function() { var btn = $(this), disabled = btn.html() === 'Enable', - id = $(this).parents('[data-index]').attr('data-index'); + id = $(this).parents('[data-id]').attr('data-id'); btn.toggleClass('btn-warning').toggleClass('btn-success').html(disabled ? 'Enable' : 'Disable'); // send disable api call @@ -48,7 +48,7 @@ define('admin/extend/rewards', function() { function select(el) { el.val(el.attr('data-selected')); switch (el.attr('name')) { - case 'id': + case 'rid': selectReward(el); break; } @@ -57,23 +57,23 @@ define('admin/extend/rewards', function() { function update(el) { el.attr('data-selected', el.val()); switch (el.attr('name')) { - case 'id': + case 'rid': selectReward(el); break; } } function selectReward(el) { - var parent = el.parents('[data-id]'), + var parent = el.parents('[data-rid]'), div = parent.find('.inputs'), inputs, html = ''; for (var reward in available) { if (available.hasOwnProperty(reward)) { - if (available[reward].id === el.attr('data-selected')) { + if (available[reward].rid === el.attr('data-selected')) { inputs = available[reward].inputs; - parent.attr('data-id', available[reward].id); + parent.attr('data-rid', available[reward].rid); break; } } @@ -105,7 +105,7 @@ define('admin/extend/rewards', function() { } function populateInputs() { - $('[data-id]').each(function(i) { + $('[data-rid]').each(function(i) { var div = $(this).find('.inputs'), rewards = active[i].rewards; @@ -121,8 +121,8 @@ define('admin/extend/rewards', function() { var ul = $('#active'), li = $('#active li').last().clone(true); - li.attr('data-index', parseInt(li.attr('data-index') + 1, 10)) - .attr('data-id', ''); + li.attr('data-id', parseInt(li.attr('data-id') + 1, 10)) + .attr('data-rid', ''); li.find('.inputs').html(''); li.find('[name="reward"]').val(''); @@ -147,6 +147,7 @@ define('admin/extend/rewards', function() { data.rewards[obj.name] = obj.value; }); + data.id = $(this).attr('data-id'); data.disabled = $(this).find('.toggle').html() === 'Enable'; activeRewards.push(data); diff --git a/src/rewards/admin.js b/src/rewards/admin.js index a279f44284..688f3dcce5 100644 --- a/src/rewards/admin.js +++ b/src/rewards/admin.js @@ -31,7 +31,11 @@ var defaults = { }; rewards.save = function(data, callback) { - + data.forEach(function(reward) { + if (reward.disabled) { + //db.setAdd + } + }); }; rewards.get = function(callback) { @@ -59,7 +63,7 @@ rewards.get = function(callback) { rewards: function(next) { plugins.fireHook('filter:rewards.rewards', [ { - "id": "core:add-to-group", + "rid": "core:add-to-group", "name": "Add to Group", "inputs": [ { @@ -84,7 +88,7 @@ rewards.get = function(callback) { ] }, { - "id": "core:alert-user", + "rid": "core:alert-user", "name": "Send alert message", "inputs": [ { @@ -111,7 +115,8 @@ function getConditions() { function getActiveRewards(callback) { callback(false, [ { - "id": "core:alert-user", + "id": 0, + "rid": "core:alert-user", "condition": "postcount", "conditional": "greaterthan", "rewards": { @@ -122,7 +127,8 @@ function getActiveRewards(callback) { "disabled": false }, { - "id": "core:add-to-group", + "id": 1, + "rid": "core:add-to-group", "condition": "lastLoggedIn", "conditional": "lesserthan", "rewards": { diff --git a/src/views/admin/extend/rewards.tpl b/src/views/admin/extend/rewards.tpl index 9d060fe965..672e41fc7c 100644 --- a/src/views/admin/extend/rewards.tpl +++ b/src/views/admin/extend/rewards.tpl @@ -5,7 +5,7 @@
                -
              • +

              • @@ -26,9 +26,9 @@

                - - +
                From 5f54de3b37cc3ffa9ad777a8fbc636b0ae9e5fdb Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 19 Feb 2015 23:39:39 -0500 Subject: [PATCH 072/142] successfully got loading and saving of rewards working --- public/src/admin/extend/rewards.js | 16 +++++----- src/rewards/admin.js | 50 ++++++++++++++++++++++++++---- src/socket.io/admin/rewards.js | 12 +++---- 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index b9e837159d..645f5b0d98 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -106,12 +106,14 @@ define('admin/extend/rewards', function() { function populateInputs() { $('[data-rid]').each(function(i) { - var div = $(this).find('.inputs'), - rewards = active[i].rewards; - - for (var reward in rewards) { - if (rewards.hasOwnProperty(reward)) { - div.find('[name="' + reward + '"]').val(rewards[reward]); + if (active[i]) { + var div = $(this).find('.inputs'), + rewards = active[i].rewards; + + for (var reward in rewards) { + if (rewards.hasOwnProperty(reward)) { + div.find('[name="' + reward + '"]').val(rewards[reward]); + } } } }); @@ -121,7 +123,7 @@ define('admin/extend/rewards', function() { var ul = $('#active'), li = $('#active li').last().clone(true); - li.attr('data-id', parseInt(li.attr('data-id') + 1, 10)) + li.attr('data-id', parseInt(li.attr('data-id'), 10) + 1) .attr('data-rid', ''); li.find('.inputs').html(''); diff --git a/src/rewards/admin.js b/src/rewards/admin.js index 688f3dcce5..8f5fc8b40c 100644 --- a/src/rewards/admin.js +++ b/src/rewards/admin.js @@ -31,11 +31,24 @@ var defaults = { }; rewards.save = function(data, callback) { - data.forEach(function(reward) { - if (reward.disabled) { - //db.setAdd - } - }); + function save(data, next) { + var rewards = data.rewards; + delete data.rewards; + + async.parallel([ + function(next) { + db.setAdd('rewards:list', data.id, next); + }, + function(next) { + db.setObject('rewards:id:' + data.id, data, next); + }, + function(next) { + db.setObject('rewards:id:' + data.id + ':rewards', rewards, next); + } + ], next); + } + + async.each(data, save, callback); }; rewards.get = function(callback) { @@ -113,6 +126,31 @@ function getConditions() { } function getActiveRewards(callback) { + var activeRewards = []; + + function load(id, next) { + async.parallel({ + main: function(next) { + db.getObject('rewards:id:' + id, next); + }, + rewards: function(next) { + db.getObject('rewards:id:' + id + ':rewards', next); + } + }, function(err, data) { + data.main.rewards = data.rewards; + activeRewards.push(data.main); + + next(err); + }); + } + + db.getSetMembers('rewards:list', function(err, rewards) { + async.eachSeries(rewards, load, function(err) { + callback(err, activeRewards); + }); + }); + +/* callback(false, [ { "id": 0, @@ -137,7 +175,7 @@ function getActiveRewards(callback) { "value": 10, "disabled": true } - ]); + ]);*/ } module.exports = rewards; \ No newline at end of file diff --git a/src/socket.io/admin/rewards.js b/src/socket.io/admin/rewards.js index 70ac1fecf5..36e8b66c2c 100644 --- a/src/socket.io/admin/rewards.js +++ b/src/socket.io/admin/rewards.js @@ -1,13 +1,11 @@ "use strict"; -var rewards = require('../../rewards'), - rewards = {}; +var rewardsAdmin = require('../../rewards/admin'), + SocketRewards = {}; -rewards.save = function(socket, data, callback) { - console.log(data); - callback(new Error('derp')); - //callback(err ? err.message : null); +SocketRewards.save = function(socket, data, callback) { + rewardsAdmin.save(data, callback); }; -module.exports = rewards; \ No newline at end of file +module.exports = SocketRewards; \ No newline at end of file From c844400ab9834ee1a36d9b804b5394159c37e167 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 19 Feb 2015 23:40:23 -0500 Subject: [PATCH 073/142] cleanup and fix for adding new rewards --- public/src/admin/extend/rewards.js | 2 +- src/rewards/admin.js | 27 --------------------------- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index 645f5b0d98..b57c1c4cee 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -127,7 +127,7 @@ define('admin/extend/rewards', function() { .attr('data-rid', ''); li.find('.inputs').html(''); - li.find('[name="reward"]').val(''); + li.find('[name="rid"]').val(''); li.find('.toggle').removeClass('btn-warning').addClass('btn-success').html('Enable'); ul.append(li); diff --git a/src/rewards/admin.js b/src/rewards/admin.js index 8f5fc8b40c..dc1103f3ae 100644 --- a/src/rewards/admin.js +++ b/src/rewards/admin.js @@ -149,33 +149,6 @@ function getActiveRewards(callback) { callback(err, activeRewards); }); }); - -/* - callback(false, [ - { - "id": 0, - "rid": "core:alert-user", - "condition": "postcount", - "conditional": "greaterthan", - "rewards": { - "title": "Here is a title", - "message": "here is a message" - }, - "value": 100, - "disabled": false - }, - { - "id": 1, - "rid": "core:add-to-group", - "condition": "lastLoggedIn", - "conditional": "lesserthan", - "rewards": { - "groupname": "group2" - }, - "value": 10, - "disabled": true - } - ]);*/ } module.exports = rewards; \ No newline at end of file From 09086f7d30d5a89e80102cdb2341c3ee007e1b59 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 19 Feb 2015 23:49:36 -0500 Subject: [PATCH 074/142] deleting rewards --- public/src/admin/extend/rewards.js | 25 ++++++++++++++----------- src/rewards/admin.js | 16 +++++++++++++++- src/socket.io/admin/rewards.js | 4 ++++ 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index b57c1c4cee..5dd26c1821 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -19,12 +19,19 @@ define('admin/extend/rewards', function() { update($(this)); }); + populateInputs(); + $('.delete').on('click', function() { var parent = $(this).parents('[data-id]'), id = parent.attr('data-id'); - delete active[id]; - // send delete api call + socket.emit('admin.rewards.delete', {id: id}, function(err) { + if (err) { + app.alertError(err.message); + } else { + app.alertSuccess('Successfully deleted reward'); + } + }); parent.remove(); return false; @@ -100,20 +107,16 @@ define('admin/extend/rewards', function() { }); div.html(html); - - populateInputs(); } function populateInputs() { $('[data-rid]').each(function(i) { - if (active[i]) { - var div = $(this).find('.inputs'), - rewards = active[i].rewards; + var div = $(this).find('.inputs'), + rewards = active[i].rewards; - for (var reward in rewards) { - if (rewards.hasOwnProperty(reward)) { - div.find('[name="' + reward + '"]').val(rewards[reward]); - } + for (var reward in rewards) { + if (rewards.hasOwnProperty(reward)) { + div.find('[name="' + reward + '"]').val(rewards[reward]); } } }); diff --git a/src/rewards/admin.js b/src/rewards/admin.js index dc1103f3ae..658c286dcb 100644 --- a/src/rewards/admin.js +++ b/src/rewards/admin.js @@ -32,7 +32,7 @@ var defaults = { rewards.save = function(data, callback) { function save(data, next) { - var rewards = data.rewards; + var rewards = data.rewards || {}; delete data.rewards; async.parallel([ @@ -51,6 +51,20 @@ rewards.save = function(data, callback) { async.each(data, save, callback); }; +rewards.delete = function(data, callback) { + async.parallel([ + function(next) { + db.setRemove('rewards:list', data.id, next); + }, + function(next) { + db.delete('rewards:id:' + data.id, next); + }, + function(next) { + db.delete('rewards:id:' + data.id + ':rewards', next); + } + ], callback); +}; + rewards.get = function(callback) { async.parallel({ active: getActiveRewards, diff --git a/src/socket.io/admin/rewards.js b/src/socket.io/admin/rewards.js index 36e8b66c2c..b130a25455 100644 --- a/src/socket.io/admin/rewards.js +++ b/src/socket.io/admin/rewards.js @@ -7,5 +7,9 @@ SocketRewards.save = function(socket, data, callback) { rewardsAdmin.save(data, callback); }; +SocketRewards.delete = function(socket, data, callback) { + rewardsAdmin.delete(data, callback); +}; + module.exports = SocketRewards; \ No newline at end of file From 8373f7359ef2443531e1db1bf28d19a78903438b Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 19 Feb 2015 23:53:50 -0500 Subject: [PATCH 075/142] don't error out if reward not filled --- src/rewards/admin.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/rewards/admin.js b/src/rewards/admin.js index 658c286dcb..185ad6b1e5 100644 --- a/src/rewards/admin.js +++ b/src/rewards/admin.js @@ -32,7 +32,11 @@ var defaults = { rewards.save = function(data, callback) { function save(data, next) { - var rewards = data.rewards || {}; + if (!Object.keys(data.rewards).length) { + return next(); + } + + var rewards = data.rewards; delete data.rewards; async.parallel([ From 1998f102d6380060c4f4cbadb648cb37af0c6298 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 20 Feb 2015 00:26:19 -0500 Subject: [PATCH 076/142] using tjs to load new lines instead of cloning --- public/src/admin/extend/rewards.js | 93 +++++++++++++++++------------- src/views/admin/extend/rewards.tpl | 2 + 2 files changed, 56 insertions(+), 39 deletions(-) diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index 5dd26c1821..5fa1e25322 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -6,49 +6,54 @@ define('admin/extend/rewards', function() { var available, - active; + active, + conditions, + conditionals; rewards.init = function() { $(window).on('action:ajaxify.end', function() { available = JSON.parse(ajaxify.variables.get('rewards')); active = JSON.parse(ajaxify.variables.get('active')); + conditions = JSON.parse(ajaxify.variables.get('conditions')); + conditionals = JSON.parse(ajaxify.variables.get('conditionals')); $('[data-selected]').each(function() { select($(this)); - }).on('change', function() { - update($(this)); }); - populateInputs(); - - $('.delete').on('click', function() { - var parent = $(this).parents('[data-id]'), - id = parent.attr('data-id'); - - socket.emit('admin.rewards.delete', {id: id}, function(err) { - if (err) { - app.alertError(err.message); - } else { - app.alertSuccess('Successfully deleted reward'); - } + $('#active') + .on('[data-selected]', 'change', function() { + update($(this)); + }) + .on('.delete', 'click', function() { + var parent = $(this).parents('[data-id]'), + id = parent.attr('data-id'); + + socket.emit('admin.rewards.delete', {id: id}, function(err) { + if (err) { + app.alertError(err.message); + } else { + app.alertSuccess('Successfully deleted reward'); + } + }); + + parent.remove(); + return false; + }) + .on('.toggle', 'click', function() { + var btn = $(this), + disabled = btn.html() === 'Enable', + id = $(this).parents('[data-id]').attr('data-id'); + + btn.toggleClass('btn-warning').toggleClass('btn-success').html(disabled ? 'Enable' : 'Disable'); + // send disable api call + return false; }); - parent.remove(); - return false; - }); - - $('.toggle').on('click', function() { - var btn = $(this), - disabled = btn.html() === 'Enable', - id = $(this).parents('[data-id]').attr('data-id'); - - btn.toggleClass('btn-warning').toggleClass('btn-success').html(disabled ? 'Enable' : 'Disable'); - // send disable api call - return false; - }); - $('#new').on('click', newReward); $('#save').on('click', saveRewards); + + populateInputs(); }); }; @@ -124,16 +129,26 @@ define('admin/extend/rewards', function() { function newReward() { var ul = $('#active'), - li = $('#active li').last().clone(true); - - li.attr('data-id', parseInt(li.attr('data-id'), 10) + 1) - .attr('data-rid', ''); - - li.find('.inputs').html(''); - li.find('[name="rid"]').val(''); - li.find('.toggle').removeClass('btn-warning').addClass('btn-success').html('Enable'); - - ul.append(li); + id = parseInt($('#active li').last().attr('data-id'), 10); + + var data = { + active: [{ + id: id ? id + 1 : 0, + disabled: true, + value: '' + }], + conditions: conditions, + conditionals: conditionals, + rewards: available + }; + + var ul = $('#active'); + + templates.parse('admin/extend/rewards', 'active', data, function(li) { + li = $(li); + li.find('[name="rid"]').val(''); + ul.append(li); + }); } function saveRewards() { diff --git a/src/views/admin/extend/rewards.tpl b/src/views/admin/extend/rewards.tpl index 672e41fc7c..87129fe3c7 100644 --- a/src/views/admin/extend/rewards.tpl +++ b/src/views/admin/extend/rewards.tpl @@ -52,6 +52,8 @@
              + +
          From 5fcbffe65235cbe2be5cdbcd379adc1bc4700329 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 20 Feb 2015 00:29:15 -0500 Subject: [PATCH 077/142] fixed delegate syntax --- public/src/admin/extend/rewards.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index 5fa1e25322..63108e8ad2 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -22,10 +22,10 @@ define('admin/extend/rewards', function() { }); $('#active') - .on('[data-selected]', 'change', function() { + .on('change', '[data-selected]', function() { update($(this)); }) - .on('.delete', 'click', function() { + .on('click', '.delete', function() { var parent = $(this).parents('[data-id]'), id = parent.attr('data-id'); @@ -40,7 +40,7 @@ define('admin/extend/rewards', function() { parent.remove(); return false; }) - .on('.toggle', 'click', function() { + .on('click', '.toggle', function() { var btn = $(this), disabled = btn.html() === 'Enable', id = $(this).parents('[data-id]').attr('data-id'); @@ -142,8 +142,6 @@ define('admin/extend/rewards', function() { rewards: available }; - var ul = $('#active'); - templates.parse('admin/extend/rewards', 'active', data, function(li) { li = $(li); li.find('[name="rid"]').val(''); From 332110b8e28576965632591632e8ff2297f789b7 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 20 Feb 2015 12:16:04 -0500 Subject: [PATCH 078/142] bugfix --- public/src/admin/extend/rewards.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index 63108e8ad2..cff8f7f74e 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -129,11 +129,11 @@ define('admin/extend/rewards', function() { function newReward() { var ul = $('#active'), - id = parseInt($('#active li').last().attr('data-id'), 10); + id = $('#active li').last().attr('data-id'); var data = { active: [{ - id: id ? id + 1 : 0, + id: id ? parseInt(id, 10) + 1 : 0, disabled: true, value: '' }], From 7fa61f03e22f7b95fbb316a8aa2d8794ab06aab5 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 20 Feb 2015 12:16:13 -0500 Subject: [PATCH 079/142] saving conditions --- src/rewards/admin.js | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/rewards/admin.js b/src/rewards/admin.js index 185ad6b1e5..1f13ba7854 100644 --- a/src/rewards/admin.js +++ b/src/rewards/admin.js @@ -52,7 +52,9 @@ rewards.save = function(data, callback) { ], next); } - async.each(data, save, callback); + async.each(data, save, function(err) { + saveConditions(data, callback); + }); }; rewards.delete = function(data, callback) { @@ -76,15 +78,15 @@ rewards.get = function(callback) { plugins.fireHook('filter:rewards.conditions', [ { "name": "Reputation", - "condition": "reputation" + "condition": "core:user.reputation" }, { "name": "Post Count", - "condition": "postcount" + "condition": "core:user.postcount" }, { "name": "Last Logged in Time", - "condition": "lastLoggedIn" + "condition": "core:user.lastonline" } ], next); }, @@ -139,8 +141,27 @@ rewards.get = function(callback) { }, callback); }; -function getConditions() { +function saveConditions(data, callback) { + db.delete('conditions:active', function(err) { + if (err) { + return callback(err); + } + + var conditions = [], + rewardsPerCondition = {}; + data.forEach(function(reward) { + conditions.push(reward.condition); + rewardsPerCondition[reward.condition] = rewardsPerCondition[reward.condition] || []; + rewardsPerCondition[reward.condition].push(reward.rid); + }); + + db.setAdd('conditions:active', conditions, callback); + + async.each(Object.keys(rewardsPerCondition), function(condition, next) { + db.setAdd('condition:' + condition + ':rewards', rewardsPerCondition[condition]); + }, callback); + }); } function getActiveRewards(callback) { From a7c3d193cc42f6e566c709fb6a452b30edceee3b Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 20 Feb 2015 12:17:58 -0500 Subject: [PATCH 080/142] include nodebb-rewards namespace --- src/plugins.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins.js b/src/plugins.js index b33fe46ac7..eed2db94b8 100644 --- a/src/plugins.js +++ b/src/plugins.js @@ -263,7 +263,7 @@ var fs = require('fs'), function(dirs, next) { dirs = dirs.filter(function(dir){ - return dir.startsWith('nodebb-plugin-') || dir.startsWith('nodebb-widget-') || dir.startsWith('nodebb-theme-') + return dir.startsWith('nodebb-plugin-') || dir.startsWith('nodebb-widget-') || dir.startsWith('nodebb-rewards-') || dir.startsWith('nodebb-theme-') }).map(function(dir){ return path.join(npmPluginPath, dir); }); From 54c5034ce07c477a4569d3c745497698561ed5fc Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 20 Feb 2015 12:31:38 -0500 Subject: [PATCH 081/142] moved rewards/conditions/conditoinals out of core into rewards-essentials --- src/rewards/admin.js | 85 ++------------------------------------------ 1 file changed, 3 insertions(+), 82 deletions(-) diff --git a/src/rewards/admin.js b/src/rewards/admin.js index 1f13ba7854..dd1acf6180 100644 --- a/src/rewards/admin.js +++ b/src/rewards/admin.js @@ -5,30 +5,6 @@ var rewards = {}, plugins = require('../plugins'), db = require('../database'); -var defaults = { - conditionals: [ - { - "name": ">", - "conditional": "greaterthan" - }, - { - "name": ">=", - "conditional": "greaterorequalthan" - }, - { - "name": "<", - "conditional": "lesserthan" - }, - { - "name": "<=", - "conditional": "lesserorequalthan" - }, - { - "name": "string:", - "conditional": "string" - } - ] -}; rewards.save = function(data, callback) { function save(data, next) { @@ -75,68 +51,13 @@ rewards.get = function(callback) { async.parallel({ active: getActiveRewards, conditions: function(next) { - plugins.fireHook('filter:rewards.conditions', [ - { - "name": "Reputation", - "condition": "core:user.reputation" - }, - { - "name": "Post Count", - "condition": "core:user.postcount" - }, - { - "name": "Last Logged in Time", - "condition": "core:user.lastonline" - } - ], next); + plugins.fireHook('filter:rewards.conditions', [], next); }, conditionals: function(next) { - plugins.fireHook('filter:rewards.conditionals', defaults.conditionals, next); + plugins.fireHook('filter:rewards.conditionals', [], next); }, rewards: function(next) { - plugins.fireHook('filter:rewards.rewards', [ - { - "rid": "core:add-to-group", - "name": "Add to Group", - "inputs": [ - { - "type": "select", - "name": "groupname", - "label": "Group Name:", - "values": [ - { - "name": "Group 1", - "value": "group1" - }, - { - "name": "Group 2", - "value": "group2" - }, - { - "name": "Group 3", - "value": "group3" - } - ], - } - ] - }, - { - "rid": "core:alert-user", - "name": "Send alert message", - "inputs": [ - { - "type": "text", - "name": "title", - "label": "Title:" - }, - { - "type": "text", - "name": "message", - "label": "Message:" - } - ] - } - ], next); + plugins.fireHook('filter:rewards.rewards', [], next); } }, callback); }; From fc23dea1d2294a05d80a515f525dcd76578d852b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 20 Feb 2015 12:36:48 -0500 Subject: [PATCH 082/142] up versions --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index f8bfb660ee..bf77daccd2 100644 --- a/package.json +++ b/package.json @@ -44,9 +44,9 @@ "nodebb-plugin-mentions": "^0.9.0", "nodebb-plugin-soundpack-default": "~0.1.1", "nodebb-plugin-spam-be-gone": "^0.4.0", - "nodebb-theme-lavender": "^1.0.4", - "nodebb-theme-vanilla": "^1.0.5", - "nodebb-widget-essentials": "~0.2.11", + "nodebb-theme-lavender": "^1.0.6", + "nodebb-theme-vanilla": "^1.0.14", + "nodebb-widget-essentials": "~0.2.12", "npm": "^2.1.4", "passport": "^0.2.1", "passport-local": "1.0.0", From 949fcbbf11b631703c7753e04d1bf0ce1006fff1 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 20 Feb 2015 13:40:19 -0500 Subject: [PATCH 083/142] rewards: big feature sprint --- src/rewards/admin.js | 2 +- src/rewards/index.js | 95 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/src/rewards/admin.js b/src/rewards/admin.js index dd1acf6180..67271d2695 100644 --- a/src/rewards/admin.js +++ b/src/rewards/admin.js @@ -74,7 +74,7 @@ function saveConditions(data, callback) { data.forEach(function(reward) { conditions.push(reward.condition); rewardsPerCondition[reward.condition] = rewardsPerCondition[reward.condition] || []; - rewardsPerCondition[reward.condition].push(reward.rid); + rewardsPerCondition[reward.condition].push(reward.id); }); db.setAdd('conditions:active', conditions, callback); diff --git a/src/rewards/index.js b/src/rewards/index.js index ba68fae92c..821d093d20 100644 --- a/src/rewards/index.js +++ b/src/rewards/index.js @@ -1,7 +1,100 @@ "use strict"; -var rewards = {}; +var rewards = {}, + db = require('../database'), + plugins = require('../plugins'), + async = require('async'); +rewards.checkConditionAndRewardUser = function(uid, condition, method, callback) { + async.waterfall([ + function(next) { + isConditionActive(condition, function(err, isActive) { + if (!isActive) { + return back(err); + } + + next(err); + }); + }, + function(next) { + getIDsByCondition(condition, function(err, ids) { + next(err, ids); + }); + }, + function(next, ids) { + filterIncompleteIDs(uid, ids, function(err, filtered) { + if (!filtered || !filtered.length) { + return back(err); + } + + next(err, filtered); + }); + }, + function(next, ids) { + getRewardDataByIDs(ids, next); + }, + function(next, rewards) { + async.filter(rewards, function(reward, next) { + checkCondition(reward, method, next); + }, function(err, eligible) { + giveRewards(uid, eligible, next); + }); + } + ], back); + + + function back(err) { + if (typeof callback === 'function') { + callback(err); + } + } +}; + +function isConditionActive(condition, callback) { + db.isSetMember('conditions:active', condition, callback); +} + +function getIDsByCondition(condition, callback) { + db.getObject('condition:' + condition + ':rewards', callback); +} + +function filterIncompleteIDs(ids, callback) { + // todo + callback(false, ids); +} + +function getRewardDataByIDs(ids, callback) { + ids.map(function(id) { + return 'rewards:id:' + id; + }); + + db.getObjects(ids, callback); +} + +function getRewardsByRewardData(rewards, callback) { + rewards.map(function(reward) { + return 'rewards:id:' + reward.id + ':rewards'; + }); + + db.getObjects(rewards, callback); +} + +function checkCondition(reward, method, callback) { + method(function(err, value) { + plugins.fireHook('filter:rewards.checkConditional:' + reward.conditional, {left: value, right: reward.value}, callback); + }); +} + +function giveRewards(uid, rewards, callback) { + getRewardsByRewardData(rewards, function(err, rewardData) { + async.each(rewards, function(reward, next) { + var index = rewards.indexOf(reward); + + plugins.fireHook('action:rewards.award:' + reward.rid, {uid: uid, reward: rewardData[index]}); + }); + }); +} + module.exports = rewards; \ No newline at end of file From f9581e435d93cd6090c2bc564a18f9ac34874bf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 20 Feb 2015 14:20:49 -0500 Subject: [PATCH 084/142] getGroupsData method and filter:groups.get --- src/groups.js | 52 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/src/groups.js b/src/groups.js index 6cd0963525..1eae52699b 100644 --- a/src/groups.js +++ b/src/groups.js @@ -825,40 +825,58 @@ var async = require('async'), ], callback); }; + Groups.getGroupsData = function(groupNames, callback) { + if (!Array.isArray(groupNames) || !groupNames.length) { + return callback(null, []); + } + var keys = groupNames.map(function(groupName) { + return 'group:' + groupName; + }); + + db.getObjects(keys, function(err, groupData) { + if (err) { + return callback(err); + } + groupData = groupData.map(function(group) { + if (group) { + group.labelColor = group.labelColor || '#000000'; + group.createtimeISO = utils.toISOString(group.createtime); + group.hidden = parseInt(group.hidden, 10) === 1; + + if (!group['cover:url']) { + group['cover:url'] = nconf.get('relative_path') + '/images/cover-default.png'; + group['cover:position'] = '50% 50%'; + } + } + return group; + }); + + plugins.fireHook('filter:groups.get', {groups: groupData}, function(err, data) { + callback(err, data ? data.groups : null); + }); + }); + }; + Groups.getUserGroups = function(uids, callback) { db.getSortedSetRevRange('groups:createtime', 0, -1, function(err, groupNames) { if (err) { return callback(err); } - var groupKeys = groupNames.filter(function(groupName) { + groupNames = groupNames.filter(function(groupName) { return groupName !== 'registered-users' && groupName.indexOf(':privileges:') === -1; - }).map(function(groupName) { - return 'group:' + groupName; }); - db.getObjects(groupKeys, function(err, groupData) { + Groups.getGroupsData(groupNames, function(err, groupData) { if (err) { return callback(err); } groupData = groupData.filter(function(group) { - return parseInt(group.hidden, 10) !== 1 && !!group.userTitle; - }).map(function(group) { - group.createtimeISO = utils.toISOString(group.createtime); - return group; + return group && parseInt(group.hidden, 10) !== 1 && !!group.userTitle; }); - var groupSets = groupData.map(function(group) { - group.labelColor = group.labelColor || '#000000'; - group.createtimeISO = utils.toISOString(group.createtime); - - if (!group['cover:url']) { - group['cover:url'] = nconf.get('relative_path') + '/images/cover-default.png'; - group['cover:position'] = '50% 50%'; - } - return 'group:' + group.name + ':members'; }); From 7e9095b21b2167632efbb4672158524f9f542926 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 20 Feb 2015 14:21:33 -0500 Subject: [PATCH 085/142] added "owner" to action:post.upvote/downvote/unvote --- src/favourites.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/favourites.js b/src/favourites.js index fec00fd9cc..c8a571e758 100644 --- a/src/favourites.js +++ b/src/favourites.js @@ -174,6 +174,7 @@ var async = require('async'), plugins.fireHook('action:post.' + hook, { pid: pid, uid: uid, + owner: owner, current: current }); From aa38d6dda2309fc66dc863472837d7cf79d8779a Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 20 Feb 2015 14:21:40 -0500 Subject: [PATCH 086/142] rewards bug fixes --- src/rewards/index.js | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/rewards/index.js b/src/rewards/index.js index 821d093d20..6a2f224640 100644 --- a/src/rewards/index.js +++ b/src/rewards/index.js @@ -22,7 +22,7 @@ rewards.checkConditionAndRewardUser = function(uid, condition, method, callback) next(err, ids); }); }, - function(next, ids) { + function(ids, next) { filterIncompleteIDs(uid, ids, function(err, filtered) { if (!filtered || !filtered.length) { return back(err); @@ -31,13 +31,21 @@ rewards.checkConditionAndRewardUser = function(uid, condition, method, callback) next(err, filtered); }); }, - function(next, ids) { + function(ids, next) { getRewardDataByIDs(ids, next); }, - function(next, rewards) { + function(rewards, next) { async.filter(rewards, function(reward, next) { + if (!reward) { + return next(false); + } + checkCondition(reward, method, next); }, function(err, eligible) { + if (!eligible) { + return next(false); + } + giveRewards(uid, eligible, next); }); } @@ -56,24 +64,26 @@ function isConditionActive(condition, callback) { } function getIDsByCondition(condition, callback) { - db.getObject('condition:' + condition + ':rewards', callback); + db.getSetMembers('condition:' + condition + ':rewards', callback); } -function filterIncompleteIDs(ids, callback) { +function filterIncompleteIDs(uid, ids, callback) { // todo callback(false, ids); } function getRewardDataByIDs(ids, callback) { - ids.map(function(id) { + ids = ids.map(function(id) { return 'rewards:id:' + id; }); - db.getObjects(ids, callback); + db.getObjects(ids, function(err, objs) { + callback(err, objs); + }); } function getRewardsByRewardData(rewards, callback) { - rewards.map(function(reward) { + rewards = rewards.map(function(reward) { return 'rewards:id:' + reward.id + ':rewards'; }); From 2ba1363e12ff47acdbd53c62c5863a4345125378 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 20 Feb 2015 14:23:57 -0500 Subject: [PATCH 087/142] results.owner* --- src/favourites.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/favourites.js b/src/favourites.js index c8a571e758..0c9c81dc46 100644 --- a/src/favourites.js +++ b/src/favourites.js @@ -174,7 +174,7 @@ var async = require('async'), plugins.fireHook('action:post.' + hook, { pid: pid, uid: uid, - owner: owner, + owner: results.owner, current: current }); From b26bf9f22de530477a0d9e4e7b4a22850eb2f0a8 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 20 Feb 2015 14:45:07 -0500 Subject: [PATCH 088/142] rewards: bug fixes and cleanup, pretty much done part 1 ;) --- src/rewards/index.js | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/rewards/index.js b/src/rewards/index.js index 6a2f224640..30ef85b11a 100644 --- a/src/rewards/index.js +++ b/src/rewards/index.js @@ -41,7 +41,7 @@ rewards.checkConditionAndRewardUser = function(uid, condition, method, callback) } checkCondition(reward, method, next); - }, function(err, eligible) { + }, function(eligible) { if (!eligible) { return next(false); } @@ -73,35 +73,29 @@ function filterIncompleteIDs(uid, ids, callback) { } function getRewardDataByIDs(ids, callback) { - ids = ids.map(function(id) { + db.getObjects(ids.map(function(id) { return 'rewards:id:' + id; - }); - - db.getObjects(ids, function(err, objs) { - callback(err, objs); - }); + }), callback); } function getRewardsByRewardData(rewards, callback) { - rewards = rewards.map(function(reward) { + db.getObjects(rewards.map(function(reward) { return 'rewards:id:' + reward.id + ':rewards'; - }); - - db.getObjects(rewards, callback); + }), callback); } function checkCondition(reward, method, callback) { method(function(err, value) { - plugins.fireHook('filter:rewards.checkConditional:' + reward.conditional, {left: value, right: reward.value}, callback); + plugins.fireHook('filter:rewards.checkConditional:' + reward.conditional, {left: value, right: reward.value}, function(err, bool) { + callback(bool); + }); }); } function giveRewards(uid, rewards, callback) { getRewardsByRewardData(rewards, function(err, rewardData) { async.each(rewards, function(reward, next) { - var index = rewards.indexOf(reward); - - plugins.fireHook('action:rewards.award:' + reward.rid, {uid: uid, reward: rewardData[index]}); + plugins.fireHook('action:rewards.award:' + reward.rid, {uid: uid, reward: rewardData[rewards.indexOf(reward)]}); }); }); } From fbbd405b00b64f2a0a2cc0f43b369fbd8aa0c9d3 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 20 Feb 2015 15:07:43 -0500 Subject: [PATCH 089/142] even:alert socket call -> app.alert --- public/src/app.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/src/app.js b/public/src/app.js index 5010ead25d..b4a091f960 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -70,6 +70,10 @@ app.cacheBuster = null; window.location.href = config.relative_path + '/'; }, 1000); }); + + socket.on('event:alert', function(data) { + app.alert(data); + }); } function onSocketConnect(data) { From fe83adcbae862d1c805b5c89c85aca415ed33a5d Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 20 Feb 2015 15:21:09 -0500 Subject: [PATCH 090/142] prevent rewards from clobbering each other on save --- src/rewards/admin.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rewards/admin.js b/src/rewards/admin.js index 67271d2695..4d3681b094 100644 --- a/src/rewards/admin.js +++ b/src/rewards/admin.js @@ -12,10 +12,13 @@ rewards.save = function(data, callback) { return next(); } - var rewards = data.rewards; + var rewardsData = data.rewards; delete data.rewards; async.parallel([ + function(next) { + rewards.delete(data, next); + }, function(next) { db.setAdd('rewards:list', data.id, next); }, @@ -23,7 +26,7 @@ rewards.save = function(data, callback) { db.setObject('rewards:id:' + data.id, data, next); }, function(next) { - db.setObject('rewards:id:' + data.id + ':rewards', rewards, next); + db.setObject('rewards:id:' + data.id + ':rewards', rewardsData, next); } ], next); } From 5a6457ec7c23951681c4a1a1bde770efac508d8d Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 20 Feb 2015 16:09:20 -0500 Subject: [PATCH 091/142] adding rewards-essentials to core --- package.json | 1 + src/install.js | 1 + 2 files changed, 2 insertions(+) diff --git a/package.json b/package.json index 8561984d0d..d6d4dc3e14 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "nodebb-theme-lavender": "^1.0.4", "nodebb-theme-vanilla": "^1.0.5", "nodebb-widget-essentials": "~0.2.11", + "nodebb-rewards-essentials": "^0.0.1", "npm": "^2.1.4", "passport": "^0.2.1", "passport-local": "1.0.0", diff --git a/src/install.js b/src/install.js index 45d44d75ad..10c4d25e05 100644 --- a/src/install.js +++ b/src/install.js @@ -422,6 +422,7 @@ function enableDefaultPlugins(next) { 'nodebb-plugin-markdown', 'nodebb-plugin-mentions', 'nodebb-widget-essentials', + 'nodebb-rewards-essentials', 'nodebb-plugin-soundpack-default' ]; var db = require('./database'); From 61b1251a73a03fe60a13917d4b82e945cd4695fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 20 Feb 2015 16:34:31 -0500 Subject: [PATCH 092/142] removed sort methods --- src/search.js | 48 ++++++++++++++++-------------------------------- 1 file changed, 16 insertions(+), 32 deletions(-) diff --git a/src/search.js b/src/search.js index 5cab307a98..190a764bf2 100644 --- a/src/search.js +++ b/src/search.js @@ -365,47 +365,31 @@ function sortPosts(posts, data) { if (isNumeric) { if (data.sortDirection === 'desc') { - sortDescendingNumeric(posts, fields); + posts.sort(function(p1, p2) { + return p2[fields[0]][fields[1]] - p1[fields[0]][fields[1]]; + }); } else { - sortAscendingNumeric(posts, fields); + posts.sort(function(p1, p2) { + return p1[fields[0]][fields[1]] - p2[fields[0]][fields[1]]; + }); } } else { if (data.sortDirection === 'desc') { - sortDescendingAlpha(posts, fields); + posts.sort(function(p1, p2) { + if (p1[fields[0]][fields[1]] < p2[fields[0]][fields[1]]) return -1; + if (p1[fields[0]][fields[1]] > p2[fields[0]][fields[1]]) return 1; + return 0; + }); } else { - sortAscendingAlpha(posts, fields); + posts.sort(function(p1, p2) { + if (p1[fields[0]][fields[1]] > p2[fields[0]][fields[1]]) return -1; + if (p1[fields[0]][fields[1]] < p2[fields[0]][fields[1]]) return 1; + return 0; + }); } } } -function sortAscendingNumeric(posts, fields) { - posts.sort(function(p1, p2) { - return p1[fields[0]][fields[1]] - p2[fields[0]][fields[1]]; - }); -} - -function sortDescendingNumeric(posts, fields) { - posts.sort(function(p1, p2) { - return p2[fields[0]][fields[1]] - p1[fields[0]][fields[1]]; - }); -} - -function sortAscendingAlpha(posts, fields) { - posts.sort(function(p1, p2) { - if (p1[fields[0]][fields[1]] > p2[fields[0]][fields[1]]) return -1; - if (p1[fields[0]][fields[1]] < p2[fields[0]][fields[1]]) return 1; - return 0; - }); -} - -function sortDescendingAlpha(posts, fields) { - posts.sort(function(p1, p2) { - if (p1[fields[0]][fields[1]] < p2[fields[0]][fields[1]]) return -1; - if (p1[fields[0]][fields[1]] > p2[fields[0]][fields[1]]) return 1; - return 0; - }); -} - function getSearchCategories(data, callback) { if (!Array.isArray(data.categories) || !data.categories.length || data.categories.indexOf('all') !== -1) { return callback(null, []); From bb35950a5a6504605c017db956ea337c703574c7 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 20 Feb 2015 17:19:39 -0500 Subject: [PATCH 093/142] rewards acp - ability to set amount of times a user can claim reward --- public/less/admin/extend/rewards.less | 2 +- public/src/admin/extend/rewards.js | 5 +++-- src/views/admin/extend/rewards.tpl | 24 +++++++++++++++++------- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/public/less/admin/extend/rewards.less b/public/less/admin/extend/rewards.less index cf62159695..2dc84b5bc3 100644 --- a/public/less/admin/extend/rewards.less +++ b/public/less/admin/extend/rewards.less @@ -1,5 +1,5 @@ #rewards { - .well { + .well, .panel-body { vertical-align: top; min-height: 100px; diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index cff8f7f74e..9a5f6327b8 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -135,11 +135,12 @@ define('admin/extend/rewards', function() { active: [{ id: id ? parseInt(id, 10) + 1 : 0, disabled: true, - value: '' + value: '', + claimable: 1 }], conditions: conditions, conditionals: conditionals, - rewards: available + rewards: available, }; templates.parse('admin/extend/rewards', 'active', data, function(li) { diff --git a/src/views/admin/extend/rewards.tpl b/src/views/admin/extend/rewards.tpl index 87129fe3c7..29bf0b8288 100644 --- a/src/views/admin/extend/rewards.tpl +++ b/src/views/admin/extend/rewards.tpl @@ -37,14 +37,24 @@
          +
          -
          - - - - - - +
          +
          +
          +
          + + Enter 0 for infinite +
          +
          +
          + + + + + + +
        • From 7a8d84c011f3a77b93413e5b768facbc0678ec14 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 20 Feb 2015 17:33:29 -0500 Subject: [PATCH 094/142] ability to add a claimability limit to rewards --- src/rewards/index.js | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/rewards/index.js b/src/rewards/index.js index 30ef85b11a..fb4fc9d0e8 100644 --- a/src/rewards/index.js +++ b/src/rewards/index.js @@ -23,7 +23,10 @@ rewards.checkConditionAndRewardUser = function(uid, condition, method, callback) }); }, function(ids, next) { - filterIncompleteIDs(uid, ids, function(err, filtered) { + getRewardDataByIDs(ids, next); + }, + function(rewards, next) { + filterCompletedRewards(uid, rewards, function(err, filtered) { if (!filtered || !filtered.length) { return back(err); } @@ -31,9 +34,6 @@ rewards.checkConditionAndRewardUser = function(uid, condition, method, callback) next(err, filtered); }); }, - function(ids, next) { - getRewardDataByIDs(ids, next); - }, function(rewards, next) { async.filter(rewards, function(reward, next) { if (!reward) { @@ -67,9 +67,26 @@ function getIDsByCondition(condition, callback) { db.getSetMembers('condition:' + condition + ':rewards', callback); } -function filterIncompleteIDs(uid, ids, callback) { - // todo - callback(false, ids); +function filterCompletedRewards(uid, rewards, callback) { + db.getSortedSetRangeByScoreWithScores('uid:' + uid + ':rewards', 0, -1, 1, Infinity, function(err, data) { + var userRewards = {}; + + data.forEach(function(obj) { + userRewards[obj.value] = parseInt(obj.score, 10); + }); + + rewards = rewards.filter(function(reward) { + var claimable = parseInt(reward.claimable, 10); + + if (claimable === 0) { + return true; + } + + return (userRewards[reward.id] > reward.claimable) ? false : true; + }); + + callback(false, rewards); + }); } function getRewardDataByIDs(ids, callback) { @@ -96,7 +113,8 @@ function giveRewards(uid, rewards, callback) { getRewardsByRewardData(rewards, function(err, rewardData) { async.each(rewards, function(reward, next) { plugins.fireHook('action:rewards.award:' + reward.rid, {uid: uid, reward: rewardData[rewards.indexOf(reward)]}); - }); + db.sortedSetIncrBy('uid:' + uid + ':rewards', 1, reward.id, next); + }, callback); }); } From def57d29268c8ce0046d47d68a64db44fadc4ee9 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 20 Feb 2015 17:37:34 -0500 Subject: [PATCH 095/142] don't rely on client side to increment id (duh) --- public/src/admin/extend/rewards.js | 4 +--- src/rewards/admin.js | 32 +++++++++++++++++------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index 9a5f6327b8..50d7503f5a 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -128,12 +128,10 @@ define('admin/extend/rewards', function() { } function newReward() { - var ul = $('#active'), - id = $('#active li').last().attr('data-id'); + var ul = $('#active'); var data = { active: [{ - id: id ? parseInt(id, 10) + 1 : 0, disabled: true, value: '', claimable: 1 diff --git a/src/rewards/admin.js b/src/rewards/admin.js index 4d3681b094..796127b5f3 100644 --- a/src/rewards/admin.js +++ b/src/rewards/admin.js @@ -15,20 +15,24 @@ rewards.save = function(data, callback) { var rewardsData = data.rewards; delete data.rewards; - async.parallel([ - function(next) { - rewards.delete(data, next); - }, - function(next) { - db.setAdd('rewards:list', data.id, next); - }, - function(next) { - db.setObject('rewards:id:' + data.id, data, next); - }, - function(next) { - db.setObject('rewards:id:' + data.id + ':rewards', rewardsData, next); - } - ], next); + db.incrObjectField('global', 'nextRid', function(err, id) { + data.id = id; + + async.parallel([ + function(next) { + rewards.delete(data, next); + }, + function(next) { + db.setAdd('rewards:list', data.id, next); + }, + function(next) { + db.setObject('rewards:id:' + data.id, data, next); + }, + function(next) { + db.setObject('rewards:id:' + data.id + ':rewards', rewardsData, next); + } + ], next); + }); } async.each(data, save, function(err) { From f5eaa083402ec8482a79cd365a869ed8996cdc0c Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 20 Feb 2015 19:29:57 -0500 Subject: [PATCH 096/142] fixed complications with deleted id's --- public/src/admin/extend/rewards.js | 4 +++- src/rewards/admin.js | 26 ++++++++++++++++++-------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index 50d7503f5a..7fc023eda5 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -134,7 +134,9 @@ define('admin/extend/rewards', function() { active: [{ disabled: true, value: '', - claimable: 1 + claimable: 1, + rid: null, + id: null }], conditions: conditions, conditionals: conditionals, diff --git a/src/rewards/admin.js b/src/rewards/admin.js index 796127b5f3..6136e4f4b3 100644 --- a/src/rewards/admin.js +++ b/src/rewards/admin.js @@ -8,14 +8,11 @@ var rewards = {}, rewards.save = function(data, callback) { function save(data, next) { - if (!Object.keys(data.rewards).length) { - return next(); - } - - var rewardsData = data.rewards; - delete data.rewards; + function commit(err, id) { + if (err) { + return callback(err); + } - db.incrObjectField('global', 'nextRid', function(err, id) { data.id = id; async.parallel([ @@ -32,7 +29,20 @@ rewards.save = function(data, callback) { db.setObject('rewards:id:' + data.id + ':rewards', rewardsData, next); } ], next); - }); + } + + if (!Object.keys(data.rewards).length) { + return next(); + } + + var rewardsData = data.rewards; + delete data.rewards; + + if (!parseInt(data.id, 10)) { + db.incrObjectField('global', 'rewards:id', commit); + } else { + commit(false, data.id); + } } async.each(data, save, function(err) { From dfabbb5fa2c02655362897d529a91409f457e616 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Sat, 21 Feb 2015 23:13:53 -0500 Subject: [PATCH 097/142] latest translations --- public/language/de/category.json | 2 +- public/language/de/topic.json | 2 +- public/language/fr/category.json | 2 +- public/language/fr/error.json | 2 +- public/language/fr/global.json | 10 ++--- public/language/fr/groups.json | 16 +++---- public/language/fr/modules.json | 4 +- public/language/fr/search.json | 2 +- public/language/fr/tags.json | 2 +- public/language/fr/topic.json | 10 ++--- public/language/fr/user.json | 8 ++-- public/language/fr/users.json | 6 +-- public/language/it/search.json | 54 +++++++++++----------- public/language/ru/email.json | 10 ++--- public/language/ru/error.json | 24 +++++----- public/language/ru/global.json | 8 ++-- public/language/ru/groups.json | 4 +- public/language/ru/notifications.json | 2 +- public/language/ru/pages.json | 2 +- public/language/ru/register.json | 12 ++--- public/language/ru/reset_password.json | 16 +++---- public/language/ru/search.json | 62 +++++++++++++------------- public/language/ru/topic.json | 2 +- public/language/ru/unread.json | 2 +- public/language/ru/user.json | 28 ++++++------ public/language/ru/users.json | 8 ++-- 26 files changed, 150 insertions(+), 150 deletions(-) diff --git a/public/language/de/category.json b/public/language/de/category.json index 8635b5738c..f6a13932c2 100644 --- a/public/language/de/category.json +++ b/public/language/de/category.json @@ -1,6 +1,6 @@ { "new_topic_button": "Neues Thema", - "guest-login-post": "Log in to post", + "guest-login-post": "Anmelden um einen Beitrag zu erstellen", "no_topics": "Es gibt noch keine Themen in dieser Kategorie.
          Warum beginnst du nicht eins?", "browsing": "Aktiv", "no_replies": "Niemand hat geantwortet", diff --git a/public/language/de/topic.json b/public/language/de/topic.json index 1ef0d0741b..7036cb823a 100644 --- a/public/language/de/topic.json +++ b/public/language/de/topic.json @@ -12,7 +12,7 @@ "notify_me": "Erhalte eine Benachrichtigung bei neuen Antworten zu diesem Thema.", "quote": "zitieren", "reply": "antworten", - "guest-login-reply": "Log in to reply", + "guest-login-reply": "Anmelden zum Antworten", "edit": "bearbeiten", "delete": "löschen", "purge": "bereinigen", diff --git a/public/language/fr/category.json b/public/language/fr/category.json index 758bd68885..98a8ae2b90 100644 --- a/public/language/fr/category.json +++ b/public/language/fr/category.json @@ -1,6 +1,6 @@ { "new_topic_button": "Nouveau sujet", - "guest-login-post": "Log in to post", + "guest-login-post": "Se connecter pour poster", "no_topics": "Il n'y a aucun sujet dans cette catégorie.
          Pourquoi ne pas en créer un ?", "browsing": "parcouru par", "no_replies": "Personne n'a répondu", diff --git a/public/language/fr/error.json b/public/language/fr/error.json index bfa70c579c..3ceda0c847 100644 --- a/public/language/fr/error.json +++ b/public/language/fr/error.json @@ -17,7 +17,7 @@ "invalid-pagination-value": "Valeur de pagination invalide", "username-taken": "Nom d’utilisateur déjà utilisé", "email-taken": "Email déjà utilisé", - "email-not-confirmed": "Votre adresse email n'est pas confirmée, cliquer ici pour la valider.", + "email-not-confirmed": "Votre adresse email n'est pas confirmée, cliquez ici pour la valider.", "email-not-confirmed-chat": "Vous ne pouver discuter tant que votre email n'est pas confirmé", "username-too-short": "Nom d'utilisateur trop court", "username-too-long": "Nom d'utilisateur trop long", diff --git a/public/language/fr/global.json b/public/language/fr/global.json index bb53439c13..d09ab26cfc 100644 --- a/public/language/fr/global.json +++ b/public/language/fr/global.json @@ -3,7 +3,7 @@ "search": "Recherche", "buttons.close": "Fermer", "403.title": "Accès refusé", - "403.message": "Il semble que vous ayez atteint une page dont vous n'avez pas accès.", + "403.message": "Il semble que vous ayez atteint une page à laquelle vous n'avez pas accès.", "403.login": "Peut-être deviez vous essayer de vous connecter?", "404.title": "Introuvable", "404.message": "Il semble que vous ayez atteint une page qui n'existe pas. Retourner à la page d'accueil.", @@ -14,7 +14,7 @@ "please_log_in": "Veuillez vous connecter", "logout": "Déconnexion", "posting_restriction_info": "L'envoi de messages est réservé aux membres inscrits, cliquez ici pour vous connecter.", - "welcome_back": "Bon retour", + "welcome_back": "Bienvenue", "you_have_successfully_logged_in": "Vous vous êtes bien connecté", "save_changes": "Enregistrer les changements", "close": "Fermer", @@ -24,7 +24,7 @@ "header.admin": "Admin", "header.recent": "Récent", "header.unread": "Non lu", - "header.tags": "Tags", + "header.tags": "Mots-clés", "header.popular": "Populaire", "header.users": "Utilisateurs", "header.groups": "Groupes", @@ -32,7 +32,7 @@ "header.notifications": "Notifications", "header.search": "Recherche", "header.profile": "Profil", - "notifications.loading": "Chargement des Notifications", + "notifications.loading": "Chargement des notifications", "chats.loading": "Chargement des chats", "motd.welcome": "Bienvenue sur NodeBB, la plate-forme de discussion du futur.", "previouspage": "Page précédente", @@ -77,5 +77,5 @@ "privacy": "Vie privée", "follow": "S'abonner", "unfollow": "Se désabonner", - "delete_all": "Supprimer Tout" + "delete_all": "Tout supprimer" } \ No newline at end of file diff --git a/public/language/fr/groups.json b/public/language/fr/groups.json index abbca27470..39a9efcff2 100644 --- a/public/language/fr/groups.json +++ b/public/language/fr/groups.json @@ -1,10 +1,10 @@ { "groups": "Groupes", "view_group": "Voir le groupe", - "owner": "Propriétaire du Groupe", - "new_group": "Créer un Nouveau Groupe", + "owner": "Propriétaire du groupe", + "new_group": "Créer un nouveau groupe", "no_groups_found": "Il n'y a aucun groupe", - "cover-instructions": "Glisser et Coller une image, ajuster la position, et cliquer sur Enregistrer", + "cover-instructions": "Glissez-déposez une image, ajustez la position, et cliquez sur Enregistrer", "cover-change": "Modifier", "cover-save": "Enregistrer", "cover-saving": "Enregistrement", @@ -13,11 +13,11 @@ "details.pending": "Membres en attente", "details.has_no_posts": "Les membres de ce groupe n'ont envoyé aucun message.", "details.latest_posts": "Derniers messages", - "details.private": "Groupe Privé", - "details.public": "Groupe Public", - "details.grant": "Accorder/Résilier Propriétaire", - "details.kick": "Retirer", - "details.owner_options": "Administration du Groupe", + "details.private": "Groupe privé", + "details.public": "Groupe public", + "details.grant": "Promouvoir/rétrograder comme propriétaire", + "details.kick": "Exclure", + "details.owner_options": "Administration du groupe", "event.updated": "Les détails du groupe ont été mis à jour", "event.deleted": "Le groupe é%1\" a été supprimé" } \ No newline at end of file diff --git a/public/language/fr/modules.json b/public/language/fr/modules.json index 054c1c70f8..c472c6b402 100644 --- a/public/language/fr/modules.json +++ b/public/language/fr/modules.json @@ -1,11 +1,11 @@ { "chat.chatting_with": "Discuter avec ", - "chat.placeholder": "Taper votre message ici, appuyer sur Entrer pour envoyer", + "chat.placeholder": "Tapez votre message ici, appuyez sur Entrer pour envoyer", "chat.send": "Envoyer", "chat.no_active": "Vous n'avez aucune discussion en cours.", "chat.user_typing": "%1 est en train d'écrire ...", "chat.user_has_messaged_you": "%1 vous a envoyé un message.", - "chat.see_all": "Voir toutes les Discussions", + "chat.see_all": "Voir toutes les discussions", "chat.no-messages": "Veuillez sélectionner un destinataire pour voir l'historique des discussions", "chat.recent-chats": "Discussions récentes", "chat.contacts": "Contacts", diff --git a/public/language/fr/search.json b/public/language/fr/search.json index 20e22d2fa0..01574ac59b 100644 --- a/public/language/fr/search.json +++ b/public/language/fr/search.json @@ -29,7 +29,7 @@ "number-of-views": "Nombre de vues", "topic-start-date": "Date de création du sujet", "username": "Nom d'utilisateur", - "category": "Catgorie", + "category": "Catégorie", "descending": "Par ordre décroissant", "ascending": "Par ordre croissant" } \ No newline at end of file diff --git a/public/language/fr/tags.json b/public/language/fr/tags.json index 20bd5dccee..91a1f0aaa4 100644 --- a/public/language/fr/tags.json +++ b/public/language/fr/tags.json @@ -2,6 +2,6 @@ "no_tag_topics": "Il n'y a aucun sujet ayant ce mot-clé", "tags": "Mots-clés", "enter_tags_here": "Entrez les mots-clés ici. Appuyez sur entrer après chaque mot-clé.", - "enter_tags_here_short": "Entrez des tags...", + "enter_tags_here_short": "Entrez des mots-clés...", "no_tags": "Il n'y a pas encore de mots-clés." } \ No newline at end of file diff --git a/public/language/fr/topic.json b/public/language/fr/topic.json index b9368f16eb..8248eb2580 100644 --- a/public/language/fr/topic.json +++ b/public/language/fr/topic.json @@ -12,7 +12,7 @@ "notify_me": "Être notifié des réponses dans ce sujet", "quote": "Citer", "reply": "Répondre", - "guest-login-reply": "Log in to reply", + "guest-login-reply": "Se connecter pour répondre", "edit": "Éditer", "delete": "Supprimer", "purge": "Supprimer définitivement", @@ -73,20 +73,20 @@ "topic_will_be_moved_to": "Ce sujet sera déplacé vers la catégorie", "fork_topic_instruction": "Cliquez sur les postes à scinder", "fork_no_pids": "Aucun post sélectionné !", - "fork_success": "Sujet copié avec succès! Cliquez ici pour aller au sujet copié.", - "composer.title_placeholder": "Entrer le titre du sujet ici ...", + "fork_success": "Sujet copié avec succès ! Cliquez ici pour aller au sujet copié.", + "composer.title_placeholder": "Entrer le titre du sujet ici…", "composer.handle_placeholder": "Nom", "composer.discard": "Abandonner", "composer.submit": "Envoyer", "composer.replying_to": "Réponse à %1", "composer.new_topic": "Nouveau sujet", - "composer.uploading": "envoi en cours ...", + "composer.uploading": "envoi en cours…", "composer.thumb_url_label": "Coller une URL de vignette du sujet", "composer.thumb_title": "Ajouter une vignette à ce sujet", "composer.thumb_url_placeholder": "http://exemple.com/vignette.png", "composer.thumb_file_label": "Ou envoyer un fichier", "composer.thumb_remove": "Effacer les champs", - "composer.drag_and_drop_images": "Glisser-déposer les images ici", + "composer.drag_and_drop_images": "Glissez-déposez les images ici", "more_users_and_guests": "%1 autre(s) utilisateur(s) et %2 invité(s)", "more_users": "%1 autre(s) utilisateur(s)", "more_guests": "%1 autre(s) invité(s)", diff --git a/public/language/fr/user.json b/public/language/fr/user.json index d2cf37feec..8786929c78 100644 --- a/public/language/fr/user.json +++ b/public/language/fr/user.json @@ -2,8 +2,8 @@ "banned": "Banni", "offline": "Hors-ligne", "username": "Nom d'utilisateur", - "joindate": "Date d'Adhésion", - "postcount": "Nombre de Messages", + "joindate": "Date d'adhésion", + "postcount": "Nombre de messages", "email": "Email", "confirm_email": "Confirmer l'adresse email", "delete_account": "Supprimer le compte", @@ -29,7 +29,7 @@ "unfollow": "Se désabonner", "profile_update_success": "Le profil a bien été mis à jour !", "change_picture": "Changer d'image", - "edit": "Editer", + "edit": "Éditer", "uploaded_picture": "Image envoyée", "upload_new_picture": "Envoyer une nouvelle image", "upload_new_picture_from_url": "Envoyer une nouvelle image depuis un URL", @@ -44,7 +44,7 @@ "confirm_password": "Confirmer le mot de passe", "password": "Mot de passe", "username_taken_workaround": "Le nom d'utilisateur désiré est déjà utilisé, nous l'avons donc légèrement modifié. Vous êtes maintenant connu comme %1", - "upload_picture": "Envoyer une image", + "upload_picture": "Envoyer l'image", "upload_a_picture": "Envoyer une image", "image_spec": "Vous ne pouvez envoyer que des fichiers PNG, JPG ou GIF", "max": "max.", diff --git a/public/language/fr/users.json b/public/language/fr/users.json index 1028292a9b..3467b5b864 100644 --- a/public/language/fr/users.json +++ b/public/language/fr/users.json @@ -6,7 +6,7 @@ "enter_username": "Entrer un nom d'utilisateur pour rechercher", "load_more": "Charger la suite", "users-found-search-took": "%1 utilisateur(s) trouvé(s)! La recherche a pris %2 secondes.", - "filter-by": "Filtrer Par", - "online-only": "En Ligne uniquement", - "picture-only": "Avec Image uniquement" + "filter-by": "Filtrer par", + "online-only": "En ligne uniquement", + "picture-only": "Avec image uniquement" } \ No newline at end of file diff --git a/public/language/it/search.json b/public/language/it/search.json index 9d021ec7c5..479fec6287 100644 --- a/public/language/it/search.json +++ b/public/language/it/search.json @@ -1,35 +1,35 @@ { "results_matching": "%1 risultato(i) corrispondente(i) \"%2\", (%3 secondi)", - "no-matches": "No matches found", + "no-matches": "Nessuna corrispondenza trovata", "in": "In", - "by": "By", - "titles": "Titles", - "titles-posts": "Titles and Posts", - "posted-by": "Posted by", - "in-categories": "In Categories", + "by": "Da", + "titles": "Titoli", + "titles-posts": "Titoli e Messaggi", + "posted-by": "Pubblicato da", + "in-categories": "In Categorie", "search-child-categories": "Search child categories", - "reply-count": "Reply Count", + "reply-count": "Numero Risposte", "at-least": "At least", "at-most": "At most", - "post-time": "Post time", - "newer-than": "Newer than", - "older-than": "Older than", - "any-date": "Any date", - "yesterday": "Yesterday", - "one-week": "One week", - "two-weeks": "Two weeks", - "one-month": "One month", - "three-months": "Three months", - "six-months": "Six months", - "one-year": "One year", - "sort-by": "Sort by", + "post-time": "Ora invio", + "newer-than": "Più nuovi di", + "older-than": "Più vecchi di", + "any-date": "Qualsiasi data", + "yesterday": "Ieri", + "one-week": "Una settimana", + "two-weeks": "Due settimane", + "one-month": "Un mese", + "three-months": "Tre mesi", + "six-months": "Sei mesi", + "one-year": "Un anno", + "sort-by": "Ordina per", "last-reply-time": "Last reply time", - "topic-title": "Topic title", - "number-of-replies": "Number of replies", - "number-of-views": "Number of views", - "topic-start-date": "Topic start date", - "username": "Username", - "category": "Category", - "descending": "In descending order", - "ascending": "In ascending order" + "topic-title": "Titolo argomento", + "number-of-replies": "Numero di risposte", + "number-of-views": "Numero di visite", + "topic-start-date": "Data inizio argomento", + "username": "Nome utente", + "category": "Categoria", + "descending": "In ordine decrescente", + "ascending": "In ordine crescente" } \ No newline at end of file diff --git a/public/language/ru/email.json b/public/language/ru/email.json index eed54608d0..4353d9101e 100644 --- a/public/language/ru/email.json +++ b/public/language/ru/email.json @@ -9,9 +9,9 @@ "reset.text1": "Мы получили запрос на сброс Вашего пароля. Если Вы не подавали запрос, пожалуйста, проигнорируйте это сообщение.", "reset.text2": "Для продолжения процедуры изменения пароля, пожалуйста, перейдите по ссылке:", "reset.cta": "Кликните здесь для изменения пароля", - "reset.notify.subject": "Password successfully changed", - "reset.notify.text1": "We are notifying you that on %1, your password was changed successfully.", - "reset.notify.text2": "If you did not authorise this, please notify an administrator immediately.", + "reset.notify.subject": "Пароль был успешно изменен", + "reset.notify.text1": "Мы уведомляем вас о том, что %1 ваш пароль был успешно изменен. ", + "reset.notify.text2": "Если вы не совершали этого действия, пожалуйста, незамедлительно свяжитесь с администратором.", "digest.notifications": "У Вас непрочитанные уведомления от %1:", "digest.latest_topics": "Последние темы %1", "digest.cta": "Кликните здесь для просмотра %1", @@ -20,8 +20,8 @@ "notif.chat.subject": "Новое сообщение от %1", "notif.chat.cta": "Нажмите для продолжения диалога", "notif.chat.unsub.info": "Вы получили это уведомление в соответствии с настройками подписок.", - "notif.post.cta": "Click here to read the full topic", - "notif.post.unsub.info": "This post notification was sent to you due to your subscription settings.", + "notif.post.cta": "Кликните для просмотра всей темы.", + "notif.post.unsub.info": "Вы получили это уведомление согласно вашим настройкам подписки.", "test.text1": "Это тестовое сообщение для проверки почтового сервиса NodeBB.", "unsub.cta": "Изменить настройки", "closing": "Спасибо!" diff --git a/public/language/ru/error.json b/public/language/ru/error.json index 5683d863d1..25309f37d4 100644 --- a/public/language/ru/error.json +++ b/public/language/ru/error.json @@ -1,17 +1,17 @@ { - "invalid-data": "Неверные Данные", + "invalid-data": "Неверные данные", "not-logged-in": "Вы не вошли в свой аккаунт.", "account-locked": "Ваш аккаунт временно заблокирован", "search-requires-login": "Поиск доступен зарегистрированным пользователям! Пожалуйста, войдите или зарегистрируйтесь!", - "invalid-cid": "Неверный ID Категории", - "invalid-tid": "Неверный ID Темы", - "invalid-pid": "Неверный ID Поста", - "invalid-uid": "Неверный ID Пользователя", - "invalid-username": "Неверное Имя пользователя", + "invalid-cid": "Неверный ID категории", + "invalid-tid": "Неверный ID темы", + "invalid-pid": "Неверный ID поста", + "invalid-uid": "Неверный ID пользователя", + "invalid-username": "Неверное имя пользователя", "invalid-email": "Неверный Email", - "invalid-title": "Неверный заголовок!", - "invalid-user-data": "Неверные Пользовательские Данные", - "invalid-password": "Неверный Пароль", + "invalid-title": "Неверный заголовок", + "invalid-user-data": "Неверные пользовательские данные", + "invalid-password": "Неверный пароль", "invalid-username-or-password": "Пожалуйста, укажите и имя пользователя и пароль", "invalid-search-term": "Неверный поисковой запрос", "invalid-pagination-value": "Неверное значение пагинации", @@ -35,7 +35,7 @@ "topic-locked": "Тема закрыта", "still-uploading": "Пожалуйста, подождите завершения загрузки.", "content-too-short": "Пост должен содержать минимум %1 симв.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", + "content-too-long": "Размер поста не должен превышать %1 символов. Пожалуйста, сделайте его короче.", "title-too-short": "Заголовок должен содержать минимум %1 симв.", "title-too-long": "Заголовок не может быть длиннее %1 символов.", "too-many-posts": "Вы можете делать пост один раз в %1 сек.", @@ -45,7 +45,7 @@ "already-favourited": "Вы уже добавили этот пост в избранное", "already-unfavourited": "Вы уже удалили этот пост из избранного", "cant-ban-other-admins": "Вы не можете забанить других администраторов!", - "invalid-image-type": "Invalid image type. Allowed types are: %1", + "invalid-image-type": "Неверный формат изображения. Поддерживаемые форматы: %1", "invalid-image-extension": "Недопустимое расширение файла", "group-name-too-short": "Название группы слишком короткое", "group-already-exists": "Группа уже существует", @@ -70,5 +70,5 @@ "not-enough-reputation-to-flag": "У Вас недостаточно репутации, чтобы пометить этот пост.", "reload-failed": "NodeBB обнаружил проблему при перезагрузке: \"%1\". NodeBB продолжит работать с существующими ресурсами клиента, но Вы должны отменить то, что сделали перед перезагрузкой.", "registration-error": "Ошибка при регистрации", - "parse-error": "Something went wrong while parsing server response" + "parse-error": "Похоже, что-то пошло не так в процессе обработки ответа сервера." } \ No newline at end of file diff --git a/public/language/ru/global.json b/public/language/ru/global.json index 1983e9f1ab..700ed17127 100644 --- a/public/language/ru/global.json +++ b/public/language/ru/global.json @@ -3,10 +3,10 @@ "search": "Поиск", "buttons.close": "Закрыть", "403.title": "Доступ запрещен", - "403.message": "You seem to have stumbled upon a page that you do not have access to.", + "403.message": "Вы пытаетесь перейти на страницу, к которой у вас нет прав доступа.", "403.login": "Возможно Вам следует войти под своим аккаунтом?", "404.title": "Страница не найдена", - "404.message": "You seem to have stumbled upon a page that does not exist. Return to the home page.", + "404.message": "Вы пытаетесь перейти на страницу, которой не существует. Вам стоит вернутся на главную страницу.", "500.title": "Внутренняя ошибка.", "500.message": "Упс! Похоже, что-то пошло не так!", "register": "Зарегистрироваться", @@ -75,7 +75,7 @@ "updated.title": "Форум обновлен", "updated.message": "Форум был обновлен до последней версии. Нажмите здесь, чтобы обновить страницу.", "privacy": "Безопасность", - "follow": "Follow", - "unfollow": "Unfollow", + "follow": "Подписаться", + "unfollow": "Отписаться", "delete_all": "Удалить все" } \ No newline at end of file diff --git a/public/language/ru/groups.json b/public/language/ru/groups.json index 20a4c0b01c..544998a50e 100644 --- a/public/language/ru/groups.json +++ b/public/language/ru/groups.json @@ -3,9 +3,9 @@ "view_group": "Просмотр группы", "owner": "Администратор группы", "new_group": "Создать группу", - "no_groups_found": "There are no groups to see", + "no_groups_found": "Нет групп для отображения", "cover-instructions": "Перетяните сюда изображение, переместите на нужную позицию и нажмите Сохранить", - "cover-change": "Change", + "cover-change": "Изменить", "cover-save": "Сохранить", "cover-saving": "Сохраняем", "details.title": "Информация о группе", diff --git a/public/language/ru/notifications.json b/public/language/ru/notifications.json index 73a6dfb38b..4cc64159b5 100644 --- a/public/language/ru/notifications.json +++ b/public/language/ru/notifications.json @@ -19,7 +19,7 @@ "user_posted_topic": "%1 открыл новую тему: %2", "user_mentioned_you_in": "%1 упомянул Вас в %2", "user_started_following_you": "%1 подписался на Вас.", - "email-confirmed": "Email Подтвержден", + "email-confirmed": "Email подтвержден", "email-confirmed-message": "Спасибо за подтверждение Вашего Email-адреса. Ваш аккаунт активирован.", "email-confirm-error": "Произошла ошибка...", "email-confirm-error-message": "Ошибка проверки Email-адреса. Возможно, код неверен, либо у него истек срок действия.", diff --git a/public/language/ru/pages.json b/public/language/ru/pages.json index f8a014193b..9ad5cae0c2 100644 --- a/public/language/ru/pages.json +++ b/public/language/ru/pages.json @@ -11,7 +11,7 @@ "user.followers": "Читают %1", "user.posts": "Пост написан %1", "user.topics": "Темы созданы %1", - "user.groups": "%1's Groups", + "user.groups": "Группы %1", "user.favourites": "Избранные сообщения %1", "user.settings": "Настройки", "maintenance.text": "%1 в настоящее время на обслуживании. Пожалуйста, возвращайтесь позже.", diff --git a/public/language/ru/register.json b/public/language/ru/register.json index 419a808877..eae56a5403 100644 --- a/public/language/ru/register.json +++ b/public/language/ru/register.json @@ -3,16 +3,16 @@ "help.email": "По умолчанию, ваш email будет скрыт.", "help.username_restrictions": "Уникальное Имя между %1 и %2 символов. Другие пользователи смогут упоминать вас по @Имени.", "help.minimum_password_length": "Длина вашего пароля должна быть минимум %1 символов.", - "email_address": "Email Адрес", + "email_address": "Email адрес", "email_address_placeholder": "Введите Email адрес", "username": "Имя пользователя", - "username_placeholder": "Введите Имя пользователя", + "username_placeholder": "Введите имя пользователя", "password": "Пароль", - "password_placeholder": "Введите Пароль", - "confirm_password": "Подтвердите Пароль", - "confirm_password_placeholder": "Подтвердите Пароль", + "password_placeholder": "Введите пароль", + "confirm_password": "Подтвердите пароль", + "confirm_password_placeholder": "Подтвердите пароль", "register_now_button": "Зарегистрироваться", - "alternative_registration": "Альтернативная Регистрация", + "alternative_registration": "Альтернативная регистрация", "terms_of_use": "Условия использования", "agree_to_terms_of_use": "Я согласен с условиями" } \ No newline at end of file diff --git a/public/language/ru/reset_password.json b/public/language/ru/reset_password.json index c9b523cf2e..a2226d5e60 100644 --- a/public/language/ru/reset_password.json +++ b/public/language/ru/reset_password.json @@ -1,16 +1,16 @@ { - "reset_password": "Восстановить Пароль", - "update_password": "Изменить Пароль", - "password_changed.title": "Пароль Изменен", + "reset_password": "Восстановить пароль", + "update_password": "Изменить пароль", + "password_changed.title": "Пароль изменен", "password_changed.message": "

          Пароль успешно восстановлен, пожалуйста войдите еще раз.", "wrong_reset_code.title": "Неверный код восстановления", "wrong_reset_code.message": "Неправильный код восстановления пароля. Попробуйте еще раз, или запросите новый код восстановления.", - "new_password": "Новый Пароль", - "repeat_password": "Подтвердите Пароль", + "new_password": "Новый пароль", + "repeat_password": "Подтвердите пароль", "enter_email": "Пожалуйста введите ваш email адрес и мы отправим Вам письмо с инструкцией восстановления пароля.", "enter_email_address": "Введите Email адрес", - "password_reset_sent": "Пароль Отправлен", + "password_reset_sent": "Пароль отправлен", "invalid_email": "Неверный Email / Email не существует!", - "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "password_too_short": "Введенный пароль слишком короткий, пожалуйста, введите более длинный пароль.", + "passwords_do_not_match": "Введенные пароли не совпадают." } \ No newline at end of file diff --git a/public/language/ru/search.json b/public/language/ru/search.json index f6ecb8d611..943b857c9b 100644 --- a/public/language/ru/search.json +++ b/public/language/ru/search.json @@ -1,35 +1,35 @@ { "results_matching": "%1 результатов по фразе \"%2\", (%3 секунды) ", "no-matches": "Совпадений не найдено", - "in": "In", - "by": "By", - "titles": "Titles", - "titles-posts": "Titles and Posts", - "posted-by": "Posted by", - "in-categories": "In Categories", - "search-child-categories": "Search child categories", - "reply-count": "Reply Count", - "at-least": "At least", - "at-most": "At most", - "post-time": "Post time", - "newer-than": "Newer than", - "older-than": "Older than", - "any-date": "Any date", - "yesterday": "Yesterday", - "one-week": "One week", - "two-weeks": "Two weeks", - "one-month": "One month", - "three-months": "Three months", - "six-months": "Six months", - "one-year": "One year", - "sort-by": "Sort by", - "last-reply-time": "Last reply time", - "topic-title": "Topic title", - "number-of-replies": "Number of replies", - "number-of-views": "Number of views", - "topic-start-date": "Topic start date", - "username": "Username", - "category": "Category", - "descending": "In descending order", - "ascending": "In ascending order" + "in": "В", + "by": "От", + "titles": "Названия", + "titles-posts": "Названия и сообщения", + "posted-by": "Написано ", + "in-categories": "В разделах", + "search-child-categories": "Искать во вложенных разделах", + "reply-count": "Количество ответов", + "at-least": "Минимум", + "at-most": "Максимум", + "post-time": "Время публикации", + "newer-than": "Ранее чем", + "older-than": "Позже чем", + "any-date": "Любая дата", + "yesterday": "Вчера", + "one-week": "Одна неделя", + "two-weeks": "Две недели", + "one-month": "Один месяц", + "three-months": "Три месяца", + "six-months": "Шесть месяцев", + "one-year": "Год", + "sort-by": "Сортировать по", + "last-reply-time": "Время последнего ответа", + "topic-title": "Название темы", + "number-of-replies": "Количество ответов", + "number-of-views": "Количество просмотров", + "topic-start-date": "Время создания темы", + "username": "Имя пользователя", + "category": "Категория", + "descending": "В порядке возрастания", + "ascending": "В порядке убывания" } \ No newline at end of file diff --git a/public/language/ru/topic.json b/public/language/ru/topic.json index ec980e67a7..635e31a765 100644 --- a/public/language/ru/topic.json +++ b/public/language/ru/topic.json @@ -12,7 +12,7 @@ "notify_me": "Сообщать мне об ответах в этой теме", "quote": "Цитировать", "reply": "Ответить", - "guest-login-reply": "Log in to reply", + "guest-login-reply": "Войдите чтобы оставить сообщение", "edit": "Редактировать", "delete": "Удалить", "purge": "Очистить", diff --git a/public/language/ru/unread.json b/public/language/ru/unread.json index 325a793d59..0bdf6c4933 100644 --- a/public/language/ru/unread.json +++ b/public/language/ru/unread.json @@ -2,7 +2,7 @@ "title": "Непрочитанные темы", "no_unread_topics": "Нет непрочитанных тем.", "load_more": "Загрузить еще", - "mark_as_read": "Пометить Прочитанным", + "mark_as_read": "Пометить прочитанным", "selected": "Выбраны", "all": "Все", "topics_marked_as_read.success": "Темы помечены как прочитанные!" diff --git a/public/language/ru/user.json b/public/language/ru/user.json index ba337e227b..0b5a541199 100644 --- a/public/language/ru/user.json +++ b/public/language/ru/user.json @@ -6,7 +6,7 @@ "postcount": "Сообщений", "email": "Email", "confirm_email": "Подтвердить Email", - "delete_account": "Удалить Аккаунт", + "delete_account": "Удалить аккаунт", "delete_account_confirm": "Вы уверены, что хотите удалить аккаунт?
          Это действие необратимо, Вы не сможете восстановить свои данные

          Введите имя пользователя для подтверждения уничтожения аккаунта.", "fullname": "Полное имя", "website": "Сайт", @@ -18,7 +18,7 @@ "profile_views": "Просмотров профиля", "reputation": "Репутация", "favourites": "Избранное", - "watched": "Watched", + "watched": "Просмотров", "followers": "Читателей", "following": "Читаемых", "signature": "Подпись", @@ -30,18 +30,18 @@ "profile_update_success": "Профиль обновлен!", "change_picture": "Изменить фотографию", "edit": "Редактировать", - "uploaded_picture": "Загруженные Фотографии", + "uploaded_picture": "Загруженные фотографии", "upload_new_picture": "Загрузить новую фотографию", "upload_new_picture_from_url": "Загрузить новое изображение с адреса URL", - "current_password": "Текущий Пароль", - "change_password": "Изменить Пароль", - "change_password_error": "Неверный Пароль!", + "current_password": "Текущий пароль", + "change_password": "Изменить пароль", + "change_password_error": "Неверный пароль!", "change_password_error_wrong_current": "Текущий пароль неверен!", "change_password_error_length": "Пароль слишком короткий!", "change_password_error_match": "Пароли должны совпадать!", "change_password_error_privileges": "Вы не можете сменить этот пароль.", "change_password_success": "Ваш пароль изменен!", - "confirm_password": "Подтвердите Пароль", + "confirm_password": "Подтвердите пароль", "password": "Пароль", "username_taken_workaround": "Логин, который Вы запросили, уже занят. Мы его немного изменили. Теперь Ваш логин %1", "upload_picture": "Загрузить фотографию", @@ -52,20 +52,20 @@ "show_email": "Показывать мой Email", "show_fullname": "Показывать Полное Имя", "restrict_chats": "Разрешить чат только с теми, на кого я подписан", - "digest_label": "Подписаться на Дайджест", + "digest_label": "Подписаться на дайджест", "digest_description": "Подписаться на Email обновления этого форума (новые уведомления и топики) согласно расписанию", "digest_off": "Выключить", - "digest_daily": "За День", - "digest_weekly": "За Неделю", - "digest_monthly": "За Месяц", + "digest_daily": "За день", + "digest_weekly": "За неделю", + "digest_monthly": "За месяц", "send_chat_notifications": "Уведомлять на E-mail при поступлении нового сообщения чата, когда я оффлайн", "send_post_notifications": "Отправлять email, когда отвечают в темы, на которые я подписан(а)", "has_no_follower": "Этого пользователя никто не читает :(", "follows_no_one": "Этот пользователь никого не читает :(", "has_no_posts": "Это пользователь еще ничего не написал.", "has_no_topics": "Этот пользователь еще не создал ни одной темы", - "has_no_watched_topics": "This user didn't watch any topics yet.", - "email_hidden": "Email Скрыт", + "has_no_watched_topics": "Этот пользователь еще не просмотрел ни одной темы", + "email_hidden": "Email скрыт", "hidden": "скрыто", "paginate_description": "Использовать пагинацию тем и постов вместо бесконечной прокрутки", "topics_per_page": "Тем на Странице", @@ -73,7 +73,7 @@ "notification_sounds": "Звук при получении уведомления", "browsing": "Настройки просмотра", "open_links_in_new_tab": "Открывать ссылки, ведущие на другие сайты, в новой вкладке?", - "enable_topic_searching": "Включить Поиск по всей теме", + "enable_topic_searching": "Активировать поиск внутри тем", "topic_search_help": "Если включено, то стандартный \"Поиск на странице\" Вашего браузера будет осуществлять поиск по всей теме вместо одной её страницы.", "follow_topics_you_reply_to": "Следить за темами, в которых Вы отвечали.", "follow_topics_you_create": "Следить за темами, которые Вы создали." diff --git a/public/language/ru/users.json b/public/language/ru/users.json index e4253bcf62..ffc028bcf3 100644 --- a/public/language/ru/users.json +++ b/public/language/ru/users.json @@ -5,8 +5,8 @@ "search": "Поиск", "enter_username": "Введите имя пользователя для поиска", "load_more": "Загрузить еще", - "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", - "filter-by": "Filter By", - "online-only": "Online only", - "picture-only": "Picture only" + "users-found-search-took": "Найдено %1 пользователя(ей). Поиск занял %2 секунд. ", + "filter-by": "Сортировать по", + "online-only": "Только онлайн", + "picture-only": "Только с аватаром" } \ No newline at end of file From 56d18666d60d806e680113c834153ffd18e6dfd5 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Sun, 22 Feb 2015 14:47:07 -0500 Subject: [PATCH 098/142] closes #2752 --- src/posts/delete.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/posts/delete.js b/src/posts/delete.js index f3fab5bd87..bd130ade46 100644 --- a/src/posts/delete.js +++ b/src/posts/delete.js @@ -29,7 +29,7 @@ module.exports = function(Posts) { removeFromCategoryRecentPosts(pid, postData.tid, next); }, function(next) { - Posts.dismissFlags(pid, next); + Posts.dismissFlag(pid, next); } ], function(err) { callback(err, postData); @@ -127,7 +127,7 @@ module.exports = function(Posts) { db.sortedSetsRemove(['posts:pid', 'posts:flagged'], pid, next); }, function(next) { - Posts.dismissFlags(pid, next); + Posts.dismissFlag(pid, next); } ], function(err) { if (err) { From 218ecce6c1e3d05ee067f115a64eb02207ffece7 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 23 Feb 2015 09:12:06 -0500 Subject: [PATCH 099/142] accessing search route with no term auto-expands the form now, part 1 of 2, #2757 --- src/controllers/search.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/controllers/search.js b/src/controllers/search.js index 2020612094..7ed16d8bb8 100644 --- a/src/controllers/search.js +++ b/src/controllers/search.js @@ -31,7 +31,8 @@ searchController.search = function(req, res, next) { users: [], tags: [], categories: categories, - breadcrumbs: breadcrumbs + breadcrumbs: breadcrumbs, + expandSearch: true }; plugins.fireHook('filter:search.build', {data: {}, results: results}, function(err, data) { if (err) { @@ -75,6 +76,7 @@ searchController.search = function(req, res, next) { results.showAsTopics = req.query.showAs === 'topics'; results.breadcrumbs = breadcrumbs; results.categories = categories; + results.expandSearch = false; plugins.fireHook('filter:search.build', {data: data, results: results}, function(err, data) { if (err) { From 53db052ccafe930839c09529fb166a48ee2c69fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 23 Feb 2015 12:55:44 -0500 Subject: [PATCH 100/142] timeago widgets --- public/src/widgets.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/public/src/widgets.js b/public/src/widgets.js index 936ce9cd95..fea7814c9d 100644 --- a/public/src/widgets.js +++ b/public/src/widgets.js @@ -67,9 +67,11 @@ area.addClass('hidden'); ajaxify.widgets.reposition(location); } - - $('#content [widget-area] img:not(.user-img)').addClass('img-responsive'); } + + var widgetAreas = $('#content [widget-area]'); + widgetAreas.find('img:not(.user-img)').addClass('img-responsive'); + widgetAreas.find('span.timeago').timeago(); $(window).trigger('action:widgets.loaded', {}); From 8b81f3d83548afaef4bb9b9d8430e50fa903ed47 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 23 Feb 2015 13:05:27 -0500 Subject: [PATCH 101/142] server-side support for hidden option in group admin, #2758 --- public/language/en_GB/groups.json | 15 +++++++++++++-- src/groups.js | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/public/language/en_GB/groups.json b/public/language/en_GB/groups.json index 964b5f42c6..a72cf20110 100644 --- a/public/language/en_GB/groups.json +++ b/public/language/en_GB/groups.json @@ -5,6 +5,9 @@ "new_group": "Create New Group", "no_groups_found": "There are no groups to see", + "pending.accept": "Accept", + "pending.reject": "Reject", + "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", "cover-change": "Change", "cover-save": "Save", @@ -15,12 +18,20 @@ "details.pending": "Pending Members", "details.has_no_posts": "This group's members have not made any posts.", "details.latest_posts": "Latest Posts", - "details.private": "Private Group", - "details.public": "Public Group", + "details.private": "Private", "details.grant": "Grant/Rescind Ownership", "details.kick": "Kick", "details.owner_options": "Group Administration", + "details.group_name": "Group Name", + "details.description": "Description", + "details.badge_preview": "Badge Preview", + "details.change_icon": "Change Icon", + "details.change_colour": "Change Colour", + "details.badge_text": "Badge Text", + "details.private_help": "If enabled, joining of groups requires approval from a group owner", + "details.hidden": "Hidden", + "details.hidden_help": "If enabled, this group will not be found in the groups listing, and users will have to be invited manually", "event.updated": "Group details have been updated", "event.deleted": "The group \"%1\" has been deleted" diff --git a/src/groups.js b/src/groups.js index 1eae52699b..985cfc2e9d 100644 --- a/src/groups.js +++ b/src/groups.js @@ -510,7 +510,7 @@ var async = require('async'), description: values.description || '', icon: values.icon || '', labelColor: values.labelColor || '#000000', - hidden: values.hidden || '0', + hidden: values.hidden === true ? '1' : '0', 'private': values.private === false ? '0' : '1' }; From 1d5e15eab40192d508680ba380eb59c37bad9ad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 23 Feb 2015 13:39:17 -0500 Subject: [PATCH 102/142] closes #2560 --- public/language/en_GB/error.json | 1 + public/src/app.js | 16 +++++++++++++++- src/middleware/middleware.js | 4 ++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/public/language/en_GB/error.json b/public/language/en_GB/error.json index 812ae03a17..b2f9a3b86b 100644 --- a/public/language/en_GB/error.json +++ b/public/language/en_GB/error.json @@ -24,6 +24,7 @@ "email-taken": "Email taken", "email-not-confirmed": "Your email has not been confirmed yet, please click here to confirm your email.", "email-not-confirmed-chat": "You are unable to chat until your email is confirmed", + "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", "username-too-short": "Username too short", "username-too-long": "Username too long", diff --git a/public/src/app.js b/public/src/app.js index b4a091f960..d3a47a1bc6 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -570,7 +570,21 @@ app.cacheBuster = null; }; function showEmailConfirmWarning() { - if (config.requireEmailConfirmation && app.user.uid && !app.user['email:confirmed']) { + if (!config.requireEmailConfirmation || !app.user.uid) { + return; + } + if (!app.user.email) { + app.alert({ + alert_id: 'email_confirm', + message: '[[error:no-email-to-confirm]]', + type: 'warning', + timeout: 0, + clickfn: function() { + app.removeAlert('email_confirm'); + ajaxify.go('user/' + app.user.userslug + '/edit'); + } + }); + } else if (!app.user['email:confirmed']) { app.alert({ alert_id: 'email_confirm', message: '[[error:email-not-confirmed]]', diff --git a/src/middleware/middleware.js b/src/middleware/middleware.js index 43e2a2ea86..658b9e0c63 100644 --- a/src/middleware/middleware.js +++ b/src/middleware/middleware.js @@ -317,7 +317,7 @@ middleware.renderHeader = function(req, res, callback) { }, user: function(next) { if (uid) { - user.getUserFields(uid, ['username', 'userslug', 'picture', 'status', 'email:confirmed', 'banned'], next); + user.getUserFields(uid, ['username', 'userslug', 'email', 'picture', 'status', 'email:confirmed', 'banned'], next); } else { next(null, { username: '[[global:guest]]', @@ -342,7 +342,7 @@ middleware.renderHeader = function(req, res, callback) { results.user.isAdmin = results.isAdmin || false; results.user.uid = parseInt(results.user.uid, 10); results.user['email:confirmed'] = parseInt(results.user['email:confirmed'], 10) === 1; - + templateValues.browserTitle = results.title; templateValues.isAdmin = results.user.isAdmin; templateValues.user = results.user; From e6e755dd4617a3b15bab1fc3eb1c526fc10b4295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 23 Feb 2015 14:05:43 -0500 Subject: [PATCH 103/142] #2560 --- src/middleware/admin.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/middleware/admin.js b/src/middleware/admin.js index a7ed1997fb..a23781fdef 100644 --- a/src/middleware/admin.js +++ b/src/middleware/admin.js @@ -40,13 +40,14 @@ middleware.buildHeader = function(req, res, next) { 'authentication': [] }; - user.getUserFields(uid, ['username', 'userslug', 'picture', 'email:confirmed'], function(err, userData) { + user.getUserFields(uid, ['username', 'userslug', 'email', 'picture', 'email:confirmed'], function(err, userData) { if (err) { return next(err); } userData.uid = uid; - + userData['email:confirmed'] = parseInt(userData['email:confirmed'], 10) === 1; + async.parallel({ scripts: function(next) { plugins.fireHook('filter:admin.scripts.get', [], function(err, scripts) { @@ -72,16 +73,15 @@ middleware.buildHeader = function(req, res, next) { return next(err); } res.locals.config = results.config; + var data = { relative_path: nconf.get('relative_path'), configJSON: JSON.stringify(results.config), + user: userData, userJSON: JSON.stringify(userData), plugins: results.custom_header.plugins, authentication: results.custom_header.authentication, scripts: results.scripts, - userpicture: userData.picture, - username: userData.username, - userslug: userData.userslug, 'cache-buster': meta.config['cache-buster'] ? 'v=' + meta.config['cache-buster'] : '', env: process.env.NODE_ENV ? true : false }; From 89f2520eba2434e5ff6304f07f1ac5475a61268b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 23 Feb 2015 14:07:51 -0500 Subject: [PATCH 104/142] #2560 fix user link pic --- src/views/admin/header.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/admin/header.tpl b/src/views/admin/header.tpl index 7a5d658eb2..89b0dc4ac4 100644 --- a/src/views/admin/header.tpl +++ b/src/views/admin/header.tpl @@ -85,11 +85,11 @@

      + + + + + + + From dee5d18439cf7a7173175e2cb68a78f15f555ff0 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 23 Feb 2015 16:08:22 -0500 Subject: [PATCH 112/142] require npm module only as needed - shaves off a full second of loading time yay --- src/plugins.js | 5 ++++- src/plugins/install.js | 9 ++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/plugins.js b/src/plugins.js index fbc74e85ac..2e5202e240 100644 --- a/src/plugins.js +++ b/src/plugins.js @@ -1,5 +1,6 @@ 'use strict'; +var start = process.hrtime(); var fs = require('fs'), path = require('path'), async = require('async'), @@ -18,10 +19,12 @@ var fs = require('fs'), controllers = require('./controllers'), app, middleware; +process.profile('requiring modules', start); (function(Plugins) { - +var start = process.hrtime(); require('./plugins/install')(Plugins); +process.profile('requiring modules 2', start); require('./plugins/load')(Plugins); require('./plugins/hooks')(Plugins); diff --git a/src/plugins/install.js b/src/plugins/install.js index de63cd9f3b..6d6865301a 100644 --- a/src/plugins/install.js +++ b/src/plugins/install.js @@ -2,7 +2,6 @@ var winston = require('winston'), async = require('async'), - npm = require('npm'), path = require('path'), fs = require('fs'), nconf = require('nconf'), @@ -88,10 +87,10 @@ module.exports = function(Plugins) { next(); }, function(next) { - npm.load({}, next); + require('npm').load({}, next); }, function(res, next) { - npm.commands[type](installed ? id : [id + '@' + (version || 'latest')], next); + require('npm').commands[type](installed ? id : [id + '@' + (version || 'latest')], next); } ], function(err) { if (err) { @@ -111,10 +110,10 @@ module.exports = function(Plugins) { function upgrade(id, version, callback) { async.waterfall([ function(next) { - npm.load({}, next); + require('npm').load({}, next); }, function(res, next) { - npm.commands.install([id + '@' + (version || 'latest')], next); + require('npm').commands.install([id + '@' + (version || 'latest')], next); } ], callback); } From 61b8fd24028212ccb4ab5b7397b4c1004a663b2d Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 23 Feb 2015 16:13:04 -0500 Subject: [PATCH 113/142] woops, left in some debug statements --- src/plugins.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/plugins.js b/src/plugins.js index 2e5202e240..b9e7118df9 100644 --- a/src/plugins.js +++ b/src/plugins.js @@ -1,6 +1,5 @@ 'use strict'; -var start = process.hrtime(); var fs = require('fs'), path = require('path'), async = require('async'), @@ -19,12 +18,9 @@ var fs = require('fs'), controllers = require('./controllers'), app, middleware; -process.profile('requiring modules', start); (function(Plugins) { -var start = process.hrtime(); require('./plugins/install')(Plugins); -process.profile('requiring modules 2', start); require('./plugins/load')(Plugins); require('./plugins/hooks')(Plugins); From 13fa1f6b9a03dbaf92ec42c8d6ec370ecb43f3a0 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 23 Feb 2015 16:24:43 -0500 Subject: [PATCH 114/142] require socket.io only when you need it, shaves off another 0.5s on load --- src/controllers/accounts.js | 5 ++--- src/messaging.js | 5 ++--- src/posts/user.js | 3 +-- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/controllers/accounts.js b/src/controllers/accounts.js index 600c6a2347..eb78226145 100644 --- a/src/controllers/accounts.js +++ b/src/controllers/accounts.js @@ -22,8 +22,7 @@ var fs = require('fs'), languages = require('../languages'), image = require('../image'), file = require('../file'), - helpers = require('./helpers'), - websockets = require('../socket.io'); + helpers = require('./helpers'); function getUserDataByUserSlug(userslug, callerUID, callback) { user.getUidByUserslug(userslug, function(err, uid) { @@ -91,7 +90,7 @@ function getUserDataByUserSlug(userslug, callerUID, callback) { userData.disableSignatures = meta.config.disableSignatures !== undefined && parseInt(meta.config.disableSignatures, 10) === 1; userData['email:confirmed'] = !!parseInt(userData['email:confirmed'], 10); userData.profile_links = results.profile_links; - userData.status = websockets.isUserOnline(userData.uid) ? (userData.status || 'online') : 'offline'; + userData.status = require('../socket.io').isUserOnline(userData.uid) ? (userData.status || 'online') : 'offline'; userData.banned = parseInt(userData.banned, 10) === 1; userData.websiteName = userData.website.replace(validator.escape('http://'), '').replace(validator.escape('https://'), ''); userData.followingCount = parseInt(userData.followingCount, 10) || 0; diff --git a/src/messaging.js b/src/messaging.js index 86ecf7014d..4a367fbd38 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -10,7 +10,6 @@ var db = require('./database'), utils = require('../public/src/utils'), notifications = require('./notifications'), userNotifications = require('./user/notifications'), - websockets = require('./socket.io'), emailer = require('./emailer'); (function(Messaging) { @@ -257,7 +256,7 @@ var db = require('./database'), results.users.forEach(function(user, index) { if (user) { user.unread = results.unread[index]; - user.status = websockets.isUserOnline(user.uid) ? user.status : 'offline'; + user.status = require('./socket.io').isUserOnline(user.uid) ? user.status : 'offline'; } }); @@ -320,7 +319,7 @@ var db = require('./database'), }; function sendNotifications(fromuid, touid, messageObj, callback) { - if (websockets.isUserOnline(touid)) { + if (require('./socket.io').isUserOnline(touid)) { return callback(); } diff --git a/src/posts/user.js b/src/posts/user.js index 555b60e46d..02b7816ed9 100644 --- a/src/posts/user.js +++ b/src/posts/user.js @@ -6,7 +6,6 @@ var async = require('async'), user = require('../user'), groups = require('../groups'), meta = require('../meta'), - websockets = require('../socket.io'), postTools = require('../postTools'), plugins = require('../plugins'); @@ -22,7 +21,7 @@ module.exports = function(Posts) { user.getMultipleUserFields(uids, ['uid', 'username', 'userslug', 'reputation', 'postcount', 'picture', 'signature', 'banned', 'status'], next); }, online: function(next) { - websockets.isUsersOnline(uids, next); + require('../socket.io').isUsersOnline(uids, next); } }, function(err, results) { if (err) { From 03e39f7b9b2ee30af3bcbc8ca26da9725d668cb9 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 23 Feb 2015 16:31:18 -0500 Subject: [PATCH 115/142] isInvited mechanic for #2758 --- public/src/modules/helpers.js | 2 ++ src/controllers/groups.js | 7 ++++- src/groups.js | 48 +++++++++++++++++++++++++++++------ 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/public/src/modules/helpers.js b/public/src/modules/helpers.js index e8f77a5614..c4676a0e37 100644 --- a/public/src/modules/helpers.js +++ b/public/src/modules/helpers.js @@ -33,6 +33,8 @@ } else { if (groupObj.isPending) { return ''; + } else if (groupObj.isInvited) { + return ''; } else { return ''; } diff --git a/src/controllers/groups.js b/src/controllers/groups.js index d18aac4fc2..51200d0a40 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -42,7 +42,12 @@ groupsController.details = function(req, res, next) { next(null, true); } else { // If not, only members are granted access - groups.isMember(uid, res.locals.groupName, next); + async.parallel([ + async.apply(groups.isMember, uid, res.locals.groupName), + async.apply(groups.isInvited, uid, res.locals.groupName) + ], function(err, checks) { + next(err, checks[0] || checks[1]); + }); } } ], function(err, ok) { diff --git a/src/groups.js b/src/groups.js index 177d194fff..54ff60a195 100644 --- a/src/groups.js +++ b/src/groups.js @@ -31,7 +31,7 @@ var async = require('async'), if (!group) { return false; } - if (group.deleted || (group.hidden && !group.system && !group.isMember && !options.isAdmin) || (!options.showSystemGroups && group.system)) { + if (group.deleted || (group.hidden && !(group.system || group.isMember || options.isAdmin || group.isInvited)) || (!options.showSystemGroups && group.system)) { return false; } else if (options.removeEphemeralGroups && ephemeralGroups.indexOf(group.name) !== -1) { return false; @@ -179,6 +179,7 @@ var async = require('async'), db.isSetMember('group:' + groupName + ':pending', options.uid, next); }, + isInvited: async.apply(Groups.isInvited, options.uid, groupName), isOwner: function(next) { // Retrieve group ownership state, if uid is passed in if (!options.uid) { @@ -228,6 +229,7 @@ var async = require('async'), results.base.truncated = truncated; results.base.isMember = results.isMember; results.base.isPending = results.isPending; + results.base.isInvited = results.isInvited; results.base.isOwner = results.isOwner; @@ -408,6 +410,11 @@ var async = require('async'), }); }; + Groups.isInvited = function(uid, groupName, callback) { + if (!uid) { return callback(null, false); } + db.isSetMember('group:' + groupName + ':invited', uid, callback); + }; + Groups.exists = function(name, callback) { if (Array.isArray(name)) { var slugs = name.map(function(groupName) { @@ -607,6 +614,7 @@ var async = require('async'), async.apply(db.rename, 'group:' + oldName + ':members', 'group:' + newName + ':members'), async.apply(db.rename, 'group:' + oldName + ':owners', 'group:' + newName + ':owners'), async.apply(db.rename, 'group:' + oldName + ':pending', 'group:' + newName + ':pending'), + async.apply(db.rename, 'group:' + oldName + ':invited', 'group:' + newName + ':invited'), async.apply(renameGroupMember, 'groups:createtime', oldName, newName), function(next) { plugins.fireHook('action:group.rename', { @@ -651,6 +659,7 @@ var async = require('async'), async.apply(db.sortedSetRemove, 'groups:createtime', groupName), async.apply(db.delete, 'group:' + groupName + ':members'), async.apply(db.delete, 'group:' + groupName + ':pending'), + async.apply(db.delete, 'group:' + groupName + ':invited'), async.apply(db.delete, 'group:' + groupName + ':owners'), async.apply(db.deleteObjectField, 'groupslug:groupname', utils.slugify(groupName)), function(next) { @@ -747,18 +756,41 @@ var async = require('async'), Groups.acceptMembership = function(groupName, uid, callback) { // Note: For simplicity, this method intentially doesn't check the caller uid for ownership! async.waterfall([ - function(next) { - db.setRemove('group:' + groupName + ':pending', uid, next); - }, - function(next) { - Groups.join(groupName, uid, next); - } + async.apply(db.setRemove, 'group:' + groupName + ':pending', uid), + async.apply(db.setRemove, 'group:' + groupName + ':invited', uid), + async.apply(Groups.join, groupName, uid) ], callback); }; Groups.rejectMembership = function(groupName, uid, callback) { // Note: For simplicity, this method intentially doesn't check the caller uid for ownership! - db.setRemove('group:' + groupName + ':pending', uid, callback); + async.parallel([ + async.apply(db.setRemove, 'group:' + groupName + ':pending', uid), + async.apply(db.setRemove, 'group:' + groupName + ':invited', uid) + ], callback); + }; + + Groups.invite = function(groupName, uid, callback) { + async.parallel({ + exists: async.apply(Groups.exists, groupName), + isMember: async.apply(Groups.isMember, uid, groupName) + }, function(err, checks) { + if (!checks.exists) { + return callback(new Error('[[error:no-group]]')); + } else if (checks.isMember) { + return callback(new Error('[[error:group-already-member]]')); + } + + if (parseInt(uid, 10) > 0) { + db.setAdd('group:' + groupName + ':invited', uid, callback); + plugins.fireHook('action:group.inviteMember', { + groupName: groupName, + uid: uid + }); + } else { + callback(new Error('[[error:not-logged-in]]')); + } + }); }; Groups.leave = function(groupName, uid, callback) { From 3e3fff9fe85d6bf4e8db9679c71bfdd789975426 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 23 Feb 2015 16:46:45 -0500 Subject: [PATCH 116/142] hooking up accept/reject invite, #2758 --- public/src/client/groups/details.js | 2 ++ src/socket.io/groups.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/public/src/client/groups/details.js b/public/src/client/groups/details.js index 1d651fad27..f4f56a686e 100644 --- a/public/src/client/groups/details.js +++ b/public/src/client/groups/details.js @@ -64,6 +64,8 @@ define('forum/groups/details', ['iconSelect', 'vendor/colorpicker/colorpicker', case 'leave': case 'accept': case 'reject': + case 'acceptInvite': + case 'rejectInvite': socket.emit('groups.' + action, { toUid: uid, groupName: ajaxify.variables.get('group_name') diff --git a/src/socket.io/groups.js b/src/socket.io/groups.js index ba35e128c4..bb814e6006 100644 --- a/src/socket.io/groups.js +++ b/src/socket.io/groups.js @@ -101,6 +101,34 @@ SocketGroups.reject = function(socket, data, callback) { }); }; +SocketGroups.acceptInvite = function(socket, data, callback) { + if (!data) { + return callback(new Error('[[error:invalid-data]]')); + } + + groups.isInvited(socket.uid, data.groupName, function(err, invited) { + if (!invited) { + return callback(new Error('[[error:no-privileges]]')); + } + + groups.acceptMembership(data.groupName, socket.uid, callback); + }); +}; + +SocketGroups.rejectInvite = function(socket, data, callback) { + if (!data) { + return callback(new Error('[[error:invalid-data]]')); + } + + groups.isInvited(socket.uid, data.groupName, function(err, invited) { + if (!invited) { + return callback(new Error('[[error:no-privileges]]')); + } + + groups.rejectMembership(data.groupName, socket.uid, callback); + }); +}; + SocketGroups.update = function(socket, data, callback) { if (!data) { return callback(new Error('[[error:invalid-data]]')); From f408c76a987ebbd25512b491d65226c65f4eaaaa Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 23 Feb 2015 17:24:20 -0500 Subject: [PATCH 117/142] requiring plugins in meta when needed --- src/meta.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/meta.js b/src/meta.js index 0e07681223..b58ac1a9b2 100644 --- a/src/meta.js +++ b/src/meta.js @@ -8,7 +8,6 @@ var async = require('async'), user = require('./user'), groups = require('./groups'), - plugins = require('./plugins'), emitter = require('./emitter'), pubsub = require('./pubsub'), auth = require('./routes/authentication'); @@ -49,6 +48,8 @@ var async = require('async'), function reload(callback) { callback = callback || function() {}; + + var plugins = require('./plugins'); async.series([ async.apply(plugins.clearRequireCache), async.apply(plugins.reload), From fdc8b67449e0326ec8cae1705d65bbc7befff030 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 23 Feb 2015 17:25:36 -0500 Subject: [PATCH 118/142] require pkg when needed --- src/plugins.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins.js b/src/plugins.js index b9e7118df9..cb67084b28 100644 --- a/src/plugins.js +++ b/src/plugins.js @@ -14,7 +14,6 @@ var fs = require('fs'), translator = require('../public/src/translator'), utils = require('../public/src/utils'), hotswap = require('./hotswap'), - pkg = require('../package.json'), controllers = require('./controllers'), app, middleware; @@ -168,8 +167,9 @@ var fs = require('fs'), }; Plugins.getAll = function(callback) { - var request = require('request'); - request((nconf.get('registry') || 'https://packages.nodebb.org') + '/api/v1/plugins?version=' + pkg.version, function(err, res, body) { + var url = (nconf.get('registry') || 'https://packages.nodebb.org') + '/api/v1/plugins?version=' + require('../package.json').version; + + require('request')(url, function(err, res, body) { var plugins = []; try { From ab2315f3de8dab76a90fbe41821a145d47dbb80d Mon Sep 17 00:00:00 2001 From: psychobunny Date: Tue, 24 Feb 2015 11:45:58 -0500 Subject: [PATCH 119/142] #2751 --- public/src/admin/extend/rewards.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index 45e81cc289..967e098bf7 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -143,8 +143,8 @@ define('admin/extend/rewards', function() { templates.parse('admin/extend/rewards', 'active', data, function(li) { li = $(li); - li.find('[name="rid"]').val(''); ul.append(li); + li.find('select').val(''); }); } From 9de0c0cdd40e9bc96ec80268e9319f50417b4a47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 24 Feb 2015 11:55:07 -0500 Subject: [PATCH 120/142] closes #2761 --- src/database/mongo/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/database/mongo/main.js b/src/database/mongo/main.js index 475360c2ca..0488260946 100644 --- a/src/database/mongo/main.js +++ b/src/database/mongo/main.js @@ -138,7 +138,7 @@ module.exports = function(db, module) { }; module.pexpire = function(key, ms, callback) { - module.expireAt(key, Date.now() + parseInt(ms, 10), callback); + module.setObjectField(key, 'expireAt', new Date(Date.now() + parseInt(ms, 10)), callback); }; module.pexpireAt = function(key, timestamp, callback) { From 52aa7d27e8240eb835cb66303dcc02d3723949b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 24 Feb 2015 11:58:09 -0500 Subject: [PATCH 121/142] #2761 --- src/database/mongo/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/database/mongo/main.js b/src/database/mongo/main.js index 0488260946..8509475bc4 100644 --- a/src/database/mongo/main.js +++ b/src/database/mongo/main.js @@ -138,7 +138,7 @@ module.exports = function(db, module) { }; module.pexpire = function(key, ms, callback) { - module.setObjectField(key, 'expireAt', new Date(Date.now() + parseInt(ms, 10)), callback); + module.pexpireAt(key, Date.now() + parseInt(ms, 10), callback); }; module.pexpireAt = function(key, timestamp, callback) { From 242f66934c5230cf9e0a93c2ab57cc53a5c9c033 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Tue, 24 Feb 2015 12:23:35 -0500 Subject: [PATCH 122/142] another attempt --- Gruntfile.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 4db400bf83..eb57d4984e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -7,7 +7,10 @@ module.exports = function(grunt) { script: 'app.js', node_env: 'development', args: ['dev', 'from-file'], - output: "NodeBB Ready" + output: "NodeBB Ready", + interrupt: true, + spawn: false, + interval: 100 } }, minifyJS: { @@ -15,7 +18,10 @@ module.exports = function(grunt) { script: 'app.js', node_env: 'development', args: ['dev', 'minify-js'], - output: "NodeBB Ready" + output: "NodeBB Ready", + interrupt: true, + spawn: false, + interval: 100 } }, compileLESS: { @@ -23,7 +29,10 @@ module.exports = function(grunt) { script: 'app.js', node_env: 'development', args: ['dev', 'compile-less'], - output: "NodeBB Ready" + output: "NodeBB Ready", + interrupt: true, + spawn: false, + interval: 100 } } }, From eb6754c5ebad9dc1bebf58d9a8398c65c8a6d904 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Tue, 24 Feb 2015 12:56:49 -0500 Subject: [PATCH 123/142] nice, I figured out how to speed up grunt watch --- Gruntfile.js | 50 ++++++++++++++++++++++++++++++------------------ app.js | 2 +- src/webserver.js | 4 ++-- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index eb57d4984e..edae80b7d8 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -6,30 +6,41 @@ module.exports = function(grunt) { options: { script: 'app.js', node_env: 'development', - args: ['dev', 'from-file'], - output: "NodeBB Ready", + args: ['--log-level=info'], + output: 'NodeBB Ready', interrupt: true, spawn: false, interval: 100 } }, - minifyJS: { + serverUpdated: { options: { script: 'app.js', node_env: 'development', - args: ['dev', 'minify-js'], - output: "NodeBB Ready", + args: ['--from-file=less,js', '--log-level=info'], + output: 'NodeBB Ready', interrupt: true, spawn: false, interval: 100 } }, - compileLESS: { + clientUpdated: { options: { script: 'app.js', node_env: 'development', - args: ['dev', 'compile-less'], - output: "NodeBB Ready", + args: ['--from-file=less', '--log-level=info'], + output: 'NodeBB Ready', + interrupt: true, + spawn: false, + interval: 100 + } + }, + lessUpdated: { + options: { + script: 'app.js', + node_env: 'development', + args: ['--from-file=js', '--log-level=info'], + output: 'NodeBB Ready', interrupt: true, spawn: false, interval: 100 @@ -39,17 +50,22 @@ module.exports = function(grunt) { less: { development: { files: { - "public/bin/manifest.css": "source/manifest.less" + 'public/bin/manifest.css': 'source/manifest.less' } } }, watch: { - compileLESS: { - files: "**/*.less", - tasks: ['express:compileLESS'], - options: { - livereload: true, - } + lessUpdated: { + files: 'public/**/*.less', + tasks: ['express:lessUpdated'] + }, + clientUpdated: { + files: 'public/src/**/*.js', + tasks: ['express:clientUpdated'] + }, + serverUpdated: { + files: ['*.js', 'src/**/*.js'], + tasks: ['express:serverUpdated'] } } }); @@ -58,8 +74,4 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-express-server'); grunt.registerTask('default', ['express:dev', 'watch']); - - grunt.event.on('watch', function(action, filepath, target) { - grunt.log.writeln(target + ': ' + filepath + ' has ' + action); - }); }; \ No newline at end of file diff --git a/app.js b/app.js index f208072719..ef113e5ece 100644 --- a/app.js +++ b/app.js @@ -43,7 +43,7 @@ winston.add(winston.transports.Console, { var date = new Date(); return date.getDate() + '/' + (date.getMonth() + 1) + ' ' + date.toTimeString().substr(0,5) + ' [' + global.process.pid + ']'; }, - level: global.env === 'production' ? 'info' : 'verbose' + level: (global.env === 'production' || nconf.get('log-level') === 'info') ? 'info' : 'verbose' }); if(os.platform() === 'linux') { diff --git a/src/webserver.js b/src/webserver.js index 5512c84e40..01dee2b91c 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -39,8 +39,8 @@ if(nconf.get('ssl')) { // Preparation dependent on plugins plugins.ready(function() { async.parallel([ - async.apply((!nconf.get('minify-js') && !nconf.get('from-file')) ? meta.js.minify : meta.js.getFromFile, app.enabled('minification')), - async.apply((!nconf.get('compile-less') && !nconf.get('from-file')) ? meta.css.minify : meta.css.getFromFile), + async.apply(!nconf.get('from-file') ? meta.js.minify : meta.js.getFromFile, app.enabled('minification')), + async.apply(!nconf.get('from-file') ? meta.css.minify : meta.css.getFromFile), async.apply(meta.sounds.init) ]); }); From c94c1430b7d9d57c8960745fcfe7b868cf2ddc84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 24 Feb 2015 13:02:58 -0500 Subject: [PATCH 124/142] api/post/pid route --- src/controllers/index.js | 1 + src/controllers/posts.js | 36 ++++++++++++++++++++++++++++++++++++ src/posts/category.js | 5 +---- src/privileges/categories.js | 3 +++ src/privileges/posts.js | 5 +++-- src/routes/api.js | 1 + src/topics/fork.js | 4 ++++ 7 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 src/controllers/posts.js diff --git a/src/controllers/index.js b/src/controllers/index.js index 8a43da5c9f..d8ddf81735 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -16,6 +16,7 @@ var async = require('async'), helpers = require('./helpers'); var Controllers = { + posts: require('./posts'), topics: require('./topics'), categories: require('./categories'), tags: require('./tags'), diff --git a/src/controllers/posts.js b/src/controllers/posts.js new file mode 100644 index 0000000000..e5a32e9791 --- /dev/null +++ b/src/controllers/posts.js @@ -0,0 +1,36 @@ +"use strict"; + +var async = require('async'), + + posts = require('../posts'), + privileges = require('../privileges'), + helpers = require('./helpers'), + postsController = {}; + +postsController.getPost = function(req, res, next) { + var uid = req.user ? parseInt(req.user.uid) : 0; + async.parallel({ + canRead: function(next) { + privileges.posts.can('read', req.params.pid, uid, next); + }, + postData: function(next) { + posts.getPostData(req.params.pid, next); + } + }, function(err, results) { + if (err) { + return next(err); + } + if (!results.postData) { + return helpers.notFound(req, res); + } + if (!results.canRead) { + return helpers.notAllowed(req, res); + } + + res.json(results.postData); + }); +}; + + + +module.exports = postsController; diff --git a/src/posts/category.js b/src/posts/category.js index 761738cb1f..68ae42474b 100644 --- a/src/posts/category.js +++ b/src/posts/category.js @@ -13,10 +13,7 @@ module.exports = function(Posts) { }, function(tid, next) { topics.getTopicField(tid, 'cid', next); - }, - function(cid, next) { - next(!cid ? new Error('[[error:invalid-cid]]') : null, cid); - } + } ], callback); }; diff --git a/src/privileges/categories.js b/src/privileges/categories.js index 5ff70730d4..267c9e2c81 100644 --- a/src/privileges/categories.js +++ b/src/privileges/categories.js @@ -46,6 +46,9 @@ module.exports = function(privileges) { }; privileges.categories.can = function(privilege, cid, uid, callback) { + if (!cid) { + return callback(null, false); + } categories.getCategoryField(cid, 'disabled', function(err, disabled) { if (err) { return callback(err); diff --git a/src/privileges/posts.js b/src/privileges/posts.js index 7de1167726..7c304a1a2c 100644 --- a/src/privileges/posts.js +++ b/src/privileges/posts.js @@ -153,9 +153,10 @@ module.exports = function(privileges) { helpers.some([ function(next) { posts.getCidByPid(pid, function(err, cid) { - if (err) { - return next(err); + if (err || !cid) { + return next(err, false); } + user.isModerator(uid, cid, next); }); }, diff --git a/src/routes/api.js b/src/routes/api.js index 47632ccce7..ef1c297e49 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -16,6 +16,7 @@ module.exports = function(app, middleware, controllers) { router.get('/widgets/render', controllers.api.renderWidgets); router.get('/user/uid/:uid', middleware.checkGlobalPrivacySettings, controllers.accounts.getUserByUID); + router.get('/post/:pid', controllers.posts.getPost); router.get('/get_templates_listing', templatesController.getTemplatesListing); router.get('/categories/:cid/moderators', getModerators); router.get('/recent/posts/:term?', getRecentPosts); diff --git a/src/topics/fork.js b/src/topics/fork.js index 4cd5e0c53e..3302797f38 100644 --- a/src/topics/fork.js +++ b/src/topics/fork.js @@ -41,6 +41,10 @@ module.exports = function(Topics) { posts.getCidByPid(mainPid, callback); } }, function(err, results) { + if (err) { + return callback(err); + } + Topics.create({uid: results.postData.uid, title: title, cid: results.cid}, function(err, tid) { if (err) { return callback(err); From 912de27c9d0d97192c7c296f51757608e6669d89 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 24 Feb 2015 14:43:19 -0500 Subject: [PATCH 125/142] fixed indentation issues --- src/views/admin/header.tpl | 178 ++++++++++++++++++------------------- 1 file changed, 89 insertions(+), 89 deletions(-) diff --git a/src/views/admin/header.tpl b/src/views/admin/header.tpl index 89b0dc4ac4..4193857410 100644 --- a/src/views/admin/header.tpl +++ b/src/views/admin/header.tpl @@ -16,100 +16,100 @@ - - - - + + + - - - - - - - - - - + app.inAdmin = true; + + + + + + + + + + + - - - - + + + + - - + -
      -