From 1aa70c57eb60daa51bf1181335f37e5a46ffbb72 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 23 Nov 2016 19:41:35 -0500 Subject: [PATCH 001/316] WIP --- src/controllers/admin/flags.js | 3 +- src/flags.js | 484 +++++++++++++++++++++++++++++++++ src/posts.js | 1 - src/posts/delete.js | 3 +- src/posts/flags.js | 417 ---------------------------- src/socket.io/posts/flag.js | 9 +- src/upgrade.js | 7 +- src/user/admin.js | 3 +- 8 files changed, 499 insertions(+), 428 deletions(-) create mode 100644 src/flags.js delete mode 100644 src/posts/flags.js diff --git a/src/controllers/admin/flags.js b/src/controllers/admin/flags.js index 1b31a95ff4..80c31ba60a 100644 --- a/src/controllers/admin/flags.js +++ b/src/controllers/admin/flags.js @@ -5,6 +5,7 @@ var validator = require('validator'); var posts = require('../../posts'); var user = require('../../user'); +var flags = require('../../flags'); var categories = require('../../categories'); var analytics = require('../../analytics'); var pagination = require('../../pagination'); @@ -94,7 +95,7 @@ function getFlagData(req, res, callback) { sets.push('uid:' + uid + ':flag:pids'); } - posts.getFlags(sets, cid, req.uid, start, stop, next); + flags.get(sets, cid, req.uid, start, stop, next); } ], callback); } diff --git a/src/flags.js b/src/flags.js new file mode 100644 index 0000000000..1e6294aecb --- /dev/null +++ b/src/flags.js @@ -0,0 +1,484 @@ + + +'use strict'; + +var async = require('async'); +var winston = require('winston'); +var db = require('./database'); +var user = require('./user'); +var analytics = require('./analytics'); +var topics = require('./topics'); +var posts = require('./posts'); +var utils = require('../public/src/utils'); + +var Flags = { + _defaults: { + state: 'open' + } +}; + +Flags.create = function (type, id, uid, reason, callback) { + async.waterfall([ + function (next) { + // Sanity checks + async.parallel([ + async.apply(Flags.exists, type, id, uid), + async.apply(Flags.targetExists, type, id) + ], function (err, checks) { + if (checks[0]) { + return next(new Error('[[error:already-flagged]]')); + } else if (!checks[1]) { + return next(new Error('[[error:invalid-data]]')); + } else { + next(); + } + }); + }, + function (next) { + var flagId = utils.generateUUID(); + + async.parallel([ + async.apply(db.setObject.bind(db), 'flag:' + flagId, _.defaults({ + description: reason + }), Flags._defaults), + async.apply(db.sortedSetAdd.bind(db), 'flags:datetime', now, flagId) + ], function (err) { + if (err) { + return next(err); + } + }); + } + ], function (err) { + if (err) { + return callback(err); + } + + console.log('done', arguments); + process.exit(); + }); + // if (!parseInt(uid, 10) || !reason) { + // return callback(); + // } + + // async.waterfall([ + // function (next) { + // async.parallel({ + // hasFlagged: async.apply(Flags.isFlaggedByUser, post.pid, uid), + // exists: async.apply(Posts.exists, post.pid) + // }, next); + // }, + // function (results, next) { + // if (!results.exists) { + // return next(new Error('[[error:no-post]]')); + // } + + // if (results.hasFlagged) { + // return next(new Error('[[error:already-flagged]]')); + // } + + // var now = Date.now(); + // async.parallel([ + // function (next) { + // db.sortedSetAdd('posts:flagged', now, post.pid, next); + // }, + // function (next) { + // db.sortedSetIncrBy('posts:flags:count', 1, post.pid, next); + // }, + // function (next) { + // db.incrObjectField('post:' + post.pid, 'flags', next); + // }, + // function (next) { + // db.sortedSetAdd('pid:' + post.pid + ':flag:uids', now, uid, next); + // }, + // function (next) { + // db.sortedSetAdd('pid:' + post.pid + ':flag:uid:reason', 0, uid + ':' + reason, next); + // }, + // function (next) { + // if (parseInt(post.uid, 10)) { + // async.parallel([ + // async.apply(db.sortedSetIncrBy, 'users:flags', 1, post.uid), + // async.apply(db.incrObjectField, 'user:' + post.uid, 'flags'), + // async.apply(db.sortedSetAdd, 'uid:' + post.uid + ':flag:pids', now, post.pid) + // ], next); + // } else { + // next(); + // } + // } + // ], next); + // }, + // function (data, next) { + // openNewFlag(post.pid, uid, next); + // } + // ], function (err) { + // if (err) { + // return callback(err); + // } + // analytics.increment('flags'); + // callback(); + // }); +}; + +function openNewFlag(pid, uid, callback) { + db.sortedSetScore('posts:flags:count', pid, function (err, count) { + if (err) { + return callback(err); + } + if (count === 1) { // Only update state on new flag + Flags.update(uid, pid, { + state: 'open' + }, callback); + } else { + callback(); + } + }); +} + +Flags.exists = function (type, id, uid, callback) { + db.isObjectField('flagHash:flagId', [type, id, uid].join(':'), callback); +}; + +Flags.targetExists = function (type, id, callback) { + switch (type) { + case 'topic': + topics.exists(id, callback); + break; + + case 'post': + posts.exists(id, callback); + break; + } +}; + +/* new signature (type, id, uid, callback) */ +Flags.isFlaggedByUser = function (pid, uid, callback) { + db.isSortedSetMember('pid:' + pid + ':flag:uids', uid, callback); +}; + +Flags.dismiss = function (pid, callback) { + async.waterfall([ + function (next) { + db.getObjectFields('post:' + pid, ['pid', 'uid', 'flags'], next); + }, + function (postData, next) { + if (!postData.pid) { + return callback(); + } + async.parallel([ + function (next) { + if (parseInt(postData.uid, 10)) { + if (parseInt(postData.flags, 10) > 0) { + async.parallel([ + async.apply(db.sortedSetIncrBy, 'users:flags', -postData.flags, postData.uid), + async.apply(db.incrObjectFieldBy, 'user:' + postData.uid, 'flags', -postData.flags) + ], next); + } else { + next(); + } + } else { + next(); + } + }, + function (next) { + db.sortedSetsRemove([ + 'posts:flagged', + 'posts:flags:count', + 'uid:' + postData.uid + ':flag:pids' + ], pid, next); + }, + function (next) { + async.series([ + function (next) { + db.getSortedSetRange('pid:' + pid + ':flag:uids', 0, -1, function (err, uids) { + if (err) { + return next(err); + } + + async.each(uids, function (uid, next) { + var nid = 'post_flag:' + pid + ':uid:' + uid; + async.parallel([ + async.apply(db.delete, 'notifications:' + nid), + async.apply(db.sortedSetRemove, 'notifications', 'post_flag:' + pid + ':uid:' + uid) + ], next); + }, next); + }); + }, + async.apply(db.delete, 'pid:' + pid + ':flag:uids') + ], next); + }, + async.apply(db.deleteObjectField, 'post:' + pid, 'flags'), + async.apply(db.delete, 'pid:' + pid + ':flag:uid:reason'), + async.apply(db.deleteObjectFields, 'post:' + pid, ['flag:state', 'flag:assignee', 'flag:notes', 'flag:history']) + ], next); + }, + function (results, next) { + db.sortedSetsRemoveRangeByScore(['users:flags'], '-inf', 0, next); + } + ], callback); +}; + +// Pretty sure we don't need this method... +Flags.dismissAll = function (callback) { + db.getSortedSetRange('posts:flagged', 0, -1, function (err, pids) { + if (err) { + return callback(err); + } + async.eachSeries(pids, Flags.dismiss, callback); + }); +}; + +Flags.dismissByUid = function (uid, callback) { + db.getSortedSetRange('uid:' + uid + ':flag:pids', 0, -1, function (err, pids) { + if (err) { + return callback(err); + } + async.eachSeries(pids, Flags.dismiss, callback); + }); +}; + +Flags.get = function (set, cid, uid, start, stop, callback) { + async.waterfall([ + function (next) { + if (Array.isArray(set)) { + db.getSortedSetRevIntersect({sets: set, start: start, stop: -1, aggregate: 'MAX'}, next); + } else { + db.getSortedSetRevRange(set, start, -1, next); + } + }, + function (pids, next) { + if (cid) { + Posts.filterPidsByCid(pids, cid, next); + } else { + process.nextTick(next, null, pids); + } + }, + function (pids, next) { + getFlaggedPostsWithReasons(pids, uid, next); + }, + function (posts, next) { + var count = posts.length; + var end = stop - start + 1; + next(null, {posts: posts.slice(0, stop === -1 ? undefined : end), count: count}); + } + ], callback); +}; + +function getFlaggedPostsWithReasons(pids, uid, callback) { + async.waterfall([ + function (next) { + async.parallel({ + uidsReasons: function (next) { + async.map(pids, function (pid, next) { + db.getSortedSetRange('pid:' + pid + ':flag:uid:reason', 0, -1, next); + }, next); + }, + posts: function (next) { + Posts.getPostSummaryByPids(pids, uid, {stripTags: false, extraFields: ['flags', 'flag:assignee', 'flag:state', 'flag:notes', 'flag:history']}, next); + } + }, next); + }, + function (results, next) { + async.map(results.uidsReasons, function (uidReasons, next) { + async.map(uidReasons, function (uidReason, next) { + var uid = uidReason.split(':')[0]; + var reason = uidReason.substr(uidReason.indexOf(':') + 1); + user.getUserFields(uid, ['username', 'userslug', 'picture'], function (err, userData) { + next(err, {user: userData, reason: reason}); + }); + }, next); + }, function (err, reasons) { + if (err) { + return callback(err); + } + + results.posts.forEach(function (post, index) { + if (post) { + post.flagReasons = reasons[index]; + } + }); + + next(null, results.posts); + }); + }, + async.apply(Posts.expandFlagHistory), + function (posts, next) { + // Parse out flag data into its own object inside each post hash + async.map(posts, function (postObj, next) { + for(var prop in postObj) { + postObj.flagData = postObj.flagData || {}; + + if (postObj.hasOwnProperty(prop) && prop.startsWith('flag:')) { + postObj.flagData[prop.slice(5)] = postObj[prop]; + + if (prop === 'flag:state') { + switch(postObj[prop]) { + case 'open': + postObj.flagData.labelClass = 'info'; + break; + case 'wip': + postObj.flagData.labelClass = 'warning'; + break; + case 'resolved': + postObj.flagData.labelClass = 'success'; + break; + case 'rejected': + postObj.flagData.labelClass = 'danger'; + break; + } + } + + delete postObj[prop]; + } + } + + if (postObj.flagData.assignee) { + user.getUserFields(parseInt(postObj.flagData.assignee, 10), ['username', 'picture'], function (err, userData) { + if (err) { + return next(err); + } + + postObj.flagData.assigneeUser = userData; + next(null, postObj); + }); + } else { + setImmediate(next.bind(null, null, postObj)); + } + }, next); + } + ], callback); +} + +// New method signature (type, id, flagObj, callback) and name (.update()) +// uid used in history string, which should be rewritten too. +Flags.update = function (uid, pid, flagObj, callback) { + // Retrieve existing flag data to compare for history-saving purposes + var changes = []; + var changeset = {}; + var prop; + + Posts.getPostData(pid, function (err, postData) { + if (err) { + return callback(err); + } + + // Track new additions + for(prop in flagObj) { + if (flagObj.hasOwnProperty(prop) && !postData.hasOwnProperty('flag:' + prop) && flagObj[prop].length) { + changes.push(prop); + } + } + + // Track changed items + for(prop in postData) { + if ( + postData.hasOwnProperty(prop) && prop.startsWith('flag:') && + flagObj.hasOwnProperty(prop.slice(5)) && + postData[prop] !== flagObj[prop.slice(5)] + ) { + changes.push(prop.slice(5)); + } + } + + changeset = changes.reduce(function (memo, prop) { + memo['flag:' + prop] = flagObj[prop]; + return memo; + }, {}); + + // Append changes to history string + if (changes.length) { + try { + var history = JSON.parse(postData['flag:history'] || '[]'); + + changes.forEach(function (property) { + switch(property) { + case 'assignee': // intentional fall-through + case 'state': + history.unshift({ + uid: uid, + type: property, + value: flagObj[property], + timestamp: Date.now() + }); + break; + + case 'notes': + history.unshift({ + uid: uid, + type: property, + timestamp: Date.now() + }); + } + }); + + changeset['flag:history'] = JSON.stringify(history); + } catch (e) { + winston.warn('[flags/update] Unable to deserialise post flag history, likely malformed data'); + } + } + + // Save flag data into post hash + if (changes.length) { + Posts.setPostFields(pid, changeset, callback); + } else { + setImmediate(callback); + } + }); +}; + +// To be rewritten and deprecated +Flags.expandFlagHistory = function (posts, callback) { + // Expand flag history + async.map(posts, function (post, next) { + var history; + try { + history = JSON.parse(post['flag:history'] || '[]'); + } catch (e) { + winston.warn('[flags/get] Unable to deserialise post flag history, likely malformed data'); + return callback(e); + } + + async.map(history, function (event, next) { + event.timestampISO = new Date(event.timestamp).toISOString(); + + async.parallel([ + function (next) { + user.getUserFields(event.uid, ['username', 'picture'], function (err, userData) { + if (err) { + return next(err); + } + + event.user = userData; + next(); + }); + }, + function (next) { + if (event.type === 'assignee') { + user.getUserField(parseInt(event.value, 10), 'username', function (err, username) { + if (err) { + return next(err); + } + + event.label = username || 'Unknown user'; + next(null); + }); + } else if (event.type === 'state') { + event.label = '[[topic:flag_manage_state_' + event.value + ']]'; + setImmediate(next); + } else { + setImmediate(next); + } + } + ], function (err) { + next(err, event); + }); + }, function (err, history) { + if (err) { + return next(err); + } + + post['flag:history'] = history; + next(null, post); + }); + }, callback); +}; + +module.exports = Flags; \ No newline at end of file diff --git a/src/posts.js b/src/posts.js index 047917cb5f..b476b84414 100644 --- a/src/posts.js +++ b/src/posts.js @@ -21,7 +21,6 @@ var plugins = require('./plugins'); require('./posts/category')(Posts); require('./posts/summary')(Posts); require('./posts/recent')(Posts); - require('./posts/flags')(Posts); require('./posts/tools')(Posts); require('./posts/votes')(Posts); require('./posts/bookmarks')(Posts); diff --git a/src/posts/delete.js b/src/posts/delete.js index 7a1d3d0cc8..ebf902aef2 100644 --- a/src/posts/delete.js +++ b/src/posts/delete.js @@ -8,6 +8,7 @@ var topics = require('../topics'); var user = require('../user'); var notifications = require('../notifications'); var plugins = require('../plugins'); +var flags = require('../flags'); module.exports = function (Posts) { @@ -145,7 +146,7 @@ module.exports = function (Posts) { db.sortedSetsRemove(['posts:pid', 'posts:flagged'], pid, next); }, function (next) { - Posts.dismissFlag(pid, next); + flags.dismiss(pid, next); } ], function (err) { if (err) { diff --git a/src/posts/flags.js b/src/posts/flags.js deleted file mode 100644 index e81da20f95..0000000000 --- a/src/posts/flags.js +++ /dev/null @@ -1,417 +0,0 @@ - - -'use strict'; - -var async = require('async'); -var winston = require('winston'); -var db = require('../database'); -var user = require('../user'); -var analytics = require('../analytics'); - -module.exports = function (Posts) { - - Posts.flag = function (post, uid, reason, callback) { - if (!parseInt(uid, 10) || !reason) { - return callback(); - } - - async.waterfall([ - function (next) { - async.parallel({ - hasFlagged: async.apply(Posts.isFlaggedByUser, post.pid, uid), - exists: async.apply(Posts.exists, post.pid) - }, next); - }, - function (results, next) { - if (!results.exists) { - return next(new Error('[[error:no-post]]')); - } - - if (results.hasFlagged) { - return next(new Error('[[error:already-flagged]]')); - } - - var now = Date.now(); - async.parallel([ - function (next) { - db.sortedSetAdd('posts:flagged', now, post.pid, next); - }, - function (next) { - db.sortedSetIncrBy('posts:flags:count', 1, post.pid, next); - }, - function (next) { - db.incrObjectField('post:' + post.pid, 'flags', next); - }, - function (next) { - db.sortedSetAdd('pid:' + post.pid + ':flag:uids', now, uid, next); - }, - function (next) { - db.sortedSetAdd('pid:' + post.pid + ':flag:uid:reason', 0, uid + ':' + reason, next); - }, - function (next) { - if (parseInt(post.uid, 10)) { - async.parallel([ - async.apply(db.sortedSetIncrBy, 'users:flags', 1, post.uid), - async.apply(db.incrObjectField, 'user:' + post.uid, 'flags'), - async.apply(db.sortedSetAdd, 'uid:' + post.uid + ':flag:pids', now, post.pid) - ], next); - } else { - next(); - } - } - ], next); - }, - function (data, next) { - openNewFlag(post.pid, uid, next); - } - ], function (err) { - if (err) { - return callback(err); - } - analytics.increment('flags'); - callback(); - }); - }; - - function openNewFlag(pid, uid, callback) { - db.sortedSetScore('posts:flags:count', pid, function (err, count) { - if (err) { - return callback(err); - } - if (count === 1) { // Only update state on new flag - Posts.updateFlagData(uid, pid, { - state: 'open' - }, callback); - } else { - callback(); - } - }); - } - - Posts.isFlaggedByUser = function (pid, uid, callback) { - db.isSortedSetMember('pid:' + pid + ':flag:uids', uid, callback); - }; - - Posts.dismissFlag = function (pid, callback) { - async.waterfall([ - function (next) { - db.getObjectFields('post:' + pid, ['pid', 'uid', 'flags'], next); - }, - function (postData, next) { - if (!postData.pid) { - return callback(); - } - async.parallel([ - function (next) { - if (parseInt(postData.uid, 10)) { - if (parseInt(postData.flags, 10) > 0) { - async.parallel([ - async.apply(db.sortedSetIncrBy, 'users:flags', -postData.flags, postData.uid), - async.apply(db.incrObjectFieldBy, 'user:' + postData.uid, 'flags', -postData.flags) - ], next); - } else { - next(); - } - } else { - next(); - } - }, - function (next) { - db.sortedSetsRemove([ - 'posts:flagged', - 'posts:flags:count', - 'uid:' + postData.uid + ':flag:pids' - ], pid, next); - }, - function (next) { - async.series([ - function (next) { - db.getSortedSetRange('pid:' + pid + ':flag:uids', 0, -1, function (err, uids) { - if (err) { - return next(err); - } - - async.each(uids, function (uid, next) { - var nid = 'post_flag:' + pid + ':uid:' + uid; - async.parallel([ - async.apply(db.delete, 'notifications:' + nid), - async.apply(db.sortedSetRemove, 'notifications', 'post_flag:' + pid + ':uid:' + uid) - ], next); - }, next); - }); - }, - async.apply(db.delete, 'pid:' + pid + ':flag:uids') - ], next); - }, - async.apply(db.deleteObjectField, 'post:' + pid, 'flags'), - async.apply(db.delete, 'pid:' + pid + ':flag:uid:reason'), - async.apply(db.deleteObjectFields, 'post:' + pid, ['flag:state', 'flag:assignee', 'flag:notes', 'flag:history']) - ], next); - }, - function (results, next) { - db.sortedSetsRemoveRangeByScore(['users:flags'], '-inf', 0, next); - } - ], callback); - }; - - Posts.dismissAllFlags = function (callback) { - db.getSortedSetRange('posts:flagged', 0, -1, function (err, pids) { - if (err) { - return callback(err); - } - async.eachSeries(pids, Posts.dismissFlag, callback); - }); - }; - - Posts.dismissUserFlags = function (uid, callback) { - db.getSortedSetRange('uid:' + uid + ':flag:pids', 0, -1, function (err, pids) { - if (err) { - return callback(err); - } - async.eachSeries(pids, Posts.dismissFlag, callback); - }); - }; - - Posts.getFlags = function (set, cid, uid, start, stop, callback) { - async.waterfall([ - function (next) { - if (Array.isArray(set)) { - db.getSortedSetRevIntersect({sets: set, start: start, stop: -1, aggregate: 'MAX'}, next); - } else { - db.getSortedSetRevRange(set, start, -1, next); - } - }, - function (pids, next) { - if (cid) { - Posts.filterPidsByCid(pids, cid, next); - } else { - process.nextTick(next, null, pids); - } - }, - function (pids, next) { - getFlaggedPostsWithReasons(pids, uid, next); - }, - function (posts, next) { - var count = posts.length; - var end = stop - start + 1; - next(null, {posts: posts.slice(0, stop === -1 ? undefined : end), count: count}); - } - ], callback); - }; - - function getFlaggedPostsWithReasons(pids, uid, callback) { - async.waterfall([ - function (next) { - async.parallel({ - uidsReasons: function (next) { - async.map(pids, function (pid, next) { - db.getSortedSetRange('pid:' + pid + ':flag:uid:reason', 0, -1, next); - }, next); - }, - posts: function (next) { - Posts.getPostSummaryByPids(pids, uid, {stripTags: false, extraFields: ['flags', 'flag:assignee', 'flag:state', 'flag:notes', 'flag:history']}, next); - } - }, next); - }, - function (results, next) { - async.map(results.uidsReasons, function (uidReasons, next) { - async.map(uidReasons, function (uidReason, next) { - var uid = uidReason.split(':')[0]; - var reason = uidReason.substr(uidReason.indexOf(':') + 1); - user.getUserFields(uid, ['username', 'userslug', 'picture'], function (err, userData) { - next(err, {user: userData, reason: reason}); - }); - }, next); - }, function (err, reasons) { - if (err) { - return callback(err); - } - - results.posts.forEach(function (post, index) { - if (post) { - post.flagReasons = reasons[index]; - } - }); - - next(null, results.posts); - }); - }, - async.apply(Posts.expandFlagHistory), - function (posts, next) { - // Parse out flag data into its own object inside each post hash - async.map(posts, function (postObj, next) { - for(var prop in postObj) { - postObj.flagData = postObj.flagData || {}; - - if (postObj.hasOwnProperty(prop) && prop.startsWith('flag:')) { - postObj.flagData[prop.slice(5)] = postObj[prop]; - - if (prop === 'flag:state') { - switch(postObj[prop]) { - case 'open': - postObj.flagData.labelClass = 'info'; - break; - case 'wip': - postObj.flagData.labelClass = 'warning'; - break; - case 'resolved': - postObj.flagData.labelClass = 'success'; - break; - case 'rejected': - postObj.flagData.labelClass = 'danger'; - break; - } - } - - delete postObj[prop]; - } - } - - if (postObj.flagData.assignee) { - user.getUserFields(parseInt(postObj.flagData.assignee, 10), ['username', 'picture'], function (err, userData) { - if (err) { - return next(err); - } - - postObj.flagData.assigneeUser = userData; - next(null, postObj); - }); - } else { - setImmediate(next.bind(null, null, postObj)); - } - }, next); - } - ], callback); - } - - Posts.updateFlagData = function (uid, pid, flagObj, callback) { - // Retrieve existing flag data to compare for history-saving purposes - var changes = []; - var changeset = {}; - var prop; - - Posts.getPostData(pid, function (err, postData) { - if (err) { - return callback(err); - } - - // Track new additions - for(prop in flagObj) { - if (flagObj.hasOwnProperty(prop) && !postData.hasOwnProperty('flag:' + prop) && flagObj[prop].length) { - changes.push(prop); - } - } - - // Track changed items - for(prop in postData) { - if ( - postData.hasOwnProperty(prop) && prop.startsWith('flag:') && - flagObj.hasOwnProperty(prop.slice(5)) && - postData[prop] !== flagObj[prop.slice(5)] - ) { - changes.push(prop.slice(5)); - } - } - - changeset = changes.reduce(function (memo, prop) { - memo['flag:' + prop] = flagObj[prop]; - return memo; - }, {}); - - // Append changes to history string - if (changes.length) { - try { - var history = JSON.parse(postData['flag:history'] || '[]'); - - changes.forEach(function (property) { - switch(property) { - case 'assignee': // intentional fall-through - case 'state': - history.unshift({ - uid: uid, - type: property, - value: flagObj[property], - timestamp: Date.now() - }); - break; - - case 'notes': - history.unshift({ - uid: uid, - type: property, - timestamp: Date.now() - }); - } - }); - - changeset['flag:history'] = JSON.stringify(history); - } catch (e) { - winston.warn('[posts/updateFlagData] Unable to deserialise post flag history, likely malformed data'); - } - } - - // Save flag data into post hash - if (changes.length) { - Posts.setPostFields(pid, changeset, callback); - } else { - setImmediate(callback); - } - }); - }; - - Posts.expandFlagHistory = function (posts, callback) { - // Expand flag history - async.map(posts, function (post, next) { - var history; - try { - history = JSON.parse(post['flag:history'] || '[]'); - } catch (e) { - winston.warn('[posts/getFlags] Unable to deserialise post flag history, likely malformed data'); - return callback(e); - } - - async.map(history, function (event, next) { - event.timestampISO = new Date(event.timestamp).toISOString(); - - async.parallel([ - function (next) { - user.getUserFields(event.uid, ['username', 'picture'], function (err, userData) { - if (err) { - return next(err); - } - - event.user = userData; - next(); - }); - }, - function (next) { - if (event.type === 'assignee') { - user.getUserField(parseInt(event.value, 10), 'username', function (err, username) { - if (err) { - return next(err); - } - - event.label = username || 'Unknown user'; - next(null); - }); - } else if (event.type === 'state') { - event.label = '[[topic:flag_manage_state_' + event.value + ']]'; - setImmediate(next); - } else { - setImmediate(next); - } - } - ], function (err) { - next(err, event); - }); - }, function (err, history) { - if (err) { - return next(err); - } - - post['flag:history'] = history; - next(null, post); - }); - }, callback); - }; -}; diff --git a/src/socket.io/posts/flag.js b/src/socket.io/posts/flag.js index 077b88bfc9..88b47058d1 100644 --- a/src/socket.io/posts/flag.js +++ b/src/socket.io/posts/flag.js @@ -12,6 +12,7 @@ var notifications = require('../../notifications'); var plugins = require('../../plugins'); var meta = require('../../meta'); var utils = require('../../../public/src/utils'); +var flags = require('../../flags'); module.exports = function (SocketPosts) { @@ -64,7 +65,7 @@ module.exports = function (SocketPosts) { flaggingUser = user.userData; flaggingUser.uid = socket.uid; - posts.flag(post, socket.uid, data.reason, next); + flags.create('post', post.pid, socket.uid, data.reason, next); }, function (next) { async.parallel({ @@ -119,7 +120,7 @@ module.exports = function (SocketPosts) { if (!isAdminOrGlobalModerator) { return next(new Error('[[no-privileges]]')); } - posts.dismissFlag(pid, next); + flags.dismiss(pid, next); } ], callback); }; @@ -133,7 +134,7 @@ module.exports = function (SocketPosts) { if (!isAdminOrGlobalModerator) { return next(new Error('[[no-privileges]]')); } - posts.dismissAllFlags(next); + flags.dismissAll(next); } ], callback); }; @@ -165,7 +166,7 @@ module.exports = function (SocketPosts) { return memo; }, payload); - posts.updateFlagData(socket.uid, data.pid, payload, next); + flags.update(socket.uid, data.pid, payload, next); } ], callback); }; diff --git a/src/upgrade.js b/src/upgrade.js index 79ffa6b5ee..2a605e84f9 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -462,8 +462,9 @@ Upgrade.upgrade = function (callback) { updatesMade = true; winston.info('[2016/04/29] Dismiss flags from deleted topics'); - var posts = require('./posts'), - topics = require('./topics'); + var posts = require('./posts'); + var topics = require('./topics'); + var flags = require('./flags'); var pids, tids; @@ -486,7 +487,7 @@ Upgrade.upgrade = function (callback) { }).filter(Boolean); winston.info('[2016/04/29] ' + toDismiss.length + ' dismissable flags found'); - async.each(toDismiss, posts.dismissFlag, next); + async.each(toDismiss, flags.dismiss, next); } ], function (err) { if (err) { diff --git a/src/user/admin.js b/src/user/admin.js index 8b5a6ebef4..5d2215980c 100644 --- a/src/user/admin.js +++ b/src/user/admin.js @@ -6,6 +6,7 @@ var db = require('../database'); var posts = require('../posts'); var plugins = require('../plugins'); var winston = require('winston'); +var flags = require('../flags'); module.exports = function (User) { @@ -62,7 +63,7 @@ module.exports = function (User) { } async.eachSeries(uids, function (uid, next) { - posts.dismissUserFlags(uid, next); + flags.dismissByUid(uid, next); }, callback); }; }; From 106502952a79ce18a7f0d1ce54ff036527ee955f Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 24 Nov 2016 11:56:57 -0500 Subject: [PATCH 002/316] fixed crash in flags page for now --- src/flags.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/flags.js b/src/flags.js index 1e6294aecb..89f42337d4 100644 --- a/src/flags.js +++ b/src/flags.js @@ -246,7 +246,7 @@ Flags.get = function (set, cid, uid, start, stop, callback) { }, function (pids, next) { if (cid) { - Posts.filterPidsByCid(pids, cid, next); + posts.filterPidsByCid(pids, cid, next); } else { process.nextTick(next, null, pids); } @@ -272,7 +272,7 @@ function getFlaggedPostsWithReasons(pids, uid, callback) { }, next); }, posts: function (next) { - Posts.getPostSummaryByPids(pids, uid, {stripTags: false, extraFields: ['flags', 'flag:assignee', 'flag:state', 'flag:notes', 'flag:history']}, next); + posts.getPostSummaryByPids(pids, uid, {stripTags: false, extraFields: ['flags', 'flag:assignee', 'flag:state', 'flag:notes', 'flag:history']}, next); } }, next); }, @@ -299,7 +299,7 @@ function getFlaggedPostsWithReasons(pids, uid, callback) { next(null, results.posts); }); }, - async.apply(Posts.expandFlagHistory), + async.apply(Flags.expandFlagHistory), function (posts, next) { // Parse out flag data into its own object inside each post hash async.map(posts, function (postObj, next) { @@ -355,7 +355,7 @@ Flags.update = function (uid, pid, flagObj, callback) { var changeset = {}; var prop; - Posts.getPostData(pid, function (err, postData) { + posts.getPostData(pid, function (err, postData) { if (err) { return callback(err); } @@ -417,7 +417,7 @@ Flags.update = function (uid, pid, flagObj, callback) { // Save flag data into post hash if (changes.length) { - Posts.setPostFields(pid, changeset, callback); + posts.setPostFields(pid, changeset, callback); } else { setImmediate(callback); } From 640df0379e3e3b818c9398da5a26b2585de550fd Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 25 Nov 2016 12:43:10 -0500 Subject: [PATCH 003/316] flag list page (#5232) --- public/language/en-GB/flags.json | 6 ++ src/controllers/mods.js | 9 ++- src/flags.js | 101 ++++++++++++++++++++++--------- src/routes/index.js | 2 +- 4 files changed, 87 insertions(+), 31 deletions(-) create mode 100644 public/language/en-GB/flags.json diff --git a/public/language/en-GB/flags.json b/public/language/en-GB/flags.json new file mode 100644 index 0000000000..de083196e7 --- /dev/null +++ b/public/language/en-GB/flags.json @@ -0,0 +1,6 @@ +{ + "quick-filters": "Quick Filters", + "reporter": "Reporter", + "reported-at": "Reported At", + "no-flags": "Hooray! No flags found." +} \ No newline at end of file diff --git a/src/controllers/mods.js b/src/controllers/mods.js index 0079412f87..2a1aad09dc 100644 --- a/src/controllers/mods.js +++ b/src/controllers/mods.js @@ -3,7 +3,8 @@ var async = require('async'); var user = require('../user'); -var adminFlagsController = require('./admin/flags'); +var flags = require('../flags'); +// var adminFlagsController = require('./admin/flags'); var modsController = {}; @@ -20,7 +21,11 @@ modsController.flagged = function (req, res, next) { res.locals.cids = results.moderatedCids; } - adminFlagsController.get(req, res, next); + flags.list({}, function(err, flags) { + res.render('flags', { + flags: flags + }); + }); }); }; diff --git a/src/flags.js b/src/flags.js index 89f42337d4..bdf1ac37bb 100644 --- a/src/flags.js +++ b/src/flags.js @@ -13,10 +13,71 @@ var utils = require('../public/src/utils'); var Flags = { _defaults: { - state: 'open' + state: 'open', + assignee: null } }; +Flags.list = function (filters, callback) { + if (typeof filters === 'function' && !callback) { + callback = filters; + filters = {}; + } + + async.waterfall([ + async.apply(db.getSortedSetRevRange.bind(db), 'flags:datetime', 0, 19), + function (flagIds, next) { + async.map(flagIds, function (flagId, next) { + async.waterfall([ + async.apply(db.getObject, 'flag:' + flagId), + function (flagObj, next) { + user.getUserFields(flagObj.uid, ['username', 'picture'], function (err, userObj) { + next(err, Object.assign(flagObj, { + user: { + username: userObj.username, + picture: userObj.picture, + 'icon:bgColor': userObj['icon:bgColor'], + 'icon:text': userObj['icon:text'] + } + })); + }) + } + ], function (err, flagObj) { + if (err) { + return next(err); + } + + switch(flagObj.state) { + case 'open': + flagObj.labelClass = 'info'; + break; + case 'wip': + flagObj.labelClass = 'warning'; + break; + case 'resolved': + flagObj.labelClass = 'success'; + break; + case 'rejected': + flagObj.labelClass = 'danger'; + break; + } + + next(null, Object.assign(flagObj, { + target_readable: flagObj.type.charAt(0).toUpperCase() + flagObj.type.slice(1) + ' ' + flagObj.id, + datetimeISO: new Date(parseInt(flagObj.datetime, 10)).toISOString() + })); + }); + }, next); + } + ], function (err, flags) { + if (err) { + return callback(err); + } + + return callback(null, flags); + }); +}; + Flags.create = function (type, id, uid, reason, callback) { async.waterfall([ function (next) { @@ -38,23 +99,22 @@ Flags.create = function (type, id, uid, reason, callback) { var flagId = utils.generateUUID(); async.parallel([ - async.apply(db.setObject.bind(db), 'flag:' + flagId, _.defaults({ - description: reason - }), Flags._defaults), - async.apply(db.sortedSetAdd.bind(db), 'flags:datetime', now, flagId) - ], function (err) { - if (err) { - return next(err); - } - }); + async.apply(db.setObject.bind(db), 'flag:' + flagId, Object.assign({}, Flags._defaults, { + type: type, + id: id, + description: reason, + uid: uid, + datetime: Date.now() + })), + async.apply(db.sortedSetAdd.bind(db), 'flags:datetime', Date.now(), flagId) + ], next); } ], function (err) { if (err) { return callback(err); } - console.log('done', arguments); - process.exit(); + callback(); }); // if (!parseInt(uid, 10) || !reason) { // return callback(); @@ -107,7 +167,7 @@ Flags.create = function (type, id, uid, reason, callback) { // ], next); // }, // function (data, next) { - // openNewFlag(post.pid, uid, next); + // openNewFlag(post.pid, uid, next); // removed, used to just update flag to open state if new flag // } // ], function (err) { // if (err) { @@ -118,21 +178,6 @@ Flags.create = function (type, id, uid, reason, callback) { // }); }; -function openNewFlag(pid, uid, callback) { - db.sortedSetScore('posts:flags:count', pid, function (err, count) { - if (err) { - return callback(err); - } - if (count === 1) { // Only update state on new flag - Flags.update(uid, pid, { - state: 'open' - }, callback); - } else { - callback(); - } - }); -} - Flags.exists = function (type, id, uid, callback) { db.isObjectField('flagHash:flagId', [type, id, uid].join(':'), callback); }; diff --git a/src/routes/index.js b/src/routes/index.js index f36ad1468a..0273160745 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -40,7 +40,7 @@ function mainRoutes(app, middleware, controllers) { } function modRoutes(app, middleware, controllers) { - setupPageRoute(app, '/posts/flags', middleware, [], controllers.mods.flagged); + setupPageRoute(app, '/flags', middleware, [], controllers.mods.flagged); } function globalModRoutes(app, middleware, controllers) { From 98a104564b511155cbf24a78406905b07f96ae74 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 25 Nov 2016 14:17:51 -0500 Subject: [PATCH 004/316] some light refactoring, details API (#5232) --- public/language/en-GB/flags.json | 1 + src/controllers/mods.js | 28 ++++++++-- src/flags.js | 92 ++++++++++++++++++++------------ src/routes/index.js | 3 +- 4 files changed, 86 insertions(+), 38 deletions(-) diff --git a/public/language/en-GB/flags.json b/public/language/en-GB/flags.json index de083196e7..ad8456ebf6 100644 --- a/public/language/en-GB/flags.json +++ b/public/language/en-GB/flags.json @@ -1,5 +1,6 @@ { "quick-filters": "Quick Filters", + "state": "State", "reporter": "Reporter", "reported-at": "Reported At", "no-flags": "Hooray! No flags found." diff --git a/src/controllers/mods.js b/src/controllers/mods.js index 2a1aad09dc..f5a6e9b06d 100644 --- a/src/controllers/mods.js +++ b/src/controllers/mods.js @@ -6,15 +6,19 @@ var user = require('../user'); var flags = require('../flags'); // var adminFlagsController = require('./admin/flags'); -var modsController = {}; +var modsController = { + flags: {} +}; -modsController.flagged = function (req, res, next) { +modsController.flags.list = function (req, res, next) { async.parallel({ isAdminOrGlobalMod: async.apply(user.isAdminOrGlobalMod, req.uid), moderatedCids: async.apply(user.getModeratedCids, req.uid) }, function (err, results) { - if (err || !(results.isAdminOrGlobalMod || !!results.moderatedCids.length)) { + if (err) { return next(err); + } else if (!(results.isAdminOrGlobalMod || !!results.moderatedCids.length)) { + return next(new Error('[[error:no-privileges]]')); } if (!results.isAdminOrGlobalMod && results.moderatedCids.length) { @@ -22,11 +26,27 @@ modsController.flagged = function (req, res, next) { } flags.list({}, function(err, flags) { - res.render('flags', { + res.render('flags/list', { flags: flags }); }); }); }; +modsController.flags.detail = function (req, res, next) { + async.parallel({ + isAdminOrGlobalMod: async.apply(user.isAdminOrGlobalMod, req.uid), + moderatedCids: async.apply(user.getModeratedCids, req.uid), + flagData: async.apply(flags.get, req.params.flagId) + }, function (err, results) { + if (err || !results.flagData) { + return next(err || new Error('[[error:invalid-data]]')); + } else if (!(results.isAdminOrGlobalMod || !!results.moderatedCids.length)) { + return next(new Error('[[error:no-privileges]]')); + } + + res.render('flags/detail', results.flagData); + }); +}; + module.exports = modsController; diff --git a/src/flags.js b/src/flags.js index bdf1ac37bb..8734f811a8 100644 --- a/src/flags.js +++ b/src/flags.js @@ -18,6 +18,30 @@ var Flags = { } }; +Flags.get = function (flagId, callback) { + async.waterfall([ + async.apply(async.parallel, { + base: async.apply(db.getObject.bind(db), 'flag:' + flagId), + history: async.apply(db.getSortedSetRevRange.bind(db), 'flag:' + flagId + ':history', 0, -1), + notes: async.apply(db.getSortedSetRevRange.bind(db), 'flag:' + flagId + ':notes', 0, -1) + }), + function (data, next) { + user.getUserFields(data.base.uid, ['username', 'picture'], function (err, userObj) { + next(err, Object.assign(data.base, { + history: data.history, + notes: data.notes, + reporter: { + username: userObj.username, + picture: userObj.picture, + 'icon:bgColor': userObj['icon:bgColor'], + 'icon:text': userObj['icon:text'] + } + })); + }); + } + ], callback); +}; + Flags.list = function (filters, callback) { if (typeof filters === 'function' && !callback) { callback = filters; @@ -33,7 +57,7 @@ Flags.list = function (filters, callback) { function (flagObj, next) { user.getUserFields(flagObj.uid, ['username', 'picture'], function (err, userObj) { next(err, Object.assign(flagObj, { - user: { + reporter: { username: userObj.username, picture: userObj.picture, 'icon:bgColor': userObj['icon:bgColor'], @@ -63,7 +87,7 @@ Flags.list = function (filters, callback) { } next(null, Object.assign(flagObj, { - target_readable: flagObj.type.charAt(0).toUpperCase() + flagObj.type.slice(1) + ' ' + flagObj.id, + target_readable: flagObj.type.charAt(0).toUpperCase() + flagObj.type.slice(1) + ' ' + flagObj.targetId, datetimeISO: new Date(parseInt(flagObj.datetime, 10)).toISOString() })); }); @@ -95,18 +119,19 @@ Flags.create = function (type, id, uid, reason, callback) { } }); }, - function (next) { - var flagId = utils.generateUUID(); - + async.apply(db.incrObjectField, 'global', 'nextFlagId'), + function (flagId, next) { async.parallel([ async.apply(db.setObject.bind(db), 'flag:' + flagId, Object.assign({}, Flags._defaults, { + flagId: flagId, type: type, - id: id, + targetId: id, description: reason, uid: uid, datetime: Date.now() })), - async.apply(db.sortedSetAdd.bind(db), 'flags:datetime', Date.now(), flagId) + async.apply(db.sortedSetAdd.bind(db), 'flags:datetime', Date.now(), flagId), + async.apply(db.setObjectField.bind(db), 'flagHash:flagId', [type, id, uid].join(':'), flagId) ], next); } ], function (err) { @@ -280,32 +305,33 @@ Flags.dismissByUid = function (uid, callback) { }); }; -Flags.get = function (set, cid, uid, start, stop, callback) { - async.waterfall([ - function (next) { - if (Array.isArray(set)) { - db.getSortedSetRevIntersect({sets: set, start: start, stop: -1, aggregate: 'MAX'}, next); - } else { - db.getSortedSetRevRange(set, start, -1, next); - } - }, - function (pids, next) { - if (cid) { - posts.filterPidsByCid(pids, cid, next); - } else { - process.nextTick(next, null, pids); - } - }, - function (pids, next) { - getFlaggedPostsWithReasons(pids, uid, next); - }, - function (posts, next) { - var count = posts.length; - var end = stop - start + 1; - next(null, {posts: posts.slice(0, stop === -1 ? undefined : end), count: count}); - } - ], callback); -}; +// This is the old method to get list of flags, supercede by Flags.list(); +// Flags.get = function (set, cid, uid, start, stop, callback) { +// async.waterfall([ +// function (next) { +// if (Array.isArray(set)) { +// db.getSortedSetRevIntersect({sets: set, start: start, stop: -1, aggregate: 'MAX'}, next); +// } else { +// db.getSortedSetRevRange(set, start, -1, next); +// } +// }, +// function (pids, next) { +// if (cid) { +// posts.filterPidsByCid(pids, cid, next); +// } else { +// process.nextTick(next, null, pids); +// } +// }, +// function (pids, next) { +// getFlaggedPostsWithReasons(pids, uid, next); +// }, +// function (posts, next) { +// var count = posts.length; +// var end = stop - start + 1; +// next(null, {posts: posts.slice(0, stop === -1 ? undefined : end), count: count}); +// } +// ], callback); +// }; function getFlaggedPostsWithReasons(pids, uid, callback) { async.waterfall([ diff --git a/src/routes/index.js b/src/routes/index.js index 0273160745..f71eac2c62 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -40,7 +40,8 @@ function mainRoutes(app, middleware, controllers) { } function modRoutes(app, middleware, controllers) { - setupPageRoute(app, '/flags', middleware, [], controllers.mods.flagged); + setupPageRoute(app, '/flags', middleware, [], controllers.mods.flags.list); + setupPageRoute(app, '/flags/:flagId', middleware, [], controllers.mods.flags.detail); } function globalModRoutes(app, middleware, controllers) { From 9f7c4befea24d4af92bfb60fecc065125f6b5ffe Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 25 Nov 2016 15:09:52 -0500 Subject: [PATCH 005/316] omg tests (#5232), and .create returns flag data now --- src/controllers/mods.js | 6 ++- src/flags.js | 23 +++++---- test/flags.js | 107 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 10 deletions(-) create mode 100644 test/flags.js diff --git a/src/controllers/mods.js b/src/controllers/mods.js index f5a6e9b06d..7ebb70d888 100644 --- a/src/controllers/mods.js +++ b/src/controllers/mods.js @@ -25,7 +25,11 @@ modsController.flags.list = function (req, res, next) { res.locals.cids = results.moderatedCids; } - flags.list({}, function(err, flags) { + flags.list({}, function (err, flags) { + if (err) { + return next(err); + } + res.render('flags/list', { flags: flags }); diff --git a/src/flags.js b/src/flags.js index 8734f811a8..d9f6aa136f 100644 --- a/src/flags.js +++ b/src/flags.js @@ -64,7 +64,7 @@ Flags.list = function (filters, callback) { 'icon:text': userObj['icon:text'] } })); - }) + }); } ], function (err, flagObj) { if (err) { @@ -110,6 +110,10 @@ Flags.create = function (type, id, uid, reason, callback) { async.apply(Flags.exists, type, id, uid), async.apply(Flags.targetExists, type, id) ], function (err, checks) { + if (err) { + return next(err); + } + if (checks[0]) { return next(new Error('[[error:already-flagged]]')); } else if (!checks[1]) { @@ -132,15 +136,16 @@ Flags.create = function (type, id, uid, reason, callback) { })), async.apply(db.sortedSetAdd.bind(db), 'flags:datetime', Date.now(), flagId), async.apply(db.setObjectField.bind(db), 'flagHash:flagId', [type, id, uid].join(':'), flagId) - ], next); - } - ], function (err) { - if (err) { - return callback(err); - } + ], function (err, data) { + if (err) { + return next(err); + } - callback(); - }); + next(null, flagId); + }); + }, + async.apply(Flags.get) + ], callback); // if (!parseInt(uid, 10) || !reason) { // return callback(); // } diff --git a/test/flags.js b/test/flags.js new file mode 100644 index 0000000000..ed879fba49 --- /dev/null +++ b/test/flags.js @@ -0,0 +1,107 @@ +'use strict'; +/*globals require, before, after, describe, it*/ + +var assert = require('assert'); +var async = require('async'); + +var db = require('./mocks/databasemock'); +var Flags = require('../src/flags'); +var Categories = require('../src/categories'); +var Topics = require('../src/topics'); +var User = require('../src/user'); + +describe('Flags', function () { + before(function (done) { + // Create some stuff to flag + async.waterfall([ + async.apply(User.create, {username: 'testUser', password: 'abcdef', email: 'b@c.com'}), + function (uid, next) { + Categories.create({ + name: 'test category' + }, function (err, category) { + if (err) { + return done(err); + } + + Topics.post({ + cid: category.cid, + uid: uid, + title: 'Topic to flag', + content: 'This is flaggable content' + }, next); + }); + } + ], done); + }); + + describe('.create()', function () { + it('should create a flag and return its data', function (done) { + Flags.create('post', 1, 1, 'Test flag', function (err, flagData) { + assert.ifError(err); + var compare = { + flagId: 1, + uid: 1, + targetId: 1, + type: 'post', + description: 'Test flag', + state: 'open' + }; + + for(var key in compare) { + if (compare.hasOwnProperty(key)) { + assert.ok(flagData[key]); + assert.strictEqual(flagData[key], compare[key]); + } + } + + done(); + }); + }); + }); + + describe('.get()', function () { + it('should retrieve and display a flag\'s data', function (done) { + Flags.get(1, function (err, flagData) { + assert.ifError(err); + var compare = { + flagId: 1, + uid: 1, + targetId: 1, + type: 'post', + description: 'Test flag', + state: 'open' + }; + + for(var key in compare) { + if (compare.hasOwnProperty(key)) { + assert.ok(flagData[key]); + assert.strictEqual(flagData[key], compare[key]); + } + } + + done(); + }); + }); + }); + + describe('.list()', function () { + it('should show a list of flags (with one item)', function (done) { + Flags.list({}, function (err, flags) { + assert.ifError(err); + assert.ok(Array.isArray(flags)); + assert.equal(flags.length, 1); + + Flags.get(flags[0].flagId, function (err, flagData) { + assert.ifError(err); + assert.equal(flags[0].flagId, flagData.flagId); + assert.equal(flags[0].description, flagData.description); + done(); + }); + }); + }); + }); + + after(function (done) { + db.emptydb(done); + }); +}); From fceb5cc86b600087bff8b2adc983e70c3fbb725e Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 29 Nov 2016 22:10:51 -0500 Subject: [PATCH 006/316] more work on flags detail pages (#5232) --- public/language/en-GB/flags.json | 16 +++++++++----- public/language/en-GB/topic.json | 5 ----- src/flags.js | 38 +++++++++++++++++++++++++++----- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/public/language/en-GB/flags.json b/public/language/en-GB/flags.json index ad8456ebf6..bf22e68df6 100644 --- a/public/language/en-GB/flags.json +++ b/public/language/en-GB/flags.json @@ -1,7 +1,13 @@ { - "quick-filters": "Quick Filters", - "state": "State", - "reporter": "Reporter", - "reported-at": "Reported At", - "no-flags": "Hooray! No flags found." + "quick-filters": "Quick Filters", + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "no-flags": "Hooray! No flags found.", + + "state": "State", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected" } \ No newline at end of file diff --git a/public/language/en-GB/topic.json b/public/language/en-GB/topic.json index 29a85c15cc..885afe5d62 100644 --- a/public/language/en-GB/topic.json +++ b/public/language/en-GB/topic.json @@ -41,11 +41,6 @@ "flag_manage_history": "Action History", "flag_manage_no_history": "No event history to report", "flag_manage_assignee": "Assignee", - "flag_manage_state": "State", - "flag_manage_state_open": "New/Open", - "flag_manage_state_wip": "Work in Progress", - "flag_manage_state_resolved": "Resolved", - "flag_manage_state_rejected": "Rejected", "flag_manage_notes": "Shared Notes", "flag_manage_update": "Update Flag Status", "flag_manage_history_assignee": "Assigned to %1", diff --git a/src/flags.js b/src/flags.js index d9f6aa136f..65ab029e2a 100644 --- a/src/flags.js +++ b/src/flags.js @@ -20,21 +20,30 @@ var Flags = { Flags.get = function (flagId, callback) { async.waterfall([ + // First stage async.apply(async.parallel, { base: async.apply(db.getObject.bind(db), 'flag:' + flagId), history: async.apply(db.getSortedSetRevRange.bind(db), 'flag:' + flagId + ':history', 0, -1), notes: async.apply(db.getSortedSetRevRange.bind(db), 'flag:' + flagId + ':notes', 0, -1) }), function (data, next) { - user.getUserFields(data.base.uid, ['username', 'picture'], function (err, userObj) { + // Second stage + async.parallel({ + userObj: async.apply(user.getUserFields, data.base.uid, ['username', 'picture']), + targetObj: async.apply(Flags.getTarget, data.base.type, data.base.targetId, data.base.uid) + }, function (err, payload) { + // Final object return construction next(err, Object.assign(data.base, { + datetimeISO: new Date(data.base.datetime).toISOString(), + target_readable: data.base.type.charAt(0).toUpperCase() + data.base.type.slice(1) + ' ' + data.base.targetId, + target: payload.targetObj, history: data.history, notes: data.notes, reporter: { - username: userObj.username, - picture: userObj.picture, - 'icon:bgColor': userObj['icon:bgColor'], - 'icon:text': userObj['icon:text'] + username: payload.userObj.username, + picture: payload.userObj.picture, + 'icon:bgColor': payload.userObj['icon:bgColor'], + 'icon:text': payload.userObj['icon:text'] } })); }); @@ -102,6 +111,25 @@ Flags.list = function (filters, callback) { }); }; +Flags.getTarget = function (type, id, uid, callback) { + switch (type) { + case 'post': + async.waterfall([ + async.apply(posts.getPostsByPids, [id], uid), + function (posts, next) { + topics.addPostData(posts, uid, next); + } + ], function (err, posts) { + callback(err, posts[0]); + }); + break; + + case 'user': + user.getUsersData(id, callback); + break; + } +}; + Flags.create = function (type, id, uid, reason, callback) { async.waterfall([ function (next) { From 77809b2b52002d62babde09b10067e93c845a1fe Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 30 Nov 2016 20:26:45 -0500 Subject: [PATCH 007/316] added avatar-xl size --- public/less/generics.less | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/public/less/generics.less b/public/less/generics.less index 8533dd33b9..5b3c8eff4e 100644 --- a/public/less/generics.less +++ b/public/less/generics.less @@ -107,6 +107,12 @@ } &.avatar-lg { + width: 64px; + height: 64px; + .user-icon-style(64px, 4rem); + } + + &.avatar-xl { width: 128px; height: 128px; .user-icon-style(128px, 7.5rem); From c5c2d27180a5ab39edd87d3fd83a0b86a4ff85a3 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 30 Nov 2016 20:34:06 -0500 Subject: [PATCH 008/316] flag assignees, state, notes WIP, #5232 --- public/language/en-GB/flags.json | 7 +++++- public/language/en-GB/topic.json | 4 ---- src/controllers/mods.js | 7 ++++-- src/flags.js | 40 +++++++++++++++++++++++++++++++- src/user/data.js | 13 +++++++++++ 5 files changed, 63 insertions(+), 8 deletions(-) diff --git a/public/language/en-GB/flags.json b/public/language/en-GB/flags.json index bf22e68df6..d266e2c4db 100644 --- a/public/language/en-GB/flags.json +++ b/public/language/en-GB/flags.json @@ -4,10 +4,15 @@ "reporter": "Reporter", "reported-at": "Reported At", "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "notes": "Flag Notes", + "add-note": "Add Note", "state": "State", "state-open": "New/Open", "state-wip": "Work in Progress", "state-resolved": "Resolved", - "state-rejected": "Rejected" + "state-rejected": "Rejected", + "no-assignee": "Not Assigned" } \ No newline at end of file diff --git a/public/language/en-GB/topic.json b/public/language/en-GB/topic.json index 885afe5d62..ce7f35f816 100644 --- a/public/language/en-GB/topic.json +++ b/public/language/en-GB/topic.json @@ -37,12 +37,8 @@ "flag_title": "Flag this post for moderation", "flag_success": "This post has been flagged for moderation.", - "flag_manage_title": "Flagged post in %1", "flag_manage_history": "Action History", "flag_manage_no_history": "No event history to report", - "flag_manage_assignee": "Assignee", - "flag_manage_notes": "Shared Notes", - "flag_manage_update": "Update Flag Status", "flag_manage_history_assignee": "Assigned to %1", "flag_manage_history_state": "Updated state to %1", "flag_manage_history_notes": "Updated flag notes", diff --git a/src/controllers/mods.js b/src/controllers/mods.js index 7ebb70d888..012ffde3c6 100644 --- a/src/controllers/mods.js +++ b/src/controllers/mods.js @@ -41,7 +41,8 @@ modsController.flags.detail = function (req, res, next) { async.parallel({ isAdminOrGlobalMod: async.apply(user.isAdminOrGlobalMod, req.uid), moderatedCids: async.apply(user.getModeratedCids, req.uid), - flagData: async.apply(flags.get, req.params.flagId) + flagData: async.apply(flags.get, req.params.flagId), + assignees: async.apply(user.getAdminsandGlobalModsandModerators) }, function (err, results) { if (err || !results.flagData) { return next(err || new Error('[[error:invalid-data]]')); @@ -49,7 +50,9 @@ modsController.flags.detail = function (req, res, next) { return next(new Error('[[error:no-privileges]]')); } - res.render('flags/detail', results.flagData); + res.render('flags/detail', Object.assign(results.flagData, { + assignees: results.assignees + })); }); }; diff --git a/src/flags.js b/src/flags.js index 65ab029e2a..b88dac6b2f 100644 --- a/src/flags.js +++ b/src/flags.js @@ -10,6 +10,7 @@ var analytics = require('./analytics'); var topics = require('./topics'); var posts = require('./posts'); var utils = require('../public/src/utils'); +var _ = require('underscore'); var Flags = { _defaults: { @@ -24,7 +25,7 @@ Flags.get = function (flagId, callback) { async.apply(async.parallel, { base: async.apply(db.getObject.bind(db), 'flag:' + flagId), history: async.apply(db.getSortedSetRevRange.bind(db), 'flag:' + flagId + ':history', 0, -1), - notes: async.apply(db.getSortedSetRevRange.bind(db), 'flag:' + flagId + ':notes', 0, -1) + notes: async.apply(Flags.getNotes, flagId) }), function (data, next) { // Second stage @@ -130,6 +131,43 @@ Flags.getTarget = function (type, id, uid, callback) { } }; +Flags.getNotes = function (flagId, callback) { + async.waterfall([ + async.apply(db.getSortedSetRevRangeWithScores.bind(db), 'flag:' + flagId + ':notes', 0, -1), + function (notes, next) { + var uids = []; + var noteObj; + notes = notes.map(function (note) { + try { + noteObj = JSON.parse(note.value); + uids.push(noteObj[0]); + return { + uid: noteObj[0], + content: noteObj[1], + datetime: note.score, + datetimeISO: new Date(note.score).toISOString() + }; + } catch (e) { + return next(e); + } + }); + next(null, notes, uids); + }, + function (notes, uids, next) { + user.getUsersData(uids, function (err, users) { + if (err) { + return next(err); + } + + next(null, notes.map(function (note, idx) { + note.user = users[idx]; + return note; + })); + }); + } + ], callback); +}; + Flags.create = function (type, id, uid, reason, callback) { async.waterfall([ function (next) { diff --git a/src/user/data.js b/src/user/data.js index cbaf066ded..1e10704c73 100644 --- a/src/user/data.js +++ b/src/user/data.js @@ -80,6 +80,15 @@ module.exports = function (User) { return callback(null, []); } + // Eliminate duplicates and build ref table + uids = uids.filter(function (uid, index) { + return index === uids.indexOf(uid); + }); + var ref = uids.reduce(function (memo, cur, idx) { + memo[cur] = idx; + return memo; + }, {}); + var keys = uids.map(function (uid) { return 'user:' + uid; }); @@ -89,6 +98,10 @@ module.exports = function (User) { return callback(err); } + users = uids.map(function (uid) { + return users[ref[uid]]; + }); + modifyUserData(users, [], callback); }); }; From e9ff605a2024006bd7c8c451b057981cea58bb2f Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 1 Dec 2016 09:24:49 -0500 Subject: [PATCH 009/316] some more tests for #5232 --- src/flags.js | 4 +++- test/flags.js | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/flags.js b/src/flags.js index b88dac6b2f..8be774b467 100644 --- a/src/flags.js +++ b/src/flags.js @@ -126,7 +126,9 @@ Flags.getTarget = function (type, id, uid, callback) { break; case 'user': - user.getUsersData(id, callback); + user.getUsersData([id], function (err, users) { + callback(err, users ? users[0] : undefined); + }); break; } }; diff --git a/test/flags.js b/test/flags.js index ed879fba49..c5e69aa973 100644 --- a/test/flags.js +++ b/test/flags.js @@ -101,6 +101,48 @@ describe('Flags', function () { }); }); + describe('.getTarget()', function() { + it('should return a post\'s data if queried with type "post"', function (done) { + Flags.getTarget('post', 1, 1, function (err, data) { + assert.ifError(err); + var compare = { + uid: 1, + pid: 1, + content: 'This is flaggable content' + }; + + for(var key in compare) { + if (compare.hasOwnProperty(key)) { + assert.ok(data[key]); + assert.strictEqual(data[key], compare[key]); + } + } + + done(); + }); + }); + + it('should return a user\'s data if queried with type "user"', function (done) { + Flags.getTarget('user', 1, 1, function (err, data) { + assert.ifError(err); + var compare = { + uid: 1, + username: 'testUser', + email: 'b@c.com' + }; + + for(var key in compare) { + if (compare.hasOwnProperty(key)) { + assert.ok(data[key]); + assert.strictEqual(data[key], compare[key]); + } + } + + done(); + }); + }); + });; + after(function (done) { db.emptydb(done); }); From 888c120e08e7c725c820a302aac5d0e766254114 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 1 Dec 2016 09:42:24 -0500 Subject: [PATCH 010/316] removed some unneeded methods for #5232 --- src/flags.js | 118 --------------------------------------------------- 1 file changed, 118 deletions(-) diff --git a/src/flags.js b/src/flags.js index 8be774b467..99039aa175 100644 --- a/src/flags.js +++ b/src/flags.js @@ -292,11 +292,6 @@ Flags.targetExists = function (type, id, callback) { } }; -/* new signature (type, id, uid, callback) */ -Flags.isFlaggedByUser = function (pid, uid, callback) { - db.isSortedSetMember('pid:' + pid + ':flag:uids', uid, callback); -}; - Flags.dismiss = function (pid, callback) { async.waterfall([ function (next) { @@ -378,119 +373,6 @@ Flags.dismissByUid = function (uid, callback) { }); }; -// This is the old method to get list of flags, supercede by Flags.list(); -// Flags.get = function (set, cid, uid, start, stop, callback) { -// async.waterfall([ -// function (next) { -// if (Array.isArray(set)) { -// db.getSortedSetRevIntersect({sets: set, start: start, stop: -1, aggregate: 'MAX'}, next); -// } else { -// db.getSortedSetRevRange(set, start, -1, next); -// } -// }, -// function (pids, next) { -// if (cid) { -// posts.filterPidsByCid(pids, cid, next); -// } else { -// process.nextTick(next, null, pids); -// } -// }, -// function (pids, next) { -// getFlaggedPostsWithReasons(pids, uid, next); -// }, -// function (posts, next) { -// var count = posts.length; -// var end = stop - start + 1; -// next(null, {posts: posts.slice(0, stop === -1 ? undefined : end), count: count}); -// } -// ], callback); -// }; - -function getFlaggedPostsWithReasons(pids, uid, callback) { - async.waterfall([ - function (next) { - async.parallel({ - uidsReasons: function (next) { - async.map(pids, function (pid, next) { - db.getSortedSetRange('pid:' + pid + ':flag:uid:reason', 0, -1, next); - }, next); - }, - posts: function (next) { - posts.getPostSummaryByPids(pids, uid, {stripTags: false, extraFields: ['flags', 'flag:assignee', 'flag:state', 'flag:notes', 'flag:history']}, next); - } - }, next); - }, - function (results, next) { - async.map(results.uidsReasons, function (uidReasons, next) { - async.map(uidReasons, function (uidReason, next) { - var uid = uidReason.split(':')[0]; - var reason = uidReason.substr(uidReason.indexOf(':') + 1); - user.getUserFields(uid, ['username', 'userslug', 'picture'], function (err, userData) { - next(err, {user: userData, reason: reason}); - }); - }, next); - }, function (err, reasons) { - if (err) { - return callback(err); - } - - results.posts.forEach(function (post, index) { - if (post) { - post.flagReasons = reasons[index]; - } - }); - - next(null, results.posts); - }); - }, - async.apply(Flags.expandFlagHistory), - function (posts, next) { - // Parse out flag data into its own object inside each post hash - async.map(posts, function (postObj, next) { - for(var prop in postObj) { - postObj.flagData = postObj.flagData || {}; - - if (postObj.hasOwnProperty(prop) && prop.startsWith('flag:')) { - postObj.flagData[prop.slice(5)] = postObj[prop]; - - if (prop === 'flag:state') { - switch(postObj[prop]) { - case 'open': - postObj.flagData.labelClass = 'info'; - break; - case 'wip': - postObj.flagData.labelClass = 'warning'; - break; - case 'resolved': - postObj.flagData.labelClass = 'success'; - break; - case 'rejected': - postObj.flagData.labelClass = 'danger'; - break; - } - } - - delete postObj[prop]; - } - } - - if (postObj.flagData.assignee) { - user.getUserFields(parseInt(postObj.flagData.assignee, 10), ['username', 'picture'], function (err, userData) { - if (err) { - return next(err); - } - - postObj.flagData.assigneeUser = userData; - next(null, postObj); - }); - } else { - setImmediate(next.bind(null, null, postObj)); - } - }, next); - } - ], callback); -} - // New method signature (type, id, flagObj, callback) and name (.update()) // uid used in history string, which should be rewritten too. Flags.update = function (uid, pid, flagObj, callback) { From 709a7ff7f00df8fd70c5308337f9bd774d05f759 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 1 Dec 2016 11:04:37 -0500 Subject: [PATCH 011/316] fixed issue with getUsersData not actually returning the same number of elements as was passed-in --- src/user/data.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/user/data.js b/src/user/data.js index 1e10704c73..2e5bfb2218 100644 --- a/src/user/data.js +++ b/src/user/data.js @@ -81,15 +81,15 @@ module.exports = function (User) { } // Eliminate duplicates and build ref table - uids = uids.filter(function (uid, index) { + var uniqueUids = uids.filter(function (uid, index) { return index === uids.indexOf(uid); }); - var ref = uids.reduce(function (memo, cur, idx) { + var ref = uniqueUids.reduce(function (memo, cur, idx) { memo[cur] = idx; return memo; }, {}); - var keys = uids.map(function (uid) { + var keys = uniqueUids.map(function (uid) { return 'user:' + uid; }); From d9d60c20bd743abd0a027efef474916278e3d793 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 1 Dec 2016 11:42:06 -0500 Subject: [PATCH 012/316] flag updating and note appending, #5232 --- public/language/en-GB/flags.json | 4 +- public/src/client/flags/detail.js | 60 +++++++++++ public/src/client/topic/flag.js | 2 +- src/flags.js | 156 ++++++--------------------- src/socket.io/flags.js | 167 ++++++++++++++++++++++++++++ src/socket.io/index.js | 6 +- src/socket.io/posts.js | 1 - src/socket.io/posts/flag.js | 173 ------------------------------ 8 files changed, 270 insertions(+), 299 deletions(-) create mode 100644 public/src/client/flags/detail.js create mode 100644 src/socket.io/flags.js delete mode 100644 src/socket.io/posts/flag.js diff --git a/public/language/en-GB/flags.json b/public/language/en-GB/flags.json index d266e2c4db..0c1f32b88e 100644 --- a/public/language/en-GB/flags.json +++ b/public/language/en-GB/flags.json @@ -14,5 +14,7 @@ "state-wip": "Work in Progress", "state-resolved": "Resolved", "state-rejected": "Rejected", - "no-assignee": "Not Assigned" + "no-assignee": "Not Assigned", + "updated": "Flag Details Updated", + "note-added": "Note Added" } \ No newline at end of file diff --git a/public/src/client/flags/detail.js b/public/src/client/flags/detail.js new file mode 100644 index 0000000000..d23a1941b5 --- /dev/null +++ b/public/src/client/flags/detail.js @@ -0,0 +1,60 @@ +'use strict'; + +/* globals define */ + +define('forum/flags/detail', ['components'], function (components) { + var Flags = {}; + + Flags.init = function () { + // Update attributes + $('#state').val(ajaxify.data.state).removeAttr('disabled'); + $('#assignee').val(ajaxify.data.assignee).removeAttr('disabled'); + + $('[data-action]').on('click', function () { + var action = this.getAttribute('data-action'); + + switch (action) { + case 'update': + socket.emit('flags.update', { + flagId: ajaxify.data.flagId, + data: $('#attributes').serializeArray() + }, function (err) { + if (err) { + return app.alertError(err.message); + } else { + app.alertSuccess('[[flags:updated]]'); + } + }); + break; + + case 'appendNote': + socket.emit('flags.appendNote', { + flagId: ajaxify.data.flagId, + note: document.getElementById('note').value + }, function (err, notes) { + if (err) { + return app.alertError(err.message); + } else { + app.alertSuccess('[[flags:note-added]]'); + Flags.reloadNotes(notes); + } + }); + break; + } + }); + }; + + Flags.reloadNotes = function (notes) { + templates.parse('flags/detail', 'notes', { + notes: notes + }, function (html) { + var wrapperEl = components.get('flag/notes'); + wrapperEl.empty(); + wrapperEl.html(html); + wrapperEl.find('span.timeago').timeago(); + document.getElementById('note').value = ''; + }); + }; + + return Flags; +}); diff --git a/public/src/client/topic/flag.js b/public/src/client/topic/flag.js index 78b1dd5d2a..046585ae68 100644 --- a/public/src/client/topic/flag.js +++ b/public/src/client/topic/flag.js @@ -48,7 +48,7 @@ define('forum/topic/flag', [], function () { if (!pid || !reason) { return; } - socket.emit('posts.flag', {pid: pid, reason: reason}, function (err) { + socket.emit('flags.create', {pid: pid, reason: reason}, function (err) { if (err) { return app.alertError(err.message); } diff --git a/src/flags.js b/src/flags.js index 99039aa175..323a2a1e07 100644 --- a/src/flags.js +++ b/src/flags.js @@ -373,138 +373,52 @@ Flags.dismissByUid = function (uid, callback) { }); }; -// New method signature (type, id, flagObj, callback) and name (.update()) -// uid used in history string, which should be rewritten too. -Flags.update = function (uid, pid, flagObj, callback) { +Flags.update = function (flagId, uid, changeset, callback) { // Retrieve existing flag data to compare for history-saving purposes - var changes = []; - var changeset = {}; - var prop; + var fields = ['state', 'assignee']; - posts.getPostData(pid, function (err, postData) { - if (err) { - return callback(err); - } - - // Track new additions - for(prop in flagObj) { - if (flagObj.hasOwnProperty(prop) && !postData.hasOwnProperty('flag:' + prop) && flagObj[prop].length) { - changes.push(prop); - } - } - - // Track changed items - for(prop in postData) { - if ( - postData.hasOwnProperty(prop) && prop.startsWith('flag:') && - flagObj.hasOwnProperty(prop.slice(5)) && - postData[prop] !== flagObj[prop.slice(5)] - ) { - changes.push(prop.slice(5)); - } - } - - changeset = changes.reduce(function (memo, prop) { - memo['flag:' + prop] = flagObj[prop]; - return memo; - }, {}); - - // Append changes to history string - if (changes.length) { - try { - var history = JSON.parse(postData['flag:history'] || '[]'); - - changes.forEach(function (property) { - switch(property) { - case 'assignee': // intentional fall-through - case 'state': - history.unshift({ - uid: uid, - type: property, - value: flagObj[property], - timestamp: Date.now() - }); - break; - - case 'notes': - history.unshift({ - uid: uid, - type: property, - timestamp: Date.now() - }); + async.waterfall([ + async.apply(db.getObjectFields.bind(db), 'flag:' + flagId, fields), + function (current, next) { + for(var prop in changeset) { + if (changeset.hasOwnProperty(prop)) { + if (current[prop] === changeset[prop]) { + delete changeset[prop]; } - }); + } + } - changeset['flag:history'] = JSON.stringify(history); - } catch (e) { - winston.warn('[flags/update] Unable to deserialise post flag history, likely malformed data'); + if (!Object.keys(changeset).length) { + // No changes + return next(); } - } - // Save flag data into post hash - if (changes.length) { - posts.setPostFields(pid, changeset, callback); - } else { - setImmediate(callback); + async.parallel([ + // Save new object to db (upsert) + async.apply(db.setObject, 'flag:' + flagId, changeset), + // Append history + async.apply(Flags.appendHistory, flagId, uid, Object.keys(changeset)) + ], next); } - }); + ], callback); }; -// To be rewritten and deprecated -Flags.expandFlagHistory = function (posts, callback) { - // Expand flag history - async.map(posts, function (post, next) { - var history; - try { - history = JSON.parse(post['flag:history'] || '[]'); - } catch (e) { - winston.warn('[flags/get] Unable to deserialise post flag history, likely malformed data'); - return callback(e); - } - - async.map(history, function (event, next) { - event.timestampISO = new Date(event.timestamp).toISOString(); - - async.parallel([ - function (next) { - user.getUserFields(event.uid, ['username', 'picture'], function (err, userData) { - if (err) { - return next(err); - } +Flags.appendHistory = function (flagId, uid, changeset, callback) { + return callback(); +}; - event.user = userData; - next(); - }); - }, - function (next) { - if (event.type === 'assignee') { - user.getUserField(parseInt(event.value, 10), 'username', function (err, username) { - if (err) { - return next(err); - } - - event.label = username || 'Unknown user'; - next(null); - }); - } else if (event.type === 'state') { - event.label = '[[topic:flag_manage_state_' + event.value + ']]'; - setImmediate(next); - } else { - setImmediate(next); - } - } - ], function (err) { - next(err, event); - }); - }, function (err, history) { - if (err) { - return next(err); - } +Flags.appendNote = function (flagId, uid, note, callback) { + var payload; + try { + payload = JSON.stringify([uid, note]); + } catch (e) { + return callback(e); + } - post['flag:history'] = history; - next(null, post); - }); - }, callback); + async.waterfall([ + async.apply(db.sortedSetAdd, 'flag:' + flagId + ':notes', Date.now(), payload), + async.apply(Flags.getNotes, flagId) + ], callback); }; module.exports = Flags; \ No newline at end of file diff --git a/src/socket.io/flags.js b/src/socket.io/flags.js new file mode 100644 index 0000000000..28db7551a6 --- /dev/null +++ b/src/socket.io/flags.js @@ -0,0 +1,167 @@ +'use strict'; + +var async = require('async'); +var S = require('string'); + +var user = require('../user'); +var groups = require('../groups'); +var posts = require('../posts'); +var topics = require('../topics'); +var privileges = require('../privileges'); +var notifications = require('../notifications'); +var plugins = require('../plugins'); +var meta = require('../meta'); +var utils = require('../../public/src/utils'); +var flags = require('../flags'); + +var SocketFlags = {}; + +SocketFlags.create = function (socket, data, callback) { + if (!socket.uid) { + return callback(new Error('[[error:not-logged-in]]')); + } + + if (!data || !data.pid || !data.reason) { + return callback(new Error('[[error:invalid-data]]')); + } + + var flaggingUser = {}; + var post; + + async.waterfall([ + function (next) { + posts.getPostFields(data.pid, ['pid', 'tid', 'uid', 'content', 'deleted'], next); + }, + function (postData, next) { + if (parseInt(postData.deleted, 10) === 1) { + return next(new Error('[[error:post-deleted]]')); + } + + post = postData; + topics.getTopicFields(post.tid, ['title', 'cid'], next); + }, + function (topicData, next) { + post.topic = topicData; + + async.parallel({ + isAdminOrMod: function (next) { + privileges.categories.isAdminOrMod(post.topic.cid, socket.uid, next); + }, + userData: function (next) { + user.getUserFields(socket.uid, ['username', 'reputation', 'banned'], next); + } + }, next); + }, + function (user, next) { + var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 1; + if (!user.isAdminOrMod && parseInt(user.userData.reputation, 10) < minimumReputation) { + return next(new Error('[[error:not-enough-reputation-to-flag]]')); + } + + if (parseInt(user.banned, 10) === 1) { + return next(new Error('[[error:user-banned]]')); + } + + flaggingUser = user.userData; + flaggingUser.uid = socket.uid; + + flags.create('post', post.pid, socket.uid, data.reason, next); + }, + function (next) { + async.parallel({ + post: function (next) { + posts.parsePost(post, next); + }, + admins: function (next) { + groups.getMembers('administrators', 0, -1, next); + }, + globalMods: function (next) { + groups.getMembers('Global Moderators', 0, -1, next); + }, + moderators: function (next) { + groups.getMembers('cid:' + post.topic.cid + ':privileges:mods', 0, -1, next); + } + }, next); + }, + function (results, next) { + var title = S(post.topic.title).decodeHTMLEntities().s; + var titleEscaped = title.replace(/%/g, '%').replace(/,/g, ','); + + notifications.create({ + bodyShort: '[[notifications:user_flagged_post_in, ' + flaggingUser.username + ', ' + titleEscaped + ']]', + bodyLong: post.content, + pid: data.pid, + path: '/post/' + data.pid, + nid: 'post_flag:' + data.pid + ':uid:' + socket.uid, + from: socket.uid, + mergeId: 'notifications:user_flagged_post_in|' + data.pid, + topicTitle: post.topic.title + }, function (err, notification) { + if (err || !notification) { + return next(err); + } + + plugins.fireHook('action:post.flag', {post: post, reason: data.reason, flaggingUser: flaggingUser}); + notifications.push(notification, results.admins.concat(results.moderators).concat(results.globalMods), next); + }); + } + ], callback); +}; + +SocketFlags.update = function (socket, data, callback) { + if (!data || !(data.flagId && data.data)) { + return callback(new Error('[[error:invalid-data]]')); + } + + var payload = {}; + + async.waterfall([ + function (next) { + async.parallel([ + async.apply(user.isAdminOrGlobalMod, socket.uid), + async.apply(user.isModeratorOfAnyCategory, socket.uid) + ], function (err, results) { + next(err, results[0] || results[1]); + }); + }, + function (allowed, next) { + if (!allowed) { + return next(new Error('[[no-privileges]]')); + } + + // Translate form data into object + payload = data.data.reduce(function (memo, cur) { + memo[cur.name] = cur.value; + return memo; + }, payload); + + flags.update(data.flagId, socket.uid, payload, next); + } + ], callback); +}; + +SocketFlags.appendNote = function (socket, data, callback) { + if (!data || !(data.flagId && data.note)) { + return callback(new Error('[[error:invalid-data]]')); + } + + async.waterfall([ + function (next) { + async.parallel([ + async.apply(user.isAdminOrGlobalMod, socket.uid), + async.apply(user.isModeratorOfAnyCategory, socket.uid) + ], function (err, results) { + next(err, results[0] || results[1]); + }); + }, + function (allowed, next) { + if (!allowed) { + return next(new Error('[[no-privileges]]')); + } + + flags.appendNote(data.flagId, socket.uid, data.note, next); + } + ], callback); +}; + +module.exports = SocketFlags; diff --git a/src/socket.io/index.js b/src/socket.io/index.js index 38e73c1cc1..43157f19ad 100644 --- a/src/socket.io/index.js +++ b/src/socket.io/index.js @@ -123,8 +123,10 @@ var ratelimit = require('../middleware/ratelimit'); } function requireModules() { - var modules = ['admin', 'categories', 'groups', 'meta', 'modules', - 'notifications', 'plugins', 'posts', 'topics', 'user', 'blacklist' + var modules = [ + 'admin', 'categories', 'groups', 'meta', 'modules', + 'notifications', 'plugins', 'posts', 'topics', 'user', + 'blacklist', 'flags' ]; modules.forEach(function (module) { diff --git a/src/socket.io/posts.js b/src/socket.io/posts.js index fe729a5c11..b5309dd3cf 100644 --- a/src/socket.io/posts.js +++ b/src/socket.io/posts.js @@ -20,7 +20,6 @@ require('./posts/move')(SocketPosts); require('./posts/votes')(SocketPosts); require('./posts/bookmarks')(SocketPosts); require('./posts/tools')(SocketPosts); -require('./posts/flag')(SocketPosts); SocketPosts.reply = function (socket, data, callback) { if (!data || !data.tid || !data.content) { diff --git a/src/socket.io/posts/flag.js b/src/socket.io/posts/flag.js deleted file mode 100644 index 88b47058d1..0000000000 --- a/src/socket.io/posts/flag.js +++ /dev/null @@ -1,173 +0,0 @@ -'use strict'; - -var async = require('async'); -var S = require('string'); - -var user = require('../../user'); -var groups = require('../../groups'); -var posts = require('../../posts'); -var topics = require('../../topics'); -var privileges = require('../../privileges'); -var notifications = require('../../notifications'); -var plugins = require('../../plugins'); -var meta = require('../../meta'); -var utils = require('../../../public/src/utils'); -var flags = require('../../flags'); - -module.exports = function (SocketPosts) { - - SocketPosts.flag = function (socket, data, callback) { - if (!socket.uid) { - return callback(new Error('[[error:not-logged-in]]')); - } - - if (!data || !data.pid || !data.reason) { - return callback(new Error('[[error:invalid-data]]')); - } - - var flaggingUser = {}; - var post; - - async.waterfall([ - function (next) { - posts.getPostFields(data.pid, ['pid', 'tid', 'uid', 'content', 'deleted'], next); - }, - function (postData, next) { - if (parseInt(postData.deleted, 10) === 1) { - return next(new Error('[[error:post-deleted]]')); - } - - post = postData; - topics.getTopicFields(post.tid, ['title', 'cid'], next); - }, - function (topicData, next) { - post.topic = topicData; - - async.parallel({ - isAdminOrMod: function (next) { - privileges.categories.isAdminOrMod(post.topic.cid, socket.uid, next); - }, - userData: function (next) { - user.getUserFields(socket.uid, ['username', 'reputation', 'banned'], next); - } - }, next); - }, - function (user, next) { - var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 1; - if (!user.isAdminOrMod && parseInt(user.userData.reputation, 10) < minimumReputation) { - return next(new Error('[[error:not-enough-reputation-to-flag]]')); - } - - if (parseInt(user.banned, 10) === 1) { - return next(new Error('[[error:user-banned]]')); - } - - flaggingUser = user.userData; - flaggingUser.uid = socket.uid; - - flags.create('post', post.pid, socket.uid, data.reason, next); - }, - function (next) { - async.parallel({ - post: function (next) { - posts.parsePost(post, next); - }, - admins: function (next) { - groups.getMembers('administrators', 0, -1, next); - }, - globalMods: function (next) { - groups.getMembers('Global Moderators', 0, -1, next); - }, - moderators: function (next) { - groups.getMembers('cid:' + post.topic.cid + ':privileges:mods', 0, -1, next); - } - }, next); - }, - function (results, next) { - var title = S(post.topic.title).decodeHTMLEntities().s; - var titleEscaped = title.replace(/%/g, '%').replace(/,/g, ','); - - notifications.create({ - bodyShort: '[[notifications:user_flagged_post_in, ' + flaggingUser.username + ', ' + titleEscaped + ']]', - bodyLong: post.content, - pid: data.pid, - path: '/post/' + data.pid, - nid: 'post_flag:' + data.pid + ':uid:' + socket.uid, - from: socket.uid, - mergeId: 'notifications:user_flagged_post_in|' + data.pid, - topicTitle: post.topic.title - }, function (err, notification) { - if (err || !notification) { - return next(err); - } - - plugins.fireHook('action:post.flag', {post: post, reason: data.reason, flaggingUser: flaggingUser}); - notifications.push(notification, results.admins.concat(results.moderators).concat(results.globalMods), next); - }); - } - ], callback); - }; - - SocketPosts.dismissFlag = function (socket, pid, callback) { - if (!pid || !socket.uid) { - return callback(new Error('[[error:invalid-data]]')); - } - async.waterfall([ - function (next) { - user.isAdminOrGlobalMod(socket.uid, next); - }, - function (isAdminOrGlobalModerator, next) { - if (!isAdminOrGlobalModerator) { - return next(new Error('[[no-privileges]]')); - } - flags.dismiss(pid, next); - } - ], callback); - }; - - SocketPosts.dismissAllFlags = function (socket, data, callback) { - async.waterfall([ - function (next) { - user.isAdminOrGlobalMod(socket.uid, next); - }, - function (isAdminOrGlobalModerator, next) { - if (!isAdminOrGlobalModerator) { - return next(new Error('[[no-privileges]]')); - } - flags.dismissAll(next); - } - ], callback); - }; - - SocketPosts.updateFlag = function (socket, data, callback) { - if (!data || !(data.pid && data.data)) { - return callback(new Error('[[error:invalid-data]]')); - } - - var payload = {}; - - async.waterfall([ - function (next) { - async.parallel([ - async.apply(user.isAdminOrGlobalMod, socket.uid), - async.apply(user.isModeratorOfAnyCategory, socket.uid) - ], function (err, results) { - next(err, results[0] || results[1]); - }); - }, - function (allowed, next) { - if (!allowed) { - return next(new Error('[[no-privileges]]')); - } - - // Translate form data into object - payload = data.data.reduce(function (memo, cur) { - memo[cur.name] = cur.value; - return memo; - }, payload); - - flags.update(socket.uid, data.pid, payload, next); - } - ], callback); - }; -}; From f1d144f15e5461769e266cd69a116148df2eb021 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 1 Dec 2016 16:22:10 -0500 Subject: [PATCH 013/316] history appending, finished up notes, #5232 --- public/language/en-GB/flags.json | 3 ++ public/src/client/flags/detail.js | 23 +++++++++--- src/flags.js | 58 ++++++++++++++++++++++++++++--- src/socket.io/flags.js | 9 ++++- src/user/data.js | 17 +++++++-- 5 files changed, 98 insertions(+), 12 deletions(-) diff --git a/public/language/en-GB/flags.json b/public/language/en-GB/flags.json index 0c1f32b88e..7b9193680f 100644 --- a/public/language/en-GB/flags.json +++ b/public/language/en-GB/flags.json @@ -6,8 +6,11 @@ "no-flags": "Hooray! No flags found.", "assignee": "Assignee", "update": "Update", + "updated": "Updated", "notes": "Flag Notes", "add-note": "Add Note", + "history": "Flag History", + "back": "Back to Flags List", "state": "State", "state-open": "New/Open", diff --git a/public/src/client/flags/detail.js b/public/src/client/flags/detail.js index d23a1941b5..aef21ec925 100644 --- a/public/src/client/flags/detail.js +++ b/public/src/client/flags/detail.js @@ -2,7 +2,7 @@ /* globals define */ -define('forum/flags/detail', ['components'], function (components) { +define('forum/flags/detail', ['components', 'translator'], function (components, translator) { var Flags = {}; Flags.init = function () { @@ -18,11 +18,12 @@ define('forum/flags/detail', ['components'], function (components) { socket.emit('flags.update', { flagId: ajaxify.data.flagId, data: $('#attributes').serializeArray() - }, function (err) { + }, function (err, history) { if (err) { return app.alertError(err.message); } else { app.alertSuccess('[[flags:updated]]'); + Flags.reloadHistory(history); } }); break; @@ -31,12 +32,13 @@ define('forum/flags/detail', ['components'], function (components) { socket.emit('flags.appendNote', { flagId: ajaxify.data.flagId, note: document.getElementById('note').value - }, function (err, notes) { + }, function (err, payload) { if (err) { return app.alertError(err.message); } else { app.alertSuccess('[[flags:note-added]]'); - Flags.reloadNotes(notes); + Flags.reloadNotes(payload.notes); + Flags.reloadHistory(payload.history); } }); break; @@ -56,5 +58,18 @@ define('forum/flags/detail', ['components'], function (components) { }); }; + Flags.reloadHistory = function (history) { + templates.parse('flags/detail', 'history', { + history: history + }, function (html) { + translator.translate(html, function (translated) { + var wrapperEl = components.get('flag/history'); + wrapperEl.empty(); + wrapperEl.html(translated); + wrapperEl.find('span.timeago').timeago(); + }); + }); + }; + return Flags; }); diff --git a/src/flags.js b/src/flags.js index 323a2a1e07..3dc1e51f32 100644 --- a/src/flags.js +++ b/src/flags.js @@ -24,7 +24,7 @@ Flags.get = function (flagId, callback) { // First stage async.apply(async.parallel, { base: async.apply(db.getObject.bind(db), 'flag:' + flagId), - history: async.apply(db.getSortedSetRevRange.bind(db), 'flag:' + flagId + ':history', 0, -1), + history: async.apply(Flags.getHistory, flagId), notes: async.apply(Flags.getNotes, flagId) }), function (data, next) { @@ -156,7 +156,7 @@ Flags.getNotes = function (flagId, callback) { next(null, notes, uids); }, function (notes, uids, next) { - user.getUsersData(uids, function (err, users) { + user.getUsersFields(uids, ['username', 'userslug', 'picture'], function (err, users) { if (err) { return next(err); } @@ -398,13 +398,61 @@ Flags.update = function (flagId, uid, changeset, callback) { async.apply(db.setObject, 'flag:' + flagId, changeset), // Append history async.apply(Flags.appendHistory, flagId, uid, Object.keys(changeset)) - ], next); + ], function (err, data) { + return next(err); + }); } ], callback); }; +Flags.getHistory = function (flagId, callback) { + var history; + var uids = []; + async.waterfall([ + async.apply(db.getSortedSetRevRangeWithScores.bind(db), 'flag:' + flagId + ':history', 0, -1), + function (_history, next) { + history = _history.map(function (entry) { + try { + entry.value = JSON.parse(entry.value); + } catch (e) { + return callback(e); + } + + uids.push(entry.value[0]); + + return { + uid: entry.value[0], + fields: entry.value[1], + datetime: entry.score, + datetimeISO: new Date(entry.score).toISOString() + }; + }); + + user.getUsersFields(uids, ['username', 'userslug', 'picture'], next); + } + ], function (err, users) { + if (err) { + return callback(err); + } + + history = history.map(function (event, idx) { + event.user = users[idx]; + return event; + }); + + callback(null, history); + }); +}; + Flags.appendHistory = function (flagId, uid, changeset, callback) { - return callback(); + var payload; + try { + payload = JSON.stringify([uid, changeset, Date.now()]); + } catch (e) { + return callback(e); + } + + db.sortedSetAdd('flag:' + flagId + ':history', Date.now(), payload, callback); }; Flags.appendNote = function (flagId, uid, note, callback) { @@ -417,7 +465,7 @@ Flags.appendNote = function (flagId, uid, note, callback) { async.waterfall([ async.apply(db.sortedSetAdd, 'flag:' + flagId + ':notes', Date.now(), payload), - async.apply(Flags.getNotes, flagId) + async.apply(Flags.appendHistory, flagId, uid, ['notes']) ], callback); }; diff --git a/src/socket.io/flags.js b/src/socket.io/flags.js index 28db7551a6..8b66dd094c 100644 --- a/src/socket.io/flags.js +++ b/src/socket.io/flags.js @@ -136,7 +136,8 @@ SocketFlags.update = function (socket, data, callback) { }, payload); flags.update(data.flagId, socket.uid, payload, next); - } + }, + async.apply(flags.getHistory, data.flagId) ], callback); }; @@ -160,6 +161,12 @@ SocketFlags.appendNote = function (socket, data, callback) { } flags.appendNote(data.flagId, socket.uid, data.note, next); + }, + function (next) { + async.parallel({ + "notes": async.apply(flags.getNotes, data.flagId), + "history": async.apply(flags.getHistory, data.flagId) + }, next); } ], callback); }; diff --git a/src/user/data.js b/src/user/data.js index 2e5bfb2218..5716208ae5 100644 --- a/src/user/data.js +++ b/src/user/data.js @@ -34,11 +34,20 @@ module.exports = function (User) { } } - if (!Array.isArray(uids) || !uids.length) { + // Eliminate duplicates and build ref table + var uniqueUids = uids.filter(function (uid, index) { + return index === uids.indexOf(uid); + }); + var ref = uniqueUids.reduce(function (memo, cur, idx) { + memo[cur] = idx; + return memo; + }, {}); + + if (!Array.isArray(uniqueUids) || !uniqueUids.length) { return callback(null, []); } - var keys = uids.map(function (uid) { + var keys = uniqueUids.map(function (uid) { return 'user:' + uid; }); @@ -60,6 +69,10 @@ module.exports = function (User) { return callback(err); } + users = uids.map(function (uid) { + return users[ref[uid]]; + }); + modifyUserData(users, fieldsToRemove, callback); }); }; From 22eeabc5c73050385eb45254eb2b2099a9781d98 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 1 Dec 2016 16:36:30 -0500 Subject: [PATCH 014/316] new strings for empty notes or history, #5232 --- public/language/en-GB/flags.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/language/en-GB/flags.json b/public/language/en-GB/flags.json index 7b9193680f..cb1f513deb 100644 --- a/public/language/en-GB/flags.json +++ b/public/language/en-GB/flags.json @@ -7,10 +7,14 @@ "assignee": "Assignee", "update": "Update", "updated": "Updated", + "notes": "Flag Notes", "add-note": "Add Note", + "no-notes": "No shared notes.", + "history": "Flag History", "back": "Back to Flags List", + "no-history": "No flag history.", "state": "State", "state-open": "New/Open", From 839a0efc0a056905e7a6b0a94a21af24e33315c2 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 2 Dec 2016 09:49:25 -0500 Subject: [PATCH 015/316] one more language string for #5232 --- public/language/en-GB/flags.json | 1 + 1 file changed, 1 insertion(+) diff --git a/public/language/en-GB/flags.json b/public/language/en-GB/flags.json index cb1f513deb..5f989cd4d0 100644 --- a/public/language/en-GB/flags.json +++ b/public/language/en-GB/flags.json @@ -3,6 +3,7 @@ "state": "State", "reporter": "Reporter", "reported-at": "Reported At", + "description": "Description", "no-flags": "Hooray! No flags found.", "assignee": "Assignee", "update": "Update", From 20fa8ebf76bb702fc8277a577f0e239185dfca20 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 2 Dec 2016 11:24:12 -0500 Subject: [PATCH 016/316] simplified flags.get a tad --- src/flags.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/flags.js b/src/flags.js index 3dc1e51f32..6022750f73 100644 --- a/src/flags.js +++ b/src/flags.js @@ -30,7 +30,7 @@ Flags.get = function (flagId, callback) { function (data, next) { // Second stage async.parallel({ - userObj: async.apply(user.getUserFields, data.base.uid, ['username', 'picture']), + userObj: async.apply(user.getUserFields, data.base.uid, ['username', 'userslug', 'picture']), targetObj: async.apply(Flags.getTarget, data.base.type, data.base.targetId, data.base.uid) }, function (err, payload) { // Final object return construction @@ -40,12 +40,7 @@ Flags.get = function (flagId, callback) { target: payload.targetObj, history: data.history, notes: data.notes, - reporter: { - username: payload.userObj.username, - picture: payload.userObj.picture, - 'icon:bgColor': payload.userObj['icon:bgColor'], - 'icon:text': payload.userObj['icon:text'] - } + reporter: payload.userObj })); }); } From 0724bee6c65b5e2e0dcb71628f0ce573da62aaf7 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 2 Dec 2016 12:10:19 -0500 Subject: [PATCH 017/316] removed deprecated dismiss methods --- src/flags.js | 81 ---------------------------------------------------- 1 file changed, 81 deletions(-) diff --git a/src/flags.js b/src/flags.js index 6022750f73..54f6f0bd30 100644 --- a/src/flags.js +++ b/src/flags.js @@ -287,87 +287,6 @@ Flags.targetExists = function (type, id, callback) { } }; -Flags.dismiss = function (pid, callback) { - async.waterfall([ - function (next) { - db.getObjectFields('post:' + pid, ['pid', 'uid', 'flags'], next); - }, - function (postData, next) { - if (!postData.pid) { - return callback(); - } - async.parallel([ - function (next) { - if (parseInt(postData.uid, 10)) { - if (parseInt(postData.flags, 10) > 0) { - async.parallel([ - async.apply(db.sortedSetIncrBy, 'users:flags', -postData.flags, postData.uid), - async.apply(db.incrObjectFieldBy, 'user:' + postData.uid, 'flags', -postData.flags) - ], next); - } else { - next(); - } - } else { - next(); - } - }, - function (next) { - db.sortedSetsRemove([ - 'posts:flagged', - 'posts:flags:count', - 'uid:' + postData.uid + ':flag:pids' - ], pid, next); - }, - function (next) { - async.series([ - function (next) { - db.getSortedSetRange('pid:' + pid + ':flag:uids', 0, -1, function (err, uids) { - if (err) { - return next(err); - } - - async.each(uids, function (uid, next) { - var nid = 'post_flag:' + pid + ':uid:' + uid; - async.parallel([ - async.apply(db.delete, 'notifications:' + nid), - async.apply(db.sortedSetRemove, 'notifications', 'post_flag:' + pid + ':uid:' + uid) - ], next); - }, next); - }); - }, - async.apply(db.delete, 'pid:' + pid + ':flag:uids') - ], next); - }, - async.apply(db.deleteObjectField, 'post:' + pid, 'flags'), - async.apply(db.delete, 'pid:' + pid + ':flag:uid:reason'), - async.apply(db.deleteObjectFields, 'post:' + pid, ['flag:state', 'flag:assignee', 'flag:notes', 'flag:history']) - ], next); - }, - function (results, next) { - db.sortedSetsRemoveRangeByScore(['users:flags'], '-inf', 0, next); - } - ], callback); -}; - -// Pretty sure we don't need this method... -Flags.dismissAll = function (callback) { - db.getSortedSetRange('posts:flagged', 0, -1, function (err, pids) { - if (err) { - return callback(err); - } - async.eachSeries(pids, Flags.dismiss, callback); - }); -}; - -Flags.dismissByUid = function (uid, callback) { - db.getSortedSetRange('uid:' + uid + ':flag:pids', 0, -1, function (err, pids) { - if (err) { - return callback(err); - } - async.eachSeries(pids, Flags.dismiss, callback); - }); -}; - Flags.update = function (flagId, uid, changeset, callback) { // Retrieve existing flag data to compare for history-saving purposes var fields = ['state', 'assignee']; From 169defd19476648c769a56fe2bbc6b062597f99b Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 2 Dec 2016 12:34:58 -0500 Subject: [PATCH 018/316] #5232, update flag history to save new value --- src/flags.js | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/flags.js b/src/flags.js index 54f6f0bd30..893bba4442 100644 --- a/src/flags.js +++ b/src/flags.js @@ -290,14 +290,18 @@ Flags.targetExists = function (type, id, callback) { Flags.update = function (flagId, uid, changeset, callback) { // Retrieve existing flag data to compare for history-saving purposes var fields = ['state', 'assignee']; + var history = []; async.waterfall([ async.apply(db.getObjectFields.bind(db), 'flag:' + flagId, fields), function (current, next) { - for(var prop in changeset) { + for (var prop in changeset) { if (changeset.hasOwnProperty(prop)) { if (current[prop] === changeset[prop]) { delete changeset[prop]; + } else { + // Append to history payload + history.push(prop + ':' + changeset[prop]); } } } @@ -311,7 +315,7 @@ Flags.update = function (flagId, uid, changeset, callback) { // Save new object to db (upsert) async.apply(db.setObject, 'flag:' + flagId, changeset), // Append history - async.apply(Flags.appendHistory, flagId, uid, Object.keys(changeset)) + async.apply(Flags.appendHistory, flagId, uid, history) ], function (err, data) { return next(err); }); @@ -334,9 +338,18 @@ Flags.getHistory = function (flagId, callback) { uids.push(entry.value[0]); + // Deserialise field object + var fields = entry.value[1].map(function (field) { + field = field.toString().split(':'); + return { + "attribute": field[0], + "value": field[1] === undefined ? null : field[1] + }; + }); + return { uid: entry.value[0], - fields: entry.value[1], + fields: fields, datetime: entry.score, datetimeISO: new Date(entry.score).toISOString() }; @@ -349,6 +362,7 @@ Flags.getHistory = function (flagId, callback) { return callback(err); } + // Append user data to each history event history = history.map(function (event, idx) { event.user = users[idx]; return event; From 9129597811ab10f2d5d8775ed1f8bc9bd1e7dae8 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 2 Dec 2016 12:51:39 -0500 Subject: [PATCH 019/316] #5232 some tweaks to flag history saving --- public/language/en-GB/flags.json | 1 - src/flags.js | 12 +++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/public/language/en-GB/flags.json b/public/language/en-GB/flags.json index 5f989cd4d0..0cdc3fbfb9 100644 --- a/public/language/en-GB/flags.json +++ b/public/language/en-GB/flags.json @@ -23,6 +23,5 @@ "state-resolved": "Resolved", "state-rejected": "Rejected", "no-assignee": "Not Assigned", - "updated": "Flag Details Updated", "note-added": "Note Added" } \ No newline at end of file diff --git a/src/flags.js b/src/flags.js index 893bba4442..58b230af06 100644 --- a/src/flags.js +++ b/src/flags.js @@ -341,9 +341,19 @@ Flags.getHistory = function (flagId, callback) { // Deserialise field object var fields = entry.value[1].map(function (field) { field = field.toString().split(':'); + + switch (field[0]) { + case 'state': + field[1] = field[1] === undefined ? null : '[[flags:state-' + field[1] + ']]'; + break; + + default: + field[1] = field[1] === undefined ? null : field[1]; + break; + } return { "attribute": field[0], - "value": field[1] === undefined ? null : field[1] + "value": field[1] }; }); From 753d4b0275e3927560d9f7c2c8217f6273d4715e Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 2 Dec 2016 15:28:28 -0500 Subject: [PATCH 020/316] wrapped up basic functionality of list and detail for flags, filter support. #5232 --- public/language/en-GB/flags.json | 7 ++ public/language/en-GB/topic.json | 6 -- public/src/admin/manage/flags.js | 172 ------------------------------ public/src/client/flags/detail.js | 4 +- public/src/client/flags/list.js | 28 +++++ src/controllers/admin.js | 1 - src/controllers/admin/flags.js | 104 ------------------ src/controllers/mods.js | 12 ++- src/flags.js | 31 +++++- src/routes/admin.js | 1 - src/socket.io/flags.js | 2 +- 11 files changed, 78 insertions(+), 290 deletions(-) delete mode 100644 public/src/admin/manage/flags.js create mode 100644 public/src/client/flags/list.js delete mode 100644 src/controllers/admin/flags.js diff --git a/public/language/en-GB/flags.json b/public/language/en-GB/flags.json index 0cdc3fbfb9..cdc3c4069e 100644 --- a/public/language/en-GB/flags.json +++ b/public/language/en-GB/flags.json @@ -9,6 +9,13 @@ "update": "Update", "updated": "Updated", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "apply-filters": "Apply Filters", + "notes": "Flag Notes", "add-note": "Add Note", "no-notes": "No shared notes.", diff --git a/public/language/en-GB/topic.json b/public/language/en-GB/topic.json index ce7f35f816..4ae208076e 100644 --- a/public/language/en-GB/topic.json +++ b/public/language/en-GB/topic.json @@ -37,12 +37,6 @@ "flag_title": "Flag this post for moderation", "flag_success": "This post has been flagged for moderation.", - "flag_manage_history": "Action History", - "flag_manage_no_history": "No event history to report", - "flag_manage_history_assignee": "Assigned to %1", - "flag_manage_history_state": "Updated state to %1", - "flag_manage_history_notes": "Updated flag notes", - "flag_manage_saved": "Flag Details Updated", "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", diff --git a/public/src/admin/manage/flags.js b/public/src/admin/manage/flags.js deleted file mode 100644 index fb8b32d602..0000000000 --- a/public/src/admin/manage/flags.js +++ /dev/null @@ -1,172 +0,0 @@ -"use strict"; -/*global define, socket, app, utils, bootbox, ajaxify*/ - -define('admin/manage/flags', [ - 'autocomplete', - 'Chart', - 'components' -], function (autocomplete, Chart, components) { - - var Flags = {}; - - Flags.init = function () { - $('.post-container .content img:not(.not-responsive)').addClass('img-responsive'); - - autocomplete.user($('#byUsername')); - - handleDismiss(); - handleDismissAll(); - handleDelete(); - handleGraphs(); - - updateFlagDetails(ajaxify.data.posts); - - components.get('posts/flags').on('click', '[component="posts/flag/update"]', updateFlag); - - // Open flag as indicated in location bar - if (window.location.hash.startsWith('#flag-pid-')) { - $(window.location.hash).collapse('toggle'); - } - }; - - function handleDismiss() { - $('.flags').on('click', '.dismiss', function () { - var btn = $(this); - var pid = btn.parents('[data-pid]').attr('data-pid'); - - socket.emit('posts.dismissFlag', pid, function (err) { - done(err, btn); - }); - }); - } - - function handleDismissAll() { - $('#dismissAll').on('click', function () { - socket.emit('posts.dismissAllFlags', function (err) { - if (err) { - return app.alertError(err.message); - } - - ajaxify.refresh(); - }); - return false; - }); - } - - function handleDelete() { - $('.flags').on('click', '.delete', function () { - var btn = $(this); - bootbox.confirm('Do you really want to delete this post?', function (confirm) { - if (!confirm) { - return; - } - var pid = btn.parents('[data-pid]').attr('data-pid'); - var tid = btn.parents('[data-pid]').attr('data-tid'); - socket.emit('posts.delete', {pid: pid, tid: tid}, function (err) { - done(err, btn); - }); - }); - }); - } - - function done(err, btn) { - if (err) { - return app.alertError(err.messaage); - } - btn.parents('[data-pid]').fadeOut(function () { - $(this).remove(); - if (!$('.flags [data-pid]').length) { - $('.post-container').text('No flagged posts!'); - } - }); - } - - function handleGraphs() { - var dailyCanvas = document.getElementById('flags:daily'); - var dailyLabels = utils.getDaysArray().map(function (text, idx) { - return idx % 3 ? '' : text; - }); - - if (utils.isMobile()) { - Chart.defaults.global.tooltips.enabled = false; - } - var data = { - 'flags:daily': { - labels: dailyLabels, - datasets: [ - { - label: "", - backgroundColor: "rgba(151,187,205,0.2)", - borderColor: "rgba(151,187,205,1)", - pointBackgroundColor: "rgba(151,187,205,1)", - pointHoverBackgroundColor: "#fff", - pointBorderColor: "#fff", - pointHoverBorderColor: "rgba(151,187,205,1)", - data: ajaxify.data.analytics - } - ] - } - }; - - dailyCanvas.width = $(dailyCanvas).parent().width(); - new Chart(dailyCanvas.getContext('2d'), { - type: 'line', - data: data['flags:daily'], - options: { - responsive: true, - animation: false, - legend: { - display: false - }, - scales: { - yAxes: [{ - ticks: { - beginAtZero: true - } - }] - } - } - }); - } - - function updateFlagDetails(source) { - // As the flag details are returned in the API, update the form controls to show the correct data - - // Create reference hash for use in this method - source = source.reduce(function (memo, cur) { - memo[cur.pid] = cur.flagData; - return memo; - }, {}); - - components.get('posts/flag').each(function (idx, el) { - var pid = el.getAttribute('data-pid'); - var el = $(el); - - if (source[pid]) { - for(var prop in source[pid]) { - if (source[pid].hasOwnProperty(prop)) { - el.find('[name="' + prop + '"]').val(source[pid][prop]); - } - } - } - }); - } - - function updateFlag() { - var pid = $(this).parents('[component="posts/flag"]').attr('data-pid'); - var formData = $($(this).parents('form').get(0)).serializeArray(); - - socket.emit('posts.updateFlag', { - pid: pid, - data: formData - }, function (err) { - if (err) { - return app.alertError(err.message); - } else { - app.alertSuccess('[[topic:flag_manage_saved]]'); - } - }); - } - - return Flags; -}); \ No newline at end of file diff --git a/public/src/client/flags/detail.js b/public/src/client/flags/detail.js index aef21ec925..e20f05dba9 100644 --- a/public/src/client/flags/detail.js +++ b/public/src/client/flags/detail.js @@ -2,7 +2,7 @@ /* globals define */ -define('forum/flags/detail', ['components', 'translator'], function (components, translator) { +define('forum/flags/detail', ['forum/flags/list', 'components', 'translator'], function (FlagsList, components, translator) { var Flags = {}; Flags.init = function () { @@ -44,6 +44,8 @@ define('forum/flags/detail', ['components', 'translator'], function (components, break; } }); + + FlagsList.enableFilterForm(); }; Flags.reloadNotes = function (notes) { diff --git a/public/src/client/flags/list.js b/public/src/client/flags/list.js new file mode 100644 index 0000000000..6111372150 --- /dev/null +++ b/public/src/client/flags/list.js @@ -0,0 +1,28 @@ +'use strict'; + +/* globals define */ + +define('forum/flags/list', ['components'], function (components) { + var Flags = {}; + + Flags.init = function () { + Flags.enableFilterForm(); + }; + + Flags.enableFilterForm = function () { + var filtersEl = components.get('flags/filters'); + + filtersEl.find('button').on('click', function () { + var payload = filtersEl.serializeArray(); + var qs = payload.map(function (filter) { + if (filter.value) { + return filter.name + '=' + filter.value; + } + }).filter(Boolean).join('&'); + + ajaxify.go('flags?' + qs); + }) + }; + + return Flags; +}); diff --git a/src/controllers/admin.js b/src/controllers/admin.js index 7f622466cd..c58def03cf 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -4,7 +4,6 @@ var adminController = { dashboard: require('./admin/dashboard'), categories: require('./admin/categories'), tags: require('./admin/tags'), - flags: require('./admin/flags'), blacklist: require('./admin/blacklist'), groups: require('./admin/groups'), appearance: require('./admin/appearance'), diff --git a/src/controllers/admin/flags.js b/src/controllers/admin/flags.js deleted file mode 100644 index 80c31ba60a..0000000000 --- a/src/controllers/admin/flags.js +++ /dev/null @@ -1,104 +0,0 @@ -"use strict"; - -var async = require('async'); -var validator = require('validator'); - -var posts = require('../../posts'); -var user = require('../../user'); -var flags = require('../../flags'); -var categories = require('../../categories'); -var analytics = require('../../analytics'); -var pagination = require('../../pagination'); - -var flagsController = {}; - -var itemsPerPage = 20; - -flagsController.get = function (req, res, next) { - var byUsername = req.query.byUsername || ''; - var cid = req.query.cid || 0; - var sortBy = req.query.sortBy || 'count'; - var page = parseInt(req.query.page, 10) || 1; - - async.parallel({ - categories: function (next) { - categories.buildForSelect(req.uid, next); - }, - flagData: function (next) { - getFlagData(req, res, next); - }, - analytics: function (next) { - analytics.getDailyStatsForSet('analytics:flags', Date.now(), 30, next); - }, - assignees: async.apply(user.getAdminsandGlobalModsandModerators) - }, function (err, results) { - if (err) { - return next(err); - } - - // Minimise data set for assignees so tjs does less work - results.assignees = results.assignees.map(function (userObj) { - return { - uid: userObj.uid, - username: userObj.username - }; - }); - - // If res.locals.cids is populated, then slim down the categories list - if (res.locals.cids) { - results.categories = results.categories.filter(function (category) { - return res.locals.cids.indexOf(String(category.cid)) !== -1; - }); - } - - var pageCount = Math.max(1, Math.ceil(results.flagData.count / itemsPerPage)); - - results.categories.forEach(function (category) { - category.selected = parseInt(category.cid, 10) === parseInt(cid, 10); - }); - - var data = { - posts: results.flagData.posts, - assignees: results.assignees, - analytics: results.analytics, - categories: results.categories, - byUsername: validator.escape(String(byUsername)), - sortByCount: sortBy === 'count', - sortByTime: sortBy === 'time', - pagination: pagination.create(page, pageCount, req.query), - title: '[[pages:flagged-posts]]' - }; - res.render('admin/manage/flags', data); - }); -}; - -function getFlagData(req, res, callback) { - var sortBy = req.query.sortBy || 'count'; - var byUsername = req.query.byUsername || ''; - var cid = req.query.cid || res.locals.cids || 0; - var page = parseInt(req.query.page, 10) || 1; - var start = (page - 1) * itemsPerPage; - var stop = start + itemsPerPage - 1; - - var sets = [sortBy === 'count' ? 'posts:flags:count' : 'posts:flagged']; - - async.waterfall([ - function (next) { - if (byUsername) { - user.getUidByUsername(byUsername, next); - } else { - process.nextTick(next, null, 0); - } - }, - function (uid, next) { - if (uid) { - sets.push('uid:' + uid + ':flag:pids'); - } - - flags.get(sets, cid, req.uid, start, stop, next); - } - ], callback); -} - - -module.exports = flagsController; diff --git a/src/controllers/mods.js b/src/controllers/mods.js index 012ffde3c6..759de90ef1 100644 --- a/src/controllers/mods.js +++ b/src/controllers/mods.js @@ -25,7 +25,17 @@ modsController.flags.list = function (req, res, next) { res.locals.cids = results.moderatedCids; } - flags.list({}, function (err, flags) { + // Parse query string params for filters + var valid = ['reporterId', 'type']; + var filters = valid.reduce(function (memo, cur) { + if (req.query.hasOwnProperty(cur)) { + memo[cur] = req.query[cur]; + } + + return memo; + }, {}); + + flags.list(filters, function (err, flags) { if (err) { return next(err); } diff --git a/src/flags.js b/src/flags.js index 58b230af06..6744d8ad59 100644 --- a/src/flags.js +++ b/src/flags.js @@ -53,8 +53,31 @@ Flags.list = function (filters, callback) { filters = {}; } + var sets = []; + if (Object.keys(filters).length > 0) { + for (var type in filters) { + switch (type) { + case 'type': + sets.push('flags:byType:' + filters[type]); + break; + + case 'reporterId': + sets.push('flags:byReporter:' + filters[type]); + break; + } + } + + } + sets = sets.length ? sets : ['flags:datetime']; // No filter default + async.waterfall([ - async.apply(db.getSortedSetRevRange.bind(db), 'flags:datetime', 0, 19), + function (next) { + if (sets.length === 1) { + db.getSortedSetRevRange(sets[0], 0, 19, next); + } else { + db.getSortedSetRevIntersect({sets: sets, start: 0, stop: -1, aggregate: 'MAX'}, next); + } + }, function (flagIds, next) { async.map(flagIds, function (flagId, next) { async.waterfall([ @@ -197,8 +220,10 @@ Flags.create = function (type, id, uid, reason, callback) { uid: uid, datetime: Date.now() })), - async.apply(db.sortedSetAdd.bind(db), 'flags:datetime', Date.now(), flagId), - async.apply(db.setObjectField.bind(db), 'flagHash:flagId', [type, id, uid].join(':'), flagId) + async.apply(db.sortedSetAdd.bind(db), 'flags:datetime', Date.now(), flagId), // by time, the default + async.apply(db.sortedSetAdd.bind(db), 'flags:byReporter:' + uid, Date.now(), flagId), // by reporter + async.apply(db.sortedSetAdd.bind(db), 'flags:byType:' + type, Date.now(), flagId), // by flag type + async.apply(db.setObjectField.bind(db), 'flagHash:flagId', [type, id, uid].join(':'), flagId) // save hash for existence checking ], function (err, data) { if (err) { return next(err); diff --git a/src/routes/admin.js b/src/routes/admin.js index 0611eede70..53b12fc4b8 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -56,7 +56,6 @@ function addRoutes(router, middleware, controllers) { router.get('/manage/categories/:category_id/analytics', middlewares, controllers.admin.categories.getAnalytics); router.get('/manage/tags', middlewares, controllers.admin.tags.get); - router.get('/manage/flags', middlewares, controllers.admin.flags.get); router.get('/manage/ip-blacklist', middlewares, controllers.admin.blacklist.get); router.get('/manage/users', middlewares, controllers.admin.users.sortByJoinDate); diff --git a/src/socket.io/flags.js b/src/socket.io/flags.js index 8b66dd094c..e6fb0be116 100644 --- a/src/socket.io/flags.js +++ b/src/socket.io/flags.js @@ -67,7 +67,7 @@ SocketFlags.create = function (socket, data, callback) { flags.create('post', post.pid, socket.uid, data.reason, next); }, - function (next) { + function (flagObj, next) { async.parallel({ post: function (next) { posts.parsePost(post, next); From 8e1d441e20d70e61d12a13ec66c48c8c029f3470 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 5 Dec 2016 12:40:25 -0500 Subject: [PATCH 021/316] Added some quick filters, #5232 --- public/language/en-GB/flags.json | 5 +++- public/src/client/flags/list.js | 5 ++++ src/controllers/mods.js | 8 ++++--- src/flags.js | 39 ++++++++++++++++++++++++-------- 4 files changed, 44 insertions(+), 13 deletions(-) diff --git a/public/language/en-GB/flags.json b/public/language/en-GB/flags.json index cdc3c4069e..fbe32a812e 100644 --- a/public/language/en-GB/flags.json +++ b/public/language/en-GB/flags.json @@ -1,5 +1,4 @@ { - "quick-filters": "Quick Filters", "state": "State", "reporter": "Reporter", "reported-at": "Reported At", @@ -9,11 +8,15 @@ "update": "Update", "updated": "Updated", + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", "filters": "Filter Options", "filter-reporterId": "Reporter UID", "filter-type": "Flag Type", "filter-type-all": "All Content", "filter-type-post": "Post", + "filter-quick-mine": "Assigned to me", "apply-filters": "Apply Filters", "notes": "Flag Notes", diff --git a/public/src/client/flags/list.js b/public/src/client/flags/list.js index 6111372150..2994e7fcf9 100644 --- a/public/src/client/flags/list.js +++ b/public/src/client/flags/list.js @@ -12,6 +12,11 @@ define('forum/flags/list', ['components'], function (components) { Flags.enableFilterForm = function () { var filtersEl = components.get('flags/filters'); + // Parse ajaxify data to set form values to reflect current filters + for(var filter in ajaxify.data.filters) { + filtersEl.find('[name="' + filter + '"]').val(ajaxify.data.filters[filter]); + } + filtersEl.find('button').on('click', function () { var payload = filtersEl.serializeArray(); var qs = payload.map(function (filter) { diff --git a/src/controllers/mods.js b/src/controllers/mods.js index 759de90ef1..f15b1a6b1a 100644 --- a/src/controllers/mods.js +++ b/src/controllers/mods.js @@ -26,7 +26,7 @@ modsController.flags.list = function (req, res, next) { } // Parse query string params for filters - var valid = ['reporterId', 'type']; + var valid = ['reporterId', 'type', 'quick']; var filters = valid.reduce(function (memo, cur) { if (req.query.hasOwnProperty(cur)) { memo[cur] = req.query[cur]; @@ -35,13 +35,15 @@ modsController.flags.list = function (req, res, next) { return memo; }, {}); - flags.list(filters, function (err, flags) { + flags.list(filters, req.uid, function (err, flags) { if (err) { return next(err); } res.render('flags/list', { - flags: flags + flags: flags, + hasFilter: !!Object.keys(filters).length, + filters: filters }); }); }); diff --git a/src/flags.js b/src/flags.js index 6744d8ad59..492e1faa43 100644 --- a/src/flags.js +++ b/src/flags.js @@ -47,7 +47,7 @@ Flags.get = function (flagId, callback) { ], callback); }; -Flags.list = function (filters, callback) { +Flags.list = function (filters, uid, callback) { if (typeof filters === 'function' && !callback) { callback = filters; filters = {}; @@ -64,16 +64,23 @@ Flags.list = function (filters, callback) { case 'reporterId': sets.push('flags:byReporter:' + filters[type]); break; + + case 'quick': + switch (filters.quick) { + case 'mine': + sets.push('flags:byAssignee:' + uid); + break; + } + break; } } - } sets = sets.length ? sets : ['flags:datetime']; // No filter default async.waterfall([ function (next) { if (sets.length === 1) { - db.getSortedSetRevRange(sets[0], 0, 19, next); + db.getSortedSetRevRange(sets[0], 0, -1, next); } else { db.getSortedSetRevIntersect({sets: sets, start: 0, stop: -1, aggregate: 'MAX'}, next); } @@ -316,6 +323,8 @@ Flags.update = function (flagId, uid, changeset, callback) { // Retrieve existing flag data to compare for history-saving purposes var fields = ['state', 'assignee']; var history = []; + var tasks = []; + var now = Date.now(); async.waterfall([ async.apply(db.getObjectFields.bind(db), 'flag:' + flagId, fields), @@ -325,6 +334,18 @@ Flags.update = function (flagId, uid, changeset, callback) { if (current[prop] === changeset[prop]) { delete changeset[prop]; } else { + // Add tasks as necessary + switch (prop) { + case 'state': + tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byState:' + changeset[prop], now, flagId)); + tasks.push(async.apply(db.sortedSetRemove.bind(db), 'flags:byState:' + current[prop], flagId)); + break; + + case 'assignee': + tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byAssignee:' + changeset[prop], now, flagId)); + break; + } + // Append to history payload history.push(prop + ':' + changeset[prop]); } @@ -336,12 +357,12 @@ Flags.update = function (flagId, uid, changeset, callback) { return next(); } - async.parallel([ - // Save new object to db (upsert) - async.apply(db.setObject, 'flag:' + flagId, changeset), - // Append history - async.apply(Flags.appendHistory, flagId, uid, history) - ], function (err, data) { + // Save new object to db (upsert) + tasks.push(async.apply(db.setObject, 'flag:' + flagId, changeset)); + // Append history + tasks.push(async.apply(Flags.appendHistory, flagId, uid, history)) + + async.parallel(tasks, function (err, data) { return next(err); }); } From 88958049ebce7abc17a40ba5223d5edcbeabea92 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 5 Dec 2016 15:32:58 -0500 Subject: [PATCH 022/316] added some more filters, and appending event to history on flag creation issue #5232 --- public/language/en-GB/flags.json | 2 ++ public/src/client/flags/list.js | 4 ++-- src/controllers/mods.js | 2 +- src/flags.js | 9 +++++++++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/public/language/en-GB/flags.json b/public/language/en-GB/flags.json index fbe32a812e..5d9799de50 100644 --- a/public/language/en-GB/flags.json +++ b/public/language/en-GB/flags.json @@ -16,6 +16,8 @@ "filter-type": "Flag Type", "filter-type-all": "All Content", "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", "filter-quick-mine": "Assigned to me", "apply-filters": "Apply Filters", diff --git a/public/src/client/flags/list.js b/public/src/client/flags/list.js index 2994e7fcf9..a5277f98df 100644 --- a/public/src/client/flags/list.js +++ b/public/src/client/flags/list.js @@ -13,7 +13,7 @@ define('forum/flags/list', ['components'], function (components) { var filtersEl = components.get('flags/filters'); // Parse ajaxify data to set form values to reflect current filters - for(var filter in ajaxify.data.filters) { + for (var filter in ajaxify.data.filters) { filtersEl.find('[name="' + filter + '"]').val(ajaxify.data.filters[filter]); } @@ -26,7 +26,7 @@ define('forum/flags/list', ['components'], function (components) { }).filter(Boolean).join('&'); ajaxify.go('flags?' + qs); - }) + }); }; return Flags; diff --git a/src/controllers/mods.js b/src/controllers/mods.js index f15b1a6b1a..512ad32222 100644 --- a/src/controllers/mods.js +++ b/src/controllers/mods.js @@ -26,7 +26,7 @@ modsController.flags.list = function (req, res, next) { } // Parse query string params for filters - var valid = ['reporterId', 'type', 'quick']; + var valid = ['assignee', 'state', 'reporterId', 'type', 'quick']; var filters = valid.reduce(function (memo, cur) { if (req.query.hasOwnProperty(cur)) { memo[cur] = req.query[cur]; diff --git a/src/flags.js b/src/flags.js index 492e1faa43..a8bca052d1 100644 --- a/src/flags.js +++ b/src/flags.js @@ -60,11 +60,19 @@ Flags.list = function (filters, uid, callback) { case 'type': sets.push('flags:byType:' + filters[type]); break; + + case 'state': + sets.push('flags:byState:' + filters[type]); + break; case 'reporterId': sets.push('flags:byReporter:' + filters[type]); break; + case 'assignee': + sets.push('flags:byAssignee:' + filters[type]); + break; + case 'quick': switch (filters.quick) { case 'mine': @@ -236,6 +244,7 @@ Flags.create = function (type, id, uid, reason, callback) { return next(err); } + Flags.appendHistory(flagId, uid, ['created']); next(null, flagId); }); }, From cd3002e812a1c41aea190d72b5941f3d6634505f Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 5 Dec 2016 15:52:01 -0500 Subject: [PATCH 023/316] removed user flag reset method and associated socket call from ACP --- public/src/admin/manage/users.js | 9 --------- src/socket.io/admin/user.js | 8 -------- src/views/admin/manage/users.tpl | 1 - 3 files changed, 18 deletions(-) diff --git a/public/src/admin/manage/users.js b/public/src/admin/manage/users.js index b8fb8d5de6..2f344e37c7 100644 --- a/public/src/admin/manage/users.js +++ b/public/src/admin/manage/users.js @@ -128,15 +128,6 @@ define('admin/manage/users', ['translator'], function (translator) { socket.emit('admin.user.resetLockouts', uids, done('Lockout(s) reset!')); }); - $('.reset-flags').on('click', function () { - var uids = getSelectedUids(); - if (!uids.length) { - return; - } - - socket.emit('admin.user.resetFlags', uids, done('Flags(s) reset!')); - }); - $('.admin-user').on('click', function () { var uids = getSelectedUids(); if (!uids.length) { diff --git a/src/socket.io/admin/user.js b/src/socket.io/admin/user.js index 4a77c224a7..272a13f7f7 100644 --- a/src/socket.io/admin/user.js +++ b/src/socket.io/admin/user.js @@ -68,14 +68,6 @@ User.resetLockouts = function (socket, uids, callback) { async.each(uids, user.auth.resetLockout, callback); }; -User.resetFlags = function (socket, uids, callback) { - if (!Array.isArray(uids)) { - return callback(new Error('[[error:invalid-data]]')); - } - - user.resetFlags(uids, callback); -}; - User.validateEmail = function (socket, uids, callback) { if (!Array.isArray(uids)) { return callback(new Error('[[error:invalid-data]]')); diff --git a/src/views/admin/manage/users.tpl b/src/views/admin/manage/users.tpl index de0cd15e62..b43d66af8a 100644 --- a/src/views/admin/manage/users.tpl +++ b/src/views/admin/manage/users.tpl @@ -19,7 +19,6 @@
  • Ban User(s) Temporarily
  • Unban User(s)
  • Reset Lockout
  • -
  • Reset Flags
  • Delete User(s)
  • Delete User(s) and Content
  • From 9f9051026b89b41d6028e8c0b59f6fa9a6e7336e Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 6 Dec 2016 16:11:56 -0500 Subject: [PATCH 024/316] more work on #5232 --- public/language/en-GB/flags.json | 8 ++ public/src/client/flags/list.js | 7 ++ public/src/client/topic/flag.js | 2 +- src/controllers/mods.js | 2 +- src/flags.js | 143 +++++++++++++++++++++++++++---- src/socket.io/flags.js | 85 ++---------------- 6 files changed, 153 insertions(+), 94 deletions(-) diff --git a/public/language/en-GB/flags.json b/public/language/en-GB/flags.json index 5d9799de50..6b4e96f4fb 100644 --- a/public/language/en-GB/flags.json +++ b/public/language/en-GB/flags.json @@ -13,6 +13,7 @@ "filter-reset": "Remove Filters", "filters": "Filter Options", "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", "filter-type": "Flag Type", "filter-type-all": "All Content", "filter-type-post": "Post", @@ -21,6 +22,13 @@ "filter-quick-mine": "Assigned to me", "apply-filters": "Apply Filters", + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "reporter": "Reporting User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + "notes": "Flag Notes", "add-note": "Add Note", "no-notes": "No shared notes.", diff --git a/public/src/client/flags/list.js b/public/src/client/flags/list.js index a5277f98df..cbdaf94f24 100644 --- a/public/src/client/flags/list.js +++ b/public/src/client/flags/list.js @@ -7,6 +7,7 @@ define('forum/flags/list', ['components'], function (components) { Flags.init = function () { Flags.enableFilterForm(); + Flags.enableChatButtons(); }; Flags.enableFilterForm = function () { @@ -29,5 +30,11 @@ define('forum/flags/list', ['components'], function (components) { }); }; + Flags.enableChatButtons = function () { + $('[data-chat]').on('click', function () { + app.newChat(this.getAttribute('data-chat')); + }); + }; + return Flags; }); diff --git a/public/src/client/topic/flag.js b/public/src/client/topic/flag.js index 046585ae68..6b3440da54 100644 --- a/public/src/client/topic/flag.js +++ b/public/src/client/topic/flag.js @@ -48,7 +48,7 @@ define('forum/topic/flag', [], function () { if (!pid || !reason) { return; } - socket.emit('flags.create', {pid: pid, reason: reason}, function (err) { + socket.emit('flags.create', {type: 'post', id: pid, reason: reason}, function (err) { if (err) { return app.alertError(err.message); } diff --git a/src/controllers/mods.js b/src/controllers/mods.js index 512ad32222..788294a01e 100644 --- a/src/controllers/mods.js +++ b/src/controllers/mods.js @@ -26,7 +26,7 @@ modsController.flags.list = function (req, res, next) { } // Parse query string params for filters - var valid = ['assignee', 'state', 'reporterId', 'type', 'quick']; + var valid = ['assignee', 'state', 'reporterId', 'type', 'targetUid', 'quick']; var filters = valid.reduce(function (memo, cur) { if (req.query.hasOwnProperty(cur)) { memo[cur] = req.query[cur]; diff --git a/src/flags.js b/src/flags.js index a8bca052d1..45aae2502e 100644 --- a/src/flags.js +++ b/src/flags.js @@ -1,23 +1,22 @@ - - 'use strict'; var async = require('async'); var winston = require('winston'); var db = require('./database'); var user = require('./user'); +var groups = require('./groups'); +var meta = require('./meta'); +var notifications = require('./notifications'); var analytics = require('./analytics'); var topics = require('./topics'); var posts = require('./posts'); +var privileges = require('./privileges'); +var plugins = require('./plugins'); var utils = require('../public/src/utils'); var _ = require('underscore'); +var S = require('string'); -var Flags = { - _defaults: { - state: 'open', - assignee: null - } -}; +var Flags = {}; Flags.get = function (flagId, callback) { async.waterfall([ @@ -72,6 +71,10 @@ Flags.list = function (filters, uid, callback) { case 'assignee': sets.push('flags:byAssignee:' + filters[type]); break; + + case 'targetUid': + sets.push('flags:byTargetUid:' + filters[type]); + break; case 'quick': switch (filters.quick) { @@ -145,6 +148,43 @@ Flags.list = function (filters, uid, callback) { }); }; +Flags.validate = function (payload, callback) { + async.parallel({ + targetExists: async.apply(Flags.targetExists, payload.type, payload.id), + target: async.apply(Flags.getTarget, payload.type, payload.id, payload.uid), + reporter: async.apply(user.getUserData, payload.uid) + }, function (err, data) { + if (err) { + return callback(err); + } + + if (data.target.deleted) { + return callback(new Error('[[error:post-deleted]]')); + } else if (data.reporter.banned) { + return callback(new Error('[[error:user-banned]]')); + } + + switch (payload.type) { + case 'post': + async.parallel({ + privileges: async.apply(privileges.posts.get, [payload.id], payload.uid) + }, function (err, subdata) { + if (err) { + return callback(err); + } + + var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 1; + if (!subdata.privileges[0].isAdminOrMod && parseInt(data.reporter.reputation, 10) < minimumReputation) { + return callback(new Error('[[error:not-enough-reputation-to-flag]]')); + } + + callback(); + }); + break; + } + }); +}; + Flags.getTarget = function (type, id, uid, callback) { switch (type) { case 'post': @@ -204,17 +244,22 @@ Flags.getNotes = function (flagId, callback) { }; Flags.create = function (type, id, uid, reason, callback) { + var targetUid; + async.waterfall([ function (next) { // Sanity checks async.parallel([ async.apply(Flags.exists, type, id, uid), - async.apply(Flags.targetExists, type, id) + async.apply(Flags.targetExists, type, id), + async.apply(Flags.getTargetUid, type, id) ], function (err, checks) { if (err) { return next(err); } + targetUid = checks[2] || null; + if (checks[0]) { return next(new Error('[[error:already-flagged]]')); } else if (!checks[1]) { @@ -226,25 +271,31 @@ Flags.create = function (type, id, uid, reason, callback) { }, async.apply(db.incrObjectField, 'global', 'nextFlagId'), function (flagId, next) { - async.parallel([ - async.apply(db.setObject.bind(db), 'flag:' + flagId, Object.assign({}, Flags._defaults, { + var tasks = [ + async.apply(db.setObject.bind(db), 'flag:' + flagId, { flagId: flagId, type: type, targetId: id, description: reason, uid: uid, datetime: Date.now() - })), + }), async.apply(db.sortedSetAdd.bind(db), 'flags:datetime', Date.now(), flagId), // by time, the default async.apply(db.sortedSetAdd.bind(db), 'flags:byReporter:' + uid, Date.now(), flagId), // by reporter async.apply(db.sortedSetAdd.bind(db), 'flags:byType:' + type, Date.now(), flagId), // by flag type async.apply(db.setObjectField.bind(db), 'flagHash:flagId', [type, id, uid].join(':'), flagId) // save hash for existence checking - ], function (err, data) { + ]; + + if (targetUid) { + tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byTargetUid:' + targetUid, Date.now(), flagId)); // by target uid + } + + async.parallel(tasks, function (err, data) { if (err) { return next(err); } - Flags.appendHistory(flagId, uid, ['created']); + Flags.update(flagId, uid, { "state": "open" }); next(null, flagId); }); }, @@ -318,7 +369,7 @@ Flags.exists = function (type, id, uid, callback) { Flags.targetExists = function (type, id, callback) { switch (type) { - case 'topic': + case 'topic': // just an example... topics.exists(id, callback); break; @@ -328,6 +379,14 @@ Flags.targetExists = function (type, id, callback) { } }; +Flags.getTargetUid = function (type, id, callback) { + switch (type) { + case 'post': + posts.getPostField(id, 'uid', callback); + break; + } +}; + Flags.update = function (flagId, uid, changeset, callback) { // Retrieve existing flag data to compare for history-saving purposes var fields = ['state', 'assignee']; @@ -369,7 +428,7 @@ Flags.update = function (flagId, uid, changeset, callback) { // Save new object to db (upsert) tasks.push(async.apply(db.setObject, 'flag:' + flagId, changeset)); // Append history - tasks.push(async.apply(Flags.appendHistory, flagId, uid, history)) + tasks.push(async.apply(Flags.appendHistory, flagId, uid, history)); async.parallel(tasks, function (err, data) { return next(err); @@ -462,4 +521,56 @@ Flags.appendNote = function (flagId, uid, note, callback) { ], callback); }; +Flags.notify = function (flagObj, uid, callback) { + // Notify administrators, mods, and other associated people + switch (flagObj.type) { + case 'post': + async.parallel({ + post: function (next) { + async.waterfall([ + async.apply(posts.getPostData, flagObj.targetId), + async.apply(posts.parsePost) + ], next); + }, + title: async.apply(topics.getTitleByPid, flagObj.targetId), + admins: async.apply(groups.getMembers, 'administrators', 0, -1), + globalMods: async.apply(groups.getMembers, 'Global Moderators', 0, -1), + moderators: function (next) { + async.waterfall([ + async.apply(posts.getCidByPid, flagObj.targetId), + function (cid, next) { + groups.getMembers('cid:' + cid + ':privileges:mods', 0, -1, next); + } + ], next); + } + }, function (err, results) { + if (err) { + return callback(err); + } + + var title = S(results.title).decodeHTMLEntities().s; + var titleEscaped = title.replace(/%/g, '%').replace(/,/g, ','); + + notifications.create({ + bodyShort: '[[notifications:user_flagged_post_in, ' + flagObj.reporter.username + ', ' + titleEscaped + ']]', + bodyLong: results.post.content, + pid: flagObj.targetId, + path: '/post/' + flagObj.targetId, + nid: 'flag:post:' + flagObj.targetId + ':uid:' + uid, + from: uid, + mergeId: 'notifications:user_flagged_post_in|' + flagObj.targetId, + topicTitle: results.title + }, function (err, notification) { + if (err || !notification) { + return callback(err); + } + + plugins.fireHook('action:post.flag', {post: results.post, reason: flagObj.description, flaggingUser: flagObj.reporter}); + notifications.push(notification, results.admins.concat(results.moderators).concat(results.globalMods), callback); + }); + }); + break; + } +}; + module.exports = Flags; \ No newline at end of file diff --git a/src/socket.io/flags.js b/src/socket.io/flags.js index e6fb0be116..ce148faa87 100644 --- a/src/socket.io/flags.js +++ b/src/socket.io/flags.js @@ -21,89 +21,22 @@ SocketFlags.create = function (socket, data, callback) { return callback(new Error('[[error:not-logged-in]]')); } - if (!data || !data.pid || !data.reason) { + if (!data || !data.type || !data.id || !data.reason) { return callback(new Error('[[error:invalid-data]]')); } - var flaggingUser = {}; - var post; - async.waterfall([ + async.apply(flags.validate, { + uid: socket.uid, + type: data.type, + id: data.id + }), function (next) { - posts.getPostFields(data.pid, ['pid', 'tid', 'uid', 'content', 'deleted'], next); - }, - function (postData, next) { - if (parseInt(postData.deleted, 10) === 1) { - return next(new Error('[[error:post-deleted]]')); - } - - post = postData; - topics.getTopicFields(post.tid, ['title', 'cid'], next); - }, - function (topicData, next) { - post.topic = topicData; - - async.parallel({ - isAdminOrMod: function (next) { - privileges.categories.isAdminOrMod(post.topic.cid, socket.uid, next); - }, - userData: function (next) { - user.getUserFields(socket.uid, ['username', 'reputation', 'banned'], next); - } - }, next); - }, - function (user, next) { - var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 1; - if (!user.isAdminOrMod && parseInt(user.userData.reputation, 10) < minimumReputation) { - return next(new Error('[[error:not-enough-reputation-to-flag]]')); - } - - if (parseInt(user.banned, 10) === 1) { - return next(new Error('[[error:user-banned]]')); - } - - flaggingUser = user.userData; - flaggingUser.uid = socket.uid; - - flags.create('post', post.pid, socket.uid, data.reason, next); + // If we got here, then no errors occurred + flags.create(data.type, data.id, socket.uid, data.reason, next); }, function (flagObj, next) { - async.parallel({ - post: function (next) { - posts.parsePost(post, next); - }, - admins: function (next) { - groups.getMembers('administrators', 0, -1, next); - }, - globalMods: function (next) { - groups.getMembers('Global Moderators', 0, -1, next); - }, - moderators: function (next) { - groups.getMembers('cid:' + post.topic.cid + ':privileges:mods', 0, -1, next); - } - }, next); - }, - function (results, next) { - var title = S(post.topic.title).decodeHTMLEntities().s; - var titleEscaped = title.replace(/%/g, '%').replace(/,/g, ','); - - notifications.create({ - bodyShort: '[[notifications:user_flagged_post_in, ' + flaggingUser.username + ', ' + titleEscaped + ']]', - bodyLong: post.content, - pid: data.pid, - path: '/post/' + data.pid, - nid: 'post_flag:' + data.pid + ':uid:' + socket.uid, - from: socket.uid, - mergeId: 'notifications:user_flagged_post_in|' + data.pid, - topicTitle: post.topic.title - }, function (err, notification) { - if (err || !notification) { - return next(err); - } - - plugins.fireHook('action:post.flag', {post: post, reason: data.reason, flaggingUser: flaggingUser}); - notifications.push(notification, results.admins.concat(results.moderators).concat(results.globalMods), next); - }); + flags.notify(flagObj, socket.uid, next); } ], callback); }; From 57fcb92bbcc14f265e238348e82d18e5ea357431 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 6 Dec 2016 20:28:54 -0500 Subject: [PATCH 025/316] added a smattering of tests for #5232 --- test/flags.js | 188 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 183 insertions(+), 5 deletions(-) diff --git a/test/flags.js b/test/flags.js index c5e69aa973..ee735afd60 100644 --- a/test/flags.js +++ b/test/flags.js @@ -8,7 +8,9 @@ var db = require('./mocks/databasemock'); var Flags = require('../src/flags'); var Categories = require('../src/categories'); var Topics = require('../src/topics'); +var Posts = require('../src/posts'); var User = require('../src/user'); +var Meta = require('../src/meta'); describe('Flags', function () { before(function (done) { @@ -43,8 +45,7 @@ describe('Flags', function () { uid: 1, targetId: 1, type: 'post', - description: 'Test flag', - state: 'open' + description: 'Test flag' }; for(var key in compare) { @@ -86,7 +87,7 @@ describe('Flags', function () { describe('.list()', function () { it('should show a list of flags (with one item)', function (done) { - Flags.list({}, function (err, flags) { + Flags.list({}, 1, function (err, flags) { assert.ifError(err); assert.ok(Array.isArray(flags)); assert.equal(flags.length, 1); @@ -101,7 +102,49 @@ describe('Flags', function () { }); }); - describe('.getTarget()', function() { + describe('.update()', function () { + it('should alter a flag\'s various attributes and persist them to the database', function (done) { + Flags.update(1, 1, { + "state": "wip", + "assignee": 1 + }, function (err) { + assert.ifError(err); + db.getObjectFields('flag:1', ['state', 'assignee'], function (err, data) { + if (err) { + throw err; + } + + assert.strictEqual('wip', data.state); + assert.strictEqual(1, data.assignee); + done(); + }); + }); + }); + + it('should persist to the flag\'s history', function (done) { + Flags.getHistory(1, function (err, history) { + if (err) { + throw err; + } + + history.forEach(function (change) { + switch (change.attribute) { + case 'state': + assert.strictEqual('[[flags:state-wip]]', change.value); + break; + + case 'assignee': + assert.strictEqual(1, change.value); + break; + } + }); + + done(); + }); + }); + }); + + describe('.getTarget()', function () { it('should return a post\'s data if queried with type "post"', function (done) { Flags.getTarget('post', 1, 1, function (err, data) { assert.ifError(err); @@ -141,7 +184,142 @@ describe('Flags', function () { done(); }); }); - });; + }); + + describe('.validate()', function () { + it('should error out if type is post and post is deleted', function (done) { + Posts.delete(1, 1, function (err) { + if (err) { + throw err; + } + + Flags.validate({ + type: 'post', + id: 1, + uid: 1 + }, function (err) { + assert.ok(err); + assert.strictEqual('[[error:post-deleted]]', err.message); + Posts.restore(1, 1, done); + }); + }); + }); + + it('should not pass validation if flag threshold is set and user rep does not meet it', function (done) { + Meta.configs.set('privileges:flag', '50', function (err) { + assert.ifError(err); + + Flags.validate({ + type: 'post', + id: 1, + uid: 1 + }, function (err) { + assert.ok(err); + assert.strictEqual('[[error:not-enough-reputation-to-flag]]', err.message); + Meta.configs.set('privileges:flag', 0, done); + }); + }); + }); + }); + + describe('.appendNote()', function () { + it('should add a note to a flag', function (done) { + Flags.appendNote(1, 1, 'this is my note', function (err) { + assert.ifError(err); + + db.getSortedSetRange('flag:1:notes', 0, -1, function (err, notes) { + if (err) { + throw err; + } + + assert.strictEqual('[1,"this is my note"]', notes[0]); + done(); + }); + }); + }); + + it('should be a JSON string', function (done) { + db.getSortedSetRange('flag:1:notes', 0, -1, function (err, notes) { + if (err) { + throw err; + } + + try { + JSON.parse(notes[0]); + } catch (e) { + assert.ifError(e); + } + + done(); + }); + }); + }); + + describe('.getNotes()', function () { + before(function (done) { + // Add a second note + Flags.appendNote(1, 1, 'this is the second note', done); + }); + + it('return should match a predefined spec', function (done) { + Flags.getNotes(1, function (err, notes) { + assert.ifError(err); + var compare = { + uid: 1, + content: 'this is my note' + }; + + var data = notes[1]; + for(var key in compare) { + if (compare.hasOwnProperty(key)) { + assert.ok(data[key]); + assert.strictEqual(data[key], compare[key]); + } + } + + done(); + }); + }); + + it('should retrieve a list of notes, from newest to oldest', function (done) { + Flags.getNotes(1, function (err, notes) { + assert.ifError(err); + assert(notes[0].datetime > notes[1].datetime); + assert.strictEqual('this is the second note', notes[0].content); + done(); + }); + }); + }); + + describe('.appendHistory()', function () { + var entries; + before(function (done) { + db.sortedSetCard('flag:1:history', function (err, count) { + entries = count; + done(err); + }); + }); + + it('should add a new entry into a flag\'s history', function (done) { + Flags.appendHistory(1, 1, ['state:rejected'], function (err) { + assert.ifError(err); + + Flags.getHistory(1, function (err, history) { + assert.strictEqual(entries + 1, history.length); + done(); + }); + }); + }); + }); + + describe('.getHistory()', function () { + it('should retrieve a flag\'s history', function (done) { + Flags.getHistory(1, function (err, history) { + assert.strictEqual(history[0].fields[0].value, '[[flags:state-rejected]]'); + done(); + }); + }); + }); after(function (done) { db.emptydb(done); From 5dd892bd010e126b2c8f13a47460144c3a77bdcf Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 7 Dec 2016 12:07:22 -0500 Subject: [PATCH 026/316] a bunch of changes here... allowing user profiles to be flagged, #5232 --- public/language/en-GB/flags.json | 13 +++- public/language/en-GB/notifications.json | 3 + public/language/en-GB/topic.json | 9 +-- public/language/en-GB/user.json | 1 + public/src/client/account/header.js | 10 +++ public/src/client/flags/list.js | 2 + public/src/client/topic/postTools.js | 9 ++- .../topic/flag.js => modules/flags.js} | 25 +++--- src/controllers/mods.js | 6 +- src/flags.js | 76 +++++++++++++++++-- src/meta/js.js | 4 +- src/notifications.js | 2 + test/flags.js | 12 ++- 13 files changed, 133 insertions(+), 39 deletions(-) rename public/src/{client/topic/flag.js => modules/flags.js} (62%) diff --git a/public/language/en-GB/flags.json b/public/language/en-GB/flags.json index 6b4e96f4fb..a9272897ca 100644 --- a/public/language/en-GB/flags.json +++ b/public/language/en-GB/flags.json @@ -29,6 +29,9 @@ "start-new-chat": "Start New Chat", "go-to-target": "View Flag Target", + "user-view": "View Profile", + "user-edit": "Edit Profile", + "notes": "Flag Notes", "add-note": "Add Note", "no-notes": "No shared notes.", @@ -43,5 +46,13 @@ "state-resolved": "Resolved", "state-rejected": "Rejected", "no-assignee": "Not Assigned", - "note-added": "Note Added" + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." } \ No newline at end of file diff --git a/public/language/en-GB/notifications.json b/public/language/en-GB/notifications.json index 5a2ed58908..0838ca17eb 100644 --- a/public/language/en-GB/notifications.json +++ b/public/language/en-GB/notifications.json @@ -21,6 +21,9 @@ "user_flagged_post_in": "%1 flagged a post in %2", "user_flagged_post_in_dual": "%1 and %2 flagged a post in %3", "user_flagged_post_in_multiple": "%1 and %2 others flagged a post in %3", + "user_flagged_user": "%1 flagged a user profile (%2)", + "user_flagged_user_dual": "%1 and %2 flagged a user profile (%3)", + "user_flagged_user_multiple": "%1 and %2 others flagged a user profile (%3)", "user_posted_to" : "%1 has posted a reply to: %2", "user_posted_to_dual" : "%1 and %2 have posted replies to: %3", "user_posted_to_multiple" : "%1 and %2 others have posted replies to: %3", diff --git a/public/language/en-GB/topic.json b/public/language/en-GB/topic.json index 4ae208076e..5571292dda 100644 --- a/public/language/en-GB/topic.json +++ b/public/language/en-GB/topic.json @@ -28,7 +28,6 @@ "link": "Link", "share": "Share", "tools": "Tools", - "flag": "Flag", "locked": "Locked", "pinned": "Pinned", "moved": "Moved", @@ -36,7 +35,6 @@ "bookmark_instructions" : "Click here to return to the last read post in this thread.", "flag_title": "Flag this post for moderation", - "flag_success": "This post has been flagged for moderation.", "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", @@ -138,10 +136,5 @@ "stale.create": "Create a new topic", "stale.reply_anyway": "Reply to this topic anyway", - "link_back": "Re: [%1](%2)\n\n", - - "spam": "Spam", - "offensive": "Offensive", - "custom-flag-reason": "Enter a flagging reason" - + "link_back": "Re: [%1](%2)\n\n" } diff --git a/public/language/en-GB/user.json b/public/language/en-GB/user.json index f0cb35f615..0725f208a3 100644 --- a/public/language/en-GB/user.json +++ b/public/language/en-GB/user.json @@ -35,6 +35,7 @@ "chat": "Chat", "chat_with": "Continue chat with %1", "new_chat_with": "Start new chat with %1", + "flag-profile": "Flag Profile", "follow": "Follow", "unfollow": "Unfollow", "more": "More", diff --git a/public/src/client/account/header.js b/public/src/client/account/header.js index d225e2cae1..b53cbb2be4 100644 --- a/public/src/client/account/header.js +++ b/public/src/client/account/header.js @@ -49,6 +49,7 @@ define('forum/account/header', [ components.get('account/ban').on('click', banAccount); components.get('account/unban').on('click', unbanAccount); components.get('account/delete').on('click', deleteAccount); + components.get('account/flag').on('click', flagAccount); }; function hidePrivateLinks() { @@ -167,6 +168,15 @@ define('forum/account/header', [ }); } + function flagAccount() { + require(['flags'], function (flags) { + flags.showFlagModal({ + type: 'user', + id: ajaxify.data.uid + }); + }); + } + function removeCover() { socket.emit('user.removeCover', { uid: ajaxify.data.uid diff --git a/public/src/client/flags/list.js b/public/src/client/flags/list.js index cbdaf94f24..bf8b4bc1a8 100644 --- a/public/src/client/flags/list.js +++ b/public/src/client/flags/list.js @@ -23,6 +23,8 @@ define('forum/flags/list', ['components'], function (components) { var qs = payload.map(function (filter) { if (filter.value) { return filter.name + '=' + filter.value; + } else { + return; } }).filter(Boolean).join('&'); diff --git a/public/src/client/topic/postTools.js b/public/src/client/topic/postTools.js index faa0567878..5618e692db 100644 --- a/public/src/client/topic/postTools.js +++ b/public/src/client/topic/postTools.js @@ -167,10 +167,11 @@ define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator postContainer.on('click', '[component="post/flag"]', function () { var pid = getData($(this), 'data-pid'); - var username = getData($(this), 'data-username'); - var userslug = getData($(this), 'data-userslug'); - require(['forum/topic/flag'], function (flag) { - flag.showFlagModal(pid, username, userslug); + require(['flags'], function (flags) { + flags.showFlagModal({ + type: 'post', + id: pid + }); }); }); diff --git a/public/src/client/topic/flag.js b/public/src/modules/flags.js similarity index 62% rename from public/src/client/topic/flag.js rename to public/src/modules/flags.js index 6b3440da54..cc9fd5103a 100644 --- a/public/src/client/topic/flag.js +++ b/public/src/modules/flags.js @@ -2,18 +2,13 @@ /* globals define, app, socket, templates */ -define('forum/topic/flag', [], function () { - +define('flags', [], function () { var Flag = {}, flagModal, flagCommit; - Flag.showFlagModal = function (pid, username, userslug) { - parseModal({ - pid: pid, - username: username, - userslug: userslug - }, function (html) { + Flag.showFlagModal = function (data) { + parseModal(data, function (html) { flagModal = $(html); flagModal.on('hidden.bs.modal', function () { @@ -23,11 +18,11 @@ define('forum/topic/flag', [], function () { flagCommit = flagModal.find('#flag-post-commit'); flagModal.on('click', '.flag-reason', function () { - flagPost(pid, $(this).text()); + createFlag(data.type, data.id, $(this).text()); }); flagCommit.on('click', function () { - flagPost(pid, flagModal.find('#flag-reason-custom').val()); + createFlag(data.type, data.id, flagModal.find('#flag-reason-custom').val()); }); flagModal.modal('show'); @@ -37,24 +32,24 @@ define('forum/topic/flag', [], function () { }; function parseModal(tplData, callback) { - templates.parse('partials/modals/flag_post_modal', tplData, function (html) { + templates.parse('partials/modals/flag_modal', tplData, function (html) { require(['translator'], function (translator) { translator.translate(html, callback); }); }); } - function flagPost(pid, reason) { - if (!pid || !reason) { + function createFlag(type, id, reason) { + if (!type || !id || !reason) { return; } - socket.emit('flags.create', {type: 'post', id: pid, reason: reason}, function (err) { + socket.emit('flags.create', {type: type, id: id, reason: reason}, function (err) { if (err) { return app.alertError(err.message); } flagModal.modal('hide'); - app.alertSuccess('[[topic:flag_success]]'); + app.alertSuccess('[[flags:modal-submit-success]]'); }); } diff --git a/src/controllers/mods.js b/src/controllers/mods.js index 788294a01e..656605f277 100644 --- a/src/controllers/mods.js +++ b/src/controllers/mods.js @@ -63,7 +63,11 @@ modsController.flags.detail = function (req, res, next) { } res.render('flags/detail', Object.assign(results.flagData, { - assignees: results.assignees + assignees: results.assignees, + type_bool: ['post', 'user'].reduce(function (memo, cur) { + memo[cur] = results.flagData.type === cur; + return memo; + }, {}) })); }); }; diff --git a/src/flags.js b/src/flags.js index 45aae2502e..9f121f81bf 100644 --- a/src/flags.js +++ b/src/flags.js @@ -167,20 +167,44 @@ Flags.validate = function (payload, callback) { switch (payload.type) { case 'post': async.parallel({ - privileges: async.apply(privileges.posts.get, [payload.id], payload.uid) + editable: async.apply(privileges.posts.canEdit, payload.id, payload.uid) }, function (err, subdata) { if (err) { return callback(err); } var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 1; - if (!subdata.privileges[0].isAdminOrMod && parseInt(data.reporter.reputation, 10) < minimumReputation) { + // Check if reporter meets rep threshold (or can edit the target post, in which case threshold does not apply) + if (!subdata.editable.flag && parseInt(data.reporter.reputation, 10) < minimumReputation) { return callback(new Error('[[error:not-enough-reputation-to-flag]]')); } callback(); }); break; + + case 'user': + async.parallel({ + editable: async.apply(privileges.users.canEdit, payload.uid, payload.id) + }, function (err, subdata) { + if (err) { + return callback(err); + } + + + var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 1; + // Check if reporter meets rep threshold (or can edit the target user, in which case threshold does not apply) + if (!subdata.editable && parseInt(data.reporter.reputation, 10) < minimumReputation) { + return callback(new Error('[[error:not-enough-reputation-to-flag]]')); + } + + callback(); + }); + break; + + default: + callback(new Error('[[error:invalid-data]]')); + break; } }); }; @@ -369,13 +393,17 @@ Flags.exists = function (type, id, uid, callback) { Flags.targetExists = function (type, id, callback) { switch (type) { - case 'topic': // just an example... - topics.exists(id, callback); - break; - case 'post': posts.exists(id, callback); break; + + case 'user': + user.exists(id, callback); + break; + + default: + callback(new Error('[[error:invalid-data]]')); + break; } }; @@ -384,6 +412,10 @@ Flags.getTargetUid = function (type, id, callback) { case 'post': posts.getPostField(id, 'uid', callback); break; + + case 'user': + setImmediate(callback, null, id); + break; } }; @@ -553,7 +585,7 @@ Flags.notify = function (flagObj, uid, callback) { notifications.create({ bodyShort: '[[notifications:user_flagged_post_in, ' + flagObj.reporter.username + ', ' + titleEscaped + ']]', - bodyLong: results.post.content, + bodyLong: flagObj.description, pid: flagObj.targetId, path: '/post/' + flagObj.targetId, nid: 'flag:post:' + flagObj.targetId + ':uid:' + uid, @@ -570,6 +602,36 @@ Flags.notify = function (flagObj, uid, callback) { }); }); break; + + case 'user': + async.parallel({ + admins: async.apply(groups.getMembers, 'administrators', 0, -1), + globalMods: async.apply(groups.getMembers, 'Global Moderators', 0, -1), + }, function (err, results) { + if (err) { + return callback(err); + } + + notifications.create({ + bodyShort: '[[notifications:user_flagged_user, ' + flagObj.reporter.username + ', ' + flagObj.target.username + ']]', + bodyLong: flagObj.description, + path: '/uid/' + flagObj.targetId, + nid: 'flag:user:' + flagObj.targetId + ':uid:' + uid, + from: uid, + mergeId: 'notifications:user_flagged_user|' + flagObj.targetId + }, function (err, notification) { + if (err || !notification) { + return callback(err); + } + + notifications.push(notification, results.admins.concat(results.globalMods), callback); + }); + }); + break; + + default: + callback(new Error('[[error:invalid-data]]')); + break; } }; diff --git a/src/meta/js.js b/src/meta/js.js index 626fa0ecd8..947550f37e 100644 --- a/src/meta/js.js +++ b/src/meta/js.js @@ -49,7 +49,6 @@ module.exports = function (Meta) { 'public/src/client/unread.js', 'public/src/client/topic.js', 'public/src/client/topic/events.js', - 'public/src/client/topic/flag.js', 'public/src/client/topic/fork.js', 'public/src/client/topic/move.js', 'public/src/client/topic/posts.js', @@ -72,7 +71,8 @@ module.exports = function (Meta) { 'public/src/modules/taskbar.js', 'public/src/modules/helpers.js', 'public/src/modules/sounds.js', - 'public/src/modules/string.js' + 'public/src/modules/string.js', + 'public/src/modules/flags.js' ], // modules listed below are routed through express (/src/modules) so they can be defined anonymously diff --git a/src/notifications.js b/src/notifications.js index b99700be01..0fb1e1ace3 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -415,6 +415,7 @@ var utils = require('../public/src/utils'); 'notifications:user_started_following_you', 'notifications:user_posted_to', 'notifications:user_flagged_post_in', + 'notifications:user_flagged_user', 'new_register' ], isolated, differentiators, differentiator, modifyIndex, set; @@ -462,6 +463,7 @@ var utils = require('../public/src/utils'); case 'notifications:user_started_following_you': case 'notifications:user_posted_to': case 'notifications:user_flagged_post_in': + case 'notifications:user_flagged_user': var usernames = set.map(function (notifObj) { return notifObj && notifObj.user && notifObj.user.username; }).filter(function (username, idx, array) { diff --git a/test/flags.js b/test/flags.js index ee735afd60..14bb1e65c6 100644 --- a/test/flags.js +++ b/test/flags.js @@ -32,6 +32,11 @@ describe('Flags', function () { content: 'This is flaggable content' }, next); }); + }, + function (topicData, next) { + User.create({ + username: 'testUser2', password: 'abcdef', email: 'c@d.com' + }, next); } ], done); }); @@ -212,7 +217,7 @@ describe('Flags', function () { Flags.validate({ type: 'post', id: 1, - uid: 1 + uid: 2 }, function (err) { assert.ok(err); assert.strictEqual('[[error:not-enough-reputation-to-flag]]', err.message); @@ -305,6 +310,10 @@ describe('Flags', function () { assert.ifError(err); Flags.getHistory(1, function (err, history) { + if (err) { + throw err; + } + assert.strictEqual(entries + 1, history.length); done(); }); @@ -315,6 +324,7 @@ describe('Flags', function () { describe('.getHistory()', function () { it('should retrieve a flag\'s history', function (done) { Flags.getHistory(1, function (err, history) { + assert.ifError(err); assert.strictEqual(history[0].fields[0].value, '[[flags:state-rejected]]'); done(); }); From a5fb4825b4c92a481e7da04246cb307b56f40bd0 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 7 Dec 2016 13:06:55 -0500 Subject: [PATCH 027/316] deprecating old hook for #5232 --- public/language/en-GB/pages.json | 2 +- src/flags.js | 7 ++++++- src/plugins/hooks.js | 3 ++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/public/language/en-GB/pages.json b/public/language/en-GB/pages.json index 801b28edea..104e2249f2 100644 --- a/public/language/en-GB/pages.json +++ b/public/language/en-GB/pages.json @@ -6,7 +6,7 @@ "popular-month": "Popular topics this month", "popular-alltime": "All time popular topics", "recent": "Recent Topics", - "flagged-posts": "Flagged Posts", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "Online Users", diff --git a/src/flags.js b/src/flags.js index 9f121f81bf..bfbe80992a 100644 --- a/src/flags.js +++ b/src/flags.js @@ -597,7 +597,9 @@ Flags.notify = function (flagObj, uid, callback) { return callback(err); } - plugins.fireHook('action:post.flag', {post: results.post, reason: flagObj.description, flaggingUser: flagObj.reporter}); + plugins.fireHook('action:flag.create', { + flag: flagObj + }); notifications.push(notification, results.admins.concat(results.moderators).concat(results.globalMods), callback); }); }); @@ -624,6 +626,9 @@ Flags.notify = function (flagObj, uid, callback) { return callback(err); } + plugins.fireHook('action:flag.create', { + flag: flagObj + }); notifications.push(notification, results.admins.concat(results.globalMods), callback); }); }); diff --git a/src/plugins/hooks.js b/src/plugins/hooks.js index b8e5af0e46..b5a32ba1b7 100644 --- a/src/plugins/hooks.js +++ b/src/plugins/hooks.js @@ -7,7 +7,8 @@ module.exports = function (Plugins) { Plugins.deprecatedHooks = { 'filter:user.custom_fields': null, // remove in v1.1.0 'filter:post.save': 'filter:post.create', - 'filter:user.profileLinks': 'filter:user.profileMenu' + 'filter:user.profileLinks': 'filter:user.profileMenu', + 'action:post.flag': 'action:flag.create' }; /* `data` is an object consisting of (* is required): From e40eb75f8f66a04811d64943426e1ff18c3f453c Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 7 Dec 2016 15:13:40 -0500 Subject: [PATCH 028/316] change history saving to append an object not a serialised array, #5232 --- src/flags.js | 41 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/src/flags.js b/src/flags.js index bfbe80992a..1655361b0e 100644 --- a/src/flags.js +++ b/src/flags.js @@ -422,7 +422,6 @@ Flags.getTargetUid = function (type, id, callback) { Flags.update = function (flagId, uid, changeset, callback) { // Retrieve existing flag data to compare for history-saving purposes var fields = ['state', 'assignee']; - var history = []; var tasks = []; var now = Date.now(); @@ -445,9 +444,6 @@ Flags.update = function (flagId, uid, changeset, callback) { tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byAssignee:' + changeset[prop], now, flagId)); break; } - - // Append to history payload - history.push(prop + ':' + changeset[prop]); } } } @@ -460,7 +456,7 @@ Flags.update = function (flagId, uid, changeset, callback) { // Save new object to db (upsert) tasks.push(async.apply(db.setObject, 'flag:' + flagId, changeset)); // Append history - tasks.push(async.apply(Flags.appendHistory, flagId, uid, history)); + tasks.push(async.apply(Flags.appendHistory, flagId, uid, changeset)); async.parallel(tasks, function (err, data) { return next(err); @@ -484,28 +480,21 @@ Flags.getHistory = function (flagId, callback) { uids.push(entry.value[0]); - // Deserialise field object - var fields = entry.value[1].map(function (field) { - field = field.toString().split(':'); - - switch (field[0]) { - case 'state': - field[1] = field[1] === undefined ? null : '[[flags:state-' + field[1] + ']]'; - break; - - default: - field[1] = field[1] === undefined ? null : field[1]; - break; - } - return { - "attribute": field[0], - "value": field[1] - }; - }); + // Deserialise changeset + var changeset = entry.value[1]; + if (changeset.hasOwnProperty('state')) { + changeset.state = changeset.state === undefined ? '' : '[[flags:state-' + changeset.state + ']]'; + } + if (changeset.hasOwnProperty('assignee')) { + changeset.assignee = changeset.assignee || ''; + } + if (changeset.hasOwnProperty('notes')) { + changeset.notes = changeset.notes || ''; + } return { uid: entry.value[0], - fields: fields, + fields: changeset, datetime: entry.score, datetimeISO: new Date(entry.score).toISOString() }; @@ -549,7 +538,9 @@ Flags.appendNote = function (flagId, uid, note, callback) { async.waterfall([ async.apply(db.sortedSetAdd, 'flag:' + flagId + ':notes', Date.now(), payload), - async.apply(Flags.appendHistory, flagId, uid, ['notes']) + async.apply(Flags.appendHistory, flagId, uid, { + notes: null + }) ], callback); }; From 6533fa066d219c05ad295a884d526e8fabf586ce Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 7 Dec 2016 15:42:47 -0500 Subject: [PATCH 029/316] removed unneeded fixes #5232 --- src/flags.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/flags.js b/src/flags.js index 1655361b0e..aafae593e0 100644 --- a/src/flags.js +++ b/src/flags.js @@ -485,12 +485,6 @@ Flags.getHistory = function (flagId, callback) { if (changeset.hasOwnProperty('state')) { changeset.state = changeset.state === undefined ? '' : '[[flags:state-' + changeset.state + ']]'; } - if (changeset.hasOwnProperty('assignee')) { - changeset.assignee = changeset.assignee || ''; - } - if (changeset.hasOwnProperty('notes')) { - changeset.notes = changeset.notes || ''; - } return { uid: entry.value[0], From 31996f9377a8142ec9be92164905f182ab0bcc45 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 7 Dec 2016 15:51:05 -0500 Subject: [PATCH 030/316] added page titles --- public/language/en-GB/pages.json | 3 +++ src/controllers/mods.js | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/public/language/en-GB/pages.json b/public/language/en-GB/pages.json index 104e2249f2..5efa686fc3 100644 --- a/public/language/en-GB/pages.json +++ b/public/language/en-GB/pages.json @@ -32,6 +32,9 @@ "chats": "Chats", "chat": "Chatting with %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", + "account/edit": "Editing \"%1\"", "account/edit/password": "Editing password of \"%1\"", "account/edit/username": "Editing username of \"%1\"", diff --git a/src/controllers/mods.js b/src/controllers/mods.js index 656605f277..62f7b6dd07 100644 --- a/src/controllers/mods.js +++ b/src/controllers/mods.js @@ -43,7 +43,8 @@ modsController.flags.list = function (req, res, next) { res.render('flags/list', { flags: flags, hasFilter: !!Object.keys(filters).length, - filters: filters + filters: filters, + title: '[[pages:flags]]' }); }); }); @@ -67,7 +68,8 @@ modsController.flags.detail = function (req, res, next) { type_bool: ['post', 'user'].reduce(function (memo, cur) { memo[cur] = results.flagData.type === cur; return memo; - }, {}) + }, {}), + title: '[[pages:flag-details, ' + req.params.flagId + ']]' })); }); }; From ebc9abd7730bac3366dd4599b5a365c58a7c5697 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 9 Dec 2016 14:33:59 -0500 Subject: [PATCH 031/316] upgrade script and graphs for #5232 --- public/src/client/flags/list.js | 51 +++++++- src/controllers/mods.js | 9 +- src/flags.js | 49 +++++--- src/upgrade.js | 87 +++++++++++++- src/views/admin/manage/flags.tpl | 196 ------------------------------- 5 files changed, 177 insertions(+), 215 deletions(-) delete mode 100644 src/views/admin/manage/flags.tpl diff --git a/public/src/client/flags/list.js b/public/src/client/flags/list.js index bf8b4bc1a8..7647e1db6d 100644 --- a/public/src/client/flags/list.js +++ b/public/src/client/flags/list.js @@ -2,12 +2,13 @@ /* globals define */ -define('forum/flags/list', ['components'], function (components) { +define('forum/flags/list', ['components', 'Chart'], function (components, Chart) { var Flags = {}; Flags.init = function () { Flags.enableFilterForm(); Flags.enableChatButtons(); + Flags.handleGraphs(); }; Flags.enableFilterForm = function () { @@ -38,5 +39,53 @@ define('forum/flags/list', ['components'], function (components) { }); }; + Flags.handleGraphs = function () { + var dailyCanvas = document.getElementById('flags:daily'); + var dailyLabels = utils.getDaysArray().map(function (text, idx) { + return idx % 3 ? '' : text; + }); + + if (utils.isMobile()) { + Chart.defaults.global.tooltips.enabled = false; + } + var data = { + 'flags:daily': { + labels: dailyLabels, + datasets: [ + { + label: "", + backgroundColor: "rgba(151,187,205,0.2)", + borderColor: "rgba(151,187,205,1)", + pointBackgroundColor: "rgba(151,187,205,1)", + pointHoverBackgroundColor: "#fff", + pointBorderColor: "#fff", + pointHoverBorderColor: "rgba(151,187,205,1)", + data: ajaxify.data.analytics + } + ] + } + }; + + dailyCanvas.width = $(dailyCanvas).parent().width(); + new Chart(dailyCanvas.getContext('2d'), { + type: 'line', + data: data['flags:daily'], + options: { + responsive: true, + animation: false, + legend: { + display: false + }, + scales: { + yAxes: [{ + ticks: { + beginAtZero: true + } + }] + } + } + }); + }; + return Flags; }); diff --git a/src/controllers/mods.js b/src/controllers/mods.js index 62f7b6dd07..39820e60a6 100644 --- a/src/controllers/mods.js +++ b/src/controllers/mods.js @@ -4,6 +4,7 @@ var async = require('async'); var user = require('../user'); var flags = require('../flags'); +var analytics = require('../analytics'); // var adminFlagsController = require('./admin/flags'); var modsController = { @@ -35,13 +36,17 @@ modsController.flags.list = function (req, res, next) { return memo; }, {}); - flags.list(filters, req.uid, function (err, flags) { + async.parallel({ + flags: async.apply(flags.list, filters, req.uid), + analytics: async.apply(analytics.getDailyStatsForSet, 'analytics:flags', Date.now(), 30) + }, function (err, data) { if (err) { return next(err); } res.render('flags/list', { - flags: flags, + flags: data.flags, + analytics: data.analytics, hasFilter: !!Object.keys(filters).length, filters: filters, title: '[[pages:flags]]' diff --git a/src/flags.js b/src/flags.js index aafae593e0..7c56ba4fb5 100644 --- a/src/flags.js +++ b/src/flags.js @@ -267,8 +267,16 @@ Flags.getNotes = function (flagId, callback) { ], callback); }; -Flags.create = function (type, id, uid, reason, callback) { +Flags.create = function (type, id, uid, reason, timestamp, callback) { var targetUid; + var doHistoryAppend = false; + + // timestamp is optional + if (typeof timestamp === 'function' && !callback) { + callback = timestamp; + timestamp = Date.now(); + doHistoryAppend = true; + } async.waterfall([ function (next) { @@ -302,16 +310,17 @@ Flags.create = function (type, id, uid, reason, callback) { targetId: id, description: reason, uid: uid, - datetime: Date.now() + datetime: timestamp }), - async.apply(db.sortedSetAdd.bind(db), 'flags:datetime', Date.now(), flagId), // by time, the default - async.apply(db.sortedSetAdd.bind(db), 'flags:byReporter:' + uid, Date.now(), flagId), // by reporter - async.apply(db.sortedSetAdd.bind(db), 'flags:byType:' + type, Date.now(), flagId), // by flag type - async.apply(db.setObjectField.bind(db), 'flagHash:flagId', [type, id, uid].join(':'), flagId) // save hash for existence checking + async.apply(db.sortedSetAdd.bind(db), 'flags:datetime', timestamp, flagId), // by time, the default + async.apply(db.sortedSetAdd.bind(db), 'flags:byReporter:' + uid, timestamp, flagId), // by reporter + async.apply(db.sortedSetAdd.bind(db), 'flags:byType:' + type, timestamp, flagId), // by flag type + async.apply(db.setObjectField.bind(db), 'flagHash:flagId', [type, id, uid].join(':'), flagId), // save hash for existence checking + async.apply(analytics.increment, 'flags') // some fancy analytics ]; if (targetUid) { - tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byTargetUid:' + targetUid, Date.now(), flagId)); // by target uid + tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byTargetUid:' + targetUid, timestamp, flagId)); // by target uid } async.parallel(tasks, function (err, data) { @@ -319,7 +328,10 @@ Flags.create = function (type, id, uid, reason, callback) { return next(err); } - Flags.update(flagId, uid, { "state": "open" }); + if (doHistoryAppend) { + Flags.update(flagId, uid, { "state": "open" }); + } + next(null, flagId); }); }, @@ -423,7 +435,7 @@ Flags.update = function (flagId, uid, changeset, callback) { // Retrieve existing flag data to compare for history-saving purposes var fields = ['state', 'assignee']; var tasks = []; - var now = Date.now(); + var now = changeset.datetime || Date.now(); async.waterfall([ async.apply(db.getObjectFields.bind(db), 'flag:' + flagId, fields), @@ -513,16 +525,24 @@ Flags.getHistory = function (flagId, callback) { Flags.appendHistory = function (flagId, uid, changeset, callback) { var payload; + var datetime = changeset.datetime || Date.now(); + delete changeset.datetime; + try { - payload = JSON.stringify([uid, changeset, Date.now()]); + payload = JSON.stringify([uid, changeset, datetime]); } catch (e) { return callback(e); } - db.sortedSetAdd('flag:' + flagId + ':history', Date.now(), payload, callback); + db.sortedSetAdd('flag:' + flagId + ':history', datetime, payload, callback); }; -Flags.appendNote = function (flagId, uid, note, callback) { +Flags.appendNote = function (flagId, uid, note, datetime, callback) { + if (typeof datetime === 'function' && !callback) { + callback = datetime; + datetime = Date.now(); + } + var payload; try { payload = JSON.stringify([uid, note]); @@ -531,9 +551,10 @@ Flags.appendNote = function (flagId, uid, note, callback) { } async.waterfall([ - async.apply(db.sortedSetAdd, 'flag:' + flagId + ':notes', Date.now(), payload), + async.apply(db.sortedSetAdd, 'flag:' + flagId + ':notes', datetime, payload), async.apply(Flags.appendHistory, flagId, uid, { - notes: null + notes: null, + datetime: datetime }) ], callback); }; diff --git a/src/upgrade.js b/src/upgrade.js index 2a605e84f9..29b806d4a7 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -12,7 +12,7 @@ var db = require('./database'), schemaDate, thisSchemaDate, // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema - latestSchema = Date.UTC(2016, 10, 22); + latestSchema = Date.UTC(2016, 11, 7); Upgrade.check = function (callback) { db.get('schemaDate', function (err, value) { @@ -1023,7 +1023,7 @@ Upgrade.upgrade = function (callback) { if (schemaDate < thisSchemaDate) { updatesMade = true; - winston.info('[2016/11/25] Creating sorted sets for pinned topcis'); + winston.info('[2016/11/25] Creating sorted sets for pinned topics'); var topics = require('./topics'); var batch = require('./batch'); @@ -1060,6 +1060,89 @@ Upgrade.upgrade = function (callback) { next(); } }, + function (next) { + thisSchemaDate = Date.UTC(2016, 11, 7); + + if (schemaDate < thisSchemaDate) { + updatesMade = true; + winston.info('[2016/12/07] Migrating flags to new schema (#5232)'); + + var batch = require('./batch'); + var posts = require('./posts'); + var flags = require('./flags'); + var migrated = 0; + + batch.processSortedSet('posts:pid', function (ids, next) { + posts.getPostsByPids(ids, 1, function (err, posts) { + if (err) { + return next(err); + } + + posts = posts.filter(function (post) { + return post.hasOwnProperty('flags'); + }); + + async.each(posts, function (post, next) { + async.parallel({ + uids: async.apply(db.getSortedSetRangeWithScores, 'pid:' + post.pid + ':flag:uids', 0, -1), + reasons: async.apply(db.getSortedSetRange, 'pid:' + post.pid + ':flag:uid:reason', 0, -1) + }, function (err, data) { + if (err) { + return next(err); + } + + // Just take the first entry + var datetime = data.uids[0].score; + var reason = data.reasons[0].split(':')[1]; + var flagObj; + + async.waterfall([ + async.apply(flags.create, 'post', post.pid, data.uids[0].value, reason, datetime), + function (_flagObj, next) { + flagObj = _flagObj; + if (post['flag:state'] || post['flag:assignee']) { + flags.update(flagObj.flagId, 1, { + state: post['flag:state'], + assignee: post['flag:assignee'], + datetime: datetime + }, next); + } else { + setImmediate(next); + } + }, + function (next) { + if (post.hasOwnProperty('flag:notes') && post['flag:notes'].length) { + try { + var history = JSON.parse(post['flag:history']); + history = history.filter(function (event) { + return event.type === 'notes'; + })[0]; + + flags.appendNote(flagObj.flagId, history.uid, post['flag:notes'], history.timestamp, next); + } catch (e) { + next(e); + } + } else { + setImmediate(next); + } + } + ], next); + }); + }, next); + }); + }, function (err) { + if (err) { + return next(err); + } + + winston.info('[2016/12/07] Migrating flags to new schema (#5232) - done'); + Upgrade.update(thisSchemaDate, next); + }); + } else { + winston.info('[2016/12/07] Migrating flags to new schema (#5232) - skipped!'); + next(); + } + } // Add new schema updates here // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 24!!! ], function (err) { diff --git a/src/views/admin/manage/flags.tpl b/src/views/admin/manage/flags.tpl deleted file mode 100644 index a14348ba42..0000000000 --- a/src/views/admin/manage/flags.tpl +++ /dev/null @@ -1,196 +0,0 @@ -
    - -
    - -
    -
    -
    -
    -

    - -

    -
    - -
    -
    - - - -
    - -
    - -
    - -
    - No flagged posts! -
    - - - -
    - -
    -
    -
    -
    -
    - - - - -
    {../user.icon:text}
    - -
    - - - {../user.username} - -
    -

    {posts.content}

    -
    - - - Posted in {posts.category.name}, • - Read More - - -
    -
    -
    - This post has been flagged {posts.flags} time(s): -
    - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - -
    -
    - - -
    -
    - - -
    - -
    -
    -
    -
    [[topic:flag_manage_history]]
    - -
    [[topic:flag_manage_no_history]]
    - -
      - -
    • -
      - - - -
      {../user.icon:text}
      - - [[topic:flag_manage_history_{posts.flagData.history.type}, {posts.flagData.history.label}]] -
    • - -
    - -
    -
    -
    -
    -
    - - -
    -
    -
    -
    From 9ada35cfb982ca332e9c097e06588bb0e986046f Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 9 Dec 2016 14:39:31 -0500 Subject: [PATCH 032/316] allowing Analytics.increment to have a callback --- src/analytics.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/analytics.js b/src/analytics.js index 6b248057da..9151bd6ce7 100644 --- a/src/analytics.js +++ b/src/analytics.js @@ -20,13 +20,17 @@ new cronJob('*/10 * * * *', function () { Analytics.writeData(); }, null, true); -Analytics.increment = function (keys) { +Analytics.increment = function (keys, callback) { keys = Array.isArray(keys) ? keys : [keys]; keys.forEach(function (key) { counters[key] = counters[key] || 0; ++counters[key]; }); + + if (typeof callback === 'function') { + callback(); + } }; Analytics.pageView = function (payload) { From 47530423066c778983d7d00caa0073a1fb72d5c9 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 9 Dec 2016 14:53:49 -0500 Subject: [PATCH 033/316] lowered analytics disk writes to every ten seconds, because why every 10 minutes? :shipit: --- src/analytics.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analytics.js b/src/analytics.js index 9151bd6ce7..c6cfbeba7e 100644 --- a/src/analytics.js +++ b/src/analytics.js @@ -16,7 +16,7 @@ var uniquevisitors = 0; var isCategory = /^(?:\/api)?\/category\/(\d+)/; -new cronJob('*/10 * * * *', function () { +new cronJob('*/10 * * * * *', function () { Analytics.writeData(); }, null, true); From aaec71bc0ca1d3613a07f6906b8d951ae4e60fab Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 9 Dec 2016 14:59:23 -0500 Subject: [PATCH 034/316] added stepSize to flags chart, #5232 --- public/src/client/flags/list.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/src/client/flags/list.js b/public/src/client/flags/list.js index 7647e1db6d..255c7176f1 100644 --- a/public/src/client/flags/list.js +++ b/public/src/client/flags/list.js @@ -79,7 +79,8 @@ define('forum/flags/list', ['components', 'Chart'], function (components, Chart) scales: { yAxes: [{ ticks: { - beginAtZero: true + beginAtZero: true, + stepSize: 1 } }] } From 01969970995810602d26ff25ebc9ea8e38760a3e Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 13 Dec 2016 09:36:46 -0500 Subject: [PATCH 035/316] fixing issues as found by @barisusakli re: #5232 and #5282 --- src/flags.js | 89 +++++++--------------------------------------------- 1 file changed, 11 insertions(+), 78 deletions(-) diff --git a/src/flags.js b/src/flags.js index 7c56ba4fb5..1fb2986d2b 100644 --- a/src/flags.js +++ b/src/flags.js @@ -139,13 +139,7 @@ Flags.list = function (filters, uid, callback) { }); }, next); } - ], function (err, flags) { - if (err) { - return callback(err); - } - - return callback(null, flags); - }); + ], callback); }; Flags.validate = function (payload, callback) { @@ -166,16 +160,14 @@ Flags.validate = function (payload, callback) { switch (payload.type) { case 'post': - async.parallel({ - editable: async.apply(privileges.posts.canEdit, payload.id, payload.uid) - }, function (err, subdata) { + privileges.posts.canEdit(payload.id, payload.uid, function (err, editable) { if (err) { return callback(err); } var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 1; // Check if reporter meets rep threshold (or can edit the target post, in which case threshold does not apply) - if (!subdata.editable.flag && parseInt(data.reporter.reputation, 10) < minimumReputation) { + if (!editable.flag && parseInt(data.reporter.reputation, 10) < minimumReputation) { return callback(new Error('[[error:not-enough-reputation-to-flag]]')); } @@ -184,17 +176,14 @@ Flags.validate = function (payload, callback) { break; case 'user': - async.parallel({ - editable: async.apply(privileges.users.canEdit, payload.uid, payload.id) - }, function (err, subdata) { + privileges.users.canEdit(payload.uid, payload.id, function (err, editable) { if (err) { return callback(err); } - var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 1; // Check if reporter meets rep threshold (or can edit the target user, in which case threshold does not apply) - if (!subdata.editable && parseInt(data.reporter.reputation, 10) < minimumReputation) { + if (!editable && parseInt(data.reporter.reputation, 10) < minimumReputation) { return callback(new Error('[[error:not-enough-reputation-to-flag]]')); } @@ -227,6 +216,10 @@ Flags.getTarget = function (type, id, uid, callback) { callback(err, users ? users[0] : undefined); }); break; + + default: + callback(new Error('[[error:invalid-data]]')); + break; } }; @@ -315,7 +308,7 @@ Flags.create = function (type, id, uid, reason, timestamp, callback) { async.apply(db.sortedSetAdd.bind(db), 'flags:datetime', timestamp, flagId), // by time, the default async.apply(db.sortedSetAdd.bind(db), 'flags:byReporter:' + uid, timestamp, flagId), // by reporter async.apply(db.sortedSetAdd.bind(db), 'flags:byType:' + type, timestamp, flagId), // by flag type - async.apply(db.setObjectField.bind(db), 'flagHash:flagId', [type, id, uid].join(':'), flagId), // save hash for existence checking + async.apply(db.sortedSetAdd.bind(db), 'flags:hash', flagId, [type, id, uid].join(':')), // save zset for duplicate checking async.apply(analytics.increment, 'flags') // some fancy analytics ]; @@ -337,70 +330,10 @@ Flags.create = function (type, id, uid, reason, timestamp, callback) { }, async.apply(Flags.get) ], callback); - // if (!parseInt(uid, 10) || !reason) { - // return callback(); - // } - - // async.waterfall([ - // function (next) { - // async.parallel({ - // hasFlagged: async.apply(Flags.isFlaggedByUser, post.pid, uid), - // exists: async.apply(Posts.exists, post.pid) - // }, next); - // }, - // function (results, next) { - // if (!results.exists) { - // return next(new Error('[[error:no-post]]')); - // } - - // if (results.hasFlagged) { - // return next(new Error('[[error:already-flagged]]')); - // } - - // var now = Date.now(); - // async.parallel([ - // function (next) { - // db.sortedSetAdd('posts:flagged', now, post.pid, next); - // }, - // function (next) { - // db.sortedSetIncrBy('posts:flags:count', 1, post.pid, next); - // }, - // function (next) { - // db.incrObjectField('post:' + post.pid, 'flags', next); - // }, - // function (next) { - // db.sortedSetAdd('pid:' + post.pid + ':flag:uids', now, uid, next); - // }, - // function (next) { - // db.sortedSetAdd('pid:' + post.pid + ':flag:uid:reason', 0, uid + ':' + reason, next); - // }, - // function (next) { - // if (parseInt(post.uid, 10)) { - // async.parallel([ - // async.apply(db.sortedSetIncrBy, 'users:flags', 1, post.uid), - // async.apply(db.incrObjectField, 'user:' + post.uid, 'flags'), - // async.apply(db.sortedSetAdd, 'uid:' + post.uid + ':flag:pids', now, post.pid) - // ], next); - // } else { - // next(); - // } - // } - // ], next); - // }, - // function (data, next) { - // openNewFlag(post.pid, uid, next); // removed, used to just update flag to open state if new flag - // } - // ], function (err) { - // if (err) { - // return callback(err); - // } - // analytics.increment('flags'); - // callback(); - // }); }; Flags.exists = function (type, id, uid, callback) { - db.isObjectField('flagHash:flagId', [type, id, uid].join(':'), callback); + db.isSortedSetMember('flags:hash', [type, id, uid].join(':'), callback); }; Flags.targetExists = function (type, id, callback) { From 831c2064a0435eb07c5bb3d010753dc0416e6166 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 13 Dec 2016 12:11:51 -0500 Subject: [PATCH 036/316] For #5232, added tests and returning flag data on socket flag creation --- src/flags.js | 4 + src/socket.io/flags.js | 12 +- test/flags.js | 612 +++++++++++++++++++++++------------------ 3 files changed, 358 insertions(+), 270 deletions(-) diff --git a/src/flags.js b/src/flags.js index 1fb2986d2b..1ae5a84ed8 100644 --- a/src/flags.js +++ b/src/flags.js @@ -494,6 +494,10 @@ Flags.appendNote = function (flagId, uid, note, datetime, callback) { Flags.notify = function (flagObj, uid, callback) { // Notify administrators, mods, and other associated people + if (!callback) { + callback = function () {}; + } + switch (flagObj.type) { case 'post': async.parallel({ diff --git a/src/socket.io/flags.js b/src/socket.io/flags.js index ce148faa87..c25bd662fa 100644 --- a/src/socket.io/flags.js +++ b/src/socket.io/flags.js @@ -34,11 +34,15 @@ SocketFlags.create = function (socket, data, callback) { function (next) { // If we got here, then no errors occurred flags.create(data.type, data.id, socket.uid, data.reason, next); - }, - function (flagObj, next) { - flags.notify(flagObj, socket.uid, next); } - ], callback); + ], function (err, flagObj) { + if (err) { + return callback(err); + } + + flags.notify(flagObj, socket.uid); + callback(null, flagObj); + }); }; SocketFlags.update = function (socket, data, callback) { diff --git a/test/flags.js b/test/flags.js index 14bb1e65c6..8d359c6930 100644 --- a/test/flags.js +++ b/test/flags.js @@ -10,6 +10,7 @@ var Categories = require('../src/categories'); var Topics = require('../src/topics'); var Posts = require('../src/posts'); var User = require('../src/user'); +var Groups = require('../src/groups'); var Meta = require('../src/meta'); describe('Flags', function () { @@ -37,300 +38,379 @@ describe('Flags', function () { User.create({ username: 'testUser2', password: 'abcdef', email: 'c@d.com' }, next); + }, + function (uid, next) { + Groups.join('administrators', uid, next); } ], done); }); - describe('.create()', function () { - it('should create a flag and return its data', function (done) { - Flags.create('post', 1, 1, 'Test flag', function (err, flagData) { - assert.ifError(err); - var compare = { - flagId: 1, - uid: 1, - targetId: 1, - type: 'post', - description: 'Test flag' - }; - - for(var key in compare) { - if (compare.hasOwnProperty(key)) { - assert.ok(flagData[key]); - assert.strictEqual(flagData[key], compare[key]); - } - } - - done(); - }); - }); - }); - - describe('.get()', function () { - it('should retrieve and display a flag\'s data', function (done) { - Flags.get(1, function (err, flagData) { - assert.ifError(err); - var compare = { - flagId: 1, - uid: 1, - targetId: 1, - type: 'post', - description: 'Test flag', - state: 'open' - }; - - for(var key in compare) { - if (compare.hasOwnProperty(key)) { - assert.ok(flagData[key]); - assert.strictEqual(flagData[key], compare[key]); - } - } - - done(); - }); - }); - }); - - describe('.list()', function () { - it('should show a list of flags (with one item)', function (done) { - Flags.list({}, 1, function (err, flags) { - assert.ifError(err); - assert.ok(Array.isArray(flags)); - assert.equal(flags.length, 1); + // describe('.create()', function () { + // it('should create a flag and return its data', function (done) { + // Flags.create('post', 1, 1, 'Test flag', function (err, flagData) { + // assert.ifError(err); + // var compare = { + // flagId: 1, + // uid: 1, + // targetId: 1, + // type: 'post', + // description: 'Test flag' + // }; + + // for(var key in compare) { + // if (compare.hasOwnProperty(key)) { + // assert.ok(flagData[key]); + // assert.strictEqual(flagData[key], compare[key]); + // } + // } + + // done(); + // }); + // }); + // }); + + // describe('.get()', function () { + // it('should retrieve and display a flag\'s data', function (done) { + // Flags.get(1, function (err, flagData) { + // assert.ifError(err); + // var compare = { + // flagId: 1, + // uid: 1, + // targetId: 1, + // type: 'post', + // description: 'Test flag', + // state: 'open' + // }; + + // for(var key in compare) { + // if (compare.hasOwnProperty(key)) { + // assert.ok(flagData[key]); + // assert.strictEqual(flagData[key], compare[key]); + // } + // } + + // done(); + // }); + // }); + // }); + + // describe('.list()', function () { + // it('should show a list of flags (with one item)', function (done) { + // Flags.list({}, 1, function (err, flags) { + // assert.ifError(err); + // assert.ok(Array.isArray(flags)); + // assert.equal(flags.length, 1); - Flags.get(flags[0].flagId, function (err, flagData) { - assert.ifError(err); - assert.equal(flags[0].flagId, flagData.flagId); - assert.equal(flags[0].description, flagData.description); - done(); - }); - }); - }); - }); - - describe('.update()', function () { - it('should alter a flag\'s various attributes and persist them to the database', function (done) { - Flags.update(1, 1, { - "state": "wip", - "assignee": 1 - }, function (err) { - assert.ifError(err); - db.getObjectFields('flag:1', ['state', 'assignee'], function (err, data) { - if (err) { - throw err; - } - - assert.strictEqual('wip', data.state); - assert.strictEqual(1, data.assignee); - done(); - }); - }); - }); - - it('should persist to the flag\'s history', function (done) { - Flags.getHistory(1, function (err, history) { - if (err) { - throw err; - } - - history.forEach(function (change) { - switch (change.attribute) { - case 'state': - assert.strictEqual('[[flags:state-wip]]', change.value); - break; + // Flags.get(flags[0].flagId, function (err, flagData) { + // assert.ifError(err); + // assert.equal(flags[0].flagId, flagData.flagId); + // assert.equal(flags[0].description, flagData.description); + // done(); + // }); + // }); + // }); + // }); + + // describe('.update()', function () { + // it('should alter a flag\'s various attributes and persist them to the database', function (done) { + // Flags.update(1, 1, { + // "state": "wip", + // "assignee": 1 + // }, function (err) { + // assert.ifError(err); + // db.getObjectFields('flag:1', ['state', 'assignee'], function (err, data) { + // if (err) { + // throw err; + // } + + // assert.strictEqual('wip', data.state); + // assert.strictEqual(1, data.assignee); + // done(); + // }); + // }); + // }); + + // it('should persist to the flag\'s history', function (done) { + // Flags.getHistory(1, function (err, history) { + // if (err) { + // throw err; + // } + + // history.forEach(function (change) { + // switch (change.attribute) { + // case 'state': + // assert.strictEqual('[[flags:state-wip]]', change.value); + // break; - case 'assignee': - assert.strictEqual(1, change.value); - break; - } - }); - - done(); - }); - }); - }); - - describe('.getTarget()', function () { - it('should return a post\'s data if queried with type "post"', function (done) { - Flags.getTarget('post', 1, 1, function (err, data) { - assert.ifError(err); - var compare = { - uid: 1, - pid: 1, - content: 'This is flaggable content' - }; - - for(var key in compare) { - if (compare.hasOwnProperty(key)) { - assert.ok(data[key]); - assert.strictEqual(data[key], compare[key]); - } - } - - done(); - }); - }); + // case 'assignee': + // assert.strictEqual(1, change.value); + // break; + // } + // }); + + // done(); + // }); + // }); + // }); + + // describe('.getTarget()', function () { + // it('should return a post\'s data if queried with type "post"', function (done) { + // Flags.getTarget('post', 1, 1, function (err, data) { + // assert.ifError(err); + // var compare = { + // uid: 1, + // pid: 1, + // content: 'This is flaggable content' + // }; + + // for(var key in compare) { + // if (compare.hasOwnProperty(key)) { + // assert.ok(data[key]); + // assert.strictEqual(data[key], compare[key]); + // } + // } + + // done(); + // }); + // }); + + // it('should return a user\'s data if queried with type "user"', function (done) { + // Flags.getTarget('user', 1, 1, function (err, data) { + // assert.ifError(err); + // var compare = { + // uid: 1, + // username: 'testUser', + // email: 'b@c.com' + // }; + + // for(var key in compare) { + // if (compare.hasOwnProperty(key)) { + // assert.ok(data[key]); + // assert.strictEqual(data[key], compare[key]); + // } + // } + + // done(); + // }); + // }); + // }); + + // describe('.validate()', function () { + // it('should error out if type is post and post is deleted', function (done) { + // Posts.delete(1, 1, function (err) { + // if (err) { + // throw err; + // } + + // Flags.validate({ + // type: 'post', + // id: 1, + // uid: 1 + // }, function (err) { + // assert.ok(err); + // assert.strictEqual('[[error:post-deleted]]', err.message); + // Posts.restore(1, 1, done); + // }); + // }); + // }); + + // it('should not pass validation if flag threshold is set and user rep does not meet it', function (done) { + // Meta.configs.set('privileges:flag', '50', function (err) { + // assert.ifError(err); + + // Flags.validate({ + // type: 'post', + // id: 1, + // uid: 2 + // }, function (err) { + // assert.ok(err); + // assert.strictEqual('[[error:not-enough-reputation-to-flag]]', err.message); + // Meta.configs.set('privileges:flag', 0, done); + // }); + // }); + // }); + // }); + + // describe('.appendNote()', function () { + // it('should add a note to a flag', function (done) { + // Flags.appendNote(1, 1, 'this is my note', function (err) { + // assert.ifError(err); + + // db.getSortedSetRange('flag:1:notes', 0, -1, function (err, notes) { + // if (err) { + // throw err; + // } + + // assert.strictEqual('[1,"this is my note"]', notes[0]); + // done(); + // }); + // }); + // }); + + // it('should be a JSON string', function (done) { + // db.getSortedSetRange('flag:1:notes', 0, -1, function (err, notes) { + // if (err) { + // throw err; + // } + + // try { + // JSON.parse(notes[0]); + // } catch (e) { + // assert.ifError(e); + // } + + // done(); + // }); + // }); + // }); + + // describe('.getNotes()', function () { + // before(function (done) { + // // Add a second note + // Flags.appendNote(1, 1, 'this is the second note', done); + // }); + + // it('return should match a predefined spec', function (done) { + // Flags.getNotes(1, function (err, notes) { + // assert.ifError(err); + // var compare = { + // uid: 1, + // content: 'this is my note' + // }; + + // var data = notes[1]; + // for(var key in compare) { + // if (compare.hasOwnProperty(key)) { + // assert.ok(data[key]); + // assert.strictEqual(data[key], compare[key]); + // } + // } + + // done(); + // }); + // }); + + // it('should retrieve a list of notes, from newest to oldest', function (done) { + // Flags.getNotes(1, function (err, notes) { + // assert.ifError(err); + // assert(notes[0].datetime > notes[1].datetime); + // assert.strictEqual('this is the second note', notes[0].content); + // done(); + // }); + // }); + // }); + + // describe('.appendHistory()', function () { + // var entries; + // before(function (done) { + // db.sortedSetCard('flag:1:history', function (err, count) { + // entries = count; + // done(err); + // }); + // }); + + // it('should add a new entry into a flag\'s history', function (done) { + // Flags.appendHistory(1, 1, ['state:rejected'], function (err) { + // assert.ifError(err); + + // Flags.getHistory(1, function (err, history) { + // if (err) { + // throw err; + // } + + // assert.strictEqual(entries + 1, history.length); + // done(); + // }); + // }); + // }); + // }); + + // describe('.getHistory()', function () { + // it('should retrieve a flag\'s history', function (done) { + // Flags.getHistory(1, function (err, history) { + // assert.ifError(err); + // assert.strictEqual(history[0].fields[0].value, '[[flags:state-rejected]]'); + // done(); + // }); + // }); + // }); + + describe('(websockets)', function () { + var SocketFlags = require('../src/socket.io/flags.js'); + var tid, pid, flag; - it('should return a user\'s data if queried with type "user"', function (done) { - Flags.getTarget('user', 1, 1, function (err, data) { - assert.ifError(err); - var compare = { - uid: 1, - username: 'testUser', - email: 'b@c.com' - }; - - for(var key in compare) { - if (compare.hasOwnProperty(key)) { - assert.ok(data[key]); - assert.strictEqual(data[key], compare[key]); - } - } + before(function (done) { + Topics.post({ + cid: 1, + uid: 1, + title: 'Another topic', + content: 'This is flaggable content' + }, function (err, topic) { + tid = topic.postData.tid; + pid = topic.postData.pid; - done(); + done(err); }); }); - }); - - describe('.validate()', function () { - it('should error out if type is post and post is deleted', function (done) { - Posts.delete(1, 1, function (err) { - if (err) { - throw err; - } - Flags.validate({ + describe('.create()', function () { + it('should create a flag with no errors', function (done) { + SocketFlags.create({ uid: 2 }, { type: 'post', - id: 1, - uid: 1 - }, function (err) { - assert.ok(err); - assert.strictEqual('[[error:post-deleted]]', err.message); - Posts.restore(1, 1, done); - }); - }); - }); - - it('should not pass validation if flag threshold is set and user rep does not meet it', function (done) { - Meta.configs.set('privileges:flag', '50', function (err) { - assert.ifError(err); + id: pid, + reason: 'foobar' + }, function (err, flagObj) { + flag = flagObj; + assert.ifError(err); - Flags.validate({ - type: 'post', - id: 1, - uid: 2 - }, function (err) { - assert.ok(err); - assert.strictEqual('[[error:not-enough-reputation-to-flag]]', err.message); - Meta.configs.set('privileges:flag', 0, done); + Flags.exists('post', pid, 1, function (err, exists) { + assert.ifError(err); + assert(true); + done(); + }); }); }); }); - }); - - describe('.appendNote()', function () { - it('should add a note to a flag', function (done) { - Flags.appendNote(1, 1, 'this is my note', function (err) { - assert.ifError(err); - - db.getSortedSetRange('flag:1:notes', 0, -1, function (err, notes) { - if (err) { - throw err; - } - assert.strictEqual('[1,"this is my note"]', notes[0]); + describe('.update()', function () { + it('should update a flag\'s properties', function (done) { + SocketFlags.update({ uid: 2 }, { + flagId: flag.flagId, + data: [{ + name: 'state', + value: 'wip' + }] + }, function (err, history) { + assert.ifError(err); + assert(Array.isArray(history)); + assert(history[0].fields.hasOwnProperty('state')); + assert.strictEqual('[[flags:state-wip]]', history[0].fields.state); done(); }); }); }); - it('should be a JSON string', function (done) { - db.getSortedSetRange('flag:1:notes', 0, -1, function (err, notes) { - if (err) { - throw err; - } - - try { - JSON.parse(notes[0]); - } catch (e) { - assert.ifError(e); - } - - done(); - }); - }); - }); - - describe('.getNotes()', function () { - before(function (done) { - // Add a second note - Flags.appendNote(1, 1, 'this is the second note', done); - }); - - it('return should match a predefined spec', function (done) { - Flags.getNotes(1, function (err, notes) { - assert.ifError(err); - var compare = { - uid: 1, - content: 'this is my note' - }; - - var data = notes[1]; - for(var key in compare) { - if (compare.hasOwnProperty(key)) { - assert.ok(data[key]); - assert.strictEqual(data[key], compare[key]); - } - } - - done(); - }); - }); - - it('should retrieve a list of notes, from newest to oldest', function (done) { - Flags.getNotes(1, function (err, notes) { - assert.ifError(err); - assert(notes[0].datetime > notes[1].datetime); - assert.strictEqual('this is the second note', notes[0].content); - done(); - }); - }); - }); - - describe('.appendHistory()', function () { - var entries; - before(function (done) { - db.sortedSetCard('flag:1:history', function (err, count) { - entries = count; - done(err); - }); - }); - - it('should add a new entry into a flag\'s history', function (done) { - Flags.appendHistory(1, 1, ['state:rejected'], function (err) { - assert.ifError(err); - - Flags.getHistory(1, function (err, history) { - if (err) { - throw err; - } - - assert.strictEqual(entries + 1, history.length); + describe('.appendNote()', function () { + it('should append a note to the flag', function (done) { + SocketFlags.appendNote({ uid: 2 }, { + flagId: flag.flagId, + note: 'lorem ipsum dolor sit amet' + }, function (err, data) { + assert(data.hasOwnProperty('notes')); + assert(Array.isArray(data.notes)); + assert.strictEqual('lorem ipsum dolor sit amet', data.notes[0].content); + assert.strictEqual(2, data.notes[0].uid); + + assert(data.hasOwnProperty('history')); + assert(Array.isArray(data.history)); + assert.strictEqual(1, Object.keys(data.history[0].fields).length); + assert(data.history[0].fields.hasOwnProperty('notes')); done(); }); }); }); }); - describe('.getHistory()', function () { - it('should retrieve a flag\'s history', function (done) { - Flags.getHistory(1, function (err, history) { - assert.ifError(err); - assert.strictEqual(history[0].fields[0].value, '[[flags:state-rejected]]'); - done(); - }); - }); - }); - after(function (done) { db.emptydb(done); }); From 380ebf67ee84f34907d1f59d8d8985bdaa66ea6b Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 13 Dec 2016 14:24:09 -0500 Subject: [PATCH 037/316] oops, uncommenting all the other tests for flags --- test/flags.js | 570 +++++++++++++++++++++++++------------------------- 1 file changed, 285 insertions(+), 285 deletions(-) diff --git a/test/flags.js b/test/flags.js index 8d359c6930..7b0252086a 100644 --- a/test/flags.js +++ b/test/flags.js @@ -45,295 +45,295 @@ describe('Flags', function () { ], done); }); - // describe('.create()', function () { - // it('should create a flag and return its data', function (done) { - // Flags.create('post', 1, 1, 'Test flag', function (err, flagData) { - // assert.ifError(err); - // var compare = { - // flagId: 1, - // uid: 1, - // targetId: 1, - // type: 'post', - // description: 'Test flag' - // }; - - // for(var key in compare) { - // if (compare.hasOwnProperty(key)) { - // assert.ok(flagData[key]); - // assert.strictEqual(flagData[key], compare[key]); - // } - // } - - // done(); - // }); - // }); - // }); - - // describe('.get()', function () { - // it('should retrieve and display a flag\'s data', function (done) { - // Flags.get(1, function (err, flagData) { - // assert.ifError(err); - // var compare = { - // flagId: 1, - // uid: 1, - // targetId: 1, - // type: 'post', - // description: 'Test flag', - // state: 'open' - // }; - - // for(var key in compare) { - // if (compare.hasOwnProperty(key)) { - // assert.ok(flagData[key]); - // assert.strictEqual(flagData[key], compare[key]); - // } - // } - - // done(); - // }); - // }); - // }); - - // describe('.list()', function () { - // it('should show a list of flags (with one item)', function (done) { - // Flags.list({}, 1, function (err, flags) { - // assert.ifError(err); - // assert.ok(Array.isArray(flags)); - // assert.equal(flags.length, 1); + describe('.create()', function () { + it('should create a flag and return its data', function (done) { + Flags.create('post', 1, 1, 'Test flag', function (err, flagData) { + assert.ifError(err); + var compare = { + flagId: 1, + uid: 1, + targetId: 1, + type: 'post', + description: 'Test flag' + }; + + for(var key in compare) { + if (compare.hasOwnProperty(key)) { + assert.ok(flagData[key]); + assert.strictEqual(flagData[key], compare[key]); + } + } + + done(); + }); + }); + }); + + describe('.get()', function () { + it('should retrieve and display a flag\'s data', function (done) { + Flags.get(1, function (err, flagData) { + assert.ifError(err); + var compare = { + flagId: 1, + uid: 1, + targetId: 1, + type: 'post', + description: 'Test flag', + state: 'open' + }; + + for(var key in compare) { + if (compare.hasOwnProperty(key)) { + assert.ok(flagData[key]); + assert.strictEqual(flagData[key], compare[key]); + } + } + + done(); + }); + }); + }); + + describe('.list()', function () { + it('should show a list of flags (with one item)', function (done) { + Flags.list({}, 1, function (err, flags) { + assert.ifError(err); + assert.ok(Array.isArray(flags)); + assert.equal(flags.length, 1); - // Flags.get(flags[0].flagId, function (err, flagData) { - // assert.ifError(err); - // assert.equal(flags[0].flagId, flagData.flagId); - // assert.equal(flags[0].description, flagData.description); - // done(); - // }); - // }); - // }); - // }); - - // describe('.update()', function () { - // it('should alter a flag\'s various attributes and persist them to the database', function (done) { - // Flags.update(1, 1, { - // "state": "wip", - // "assignee": 1 - // }, function (err) { - // assert.ifError(err); - // db.getObjectFields('flag:1', ['state', 'assignee'], function (err, data) { - // if (err) { - // throw err; - // } - - // assert.strictEqual('wip', data.state); - // assert.strictEqual(1, data.assignee); - // done(); - // }); - // }); - // }); - - // it('should persist to the flag\'s history', function (done) { - // Flags.getHistory(1, function (err, history) { - // if (err) { - // throw err; - // } - - // history.forEach(function (change) { - // switch (change.attribute) { - // case 'state': - // assert.strictEqual('[[flags:state-wip]]', change.value); - // break; + Flags.get(flags[0].flagId, function (err, flagData) { + assert.ifError(err); + assert.equal(flags[0].flagId, flagData.flagId); + assert.equal(flags[0].description, flagData.description); + done(); + }); + }); + }); + }); + + describe('.update()', function () { + it('should alter a flag\'s various attributes and persist them to the database', function (done) { + Flags.update(1, 1, { + "state": "wip", + "assignee": 1 + }, function (err) { + assert.ifError(err); + db.getObjectFields('flag:1', ['state', 'assignee'], function (err, data) { + if (err) { + throw err; + } + + assert.strictEqual('wip', data.state); + assert.strictEqual(1, data.assignee); + done(); + }); + }); + }); + + it('should persist to the flag\'s history', function (done) { + Flags.getHistory(1, function (err, history) { + if (err) { + throw err; + } + + history.forEach(function (change) { + switch (change.attribute) { + case 'state': + assert.strictEqual('[[flags:state-wip]]', change.value); + break; - // case 'assignee': - // assert.strictEqual(1, change.value); - // break; - // } - // }); - - // done(); - // }); - // }); - // }); - - // describe('.getTarget()', function () { - // it('should return a post\'s data if queried with type "post"', function (done) { - // Flags.getTarget('post', 1, 1, function (err, data) { - // assert.ifError(err); - // var compare = { - // uid: 1, - // pid: 1, - // content: 'This is flaggable content' - // }; - - // for(var key in compare) { - // if (compare.hasOwnProperty(key)) { - // assert.ok(data[key]); - // assert.strictEqual(data[key], compare[key]); - // } - // } - - // done(); - // }); - // }); - - // it('should return a user\'s data if queried with type "user"', function (done) { - // Flags.getTarget('user', 1, 1, function (err, data) { - // assert.ifError(err); - // var compare = { - // uid: 1, - // username: 'testUser', - // email: 'b@c.com' - // }; - - // for(var key in compare) { - // if (compare.hasOwnProperty(key)) { - // assert.ok(data[key]); - // assert.strictEqual(data[key], compare[key]); - // } - // } - - // done(); - // }); - // }); - // }); - - // describe('.validate()', function () { - // it('should error out if type is post and post is deleted', function (done) { - // Posts.delete(1, 1, function (err) { - // if (err) { - // throw err; - // } - - // Flags.validate({ - // type: 'post', - // id: 1, - // uid: 1 - // }, function (err) { - // assert.ok(err); - // assert.strictEqual('[[error:post-deleted]]', err.message); - // Posts.restore(1, 1, done); - // }); - // }); - // }); - - // it('should not pass validation if flag threshold is set and user rep does not meet it', function (done) { - // Meta.configs.set('privileges:flag', '50', function (err) { - // assert.ifError(err); - - // Flags.validate({ - // type: 'post', - // id: 1, - // uid: 2 - // }, function (err) { - // assert.ok(err); - // assert.strictEqual('[[error:not-enough-reputation-to-flag]]', err.message); - // Meta.configs.set('privileges:flag', 0, done); - // }); - // }); - // }); - // }); - - // describe('.appendNote()', function () { - // it('should add a note to a flag', function (done) { - // Flags.appendNote(1, 1, 'this is my note', function (err) { - // assert.ifError(err); + case 'assignee': + assert.strictEqual(1, change.value); + break; + } + }); + + done(); + }); + }); + }); + + describe('.getTarget()', function () { + it('should return a post\'s data if queried with type "post"', function (done) { + Flags.getTarget('post', 1, 1, function (err, data) { + assert.ifError(err); + var compare = { + uid: 1, + pid: 1, + content: 'This is flaggable content' + }; + + for(var key in compare) { + if (compare.hasOwnProperty(key)) { + assert.ok(data[key]); + assert.strictEqual(data[key], compare[key]); + } + } + + done(); + }); + }); + + it('should return a user\'s data if queried with type "user"', function (done) { + Flags.getTarget('user', 1, 1, function (err, data) { + assert.ifError(err); + var compare = { + uid: 1, + username: 'testUser', + email: 'b@c.com' + }; + + for(var key in compare) { + if (compare.hasOwnProperty(key)) { + assert.ok(data[key]); + assert.strictEqual(data[key], compare[key]); + } + } + + done(); + }); + }); + }); + + describe('.validate()', function () { + it('should error out if type is post and post is deleted', function (done) { + Posts.delete(1, 1, function (err) { + if (err) { + throw err; + } + + Flags.validate({ + type: 'post', + id: 1, + uid: 1 + }, function (err) { + assert.ok(err); + assert.strictEqual('[[error:post-deleted]]', err.message); + Posts.restore(1, 1, done); + }); + }); + }); + + it('should not pass validation if flag threshold is set and user rep does not meet it', function (done) { + Meta.configs.set('privileges:flag', '50', function (err) { + assert.ifError(err); + + Flags.validate({ + type: 'post', + id: 1, + uid: 2 + }, function (err) { + assert.ok(err); + assert.strictEqual('[[error:not-enough-reputation-to-flag]]', err.message); + Meta.configs.set('privileges:flag', 0, done); + }); + }); + }); + }); + + describe('.appendNote()', function () { + it('should add a note to a flag', function (done) { + Flags.appendNote(1, 1, 'this is my note', function (err) { + assert.ifError(err); - // db.getSortedSetRange('flag:1:notes', 0, -1, function (err, notes) { - // if (err) { - // throw err; - // } - - // assert.strictEqual('[1,"this is my note"]', notes[0]); - // done(); - // }); - // }); - // }); - - // it('should be a JSON string', function (done) { - // db.getSortedSetRange('flag:1:notes', 0, -1, function (err, notes) { - // if (err) { - // throw err; - // } - - // try { - // JSON.parse(notes[0]); - // } catch (e) { - // assert.ifError(e); - // } - - // done(); - // }); - // }); - // }); - - // describe('.getNotes()', function () { - // before(function (done) { - // // Add a second note - // Flags.appendNote(1, 1, 'this is the second note', done); - // }); - - // it('return should match a predefined spec', function (done) { - // Flags.getNotes(1, function (err, notes) { - // assert.ifError(err); - // var compare = { - // uid: 1, - // content: 'this is my note' - // }; + db.getSortedSetRange('flag:1:notes', 0, -1, function (err, notes) { + if (err) { + throw err; + } + + assert.strictEqual('[1,"this is my note"]', notes[0]); + done(); + }); + }); + }); + + it('should be a JSON string', function (done) { + db.getSortedSetRange('flag:1:notes', 0, -1, function (err, notes) { + if (err) { + throw err; + } + + try { + JSON.parse(notes[0]); + } catch (e) { + assert.ifError(e); + } + + done(); + }); + }); + }); + + describe('.getNotes()', function () { + before(function (done) { + // Add a second note + Flags.appendNote(1, 1, 'this is the second note', done); + }); + + it('return should match a predefined spec', function (done) { + Flags.getNotes(1, function (err, notes) { + assert.ifError(err); + var compare = { + uid: 1, + content: 'this is my note' + }; - // var data = notes[1]; - // for(var key in compare) { - // if (compare.hasOwnProperty(key)) { - // assert.ok(data[key]); - // assert.strictEqual(data[key], compare[key]); - // } - // } - - // done(); - // }); - // }); - - // it('should retrieve a list of notes, from newest to oldest', function (done) { - // Flags.getNotes(1, function (err, notes) { - // assert.ifError(err); - // assert(notes[0].datetime > notes[1].datetime); - // assert.strictEqual('this is the second note', notes[0].content); - // done(); - // }); - // }); - // }); - - // describe('.appendHistory()', function () { - // var entries; - // before(function (done) { - // db.sortedSetCard('flag:1:history', function (err, count) { - // entries = count; - // done(err); - // }); - // }); - - // it('should add a new entry into a flag\'s history', function (done) { - // Flags.appendHistory(1, 1, ['state:rejected'], function (err) { - // assert.ifError(err); - - // Flags.getHistory(1, function (err, history) { - // if (err) { - // throw err; - // } - - // assert.strictEqual(entries + 1, history.length); - // done(); - // }); - // }); - // }); - // }); - - // describe('.getHistory()', function () { - // it('should retrieve a flag\'s history', function (done) { - // Flags.getHistory(1, function (err, history) { - // assert.ifError(err); - // assert.strictEqual(history[0].fields[0].value, '[[flags:state-rejected]]'); - // done(); - // }); - // }); - // }); + var data = notes[1]; + for(var key in compare) { + if (compare.hasOwnProperty(key)) { + assert.ok(data[key]); + assert.strictEqual(data[key], compare[key]); + } + } + + done(); + }); + }); + + it('should retrieve a list of notes, from newest to oldest', function (done) { + Flags.getNotes(1, function (err, notes) { + assert.ifError(err); + assert(notes[0].datetime > notes[1].datetime); + assert.strictEqual('this is the second note', notes[0].content); + done(); + }); + }); + }); + + describe('.appendHistory()', function () { + var entries; + before(function (done) { + db.sortedSetCard('flag:1:history', function (err, count) { + entries = count; + done(err); + }); + }); + + it('should add a new entry into a flag\'s history', function (done) { + Flags.appendHistory(1, 1, ['state:rejected'], function (err) { + assert.ifError(err); + + Flags.getHistory(1, function (err, history) { + if (err) { + throw err; + } + + assert.strictEqual(entries + 1, history.length); + done(); + }); + }); + }); + }); + + describe('.getHistory()', function () { + it('should retrieve a flag\'s history', function (done) { + Flags.getHistory(1, function (err, history) { + assert.ifError(err); + assert.strictEqual(history[0].fields[0].value, '[[flags:state-rejected]]'); + done(); + }); + }); + }); describe('(websockets)', function () { var SocketFlags = require('../src/socket.io/flags.js'); From e6768ab57286749821eec15cb69e39641139577a Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 14 Dec 2016 10:03:39 -0500 Subject: [PATCH 038/316] some more fixes to flags, simplifying qs manipulation in flags search re: #5232 --- public/language/en-GB/flags.json | 1 + public/src/client/flags/list.js | 14 ++++---------- src/controllers/mods.js | 1 - 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/public/language/en-GB/flags.json b/public/language/en-GB/flags.json index a9272897ca..f1fd71bf27 100644 --- a/public/language/en-GB/flags.json +++ b/public/language/en-GB/flags.json @@ -41,6 +41,7 @@ "no-history": "No flag history.", "state": "State", + "state-all": "All states", "state-open": "New/Open", "state-wip": "Work in Progress", "state-resolved": "Resolved", diff --git a/public/src/client/flags/list.js b/public/src/client/flags/list.js index 255c7176f1..12cc19093f 100644 --- a/public/src/client/flags/list.js +++ b/public/src/client/flags/list.js @@ -20,16 +20,10 @@ define('forum/flags/list', ['components', 'Chart'], function (components, Chart) } filtersEl.find('button').on('click', function () { - var payload = filtersEl.serializeArray(); - var qs = payload.map(function (filter) { - if (filter.value) { - return filter.name + '=' + filter.value; - } else { - return; - } - }).filter(Boolean).join('&'); - - ajaxify.go('flags?' + qs); + var payload = filtersEl.serializeArray().filter(function (item) { + return !!item.value; + }); + ajaxify.go('flags?' + $.param(payload)); }); }; diff --git a/src/controllers/mods.js b/src/controllers/mods.js index 39820e60a6..666f449316 100644 --- a/src/controllers/mods.js +++ b/src/controllers/mods.js @@ -5,7 +5,6 @@ var async = require('async'); var user = require('../user'); var flags = require('../flags'); var analytics = require('../analytics'); -// var adminFlagsController = require('./admin/flags'); var modsController = { flags: {} From ebffc4460011178f2745a27f78d2aeaca6559d9a Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 14 Dec 2016 15:00:41 -0500 Subject: [PATCH 039/316] fix tests, #5232 --- src/flags.js | 2 +- test/flags.js | 22 +++++++++++++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/flags.js b/src/flags.js index 1ae5a84ed8..995e880801 100644 --- a/src/flags.js +++ b/src/flags.js @@ -47,7 +47,7 @@ Flags.get = function (flagId, callback) { }; Flags.list = function (filters, uid, callback) { - if (typeof filters === 'function' && !callback) { + if (typeof filters === 'function' && !uid && !callback) { callback = filters; filters = {}; } diff --git a/test/flags.js b/test/flags.js index 7b0252086a..607b807db3 100644 --- a/test/flags.js +++ b/test/flags.js @@ -41,6 +41,11 @@ describe('Flags', function () { }, function (uid, next) { Groups.join('administrators', uid, next); + }, + function (next) { + User.create({ + username: 'unprivileged', password: 'abcdef', email: 'd@e.com' + }, next); } ], done); }); @@ -109,6 +114,15 @@ describe('Flags', function () { }); }); }); + + it('should return a filtered list of flags if said filters are passed in', function (done) { + Flags.list({ + state: 'open' + }, 1, function (err, flags) { + assert.ifError(err); + done(); + }); + }); }); describe('.update()', function () { @@ -221,7 +235,7 @@ describe('Flags', function () { Flags.validate({ type: 'post', id: 1, - uid: 2 + uid: 3 }, function (err) { assert.ok(err); assert.strictEqual('[[error:not-enough-reputation-to-flag]]', err.message); @@ -310,7 +324,9 @@ describe('Flags', function () { }); it('should add a new entry into a flag\'s history', function (done) { - Flags.appendHistory(1, 1, ['state:rejected'], function (err) { + Flags.appendHistory(1, 1, { + state: 'rejected' + }, function (err) { assert.ifError(err); Flags.getHistory(1, function (err, history) { @@ -329,7 +345,7 @@ describe('Flags', function () { it('should retrieve a flag\'s history', function (done) { Flags.getHistory(1, function (err, history) { assert.ifError(err); - assert.strictEqual(history[0].fields[0].value, '[[flags:state-rejected]]'); + assert.strictEqual(history[0].fields.state, '[[flags:state-rejected]]'); done(); }); }); From 0927d54c9877f8b024abd167121602b0e4aff2a5 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 14 Dec 2016 15:53:57 -0500 Subject: [PATCH 040/316] ability to filter flags by cid, #5232, more tests --- public/language/en-GB/flags.json | 2 ++ src/controllers/mods.js | 13 ++++++-- src/flags.js | 32 +++++++++++++++--- test/flags.js | 57 ++++++++++++++++++++++++++++---- 4 files changed, 92 insertions(+), 12 deletions(-) diff --git a/public/language/en-GB/flags.json b/public/language/en-GB/flags.json index f1fd71bf27..2a1bf919f8 100644 --- a/public/language/en-GB/flags.json +++ b/public/language/en-GB/flags.json @@ -19,7 +19,9 @@ "filter-type-post": "Post", "filter-state": "State", "filter-assignee": "Assignee UID", + "filter-cid": "Category", "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", "apply-filters": "Apply Filters", "quick-links": "Quick Links", diff --git a/src/controllers/mods.js b/src/controllers/mods.js index 666f449316..cae9ade1ed 100644 --- a/src/controllers/mods.js +++ b/src/controllers/mods.js @@ -3,6 +3,7 @@ var async = require('async'); var user = require('../user'); +var categories = require('../categories'); var flags = require('../flags'); var analytics = require('../analytics'); @@ -26,7 +27,7 @@ modsController.flags.list = function (req, res, next) { } // Parse query string params for filters - var valid = ['assignee', 'state', 'reporterId', 'type', 'targetUid', 'quick']; + var valid = ['assignee', 'state', 'reporterId', 'type', 'targetUid', 'cid', 'quick']; var filters = valid.reduce(function (memo, cur) { if (req.query.hasOwnProperty(cur)) { memo[cur] = req.query[cur]; @@ -37,15 +38,23 @@ modsController.flags.list = function (req, res, next) { async.parallel({ flags: async.apply(flags.list, filters, req.uid), - analytics: async.apply(analytics.getDailyStatsForSet, 'analytics:flags', Date.now(), 30) + analytics: async.apply(analytics.getDailyStatsForSet, 'analytics:flags', Date.now(), 30), + categories: async.apply(categories.buildForSelect, req.uid) }, function (err, data) { if (err) { return next(err); } + // Minimal returned set for templates.js + data.categories = data.categories.reduce(function (memo, cur) { + memo[cur.cid] = cur.name; + return memo; + }, {}); + res.render('flags/list', { flags: data.flags, analytics: data.analytics, + categories: data.categories, hasFilter: !!Object.keys(filters).length, filters: filters, title: '[[pages:flags]]' diff --git a/src/flags.js b/src/flags.js index 995e880801..81b97ee408 100644 --- a/src/flags.js +++ b/src/flags.js @@ -71,11 +71,15 @@ Flags.list = function (filters, uid, callback) { case 'assignee': sets.push('flags:byAssignee:' + filters[type]); break; - + case 'targetUid': sets.push('flags:byTargetUid:' + filters[type]); break; + case 'cid': + sets.push('flags:byCid:' + filters[type]); + break; + case 'quick': switch (filters.quick) { case 'mine': @@ -262,6 +266,7 @@ Flags.getNotes = function (flagId, callback) { Flags.create = function (type, id, uid, reason, timestamp, callback) { var targetUid; + var targetCid; var doHistoryAppend = false; // timestamp is optional @@ -273,17 +278,21 @@ Flags.create = function (type, id, uid, reason, timestamp, callback) { async.waterfall([ function (next) { - // Sanity checks async.parallel([ + // Sanity checks async.apply(Flags.exists, type, id, uid), async.apply(Flags.targetExists, type, id), - async.apply(Flags.getTargetUid, type, id) + + // Extra data for zset insertion + async.apply(Flags.getTargetUid, type, id), + async.apply(Flags.getTargetCid, type, id) ], function (err, checks) { if (err) { return next(err); } targetUid = checks[2] || null; + targetCid = checks[3] || null; if (checks[0]) { return next(new Error('[[error:already-flagged]]')); @@ -315,6 +324,9 @@ Flags.create = function (type, id, uid, reason, timestamp, callback) { if (targetUid) { tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byTargetUid:' + targetUid, timestamp, flagId)); // by target uid } + if (targetCid) { + tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byCid:' + targetCid, timestamp, flagId)); // by target uid + } async.parallel(tasks, function (err, data) { if (err) { @@ -358,12 +370,24 @@ Flags.getTargetUid = function (type, id, callback) { posts.getPostField(id, 'uid', callback); break; - case 'user': + default: setImmediate(callback, null, id); break; } }; +Flags.getTargetCid = function (type, id, callback) { + switch (type) { + case 'post': + posts.getCidByPid(id, callback); + break; + + default: + setImmediate(callback, null, id); + break; + } +}; + Flags.update = function (flagId, uid, changeset, callback) { // Retrieve existing flag data to compare for history-saving purposes var fields = ['state', 'assignee']; diff --git a/test/flags.js b/test/flags.js index 607b807db3..c46ac0bcef 100644 --- a/test/flags.js +++ b/test/flags.js @@ -72,6 +72,14 @@ describe('Flags', function () { done(); }); }); + + it('should add the flag to the byCid zset for category 1 if it is of type post', function (done) { + db.isSortedSetMember('flags:byCid:' + 1, 1, function (err, isMember) { + assert.ifError(err); + assert.ok(isMember); + done(); + }); + }); }); describe('.get()', function () { @@ -115,12 +123,49 @@ describe('Flags', function () { }); }); - it('should return a filtered list of flags if said filters are passed in', function (done) { - Flags.list({ - state: 'open' - }, 1, function (err, flags) { - assert.ifError(err); - done(); + describe('(with filters)', function () { + it('should return a filtered list of flags if said filters are passed in', function (done) { + Flags.list({ + state: 'open' + }, 1, function (err, flags) { + assert.ifError(err); + assert.ok(Array.isArray(flags)); + assert.strictEqual(1, flags[0].flagId); + done(); + }); + }); + + it('should return no flags if a filter with no matching flags is used', function (done) { + Flags.list({ + state: 'rejected' + }, 1, function (err, flags) { + assert.ifError(err); + assert.ok(Array.isArray(flags)); + assert.strictEqual(0, flags.length); + done(); + }); + }); + + it('should return a flag when filtered by cid 1', function (done) { + Flags.list({ + cid: 1 + }, 1, function (err, flags) { + assert.ifError(err); + assert.ok(Array.isArray(flags)); + assert.strictEqual(1, flags.length); + done(); + }); + }); + + it('shouldn\'t return a flag when filtered by cid 2', function (done) { + Flags.list({ + cid: 2 + }, 1, function (err, flags) { + assert.ifError(err); + assert.ok(Array.isArray(flags)); + assert.strictEqual(0, flags.length); + done(); + }); }); }); }); From 7b471b76db69c3fc5eb0ea5fca17b177e07f8c93 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 19 Dec 2016 09:50:46 -0500 Subject: [PATCH 041/316] fixing tests for #5232 --- test/flags.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/flags.js b/test/flags.js index c46ac0bcef..cc8a0425e3 100644 --- a/test/flags.js +++ b/test/flags.js @@ -457,6 +457,7 @@ describe('Flags', function () { flagId: flag.flagId, note: 'lorem ipsum dolor sit amet' }, function (err, data) { + assert.ifError(err); assert(data.hasOwnProperty('notes')); assert(Array.isArray(data.notes)); assert.strictEqual('lorem ipsum dolor sit amet', data.notes[0].content); From ad633aad4583546bad9302ca1cbef9ad052c131e Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 19 Dec 2016 11:16:03 -0500 Subject: [PATCH 042/316] additional tests and proper handling for purged flag targets, #5232 --- public/language/en-GB/flags.json | 1 + src/controllers/mods.js | 9 +++-- src/flags.js | 60 +++++++++++++++++++------------- src/posts/delete.js | 3 -- src/upgrade.js | 47 ------------------------- src/user/admin.js | 10 ------ test/flags.js | 44 +++++++++++++++++++++++ 7 files changed, 87 insertions(+), 87 deletions(-) diff --git a/public/language/en-GB/flags.json b/public/language/en-GB/flags.json index 2a1bf919f8..66b9acc92a 100644 --- a/public/language/en-GB/flags.json +++ b/public/language/en-GB/flags.json @@ -7,6 +7,7 @@ "assignee": "Assignee", "update": "Update", "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", "quick-filters": "Quick Filters", "filter-active": "There are one or more filters active in this list of flags", diff --git a/src/controllers/mods.js b/src/controllers/mods.js index cae9ade1ed..242d68d708 100644 --- a/src/controllers/mods.js +++ b/src/controllers/mods.js @@ -78,8 +78,13 @@ modsController.flags.detail = function (req, res, next) { res.render('flags/detail', Object.assign(results.flagData, { assignees: results.assignees, - type_bool: ['post', 'user'].reduce(function (memo, cur) { - memo[cur] = results.flagData.type === cur; + type_bool: ['post', 'user', 'empty'].reduce(function (memo, cur) { + if (cur !== 'empty') { + memo[cur] = results.flagData.type === cur && !!Object.keys(results.flagData.target).length; + } else { + memo[cur] = !Object.keys(results.flagData.target).length; + } + return memo; }, {}), title: '[[pages:flag-details, ' + req.params.flagId + ']]' diff --git a/src/flags.js b/src/flags.js index 81b97ee408..61b7a1746d 100644 --- a/src/flags.js +++ b/src/flags.js @@ -202,31 +202,6 @@ Flags.validate = function (payload, callback) { }); }; -Flags.getTarget = function (type, id, uid, callback) { - switch (type) { - case 'post': - async.waterfall([ - async.apply(posts.getPostsByPids, [id], uid), - function (posts, next) { - topics.addPostData(posts, uid, next); - } - ], function (err, posts) { - callback(err, posts[0]); - }); - break; - - case 'user': - user.getUsersData([id], function (err, users) { - callback(err, users ? users[0] : undefined); - }); - break; - - default: - callback(new Error('[[error:invalid-data]]')); - break; - } -}; - Flags.getNotes = function (flagId, callback) { async.waterfall([ async.apply(db.getSortedSetRevRangeWithScores.bind(db), 'flag:' + flagId + ':notes', 0, -1), @@ -348,6 +323,41 @@ Flags.exists = function (type, id, uid, callback) { db.isSortedSetMember('flags:hash', [type, id, uid].join(':'), callback); }; +Flags.getTarget = function (type, id, uid, callback) { + async.waterfall([ + async.apply(Flags.targetExists, type, id), + function (exists, next) { + if (exists) { + switch (type) { + case 'post': + async.waterfall([ + async.apply(posts.getPostsByPids, [id], uid), + function (posts, next) { + topics.addPostData(posts, uid, next); + } + ], function (err, posts) { + next(err, posts[0]); + }); + break; + + case 'user': + user.getUsersData([id], function (err, users) { + next(err, users ? users[0] : undefined); + }); + break; + + default: + next(new Error('[[error:invalid-data]]')); + break; + } + } else { + // Target used to exist (otherwise flag creation'd fail), but no longer + next(null, {}); + } + } + ], callback); +}; + Flags.targetExists = function (type, id, callback) { switch (type) { case 'post': diff --git a/src/posts/delete.js b/src/posts/delete.js index ebf902aef2..32ee6b6f41 100644 --- a/src/posts/delete.js +++ b/src/posts/delete.js @@ -144,9 +144,6 @@ module.exports = function (Posts) { }, function (next) { db.sortedSetsRemove(['posts:pid', 'posts:flagged'], pid, next); - }, - function (next) { - flags.dismiss(pid, next); } ], function (err) { if (err) { diff --git a/src/upgrade.js b/src/upgrade.js index 29b806d4a7..1815d1e560 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -455,53 +455,6 @@ Upgrade.upgrade = function (callback) { next(); } }, - function (next) { - thisSchemaDate = Date.UTC(2016, 3, 29); - - if (schemaDate < thisSchemaDate) { - updatesMade = true; - winston.info('[2016/04/29] Dismiss flags from deleted topics'); - - var posts = require('./posts'); - var topics = require('./topics'); - var flags = require('./flags'); - - var pids, tids; - - async.waterfall([ - async.apply(db.getSortedSetRange, 'posts:flagged', 0, -1), - function (_pids, next) { - pids = _pids; - posts.getPostsFields(pids, ['tid'], next); - }, - function (_tids, next) { - tids = _tids.map(function (a) { - return a.tid; - }); - - topics.getTopicsFields(tids, ['deleted'], next); - }, - function (state, next) { - var toDismiss = state.map(function (a, idx) { - return parseInt(a.deleted, 10) === 1 ? pids[idx] : null; - }).filter(Boolean); - - winston.info('[2016/04/29] ' + toDismiss.length + ' dismissable flags found'); - async.each(toDismiss, flags.dismiss, next); - } - ], function (err) { - if (err) { - return next(err); - } - - winston.info('[2016/04/29] Dismiss flags from deleted topics done'); - Upgrade.update(thisSchemaDate, next); - }); - } else { - winston.info('[2016/04/29] Dismiss flags from deleted topics skipped!'); - next(); - } - }, function (next) { thisSchemaDate = Date.UTC(2016, 4, 28); diff --git a/src/user/admin.js b/src/user/admin.js index 5d2215980c..4f7ecf66fb 100644 --- a/src/user/admin.js +++ b/src/user/admin.js @@ -56,14 +56,4 @@ module.exports = function (User) { } ], callback); }; - - User.resetFlags = function (uids, callback) { - if (!Array.isArray(uids) || !uids.length) { - return callback(); - } - - async.eachSeries(uids, function (uid, next) { - flags.dismissByUid(uid, next); - }, callback); - }; }; diff --git a/test/flags.js b/test/flags.js index cc8a0425e3..58250fb27f 100644 --- a/test/flags.js +++ b/test/flags.js @@ -82,6 +82,42 @@ describe('Flags', function () { }); }); + describe('.exists()', function () { + it('should return Boolean True if a flag matching the flag hash already exists', function (done) { + Flags.exists('post', 1, 1, function (err, exists) { + assert.ifError(err); + assert.strictEqual(true, exists); + done(); + }); + }); + + it('should return Boolean False if a flag matching the flag hash does not already exists', function (done) { + Flags.exists('post', 1, 2, function (err, exists) { + assert.ifError(err); + assert.strictEqual(false, exists); + done(); + }); + }); + }); + + describe('.targetExists()', function () { + it('should return Boolean True if the targeted element exists', function (done) { + Flags.targetExists('post', 1, function (err, exists) { + assert.ifError(err); + assert.strictEqual(true, exists); + done(); + }); + }); + + it('should return Boolean False if the targeted element does not exist', function (done) { + Flags.targetExists('post', 15, function (err, exists) { + assert.ifError(err); + assert.strictEqual(false, exists); + done(); + }); + }); + }); + describe('.get()', function () { it('should retrieve and display a flag\'s data', function (done) { Flags.get(1, function (err, flagData) { @@ -252,6 +288,14 @@ describe('Flags', function () { done(); }); }); + + it('should return a plain object with no properties if the target no longer exists', function (done) { + Flags.getTarget('user', 15, 1, function (err, data) { + assert.ifError(err); + assert.strictEqual(0, Object.keys(data).length); + done(); + }); + }); }); describe('.validate()', function () { From 47c9c936936f50cc0fa4494d5ae2164f5f219fee Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 19 Dec 2016 11:54:52 -0500 Subject: [PATCH 043/316] removed old flagging tests --- test/posts.js | 215 -------------------------------------------------- 1 file changed, 215 deletions(-) diff --git a/test/posts.js b/test/posts.js index 1ddbd83399..090edd3ca1 100644 --- a/test/posts.js +++ b/test/posts.js @@ -418,221 +418,6 @@ describe('Post\'s', function () { }); }); - describe('flagging a post', function () { - var meta = require('../src/meta'); - var socketPosts = require('../src/socket.io/posts'); - it('should fail to flag a post due to low reputation', function (done) { - meta.config['privileges:flag'] = 10; - flagPost(function (err) { - assert.equal(err.message, '[[error:not-enough-reputation-to-flag]]'); - done(); - }); - }); - - it('should flag a post', function (done) { - meta.config['privileges:flag'] = -1; - flagPost(function (err) { - assert.ifError(err); - done(); - }); - }); - - it('should return nothing without a uid or a reason', function (done) { - socketPosts.flag({uid: 0}, {pid: postData.pid, reason: 'reason'}, function (err) { - assert.equal(err.message, '[[error:not-logged-in]]'); - socketPosts.flag({uid: voteeUid}, {}, function (err) { - assert.equal(err.message, '[[error:invalid-data]]'); - done(); - }); - }); - }); - - it('should return an error without an existing post', function (done) { - socketPosts.flag({uid: voteeUid}, {pid: 12312312, reason: 'reason'}, function (err) { - assert.equal(err.message, '[[error:no-post]]'); - done(); - }); - }); - - it('should return an error if the flag already exists', function (done) { - socketPosts.flag({uid: voteeUid}, {pid: postData.pid, reason: 'reason'}, function (err) { - assert.equal(err.message, '[[error:already-flagged]]'); - done(); - }); - }); - }); - - function flagPost(next) { - var socketPosts = require('../src/socket.io/posts'); - socketPosts.flag({uid: voteeUid}, {pid: postData.pid, reason: 'reason'}, next); - } - - describe('get flag data', function () { - it('should see the flagged post', function (done) { - posts.isFlaggedByUser(postData.pid, voteeUid, function (err, hasFlagged) { - assert.ifError(err); - assert(hasFlagged); - done(); - }); - }); - - it('should return the flagged post data', function (done) { - posts.getFlags('posts:flagged', cid, voteeUid, 0, -1, function (err, flagData) { - assert.ifError(err); - assert(flagData.posts); - assert(flagData.count); - assert.equal(flagData.count, 1); - assert.equal(flagData.posts.length, 1); - assert(flagData.posts[0].flagReasons); - assert.equal(flagData.posts[0].flagReasons.length, 1); - assert.strictEqual(flagData.posts[0].flagReasons[0].reason, 'reason'); - assert(flagData.posts[0].flagData); - assert.strictEqual(flagData.posts[0].flagData.state, 'open'); - done(); - }); - }); - }); - - describe('updating a flag', function () { - var socketPosts = require('../src/socket.io/posts'); - - it('should update a flag', function (done) { - async.waterfall([ - function (next) { - socketPosts.updateFlag({uid: globalModUid}, { - pid: postData.pid, - data: [ - {name: 'assignee', value: `${globalModUid}`}, - {name: 'notes', value: 'notes'} - ] - }, function (err) { - assert.ifError(err); - posts.getFlags('posts:flagged', cid, globalModUid, 0, -1, function (err, flagData) { - assert.ifError(err); - assert(flagData.posts); - assert.equal(flagData.posts.length, 1); - assert.deepEqual({ - assignee: flagData.posts[0].flagData.assignee, - notes: flagData.posts[0].flagData.notes, - state: flagData.posts[0].flagData.state, - labelClass: flagData.posts[0].flagData.labelClass - }, { - assignee: `${globalModUid}`, - notes: 'notes', - state: 'open', - labelClass: 'info' - }); - next(); - }); - }); - }, function (next) { - posts.updateFlagData(globalModUid, postData.pid, { - state: 'rejected' - }, function (err) { - assert.ifError(err); - posts.getFlags('posts:flagged', cid, globalModUid, 0, -1, function (err, flagData) { - assert.ifError(err); - assert(flagData.posts); - assert.equal(flagData.posts.length, 1); - assert.deepEqual({ - state: flagData.posts[0].flagData.state, - labelClass: flagData.posts[0].flagData.labelClass - }, { - state: 'rejected', - labelClass: 'danger' - }); - next(); - }); - }); - }, function (next) { - posts.updateFlagData(globalModUid, postData.pid, { - state: 'wip' - }, function (err) { - assert.ifError(err); - posts.getFlags('posts:flagged', cid, globalModUid, 0, -1, function (err, flagData) { - assert.ifError(err); - assert(flagData.posts); - assert.equal(flagData.posts.length, 1); - assert.deepEqual({ - state: flagData.posts[0].flagData.state, - labelClass: flagData.posts[0].flagData.labelClass - }, { - state: 'wip', - labelClass: 'warning' - }); - next(); - }); - }); - }, function (next) { - posts.updateFlagData(globalModUid, postData.pid, { - state: 'resolved' - }, function (err) { - assert.ifError(err); - posts.getFlags('posts:flagged', cid, globalModUid, 0, -1, function (err, flagData) { - assert.ifError(err); - assert(flagData.posts); - assert.equal(flagData.posts.length, 1); - assert.deepEqual({ - state: flagData.posts[0].flagData.state, - labelClass: flagData.posts[0].flagData.labelClass - }, { - state: 'resolved', - labelClass: 'success' - }); - next(); - }); - }); - } - ], done); - }); - }); - - describe('dismissing a flag', function () { - var socketPosts = require('../src/socket.io/posts'); - - it('should dismiss a flag', function (done) { - socketPosts.dismissFlag({uid: globalModUid}, postData.pid, function (err) { - assert.ifError(err); - posts.isFlaggedByUser(postData.pid, voteeUid, function (err, hasFlagged) { - assert.ifError(err); - assert(!hasFlagged); - flagPost(function (err) { - assert.ifError(err); - done(); - }); - }); - }); - }); - - it('should dismiss all of a user\'s flags', function (done) { - posts.dismissUserFlags(voteeUid, function (err) { - assert.ifError(err); - posts.isFlaggedByUser(postData.pid, voteeUid, function (err, hasFlagged) { - assert.ifError(err); - assert(!hasFlagged); - flagPost(function (err) { - assert.ifError(err); - done(); - }); - }); - }); - }); - - it('should dismiss all flags', function (done) { - socketPosts.dismissAllFlags({uid: globalModUid}, {}, function (err) { - assert.ifError(err); - posts.isFlaggedByUser(postData.pid, voteeUid, function (err, hasFlagged) { - assert.ifError(err); - assert(!hasFlagged); - flagPost(function (err) { - assert.ifError(err); - done(); - }); - }); - }); - }); - }); - describe('getPostSummaryByPids', function () { it('should return empty array for empty pids', function (done) { posts.getPostSummaryByPids([], 0, {}, function (err, data) { From 5e52cfdf86ed709d122fbaf73bd6379c7745f308 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 19 Dec 2016 12:01:00 -0500 Subject: [PATCH 044/316] removed one more old flag test for #5232 --- test/socket.io.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/test/socket.io.js b/test/socket.io.js index ddb136d3b0..3928dc64c4 100644 --- a/test/socket.io.js +++ b/test/socket.io.js @@ -245,16 +245,6 @@ describe('socket.io', function () { }); }); - it('should reset flags', function (done) { - var socketAdmin = require('../src/socket.io/admin'); - socketAdmin.user.resetFlags({uid: adminUid}, [regularUid], function (err) { - assert.ifError(err); - done(); - }); - }); - - - describe('validation emails', function () { var socketAdmin = require('../src/socket.io/admin'); var meta = require('../src/meta'); From 2ea63f3d4259407045d9d586a4bbf8b4343a23fb Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 19 Dec 2016 12:13:36 -0500 Subject: [PATCH 045/316] how much fun is it to fix tests when stack traces don't work? lots. :shipit: --- src/flags.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/flags.js b/src/flags.js index 61b7a1746d..d3735671b2 100644 --- a/src/flags.js +++ b/src/flags.js @@ -34,7 +34,7 @@ Flags.get = function (flagId, callback) { }, function (err, payload) { // Final object return construction next(err, Object.assign(data.base, { - datetimeISO: new Date(data.base.datetime).toISOString(), + datetimeISO: new Date(parseInt(data.base.datetime, 10)).toISOString(), target_readable: data.base.type.charAt(0).toUpperCase() + data.base.type.slice(1) + ' ' + data.base.targetId, target: payload.targetObj, history: data.history, @@ -216,7 +216,7 @@ Flags.getNotes = function (flagId, callback) { uid: noteObj[0], content: noteObj[1], datetime: note.score, - datetimeISO: new Date(note.score).toISOString() + datetimeISO: new Date(parseInt(note.score, 10)).toISOString() }; } catch (e) { return next(e); @@ -469,7 +469,7 @@ Flags.getHistory = function (flagId, callback) { uid: entry.value[0], fields: changeset, datetime: entry.score, - datetimeISO: new Date(entry.score).toISOString() + datetimeISO: new Date(parseInt(entry.score, 10)).toISOString() }; }); From 07ac7dce841d2d9ed96229dd7df02bf21fa20145 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 19 Dec 2016 12:31:55 -0500 Subject: [PATCH 046/316] minor tweaks to test to be redis-compatible --- test/flags.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/flags.js b/test/flags.js index 58250fb27f..dfa6f1a773 100644 --- a/test/flags.js +++ b/test/flags.js @@ -65,7 +65,7 @@ describe('Flags', function () { for(var key in compare) { if (compare.hasOwnProperty(key)) { assert.ok(flagData[key]); - assert.strictEqual(flagData[key], compare[key]); + assert.equal(flagData[key], compare[key]); } } @@ -134,7 +134,7 @@ describe('Flags', function () { for(var key in compare) { if (compare.hasOwnProperty(key)) { assert.ok(flagData[key]); - assert.strictEqual(flagData[key], compare[key]); + assert.equal(flagData[key], compare[key]); } } @@ -166,7 +166,7 @@ describe('Flags', function () { }, 1, function (err, flags) { assert.ifError(err); assert.ok(Array.isArray(flags)); - assert.strictEqual(1, flags[0].flagId); + assert.strictEqual(1, parseInt(flags[0].flagId, 10)); done(); }); }); @@ -219,7 +219,8 @@ describe('Flags', function () { } assert.strictEqual('wip', data.state); - assert.strictEqual(1, data.assignee); + assert.ok(!isNaN(parseInt(data.assignee, 10))); + assert.strictEqual(1, parseInt(data.assignee, 10)); done(); }); }); @@ -261,7 +262,7 @@ describe('Flags', function () { for(var key in compare) { if (compare.hasOwnProperty(key)) { assert.ok(data[key]); - assert.strictEqual(data[key], compare[key]); + assert.equal(data[key], compare[key]); } } @@ -281,7 +282,7 @@ describe('Flags', function () { for(var key in compare) { if (compare.hasOwnProperty(key)) { assert.ok(data[key]); - assert.strictEqual(data[key], compare[key]); + assert.equal(data[key], compare[key]); } } @@ -480,7 +481,7 @@ describe('Flags', function () { describe('.update()', function () { it('should update a flag\'s properties', function (done) { SocketFlags.update({ uid: 2 }, { - flagId: flag.flagId, + flagId: 2, data: [{ name: 'state', value: 'wip' @@ -498,7 +499,7 @@ describe('Flags', function () { describe('.appendNote()', function () { it('should append a note to the flag', function (done) { SocketFlags.appendNote({ uid: 2 }, { - flagId: flag.flagId, + flagId: 2, note: 'lorem ipsum dolor sit amet' }, function (err, data) { assert.ifError(err); From babafde7268c2d334457a12d23022af24ef5d6ab Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 19 Dec 2016 12:40:33 -0500 Subject: [PATCH 047/316] once more for good measure! --- src/flags.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/flags.js b/src/flags.js index d3735671b2..7825f5603d 100644 --- a/src/flags.js +++ b/src/flags.js @@ -156,9 +156,9 @@ Flags.validate = function (payload, callback) { return callback(err); } - if (data.target.deleted) { + if (parseInt(data.target.deleted, 10)) { return callback(new Error('[[error:post-deleted]]')); - } else if (data.reporter.banned) { + } else if (parseInt(data.reporter.banned, 10)) { return callback(new Error('[[error:user-banned]]')); } From 283ae564f2771459592e8a36a46e09e4bcafc1d1 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 19 Dec 2016 12:52:47 -0500 Subject: [PATCH 048/316] removing incorrect parseInt --- src/flags.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/flags.js b/src/flags.js index 7825f5603d..ecc5a84e3e 100644 --- a/src/flags.js +++ b/src/flags.js @@ -156,7 +156,7 @@ Flags.validate = function (payload, callback) { return callback(err); } - if (parseInt(data.target.deleted, 10)) { + if (data.target.deleted) { return callback(new Error('[[error:post-deleted]]')); } else if (parseInt(data.reporter.banned, 10)) { return callback(new Error('[[error:user-banned]]')); From 7c7cb8cfe65118ed12eb60f2057ff2da1578603d Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 20 Dec 2016 13:30:41 -0500 Subject: [PATCH 049/316] remove flag translations from development branch --- public/language/ar/admin/manage/flags.json | 19 ------------------- public/language/bg/admin/manage/flags.json | 19 ------------------- public/language/bn/admin/manage/flags.json | 19 ------------------- public/language/cs/admin/manage/flags.json | 19 ------------------- public/language/da/admin/manage/flags.json | 19 ------------------- public/language/de/admin/manage/flags.json | 19 ------------------- public/language/el/admin/manage/flags.json | 19 ------------------- public/language/en-GB/admin/manage/flags.json | 19 ------------------- public/language/en-US/admin/manage/flags.json | 19 ------------------- .../en-x-pirate/admin/manage/flags.json | 19 ------------------- public/language/es/admin/manage/flags.json | 19 ------------------- public/language/et/admin/manage/flags.json | 19 ------------------- public/language/fa-IR/admin/manage/flags.json | 19 ------------------- public/language/fi/admin/manage/flags.json | 19 ------------------- public/language/fr/admin/manage/flags.json | 19 ------------------- public/language/gl/admin/manage/flags.json | 19 ------------------- public/language/he/admin/manage/flags.json | 19 ------------------- public/language/hu/admin/manage/flags.json | 19 ------------------- public/language/id/admin/manage/flags.json | 19 ------------------- public/language/it/admin/manage/flags.json | 19 ------------------- public/language/ja/admin/manage/flags.json | 19 ------------------- public/language/ko/admin/manage/flags.json | 19 ------------------- public/language/lt/admin/manage/flags.json | 19 ------------------- public/language/ms/admin/manage/flags.json | 19 ------------------- public/language/nb/admin/manage/flags.json | 19 ------------------- public/language/nl/admin/manage/flags.json | 19 ------------------- public/language/pl/admin/manage/flags.json | 19 ------------------- public/language/pt-BR/admin/manage/flags.json | 19 ------------------- public/language/pt-PT/admin/manage/flags.json | 19 ------------------- public/language/ro/admin/manage/flags.json | 19 ------------------- public/language/ru/admin/manage/flags.json | 19 ------------------- public/language/rw/admin/manage/flags.json | 19 ------------------- public/language/sc/admin/manage/flags.json | 19 ------------------- public/language/sk/admin/manage/flags.json | 19 ------------------- public/language/sl/admin/manage/flags.json | 19 ------------------- public/language/sr/admin/manage/flags.json | 19 ------------------- public/language/sv/admin/manage/flags.json | 19 ------------------- public/language/th/admin/manage/flags.json | 19 ------------------- public/language/tr/admin/manage/flags.json | 19 ------------------- public/language/vi/admin/manage/flags.json | 19 ------------------- public/language/zh-CN/admin/manage/flags.json | 19 ------------------- public/language/zh-TW/admin/manage/flags.json | 19 ------------------- 42 files changed, 798 deletions(-) delete mode 100644 public/language/ar/admin/manage/flags.json delete mode 100644 public/language/bg/admin/manage/flags.json delete mode 100644 public/language/bn/admin/manage/flags.json delete mode 100644 public/language/cs/admin/manage/flags.json delete mode 100644 public/language/da/admin/manage/flags.json delete mode 100644 public/language/de/admin/manage/flags.json delete mode 100644 public/language/el/admin/manage/flags.json delete mode 100644 public/language/en-GB/admin/manage/flags.json delete mode 100644 public/language/en-US/admin/manage/flags.json delete mode 100644 public/language/en-x-pirate/admin/manage/flags.json delete mode 100644 public/language/es/admin/manage/flags.json delete mode 100644 public/language/et/admin/manage/flags.json delete mode 100644 public/language/fa-IR/admin/manage/flags.json delete mode 100644 public/language/fi/admin/manage/flags.json delete mode 100644 public/language/fr/admin/manage/flags.json delete mode 100644 public/language/gl/admin/manage/flags.json delete mode 100644 public/language/he/admin/manage/flags.json delete mode 100644 public/language/hu/admin/manage/flags.json delete mode 100644 public/language/id/admin/manage/flags.json delete mode 100644 public/language/it/admin/manage/flags.json delete mode 100644 public/language/ja/admin/manage/flags.json delete mode 100644 public/language/ko/admin/manage/flags.json delete mode 100644 public/language/lt/admin/manage/flags.json delete mode 100644 public/language/ms/admin/manage/flags.json delete mode 100644 public/language/nb/admin/manage/flags.json delete mode 100644 public/language/nl/admin/manage/flags.json delete mode 100644 public/language/pl/admin/manage/flags.json delete mode 100644 public/language/pt-BR/admin/manage/flags.json delete mode 100644 public/language/pt-PT/admin/manage/flags.json delete mode 100644 public/language/ro/admin/manage/flags.json delete mode 100644 public/language/ru/admin/manage/flags.json delete mode 100644 public/language/rw/admin/manage/flags.json delete mode 100644 public/language/sc/admin/manage/flags.json delete mode 100644 public/language/sk/admin/manage/flags.json delete mode 100644 public/language/sl/admin/manage/flags.json delete mode 100644 public/language/sr/admin/manage/flags.json delete mode 100644 public/language/sv/admin/manage/flags.json delete mode 100644 public/language/th/admin/manage/flags.json delete mode 100644 public/language/tr/admin/manage/flags.json delete mode 100644 public/language/vi/admin/manage/flags.json delete mode 100644 public/language/zh-CN/admin/manage/flags.json delete mode 100644 public/language/zh-TW/admin/manage/flags.json diff --git a/public/language/ar/admin/manage/flags.json b/public/language/ar/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/ar/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/bg/admin/manage/flags.json b/public/language/bg/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/bg/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/bn/admin/manage/flags.json b/public/language/bn/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/bn/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/cs/admin/manage/flags.json b/public/language/cs/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/cs/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/da/admin/manage/flags.json b/public/language/da/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/da/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/de/admin/manage/flags.json b/public/language/de/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/de/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/el/admin/manage/flags.json b/public/language/el/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/el/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/en-GB/admin/manage/flags.json b/public/language/en-GB/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/en-GB/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/en-US/admin/manage/flags.json b/public/language/en-US/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/en-US/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/en-x-pirate/admin/manage/flags.json b/public/language/en-x-pirate/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/en-x-pirate/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/es/admin/manage/flags.json b/public/language/es/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/es/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/et/admin/manage/flags.json b/public/language/et/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/et/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/fa-IR/admin/manage/flags.json b/public/language/fa-IR/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/fa-IR/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/fi/admin/manage/flags.json b/public/language/fi/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/fi/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/fr/admin/manage/flags.json b/public/language/fr/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/fr/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/gl/admin/manage/flags.json b/public/language/gl/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/gl/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/he/admin/manage/flags.json b/public/language/he/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/he/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/hu/admin/manage/flags.json b/public/language/hu/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/hu/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/id/admin/manage/flags.json b/public/language/id/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/id/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/it/admin/manage/flags.json b/public/language/it/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/it/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/ja/admin/manage/flags.json b/public/language/ja/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/ja/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/ko/admin/manage/flags.json b/public/language/ko/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/ko/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/lt/admin/manage/flags.json b/public/language/lt/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/lt/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/ms/admin/manage/flags.json b/public/language/ms/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/ms/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/nb/admin/manage/flags.json b/public/language/nb/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/nb/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/nl/admin/manage/flags.json b/public/language/nl/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/nl/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/pl/admin/manage/flags.json b/public/language/pl/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/pl/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/pt-BR/admin/manage/flags.json b/public/language/pt-BR/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/pt-BR/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/pt-PT/admin/manage/flags.json b/public/language/pt-PT/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/pt-PT/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/ro/admin/manage/flags.json b/public/language/ro/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/ro/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/ru/admin/manage/flags.json b/public/language/ru/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/ru/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/rw/admin/manage/flags.json b/public/language/rw/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/rw/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/sc/admin/manage/flags.json b/public/language/sc/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/sc/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/sk/admin/manage/flags.json b/public/language/sk/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/sk/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/sl/admin/manage/flags.json b/public/language/sl/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/sl/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/sr/admin/manage/flags.json b/public/language/sr/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/sr/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/sv/admin/manage/flags.json b/public/language/sv/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/sv/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/th/admin/manage/flags.json b/public/language/th/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/th/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/tr/admin/manage/flags.json b/public/language/tr/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/tr/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/vi/admin/manage/flags.json b/public/language/vi/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/vi/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/zh-CN/admin/manage/flags.json b/public/language/zh-CN/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/zh-CN/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file diff --git a/public/language/zh-TW/admin/manage/flags.json b/public/language/zh-TW/admin/manage/flags.json deleted file mode 100644 index bfc488a409..0000000000 --- a/public/language/zh-TW/admin/manage/flags.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", - "category": "Category", - "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", - "sort-by.most-recent": "Most Recent", - "search": "Search", - "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", - "posted-in": "Posted in %1", - "read-more": "Read More", - "flagged-x-times": "This post has been flagged %1 time(s):", - "dismiss": "Dismiss this Flag", - "delete-post": "Delete the Post", - - "alerts.confirm-delete-post": "Do you really want to delete this post?" -} \ No newline at end of file From 3ea927fa658c9fde56b938d5613b3c1e33dc311c Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 27 Dec 2016 15:29:46 -0500 Subject: [PATCH 050/316] fix minSchemaDate in upgrade.js --- src/upgrade.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/upgrade.js b/src/upgrade.js index 48e552dabe..89fe87f8d7 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -8,7 +8,7 @@ var winston = require('winston'); var Upgrade = {}; -var minSchemaDate = Date.UTC(2015, 10, 6); // This value gets updated every new MAJOR version +var minSchemaDate = Date.UTC(2016, 8, 7); // This value gets updated every new MAJOR version var schemaDate; var thisSchemaDate; From 05fdd358187d619cc6845544bc73dba6f8826ab5 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 27 Dec 2016 15:33:55 -0500 Subject: [PATCH 051/316] removed menu item for flags management in ACP --- public/language/en-GB/admin/menu.json | 1 - src/views/admin/partials/menu.tpl | 2 -- 2 files changed, 3 deletions(-) diff --git a/public/language/en-GB/admin/menu.json b/public/language/en-GB/admin/menu.json index 6a4995ea6e..985c540e8a 100644 --- a/public/language/en-GB/admin/menu.json +++ b/public/language/en-GB/admin/menu.json @@ -13,7 +13,6 @@ "manage/users": "Users", "manage/registration": "Registration Queue", "manage/groups": "Groups", - "manage/flags": "Flags", "manage/ip-blacklist": "IP Blacklist", "section-settings": "Settings", diff --git a/src/views/admin/partials/menu.tpl b/src/views/admin/partials/menu.tpl index 9ab06ed811..a92d1b5f2e 100644 --- a/src/views/admin/partials/menu.tpl +++ b/src/views/admin/partials/menu.tpl @@ -19,7 +19,6 @@
  • [[admin/menu:manage/users]]
  • [[admin/menu:manage/registration]]
  • [[admin/menu:manage/groups]]
  • -
  • [[admin/menu:manage/flags]]
  • [[admin/menu:manage/ip-blacklist]]
  • @@ -192,7 +191,6 @@
  • [[admin/menu:manage/users]]
  • [[admin/menu:manage/registration]]
  • [[admin/menu:manage/groups]]
  • -
  • [[admin/menu:manage/flags]]
  • [[admin/menu:manage/ip-blacklist]]
  • From ec544518e8d3610bcd4f20e6c32447322f32770a Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Mon, 2 Jan 2017 22:23:17 -0700 Subject: [PATCH 052/316] Use async v2 --- minifier.js | 14 +++++++++++--- package.json | 2 +- src/file.js | 20 ++++++++++++++------ src/meta/dependencies.js | 25 ++++++++++++++++--------- src/meta/themes.js | 18 ++++++++++++++---- src/plugins.js | 12 ++++++++---- src/plugins/load.js | 4 +--- src/privileges/helpers.js | 8 ++------ src/rewards/index.js | 10 ++++++---- 9 files changed, 73 insertions(+), 40 deletions(-) diff --git a/minifier.js b/minifier.js index 25c0177175..a83069ab85 100644 --- a/minifier.js +++ b/minifier.js @@ -17,13 +17,21 @@ Minifier.js.minify = function (scripts, minify, callback) { }); async.filter(scripts, function (script, next) { - file.exists(script, function (exists) { + file.exists(script, function (err, exists) { + if (err) { + return next(err); + } + if (!exists) { console.warn('[minifier] file not found, ' + script); } - next(exists); + next(null, exists); }); - }, function (scripts) { + }, function (err, scripts) { + if (err) { + return callback(err); + } + if (minify) { minifyScripts(scripts, callback); } else { diff --git a/package.json b/package.json index a9dbdfb82b..f80a1b12c9 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "coveralls": "istanbul cover _mocha --report lcovonly -- -R dot && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage" }, "dependencies": { - "async": "~1.5.0", + "async": "^2.1.4", "autoprefixer": "^6.2.3", "bcryptjs": "~2.3.0", "body-parser": "^1.9.0", diff --git a/src/file.js b/src/file.js index df820cc47f..a231ddd01d 100644 --- a/src/file.js +++ b/src/file.js @@ -87,19 +87,27 @@ file.allowedExtensions = function () { file.exists = function (path, callback) { fs.stat(path, function (err, stat) { - callback(!err && stat); + if (err) { + if (err.code === 'ENOENT') { + return callback(null, false); + } + return callback(err); + } + return callback(null, true); }); }; file.existsSync = function (path) { - var exists = false; try { - exists = fs.statSync(path); - } catch(err) { - exists = false; + fs.statSync(path); + } catch (err) { + if (err.code === 'ENOENT') { + return false; + } + throw err; } - return !!exists; + return true; }; module.exports = file; diff --git a/src/meta/dependencies.js b/src/meta/dependencies.js index 3f892c37ab..fdfa755198 100644 --- a/src/meta/dependencies.js +++ b/src/meta/dependencies.js @@ -19,14 +19,17 @@ module.exports = function (Meta) { winston.verbose('Checking dependencies for outdated modules'); - async.every(modules, function (module, next) { + async.each(modules, function (module, next) { fs.readFile(path.join(__dirname, '../../node_modules/', module, 'package.json'), { encoding: 'utf-8' }, function (err, pkgData) { - // If a bundled plugin/theme is not present, skip the dep check (#3384) - if (err && err.code === 'ENOENT' && (module === 'nodebb-rewards-essentials' || module.startsWith('nodebb-plugin') || module.startsWith('nodebb-theme'))) { - winston.warn('[meta/dependencies] Bundled plugin ' + module + ' not found, skipping dependency check.'); - return next(true); + if (err) { + // If a bundled plugin/theme is not present, skip the dep check (#3384) + if (err.code === 'ENOENT' && (module === 'nodebb-rewards-essentials' || module.startsWith('nodebb-plugin') || module.startsWith('nodebb-theme'))) { + winston.warn('[meta/dependencies] Bundled plugin ' + module + ' not found, skipping dependency check.'); + return next(); + } + return next(err); } try { @@ -34,20 +37,24 @@ module.exports = function (Meta) { } catch(e) { process.stdout.write('[' + 'missing'.red + '] ' + module.bold + ' is a required dependency but could not be found\n'); depsMissing = true; - return next(true); + return next(); } var ok = !semver.validRange(pkg.dependencies[module]) || semver.satisfies(pkgData.version, pkg.dependencies[module]); if (ok || (pkgData._resolved && pkgData._resolved.indexOf('//github.com') !== -1)) { - next(true); + next(); } else { process.stdout.write('[' + 'outdated'.yellow + '] ' + module.bold + ' installed v' + pkgData.version + ', package.json requires ' + pkg.dependencies[module] + '\n'); depsOutdated = true; - next(true); + next(); } }); - }, function (ok) { + }, function (err) { + if (err) { + return callback(err); + } + if (depsMissing) { callback(new Error('dependencies-missing')); } else if (depsOutdated) { diff --git a/src/meta/themes.js b/src/meta/themes.js index be48d1e70d..777e1af5df 100644 --- a/src/meta/themes.js +++ b/src/meta/themes.js @@ -27,18 +27,28 @@ module.exports = function (Meta) { async.filter(files, function (file, next) { fs.stat(path.join(themePath, file), function (err, fileStat) { if (err) { - return next(false); + if (err.code === 'ENOENT') { + return next(null, false); + } + return next(err); } - next((fileStat.isDirectory() && file.slice(0, 13) === 'nodebb-theme-')); + next(null, (fileStat.isDirectory() && file.slice(0, 13) === 'nodebb-theme-')); }); - }, function (themes) { + }, function (err, themes) { + if (err) { + return callback(err); + } + async.map(themes, function (theme, next) { var config = path.join(themePath, theme, 'theme.json'); fs.readFile(config, function (err, file) { if (err) { - return next(); + if (err.code === 'ENOENT') { + return next(null, null); + } + return next(err); } try { var configObj = JSON.parse(file.toString()); diff --git a/src/plugins.js b/src/plugins.js index b8ddbdc0fe..52378e7f38 100644 --- a/src/plugins.js +++ b/src/plugins.js @@ -161,9 +161,7 @@ var middleware; }); // Filter out plugins with invalid paths - async.filter(paths, file.exists, function (paths) { - next(null, paths); - }); + async.filter(paths, file.exists, next); }, function (paths, next) { async.map(paths, Plugins.loadPluginInfo, next); @@ -346,7 +344,13 @@ var middleware; async.filter(dirs, function (dir, callback) { fs.stat(dir, function (err, stats) { - callback(!err && stats.isDirectory()); + if (err) { + if (err.code === 'ENOENT') { + return callback(null, false); + } + return callback(err); + } + callback(null, stats.isDirectory()); }); }, function (plugins) { next(null, plugins); diff --git a/src/plugins/load.js b/src/plugins/load.js index 60d584f99f..98a470f914 100644 --- a/src/plugins/load.js +++ b/src/plugins/load.js @@ -31,9 +31,7 @@ module.exports = function (Plugins) { return path.join(__dirname, '../../node_modules/', plugin); }); - async.filter(plugins, file.exists, function (plugins) { - next(null, plugins); - }); + async.filter(plugins, file.exists, next); }, ], callback); }; diff --git a/src/privileges/helpers.js b/src/privileges/helpers.js index 9df9d8cd7b..02e1bed879 100644 --- a/src/privileges/helpers.js +++ b/src/privileges/helpers.js @@ -8,12 +8,8 @@ var helpers = {}; helpers.some = function (tasks, callback) { async.some(tasks, function (task, next) { - task(function (err, result) { - next(!err && result); - }); - }, function (result) { - callback(null, result); - }); + task(next); + }, callback); }; helpers.isUserAllowedTo = function (privilege, uid, cid, callback) { diff --git a/src/rewards/index.js b/src/rewards/index.js index 4ba403fce8..398abfb8f3 100644 --- a/src/rewards/index.js +++ b/src/rewards/index.js @@ -37,12 +37,14 @@ rewards.checkConditionAndRewardUser = function (uid, condition, method, callback function (rewards, next) { async.filter(rewards, function (reward, next) { if (!reward) { - return next(false); + return next(null, false); } - checkCondition(reward, method, next); - }, function (eligible) { - if (!eligible) { + checkCondition(reward, method, function (result) { + next(null, result); + }); + }, function (err, eligible) { + if (err || !eligible) { return next(false); } From 11f7cc416363b7fb116feae135ffecf721acd62f Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Mon, 2 Jan 2017 23:03:34 -0700 Subject: [PATCH 053/316] Pass tests --- public/logo.png | Bin 7189 -> 3680 bytes src/controllers/admin/themes.js | 6 +++--- src/plugins.js | 4 +--- src/plugins/load.js | 4 ++-- src/webserver.js | 4 ++-- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/public/logo.png b/public/logo.png index 6a6c184126221ac0b44a53d08ef3fe012ed73b45..d8dcba4638cbdf043ad23c7426a73f0743dadd0d 100644 GIT binary patch literal 3680 zcmc(idpMifzQ?;~`pk5u7o>_26_=2-gerBb%cg=vYY0LUYQ*jkks6~)wWO!5OT=ZW zN$5;MiwsH_(^by}s`mET ze7?zag!b#1wPI`QYl(WDRYzex&gYEOwHvUkql~9) z+l9Bhh`5S_*G`j%hKX%szTgj6q`x%`{F@&MR;(X#lZ>c#q^tzL>Y zqc%%ZDx^(63Bypak2na4B;BFn{jK@lT9-EqFAh)l8}Ew`AJJCxjun2M*YeTiOS_gy z{hg z)~uMIEcCACpVSh1R@;I!-ME9EPL;N@7Qr;ju|n+m#G3kjRUsr{SrMMJAs(>_By~a{OQK5d~ zy!DP)IareLrmyw82w*g9bp+n{3a@WdR@-()oRV*s!qK$SXjNew!{uPJfx6GrS~vn> zDj&6GUZSiA>+NjT*_RAT1Udo_{EWkjJYeUa%4cOboF>vA7sH8;243F%j8 z?yR`kcz=WI2FbUM@+T_*Km6U0QA1to$Q(aEK$C;?2P26@e;XS*Qw!;@s%!EkA{32U z%HzT1N9Uhitbl9>4ZM`thDbpJKWxs9|8Za65A4>PhX~*P`Hk85iQfECMq`DoZ^kK-ckxj)@T~S>KxZwTccPIAEUIu!!!A5Lgi$MZJqN3%;DNNj2)Iz7r%TH zT991C6#N}OL#tlYbFSiRnYn4uN79}%;x?~k);W`ILoJ{RPclDodltrGtoyHifwaV&YNrP2D8fe*8wf?e(6UCMs@9! z4Uh&+B%t1ZJyrc`Cq(2#{AHa>>02Z|WX40XokDp;O1-!F3`ul3g$%Jxl16QS;jEoLv zByrB8|D|6X9#0;k z0_kpnGt1tUP79$_6|N(U%pb(!KNai?AMX#8d>nQp`ioQFcQ3ccp$K4e@_#|Ko=z@T zhhIriMnXX(Bo5TMv~wyfG;vkw%5!RsD5^~M>-S8T?u+4i`;ic6d5h@f$@bW2k1V?^ z-D%lB=dQWFm2{?^(asd($ifa8C(8W7#}+_WLrlS+4Dimdg5SEhB8O?#V|2|J!sn5# zvs3jIh;K_88sBHH9HKUZ%z{{9${V<2iu)#2}tP4J)$t>`-`3V8a7_N`dbI;nFh8Pk|9=JEX{h=Gxx5q_4R8`oGvXQ$B>#y z9s&{{fBD0irPRgo^Q`OnOpypMWDy7OWVT3D3eUy><}3yrYvsRde8>Ma@L3#-?YOzI zWWVuYho@uJ@d+^?lwNV#cx2xui8i@}0ZeX1B>HrfBgwmdM^-VK>QuQRh(tir&^|Cg z1ONg`?OhYr8RJ*h7a+Lu4xNG_Eg$@BZ-2mr>pL^-qp3F}=~?^v8ejy*ExiB$fXod^ zDl5`~=kgotY%xNhr`jsfo_E|@hdqg^MpM277+r zQ-j|9hIa6AlDmo&K&R9O7cIdTkiC)uy2=T>#EzOww$+SaKsJvf15h!Kqoll#(m%IG8S4pOFHA{3M;4|xzKnl8^*sE_yWFT; zEJ$rY)e}j-r2#xwn#GVjBZVh!rCWyN;LMqTd%4{XNFMe$R`;OdK9jCx@}M4ri#$Lx z&#?j^Bh_sv9&BCjyObtxP}h#(K(Vp~X(k&I+F;*W#f9oKIfxSo76MX+^yZ$z6*Mg` zL(b>mEDAex06I_d7<(T`aOF3%`O;`U;~RG_eJ?Ug#ZWGfA>VhYnytJR|H>t*7vNk2r-(PmB!~!2b(}#g4fs+{CRKBXgI(tq z-o@`u!R(oizYu^@i!w6JsVm*j*br$YpNTsrUzd-+ldpy++GaXbsE$wIoMuUn<1{&s z084TcMN|S72Fx!1M^65!8Jnn4myH|2#T!vJXX#8|&5Nuj=1~n$#cv1zpcbRszk-8Z zy<~qwy2}Q=C~b3`{HB!ou0y>SPK+qN{|eD#6ILolqbi%!;{wqz_du?VH2rkfnylcC zHvzF&mHzn2^pv#XsOyGZ;#D>Pz`+g~f9X7Hl#Xhsn}+~-MnL=dnf;{oq0Q? zit)8n9Ry|{3SU3mMmIN6oIsGN7sC^__?9Vi&J@Ug97)mi(y09BXp7-}il&hL^`aQ# zdMvmeQ)kBI4@L95yF*Q}MsDgLAqOUCX7M2vw%2(AuMv=Wixm z+dQ~@I4~920;d@5kET(vc!Vh$#(i~!FTt20kFhTU!%IWK*l;rcq6&AH30U8_#^u6R z(q93Q2T)G2ZA@>%y@dRy%guSdEmUf$CN~#HB9g_PL^2(qaM7Tsbdlt*i6mAraApX0 z(I+98?uLV*fmk0F;!ZoJNmQ!djb=psyuoQ(MEP;GW2a!O-k$^r1xKo4luCf58=P}j z3&EA7*7pN|2Q-PHYqi(-YH~P-?+T!_qau){MN*1Zy+0nNH{V(5DkjqELP!_I3!BA5zsn+@W)*x8&8;e;|O{^%`J9ECC@A1w@YV>{};ZuY5e@TO6PK zaoCfP6Op4@ZV8@?w9v9YRDJkuw*J;|c>i5X(tNF8`(c4j1Zefd#`>5Hvm* zg!;#0x(C37dNNZG2ud=!BTN^J)o>et4Mjm?H5b@bG#4a40#=CehEy`2_okrnC)FG0 zP@&QDqjX{4O-_jwYIEl&p@r^$WJmP>HfA3wNX|;e<5uYvV9U4%h4l5Pb^q?>e*+Ug BPY?hA literal 7189 zcmeHM_fr!<(~gKrQ+g5Uy(3k6Cp0lMLApq4nZxnHWhMpP8O$#G&io2PU`s!K+003g#|B+iE<*Gq9iwuFJRMd5HPU>j>57I*OBpJEhmZ5fv4#AHIj|!-zRo!wr2qq|`{{Lj)8!+0-zk`+$UJ zarcY@HIyP@w9B{X3aBWH@lhcFZT_FP|7-Gp@{3W+;={dLf@@bpL;y9CC7WQn1KrQD zuwklfkyv#uPAxHZ!lgr>Bel}4Ey{#;P=b^E(*s?>fO?)oJvK@MyY#TiP-H<_pOev% zU>zw;Jh6)}W_#`6=jZAs`V-m#t;buipwpfZbCRQH`M1i5kMJtaL7#d19lbXPy(~on zxSCZEKAHn1cv=`*q&zBv#&}9=_icrQm$>7&UFY9bOQz#mH#;|Yw4+rHqqDomc;iCX zd2Kkg(6At1oeWgj`ybFIQS~0)rLfq5XequPo|BCGuRu_WE_F) z(cC3Zu|C1D=xdZKAfBQ_3Tmqww)t1wSD1$0uyZEB%$Sq7iW%s(eTP$#3^5hv5=zK~ z1;=3rMz!sE_L91whsVi9fW9)&XYYe_CAv1EMts|8AGo;Th-KAActK#{MW0uDLJ~uK zxez4!YSi)j=u@;qGhYf16@DB$=TTS@w<6|_H1_ud|rau`t7M5LX zuwWZP7R(NzF6~)m?A_r%TNXF?7wgB z%6p+VDUl(mZA_PK=rTL|!B6*Lq1CSf`+o+3*(y;238M!rjfy0(qzOA})zYQKZ<8xJ z;)X^8_Vr(Pb3GL8DzyRT{hXlH?}R!Np?Ss{X+J`fH1%;g*>x93?6_xk8S@Xcn?9!7 zNY|cs4m*xpEyN~$z&{9Yc^5iM`is}am|TN5VN%=jIwg1S{aCgDq`i&?rB~T8>@3H_54L zDqmcogr`^Fh3o8aC4u*Xg?9-83M0EpPr+UP69~JjZDAz*tGGF8evNUNe!6J7KZHH^ zklAFh#v}3XN3zAxlLn^f`{fO(JVt@ZRk2sUmiy#rAMW}EETjQZMFvjQbHICxoCkW1 z448;5dEjeuI%VMYk9FkmE8j2~ySE<352j+1PQS||q`vH>mxZUS7o9V~YSlYs1W+0# z5(`e^7(W$RwSE-dJ(1wb5S zR45BKDm-EW04ejk@u{v2l7o?*rP(7P7qegfvIy#6Zms5RhnM&J#7Qn`@+CKQ#1Lkl zP$!{!-ZPovz~lXtuaRrod7$1QMaq|Y@UqKSVA>_D46*vhEN|wL)$OK8&&3~=&5;YH z5pfvq;ivgA&!i)mOR+@nu7}#k-gKy89BFb0+YLK?FL*K)l(&knx;xaX8&sfYTol!5 z7Sx*TF?nLKEq7AAKZ?I+8y}+u8e6igF%o@Q{koN&C7i?1<{h}HiQ&?53*&(wXcHtl#{f0>mzc=82d3;Z5Bc+GB>iun?- zvQ^Lh0H6)hv;Rl;t=?|uT>`j;l{bb}dZNS9a^{PQ-WZX~!sz9T33dB>PU}y(m7exj zTvaX_a6t|$5U6XokY=aURG>xNJ8TG7%~8)GmUr-c2BNC_H76}I zLEmzfnxCk;A3RA>;!3}k(JlAEzD^!dktsZu!F}ZNTSV)gKogm^1yyMcSIopF-7uFe z;witzc#pAD*5HfVqSizBnkp$RWceD+!i2sYN0K6K?0K8H4n@L;Dof=_+XiV%=l1&9 zpK-WS!WT%k`_)fV8I-3x-RCu$>qzc5UB3HUU?<3z;!?SXg>5MP0Z>L9cyn*zRNW8poW3A}~A#<|(~oe zhcdojf%kaEonGAP(I}(0rERs+8p?i-8ucYb=$zVEH3(K@&=iafJAD5b!Tv+5i&CMo z?SdQ7(6Q-fR)QzfAQ^{jR=%E`$JA*AKwXcAF^ZuotVM-pe=XzeL*{Lf@(7>LSz~E2 z24|KD1z}H&=^Re$K(NENjN74l^=4dQA%_Xa(?mtlxIVd0pbXZO}iIVhkz-{41=G)($BqK@}S{%0Na#sZrW@Ng$mJZ`qm~J_v#!OwbrJ&)L z^9tLnGVU{>n-{sGx_2a(CSHRrq6vJ;&(Gq}@3?|PSl$yor_i3H()c?y^k_;kl={li z?@)r;j=o31NxG3&79Mf%@Cc;x{$*`6VcwJEqKW!}tnnt8_a6L{Fz-S1ejMVA-CFwz zHj|+v%WDT*UV_*U9G8pkjDJq7q4rZVJbibVqsc8FU)x4x>G>fdG-zongyP+rpgfYI zaeXeH>BpS)qQ=PlyEyjypI03PQ17Uw!NHE~^4udTJ> ziEx__GUjkuDI+M-eJon}C|!xSVhvgAXk4%pJkolQLIPAC@O}&PtjsKHJG_epUt3Si z-p@$%G;?uQZ*8wk7ytFV(;>$^3c>R|_vth#k-nwZIx6bPsHD#3AXZEAS{l*%Zf5?` z5bi2G7AA~mbylbPd$#?r>vGV76N$HZKKHae%v;ohp3Z#ls|o5EMW491g5~Y@%^V-F%gXoD{8@tZcz z!G&`h{-QMV`l92u*IBREp&rm;Hrf+>4OvuwO)URL$rFW%v;zZ3A?xDX!Wz5}?D#4| z&HEI%b6@7(LC79;EKJczSf7g6&D*|bZb!yTyd$Y|KfrPNZB;#gc8RQCdfytp0Kvoj zu~6D%8u@x+NP*)YjUKD=uJifhf#o4MDy1##!IE&X!e$fiXmX$G%hTV=Sumds#;BGZ8W zUUXjBzc&pX?+9*-rgXV9@v^&w&V}}64(p1d1t}F(S#n#pNWne{l17QG*>E<*IAs z5YpP|BC+SS1w4n~b;xnG7oHK(*Q4cgBwbZrA2c8&+LI%(`R%7aTx%Q7!fGT@#J^>5 z)tlSsF=^AKLv#Qad`#9op;3z5lg(AN3x57=Kw57h01ZDLL0gd*@T(>clJ=~@ zwgB40B?j$&8!5Gl5uI$!WItI`behP)^rLZB1MheYU60TpMxTm5p{2aW##lXYKYb$+ zA9ed*tx@Ge!L91{PbwS%FH`Qa8w;R-eCHK)kW0-R@N?n%`u44b7UB88X11^7&%|Q( ze5<6$s@M$r-wQ`0E- z)NXylq^%>B*vpHbhbwIBHYs()WlY0-${6RiEx@}@29Np8%HtaDY0HcPJJH~TG@c7e zC{Oq=6EX1(pa=7QP;)(p^}TYI=8H=`E|2Dn-%+zSP`X*c?h}+9Hgh|4Mz%X0M}-8X zEdHulTf~=u(Cas+U;RaYf?Od|m5R-dG1I3~bgbuYP{?%?b-YWfO@}Sdm_8plaXQVX z*|vm}9?!~N#OIk^zz3d)?sI;sKa3wIpter4qOt-QiENkF_{N%qJ!Apg&F~b}+JDrRQD z&leuKdD-;r67W*Aa!UQy!V(bha7#-znK?W;c#&n&5Ox6u@ z4-YpH#mGa|buet0Yk3`!Ho(kiNnRS_6>7XZZEr!VD%|1^aH`I5N(!y_>Tp)NK7mis ztAWo~hSb$g{9Q209%8Si?0{_nQp%}DfkXgD%os^!OqW+I7g3ehxJ!0`Ar^Z7-emcmgxVBA)*_-qT)7fwqWd zGv=V{4+|B>uQDOmg!Y;GGJYjXAISN#H2YQ4N;+uspB8&inh3Zw?olv;<0BTtH_Vj_XVo(qe9thC&OLNl-nf>8aEW(fR%!~#DL3LJ zIEgoc7c^>HVvHf>1g1Am)v1+j%WZ#2lkgJn^YLmbVQ};}!^br*lUq(0*|#hJj;cCG z1Fx&}oHgC2hfRL(zd5B1Ew6qbzDyXx?zFCZX*a{dXVkvzSESRnGAJ*Wjm>^B%7Q0u z51zjquLJJAN3lK48+ntZpLdmw+QjxQ$tY7Vb@j$ z+ww^{`0r^k5^s$1?B|7WsqBTfL1$B3qA$(Po|4JTd9L%g>q;e8RoE-;+%hPbECdy zOHJ5_n{(FmD3@M4F%C*7)b!31LJLXbbOH!6*PTYvK?}?|EAHGxe0jRtw^yCTX|CBG zdsWlP4i6g8dp{zIgjGmGI*hPo?%H|*?TbpmyIj%4Lds;;=I7;W4y<`fsJ%vX~3trmB$y@o&)&srPLd(VmxW{teXU_G_Iw0sJDe>FOf27Ky*-_{U1^mm;9|vFib4m zK;a&f#GhRsI_VK>Ik4yN@wNrXs^_q zC-cUi=N{Gs1}=FY;Q2q?R*Ts0U3lN&E(kA2!f<-Ii8NsuEX8=i7JEzUW z+mupQblhpf=#@G>F~=8k$vf6F3|X=s>H#WWMPE6tlQWg1UM~b6827?kgo3AaE0isaG;ls=`+$r0tq8v5r=?d@=PbO8NNB&+0(wCr{pX|iBHc6 ze|th&AqoqUoL|%j(&;4&8PC0Aj-6Xu4zxvlF3EGR?8_X#|LNtaNZvZ{HItf-c6O^g zmKpy*Yewh|(GX{_h+wfmkBNwxj$#R2cjhdi)RJy%4Y)U4!zWar)-a!ouao|8qrm8s zg;PJ=nNEYg54~L;$n!;`mznj?o>83fZhQR6&gsZjTl5URai2<;-s~+*K(aJ&n%1>Z zV(XlnDcybaPL}~GHw}nJ{tsc89> zLQ1Q)li#Ff6jdc97$!T{Rm<>CS5qj=I55_{yWX2H`1~jjzV3ot@{TH_NWHr0xS7-O@8BjLrWlSzE8>ycS^;0_fZpH5_9A8jd#YdrRRvkM(O4?-xen5B_D?zkv8GtUb$7Jhpf&i`qa2VK>6NN61oYGMJY zmgUC&1Ua@js#?qDp9}UUqHJf9o*X*Nr5R*fgt-B23)$@F`|6b=XHK5~o^O*q)NxIb zq_b5gd24jy`wFL;QJ%}{o-D=SkyzH)=Vub>tr&n5-1*+62q`$Ont1>g89yv7lotub zfRt!X!th-c)4{6Jj*9AX(UZ@pdKb<;cA^;~J5rV2!fpd;!O&C1N}LZ;UaK(yK4?L& z(lkY5{71iVR*RY576jW`2o7zBliPD}#y|f%#S-$* znChB_^pMLx#Pr^jtja{h>XAKrd~9=Pk{)f39(a1u zw|D7v#+BW1Z-IT0R5T7~fBa91%5D&}vnti8DYbsE%tmP57LHk^n~Q>ZPT6VCF|{Yg zK50)nDflN=+r#zutqjfp*1`?5B6}3!buNy!P%rd4?tZ32mjWm}w^A0{1yeQ#r2ZK^btC%=yVy>sGOVLWY zCjH~@D?xe{Ca;qAVEW`1i;>ogl@Ru&OIt<#ofug5N8Os>abEOod9~_`4!2-vXxai_3?d<7S@rt>0bxcK>0L_=t?~N^tucki~Y}&`= z9T4<3p=;FxvUFhz0tAXrpAY7T$>4S?C@a3$= ' + staticDir + '\' not found.'); } - callback(); + callback(err); }); } } diff --git a/src/webserver.js b/src/webserver.js index fec42974c6..43e3c4b324 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -262,11 +262,11 @@ module.exports.testSocket = function (socketPath, callback) { var file = require('./file'); async.series([ function (next) { - file.exists(socketPath, function (exists) { + file.exists(socketPath, function (err, exists) { if (exists) { next(); } else { - callback(); + callback(err); } }); }, From 541f51e947abfac5f3aa7b8c1138907df42f7803 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 3 Jan 2017 13:38:06 -0500 Subject: [PATCH 054/316] restrict flags interface so that moderators only see flags from cids they can actually moderate --- src/controllers/mods.js | 39 +++++++++++++++++++++++++++++-- src/flags.js | 52 ++++++++++++++++++++++++++++++++++------- test/flags.js | 35 +++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 10 deletions(-) diff --git a/src/controllers/mods.js b/src/controllers/mods.js index 242d68d708..8056b8c5b5 100644 --- a/src/controllers/mods.js +++ b/src/controllers/mods.js @@ -27,6 +27,7 @@ modsController.flags.list = function (req, res, next) { } // Parse query string params for filters + var hasFilter = false; var valid = ['assignee', 'state', 'reporterId', 'type', 'targetUid', 'cid', 'quick']; var filters = valid.reduce(function (memo, cur) { if (req.query.hasOwnProperty(cur)) { @@ -35,6 +36,24 @@ modsController.flags.list = function (req, res, next) { return memo; }, {}); + hasFilter = !!Object.keys(filters).length; + + if (res.locals.cids) { + if (!filters.cid) { + // If mod and no cid filter, add filter for their modded categories + filters.cid = res.locals.cids; + } else { + // Remove cids they do not moderate + if (Array.isArray(filters.cid)) { + filters.cid = filters.cid.filter(function (cid) { + return res.locals.cids.indexOf(String(cid)) !== -1; + }); + } else if (res.locals.cids.indexOf(String(filters.cid)) === -1) { + filters.cid = res.locals.cids; + hasFilter = false; + } + } + } async.parallel({ flags: async.apply(flags.list, filters, req.uid), @@ -45,9 +64,25 @@ modsController.flags.list = function (req, res, next) { return next(err); } + // If res.locals.cids is populated, then slim down the categories list + if (res.locals.cids) { + data.categories = data.categories.filter(function (category) { + return res.locals.cids.indexOf(String(category.cid)) !== -1; + }); + } + // Minimal returned set for templates.js data.categories = data.categories.reduce(function (memo, cur) { - memo[cur.cid] = cur.name; + if (!res.locals.cids) { + memo[cur.cid] = cur.name; + return memo; + } + + // If mod, remove categories they can't moderate + if (res.locals.cids.indexOf(String(cur.cid)) !== -1) { + memo[cur.cid] = cur.name; + } + return memo; }, {}); @@ -55,7 +90,7 @@ modsController.flags.list = function (req, res, next) { flags: data.flags, analytics: data.analytics, categories: data.categories, - hasFilter: !!Object.keys(filters).length, + hasFilter: hasFilter, filters: filters, title: '[[pages:flags]]' }); diff --git a/src/flags.js b/src/flags.js index ecc5a84e3e..168eb5a11e 100644 --- a/src/flags.js +++ b/src/flags.js @@ -53,31 +53,45 @@ Flags.list = function (filters, uid, callback) { } var sets = []; + var orSets = []; + var prepareSets = function (setPrefix, value) { + if (!Array.isArray(value)) { + sets.push(setPrefix + value); + } else if (value.length) { + value.forEach(function (x) { + orSets.push(setPrefix + x); + }); + } else { + // Empty array, do nothing + return; + } + }; + if (Object.keys(filters).length > 0) { for (var type in filters) { switch (type) { case 'type': - sets.push('flags:byType:' + filters[type]); + prepareSets('flags:byType:', filters[type]); break; case 'state': - sets.push('flags:byState:' + filters[type]); + prepareSets('flags:byState:', filters[type]); break; case 'reporterId': - sets.push('flags:byReporter:' + filters[type]); + prepareSets('flags:byReporter:', filters[type]); break; case 'assignee': - sets.push('flags:byAssignee:' + filters[type]); + prepareSets('flags:byAssignee:', filters[type]); break; case 'targetUid': - sets.push('flags:byTargetUid:' + filters[type]); + prepareSets('flags:byTargetUid:', filters[type]); break; case 'cid': - sets.push('flags:byCid:' + filters[type]); + prepareSets('flags:byCid:', filters[type]); break; case 'quick': @@ -90,14 +104,36 @@ Flags.list = function (filters, uid, callback) { } } } - sets = sets.length ? sets : ['flags:datetime']; // No filter default + sets = (sets.length || orSets.length) ? sets : ['flags:datetime']; // No filter default async.waterfall([ function (next) { if (sets.length === 1) { db.getSortedSetRevRange(sets[0], 0, -1, next); + } else if (sets.length > 1) { + db.getSortedSetRevIntersect({ sets: sets, start: 0, stop: -1, aggregate: 'MAX' }, next); + } else { + next(null, []); + } + }, + function (flagIds, next) { + // Find flags according to "or" rules, if any + if (orSets.length) { + db.getSortedSetRevUnion({ sets: orSets, start: 0, stop: -1, aggregate: 'MAX' }, function (err, _flagIds) { + if (err) { + return next(err); + } + + if (sets.length) { + // If flag ids are already present, return a subset of flags that are in both sets + next(null, _.intersection(flagIds, _flagIds)); + } else { + // Otherwise, return all flags returned via orSets + next(null, _.union(flagIds, _flagIds)); + } + }); } else { - db.getSortedSetRevIntersect({sets: sets, start: 0, stop: -1, aggregate: 'MAX'}, next); + setImmediate(next, null, flagIds); } }, function (flagIds, next) { diff --git a/test/flags.js b/test/flags.js index dfa6f1a773..9df1a11a2b 100644 --- a/test/flags.js +++ b/test/flags.js @@ -203,6 +203,41 @@ describe('Flags', function () { done(); }); }); + + it('should return a flag when filtered by both cid 1 and 2', function (done) { + Flags.list({ + cid: [1, 2] + }, 1, function (err, flags) { + assert.ifError(err); + assert.ok(Array.isArray(flags)); + assert.strictEqual(1, flags.length); + done(); + }); + }); + + it('should return one flag if filtered by both cid 1 and 2 and open state', function (done) { + Flags.list({ + cid: [1, 2], + state: 'open' + }, 1, function (err, flags) { + assert.ifError(err); + assert.ok(Array.isArray(flags)); + assert.strictEqual(1, flags.length); + done(); + }); + }); + + it('should return no flag if filtered by both cid 1 and 2 and non-open state', function (done) { + Flags.list({ + cid: [1, 2], + state: 'resolved' + }, 1, function (err, flags) { + assert.ifError(err); + assert.ok(Array.isArray(flags)); + assert.strictEqual(0, flags.length); + done(); + }); + }); }); }); From cc96e329705bf2c61804fb631b3d6694d450702f Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 9 Jan 2017 18:16:48 +0300 Subject: [PATCH 055/316] closes #5352 --- src/topics/tools.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/topics/tools.js b/src/topics/tools.js index c69b0692ab..5363650a1e 100644 --- a/src/topics/tools.js +++ b/src/topics/tools.js @@ -56,9 +56,9 @@ module.exports = function (Topics) { topicData.deleted = isDelete ? 1 : 0; if (isDelete) { - plugins.fireHook('action:topic.delete', topicData); + plugins.fireHook('action:topic.delete', {topic: topicData, uid: uid}); } else { - plugins.fireHook('action:topic.restore', topicData); + plugins.fireHook('action:topic.restore', {topic: topicData, uid: uid}); } var data = { @@ -225,7 +225,7 @@ module.exports = function (Topics) { var uniqueCids = _.unique(topicData.map(function (topicData) { return topicData && parseInt(topicData.cid, 10); })); - + if (uniqueCids.length > 1 || !uniqueCids.length || !uniqueCids[0]) { return next(new Error('[[error:invalid-data]]')); } @@ -249,7 +249,7 @@ module.exports = function (Topics) { setImmediate(next); } } - ], next); + ], next); }, next); } ], callback); From ca7f834f53efcf439603b41470591deb7be87c65 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 9 Jan 2017 16:20:33 -0500 Subject: [PATCH 056/316] updating tx assets --- .tx/config | 94 +++++++++++++++++++++++++++--------------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/.tx/config b/.tx/config index a745278980..1621a4181f 100644 --- a/.tx/config +++ b/.tx/config @@ -753,6 +753,53 @@ trans.zh_CN = public/language/zh-CN/error.json trans.zh_TW = public/language/zh-TW/error.json type = KEYVALUEJSON +[nodebb.flags] +file_filter = public/language//flags.json +source_file = public/language/en-GB/flags.json +source_lang = en_GB +trans.ar = public/language/ar/flags.json +trans.bn = public/language/bn/flags.json +trans.bg = public/language/bg/flags.json +trans.cs = public/language/cs/flags.json +trans.da = public/language/da/flags.json +trans.de = public/language/de/flags.json +trans.el = public/language/el/flags.json +trans.en_US = public/language/en-US/flags.json +trans.en@pirate = public/language/en-x-pirate/flags.json +trans.es = public/language/es/flags.json +trans.et = public/language/et/flags.json +trans.fa_IR = public/language/fa-IR/flags.json +trans.fi = public/language/fi/flags.json +trans.fr = public/language/fr/flags.json +trans.gl = public/language/gl/flags.json +trans.he = public/language/he/flags.json +trans.hu = public/language/hu/flags.json +trans.id = public/language/id/flags.json +trans.it = public/language/it/flags.json +trans.ja = public/language/ja/flags.json +trans.ko = public/language/ko/flags.json +trans.lt = public/language/lt/flags.json +trans.ms = public/language/ms/flags.json +trans.nb = public/language/nb/flags.json +trans.nl = public/language/nl/flags.json +trans.pl = public/language/pl/flags.json +trans.pt_BR = public/language/pt-BR/flags.json +trans.pt_PT = public/language/pt-PT/flags.json +trans.ru = public/language/ru/flags.json +trans.ro = public/language/ro/flags.json +trans.rw = public/language/rw/flags.json +trans.sc = public/language/sc/flags.json +trans.sk = public/language/sk/flags.json +trans.sl = public/language/sl/flags.json +trans.sr = public/language/sr/flags.json +trans.sv = public/language/sv/flags.json +trans.th = public/language/th/flags.json +trans.tr = public/language/tr/flags.json +trans.vi = public/language/vi/flags.json +trans.zh_CN = public/language/zh-CN/flags.json +trans.zh_TW = public/language/zh-TW/flags.json +type = KEYVALUEJSON + [nodebb.tags] file_filter = public/language//tags.json source_file = public/language/en-GB/tags.json @@ -2022,53 +2069,6 @@ trans.zh_CN = public/language/zh-CN/admin/manage/categories.json trans.zh_TW = public/language/zh-TW/admin/manage/categories.json type = KEYVALUEJSON -[nodebb.admin-manage-flags] -file_filter = public/language//admin/manage/flags.json -source_file = public/language/en-GB/admin/manage/flags.json -source_lang = en_GB -trans.ar = public/language/ar/admin/manage/flags.json -trans.bn = public/language/bn/admin/manage/flags.json -trans.bg = public/language/bg/admin/manage/flags.json -trans.cs = public/language/cs/admin/manage/flags.json -trans.da = public/language/da/admin/manage/flags.json -trans.de = public/language/de/admin/manage/flags.json -trans.el = public/language/el/admin/manage/flags.json -trans.en_US = public/language/en-US/admin/manage/flags.json -trans.en@pirate = public/language/en-x-pirate/admin/manage/flags.json -trans.es = public/language/es/admin/manage/flags.json -trans.et = public/language/et/admin/manage/flags.json -trans.fa_IR = public/language/fa-IR/admin/manage/flags.json -trans.fi = public/language/fi/admin/manage/flags.json -trans.fr = public/language/fr/admin/manage/flags.json -trans.gl = public/language/gl/admin/manage/flags.json -trans.he = public/language/he/admin/manage/flags.json -trans.hu = public/language/hu/admin/manage/flags.json -trans.id = public/language/id/admin/manage/flags.json -trans.it = public/language/it/admin/manage/flags.json -trans.ja = public/language/ja/admin/manage/flags.json -trans.ko = public/language/ko/admin/manage/flags.json -trans.lt = public/language/lt/admin/manage/flags.json -trans.ms = public/language/ms/admin/manage/flags.json -trans.nb = public/language/nb/admin/manage/flags.json -trans.nl = public/language/nl/admin/manage/flags.json -trans.pl = public/language/pl/admin/manage/flags.json -trans.pt_BR = public/language/pt-BR/admin/manage/flags.json -trans.pt_PT = public/language/pt-PT/admin/manage/flags.json -trans.ru = public/language/ru/admin/manage/flags.json -trans.ro = public/language/ro/admin/manage/flags.json -trans.rw = public/language/rw/admin/manage/flags.json -trans.sc = public/language/sc/admin/manage/flags.json -trans.sk = public/language/sk/admin/manage/flags.json -trans.sl = public/language/sl/admin/manage/flags.json -trans.sr = public/language/sr/admin/manage/flags.json -trans.sv = public/language/sv/admin/manage/flags.json -trans.th = public/language/th/admin/manage/flags.json -trans.tr = public/language/tr/admin/manage/flags.json -trans.vi = public/language/vi/admin/manage/flags.json -trans.zh_CN = public/language/zh-CN/admin/manage/flags.json -trans.zh_TW = public/language/zh-TW/admin/manage/flags.json -type = KEYVALUEJSON - [nodebb.admin-manage-groups] file_filter = public/language//admin/manage/groups.json source_file = public/language/en-GB/admin/manage/groups.json From 1a0e4db466d5d7bdd25afbc4adef6cc34ee72df4 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 9 Jan 2017 16:22:55 -0500 Subject: [PATCH 057/316] removing duplicate strings from flags language file --- public/language/en-GB/flags.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/public/language/en-GB/flags.json b/public/language/en-GB/flags.json index 66b9acc92a..c4bfed6f23 100644 --- a/public/language/en-GB/flags.json +++ b/public/language/en-GB/flags.json @@ -27,7 +27,6 @@ "quick-links": "Quick Links", "flagged-user": "Flagged User", - "reporter": "Reporting User", "view-profile": "View Profile", "start-new-chat": "Start New Chat", "go-to-target": "View Flag Target", @@ -43,7 +42,6 @@ "back": "Back to Flags List", "no-history": "No flag history.", - "state": "State", "state-all": "All states", "state-open": "New/Open", "state-wip": "Work in Progress", From 611be77361e4d8e6a5cb01d3c826a403090d8ad3 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 10 Jan 2017 15:16:39 +0300 Subject: [PATCH 058/316] closes #5353 --- src/posts/edit.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/posts/edit.js b/src/posts/edit.js index b989b24f8f..a538d3c695 100644 --- a/src/posts/edit.js +++ b/src/posts/edit.js @@ -66,7 +66,7 @@ module.exports = function (Posts) { postData.cid = results.topic.cid; postData.topic = results.topic; - plugins.fireHook('action:post.edit', _.clone(postData)); + plugins.fireHook('action:post.edit', {post: _.clone(postData), uid: data.uid}); cache.del(String(postData.pid)); pubsub.publish('post:edit', String(postData.pid)); @@ -137,7 +137,7 @@ module.exports = function (Posts) { function (tags, next) { topicData.tags = data.tags; topicData.oldTitle = results.topic.title; - plugins.fireHook('action:topic.edit', topicData); + plugins.fireHook('action:topic.edit', {topic: topicData, uid: data.uid}); next(null, { tid: tid, cid: results.topic.cid, From 1e9fabc5db03bf3f8c9da5ef6c83d92003ae98e6 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 10 Jan 2017 15:45:07 +0300 Subject: [PATCH 059/316] closes #5354 --- src/topics/fork.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/topics/fork.js b/src/topics/fork.js index d882cddf00..403331f902 100644 --- a/src/topics/fork.js +++ b/src/topics/fork.js @@ -2,15 +2,13 @@ 'use strict'; var async = require('async'); -var winston = require('winston'); + var db = require('../database'); -var user = require('../user'); var posts = require('../posts'); var privileges = require('../privileges'); var plugins = require('../plugins'); var meta = require('../meta'); - module.exports = function (Topics) { Topics.createTopicFromPosts = function (uid, title, pids, fromTid, callback) { @@ -55,11 +53,14 @@ module.exports = function (Topics) { } Topics.create({uid: results.postData.uid, title: title, cid: cid}, next); }, - function (results, next) { - Topics.updateTopicBookmarks(fromTid, pids, function () { next( null, results );} ); + function (_tid, next) { + Topics.updateTopicBookmarks(fromTid, pids, function (err) { + next(err, _tid); + }); }, function (_tid, next) { - function move(pid, next) { + tid = _tid; + async.eachSeries(pids, function (pid, next) { privileges.posts.canEdit(pid, uid, function (err, canEdit) { if (err || !canEdit.flag) { return next(err || new Error(canEdit.message)); @@ -67,14 +68,13 @@ module.exports = function (Topics) { Topics.movePostToTopic(pid, tid, next); }); - } - tid = _tid; - async.eachSeries(pids, move, next); + }, next); }, function (next) { Topics.updateTimestamp(tid, Date.now(), next); }, function (next) { + plugins.fireHook('action:topic.fork', {tid: tid, fromTid: fromTid, uid: uid}); Topics.getTopicData(tid, next); } ], callback); From dd9e286633ebcb4a0a6e309b63afa8e46c619639 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 10 Jan 2017 18:31:08 +0300 Subject: [PATCH 060/316] closes #5355 --- src/posts/delete.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/posts/delete.js b/src/posts/delete.js index 32ee6b6f41..d3f02a95da 100644 --- a/src/posts/delete.js +++ b/src/posts/delete.js @@ -42,7 +42,7 @@ module.exports = function (Posts) { ], next); }, function (results, next) { - plugins.fireHook('action:post.delete', pid); + plugins.fireHook('action:post.delete', {pid: pid, uid: uid}); next(null, postData); } ], callback); @@ -79,7 +79,7 @@ module.exports = function (Posts) { ], next); }, function (results, next) { - plugins.fireHook('action:post.restore', _.clone(postData)); + plugins.fireHook('action:post.restore', {post: _.clone(postData), uid: uid}); next(null, postData); } ], callback); @@ -149,7 +149,7 @@ module.exports = function (Posts) { if (err) { return next(err); } - plugins.fireHook('action:post.purge', pid); + plugins.fireHook('action:post.purge', {pid: pid, uid: uid}); db.delete('post:' + pid, next); }); } From 37bb6e9b6dec11cc15a3b1df047789ddafba2650 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 11 Jan 2017 13:59:27 +0300 Subject: [PATCH 061/316] action:user.removeUploadedPicture --- src/socket.io/user/picture.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/socket.io/user/picture.js b/src/socket.io/user/picture.js index eacc5197aa..0d8a57dd24 100644 --- a/src/socket.io/user/picture.js +++ b/src/socket.io/user/picture.js @@ -99,6 +99,9 @@ module.exports = function (SocketUser) { uploadedpicture: '', picture: userData.uploadedpicture === userData.picture ? '' : userData.picture // if current picture is uploaded picture, reset to user icon }, next); + }, + function (next) { + plugins.fireHook('action:user.removeUploadedPicture', {callerUid: socket.uid, uid: data.uid}); } ], callback); }; From 0c8aa599a0b66ef19667c6aaa2aea73d2b1e5bb8 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 11 Jan 2017 14:40:52 +0300 Subject: [PATCH 062/316] closes #5357 --- src/socket.io/user/profile.js | 2 +- src/user/profile.js | 22 ++++++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/socket.io/user/profile.js b/src/socket.io/user/profile.js index 8a59fbbff2..fbd9632ed6 100644 --- a/src/socket.io/user/profile.js +++ b/src/socket.io/user/profile.js @@ -148,7 +148,7 @@ module.exports = function (SocketUser) { data.email = oldUserData.email; } - user.updateProfile(data.uid, data, next); + user.updateProfile(socket.uid, data, next); }, function (userData, next) { function log(type, eventData) { diff --git a/src/user/profile.js b/src/user/profile.js index 37b280612f..01caa931cf 100644 --- a/src/user/profile.js +++ b/src/user/profile.js @@ -16,6 +16,9 @@ module.exports = function (User) { var fields = ['username', 'email', 'fullname', 'website', 'location', 'groupTitle', 'birthday', 'signature', 'aboutme']; + var updateUid = data.uid; + var oldData; + async.waterfall([ function (next) { plugins.fireHook('filter:user.updateProfile', {uid: uid, data: data, fields: fields}, next); @@ -27,14 +30,18 @@ module.exports = function (User) { async.series([ async.apply(isAboutMeValid, data), async.apply(isSignatureValid, data), - async.apply(isEmailAvailable, data, uid), - async.apply(isUsernameAvailable, data, uid), + async.apply(isEmailAvailable, data, updateUid), + async.apply(isUsernameAvailable, data, updateUid), async.apply(isGroupTitleValid, data) ], function (err) { next(err); }); }, function (next) { + User.getUserFields(updateUid, fields, next); + }, + function (_oldData, next) { + oldData = _oldData; async.each(fields, function (field, next) { if (!(data[field] !== undefined && typeof data[field] === 'string')) { return next(); @@ -43,21 +50,20 @@ module.exports = function (User) { data[field] = data[field].trim(); if (field === 'email') { - return updateEmail(uid, data.email, next); + return updateEmail(updateUid, data.email, next); } else if (field === 'username') { - return updateUsername(uid, data.username, next); + return updateUsername(updateUid, data.username, next); } else if (field === 'fullname') { - return updateFullname(uid, data.fullname, next); + return updateFullname(updateUid, data.fullname, next); } else if (field === 'signature') { data[field] = S(data[field]).stripTags().s; } - User.setUserField(uid, field, data[field], next); + User.setUserField(updateUid, field, data[field], next); }, next); }, function (next) { - plugins.fireHook('action:user.updateProfile', {data: data, uid: uid}); - User.getUserFields(uid, ['email', 'username', 'userslug', 'picture', 'icon:text', 'icon:bgColor'], next); + User.getUserFields(updateUid, ['email', 'username', 'userslug', 'picture', 'icon:text', 'icon:bgColor'], next); } ], callback); }; From 367078056e89666b05aae60f0c518160bb735162 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 11 Jan 2017 14:41:31 +0300 Subject: [PATCH 063/316] add hook --- src/user/profile.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/user/profile.js b/src/user/profile.js index 01caa931cf..c25dd3bf83 100644 --- a/src/user/profile.js +++ b/src/user/profile.js @@ -63,6 +63,7 @@ module.exports = function (User) { }, next); }, function (next) { + plugins.fireHook('action:user.updateProfile', {uid: uid, data: data, fields: fields, oldData: oldData}); User.getUserFields(updateUid, ['email', 'username', 'userslug', 'picture', 'icon:text', 'icon:bgColor'], next); } ], callback); From a13c6f8c65acf44fefa32f0d4119b2d8b01b0999 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 11 Jan 2017 09:43:22 -0500 Subject: [PATCH 064/316] adding a safeguard into flags upgrade script @baris --- src/upgrade.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/upgrade.js b/src/upgrade.js index 89fe87f8d7..b860a642ba 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -345,6 +345,11 @@ Upgrade.upgrade = function (callback) { return next(err); } + // Adding in another check here in case a post was improperly dismissed (flag count > 1 but no flags in db) + if (!data.uids.length || !data.reasons.length) { + setInterval(next); + } + // Just take the first entry var datetime = data.uids[0].score; var reason = data.reasons[0].split(':')[1]; From bbc02dcc5d678546be920a4fc37ca4e917e4887b Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 11 Jan 2017 09:44:54 -0500 Subject: [PATCH 065/316] lol wtf --- src/upgrade.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/upgrade.js b/src/upgrade.js index b860a642ba..8dce35b901 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -347,7 +347,7 @@ Upgrade.upgrade = function (callback) { // Adding in another check here in case a post was improperly dismissed (flag count > 1 but no flags in db) if (!data.uids.length || !data.reasons.length) { - setInterval(next); + return setImmediate(next); } // Just take the first entry From 7bbb05637ceee51d7fa9ab0c7883df7214326d3c Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 11 Jan 2017 13:20:26 -0500 Subject: [PATCH 066/316] flag translations and fallbacks --- public/language/ar/flags.json | 60 ++++++++++++++++++++++++++ public/language/bg/flags.json | 60 ++++++++++++++++++++++++++ public/language/bn/flags.json | 60 ++++++++++++++++++++++++++ public/language/cs/flags.json | 60 ++++++++++++++++++++++++++ public/language/da/flags.json | 60 ++++++++++++++++++++++++++ public/language/de/flags.json | 60 ++++++++++++++++++++++++++ public/language/el/flags.json | 60 ++++++++++++++++++++++++++ public/language/en-US/flags.json | 60 ++++++++++++++++++++++++++ public/language/en-x-pirate/flags.json | 60 ++++++++++++++++++++++++++ public/language/es/flags.json | 60 ++++++++++++++++++++++++++ public/language/et/flags.json | 60 ++++++++++++++++++++++++++ public/language/fa-IR/flags.json | 60 ++++++++++++++++++++++++++ public/language/fi/flags.json | 60 ++++++++++++++++++++++++++ public/language/fr/flags.json | 60 ++++++++++++++++++++++++++ public/language/gl/flags.json | 60 ++++++++++++++++++++++++++ public/language/he/flags.json | 60 ++++++++++++++++++++++++++ public/language/hu/flags.json | 60 ++++++++++++++++++++++++++ public/language/id/flags.json | 60 ++++++++++++++++++++++++++ public/language/it/flags.json | 60 ++++++++++++++++++++++++++ public/language/ja/flags.json | 60 ++++++++++++++++++++++++++ public/language/ko/flags.json | 60 ++++++++++++++++++++++++++ public/language/lt/flags.json | 60 ++++++++++++++++++++++++++ public/language/ms/flags.json | 60 ++++++++++++++++++++++++++ public/language/nb/flags.json | 60 ++++++++++++++++++++++++++ public/language/nl/flags.json | 60 ++++++++++++++++++++++++++ public/language/pl/flags.json | 60 ++++++++++++++++++++++++++ public/language/pt-BR/flags.json | 60 ++++++++++++++++++++++++++ public/language/pt-PT/flags.json | 60 ++++++++++++++++++++++++++ public/language/ro/flags.json | 60 ++++++++++++++++++++++++++ public/language/ru/flags.json | 60 ++++++++++++++++++++++++++ public/language/rw/flags.json | 60 ++++++++++++++++++++++++++ public/language/sc/flags.json | 60 ++++++++++++++++++++++++++ public/language/sk/flags.json | 60 ++++++++++++++++++++++++++ public/language/sl/flags.json | 60 ++++++++++++++++++++++++++ public/language/sr/flags.json | 60 ++++++++++++++++++++++++++ public/language/sv/flags.json | 60 ++++++++++++++++++++++++++ public/language/th/flags.json | 60 ++++++++++++++++++++++++++ public/language/tr/flags.json | 60 ++++++++++++++++++++++++++ public/language/vi/flags.json | 60 ++++++++++++++++++++++++++ public/language/zh-CN/flags.json | 60 ++++++++++++++++++++++++++ public/language/zh-TW/flags.json | 60 ++++++++++++++++++++++++++ 41 files changed, 2460 insertions(+) create mode 100644 public/language/ar/flags.json create mode 100644 public/language/bg/flags.json create mode 100644 public/language/bn/flags.json create mode 100644 public/language/cs/flags.json create mode 100644 public/language/da/flags.json create mode 100644 public/language/de/flags.json create mode 100644 public/language/el/flags.json create mode 100644 public/language/en-US/flags.json create mode 100644 public/language/en-x-pirate/flags.json create mode 100644 public/language/es/flags.json create mode 100644 public/language/et/flags.json create mode 100644 public/language/fa-IR/flags.json create mode 100644 public/language/fi/flags.json create mode 100644 public/language/fr/flags.json create mode 100644 public/language/gl/flags.json create mode 100644 public/language/he/flags.json create mode 100644 public/language/hu/flags.json create mode 100644 public/language/id/flags.json create mode 100644 public/language/it/flags.json create mode 100644 public/language/ja/flags.json create mode 100644 public/language/ko/flags.json create mode 100644 public/language/lt/flags.json create mode 100644 public/language/ms/flags.json create mode 100644 public/language/nb/flags.json create mode 100644 public/language/nl/flags.json create mode 100644 public/language/pl/flags.json create mode 100644 public/language/pt-BR/flags.json create mode 100644 public/language/pt-PT/flags.json create mode 100644 public/language/ro/flags.json create mode 100644 public/language/ru/flags.json create mode 100644 public/language/rw/flags.json create mode 100644 public/language/sc/flags.json create mode 100644 public/language/sk/flags.json create mode 100644 public/language/sl/flags.json create mode 100644 public/language/sr/flags.json create mode 100644 public/language/sv/flags.json create mode 100644 public/language/th/flags.json create mode 100644 public/language/tr/flags.json create mode 100644 public/language/vi/flags.json create mode 100644 public/language/zh-CN/flags.json create mode 100644 public/language/zh-TW/flags.json diff --git a/public/language/ar/flags.json b/public/language/ar/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/ar/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/bg/flags.json b/public/language/bg/flags.json new file mode 100644 index 0000000000..138feac5c5 --- /dev/null +++ b/public/language/bg/flags.json @@ -0,0 +1,60 @@ +{ + "state": "Състояние", + "reporter": "Докладвал", + "reported-at": "Докладвано на", + "description": "Описание", + "no-flags": "Ура! Няма намерени доклади.", + "assignee": "Назначен", + "update": "Обновяване", + "updated": "Обновено", + "target-purged": "Съдържанието, за което се отнася този доклад, е било изтрито и вече не е налично.", + + "quick-filters": "Бързи филтри", + "filter-active": "В този списък с доклади има един или повече филтри", + "filter-reset": "Премахване на филтрите", + "filters": "Настройки на филтрите", + "filter-reporterId": "Потр. ид. на докладвалия", + "filter-targetUid": "Потр. ид. на докладвания", + "filter-type": "Вид на доклада", + "filter-type-all": "Всичко", + "filter-type-post": "Публикация", + "filter-state": "Състояние", + "filter-assignee": "Потр. ид. на назначения", + "filter-cid": "Категория", + "filter-quick-mine": "Назначени на мен", + "filter-cid-all": "Всички категории", + "apply-filters": "Прилагане на филтрите", + + "quick-links": "Бързи връзки", + "flagged-user": "Докладван потребител", + "view-profile": "Преглед на профила", + "start-new-chat": "Започване на нов разговор", + "go-to-target": "Преглед на целта на доклада", + + "user-view": "Преглед на профила", + "user-edit": "Редактиране на профила", + + "notes": "Бележки към доклада", + "add-note": "Добавяне на бележка", + "no-notes": "Няма споделени бележки.", + + "history": "История на доклада", + "back": "Обратно към списъка с доклади", + "no-history": "Няма история на доклада.", + + "state-all": "Всички състояния", + "state-open": "Нов/отворен", + "state-wip": "В процес на работа", + "state-resolved": "Разрешен", + "state-rejected": "Отхвърлен", + "no-assignee": "Без назначение", + "note-added": "Бележката е добавена", + + "modal-title": "Докладване на неуместно съдържание", + "modal-body": "Моля, посочете причината за докладването на %1 %2 за преглед. Или използвайте някой от бутоните за бързо докладване, ако са приложими.", + "modal-reason-spam": "Спам", + "modal-reason-offensive": "Обидно", + "modal-reason-custom": "Причина за докладването на това съдържание…", + "modal-submit": "Изпращане на доклада", + "modal-submit-success": "Съдържанието беше докладвано на модераторите." +} \ No newline at end of file diff --git a/public/language/bn/flags.json b/public/language/bn/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/bn/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/cs/flags.json b/public/language/cs/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/cs/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/da/flags.json b/public/language/da/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/da/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/de/flags.json b/public/language/de/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/de/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/el/flags.json b/public/language/el/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/el/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/en-US/flags.json b/public/language/en-US/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/en-US/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/en-x-pirate/flags.json b/public/language/en-x-pirate/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/en-x-pirate/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/es/flags.json b/public/language/es/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/es/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/et/flags.json b/public/language/et/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/et/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/fa-IR/flags.json b/public/language/fa-IR/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/fa-IR/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/fi/flags.json b/public/language/fi/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/fi/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/fr/flags.json b/public/language/fr/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/fr/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/gl/flags.json b/public/language/gl/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/gl/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/he/flags.json b/public/language/he/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/he/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/hu/flags.json b/public/language/hu/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/hu/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/id/flags.json b/public/language/id/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/id/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/it/flags.json b/public/language/it/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/it/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/ja/flags.json b/public/language/ja/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/ja/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/ko/flags.json b/public/language/ko/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/ko/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/lt/flags.json b/public/language/lt/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/lt/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/ms/flags.json b/public/language/ms/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/ms/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/nb/flags.json b/public/language/nb/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/nb/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/nl/flags.json b/public/language/nl/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/nl/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/pl/flags.json b/public/language/pl/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/pl/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/pt-BR/flags.json b/public/language/pt-BR/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/pt-BR/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/pt-PT/flags.json b/public/language/pt-PT/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/pt-PT/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/ro/flags.json b/public/language/ro/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/ro/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/ru/flags.json b/public/language/ru/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/ru/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/rw/flags.json b/public/language/rw/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/rw/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/sc/flags.json b/public/language/sc/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/sc/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/sk/flags.json b/public/language/sk/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/sk/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/sl/flags.json b/public/language/sl/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/sl/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/sr/flags.json b/public/language/sr/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/sr/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/sv/flags.json b/public/language/sv/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/sv/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/th/flags.json b/public/language/th/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/th/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/tr/flags.json b/public/language/tr/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/tr/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/vi/flags.json b/public/language/vi/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/vi/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/zh-CN/flags.json b/public/language/zh-CN/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/zh-CN/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file diff --git a/public/language/zh-TW/flags.json b/public/language/zh-TW/flags.json new file mode 100644 index 0000000000..c4bfed6f23 --- /dev/null +++ b/public/language/zh-TW/flags.json @@ -0,0 +1,60 @@ +{ + "state": "State", + "reporter": "Reporter", + "reported-at": "Reported At", + "description": "Description", + "no-flags": "Hooray! No flags found.", + "assignee": "Assignee", + "update": "Update", + "updated": "Updated", + "target-purged": "The content this flag referred to has been purged and is no longer available.", + + "quick-filters": "Quick Filters", + "filter-active": "There are one or more filters active in this list of flags", + "filter-reset": "Remove Filters", + "filters": "Filter Options", + "filter-reporterId": "Reporter UID", + "filter-targetUid": "Flagged UID", + "filter-type": "Flag Type", + "filter-type-all": "All Content", + "filter-type-post": "Post", + "filter-state": "State", + "filter-assignee": "Assignee UID", + "filter-cid": "Category", + "filter-quick-mine": "Assigned to me", + "filter-cid-all": "All categories", + "apply-filters": "Apply Filters", + + "quick-links": "Quick Links", + "flagged-user": "Flagged User", + "view-profile": "View Profile", + "start-new-chat": "Start New Chat", + "go-to-target": "View Flag Target", + + "user-view": "View Profile", + "user-edit": "Edit Profile", + + "notes": "Flag Notes", + "add-note": "Add Note", + "no-notes": "No shared notes.", + + "history": "Flag History", + "back": "Back to Flags List", + "no-history": "No flag history.", + + "state-all": "All states", + "state-open": "New/Open", + "state-wip": "Work in Progress", + "state-resolved": "Resolved", + "state-rejected": "Rejected", + "no-assignee": "Not Assigned", + "note-added": "Note Added", + + "modal-title": "Report Inappropriate Content", + "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-reason-spam": "Spam", + "modal-reason-offensive": "Offensive", + "modal-reason-custom": "Reason for reporting this content...", + "modal-submit": "Submit Report", + "modal-submit-success": "Content has been flagged for moderation." +} \ No newline at end of file From 20d035bdac7da51f856a40d5d2f05e1f66431d30 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 11 Jan 2017 14:33:51 -0500 Subject: [PATCH 067/316] hotfix to utils.isRelativeUrl to handle null or undefined first argument --- public/src/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/utils.js b/public/src/utils.js index 42d4b6f3ad..5836e5f04b 100644 --- a/public/src/utils.js +++ b/public/src/utils.js @@ -193,7 +193,7 @@ }, isRelativeUrl: function (url) { - var firstChar = url.slice(0, 1); + var firstChar = String(url || '').charAt(0); return (firstChar === '.' || firstChar === '/'); }, From 8eb47e1987739690680c80dc66a83e5993e70287 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 11 Jan 2017 14:43:56 -0500 Subject: [PATCH 068/316] added back missing next(), @baris --- src/socket.io/user/picture.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/socket.io/user/picture.js b/src/socket.io/user/picture.js index 0d8a57dd24..eded1102c9 100644 --- a/src/socket.io/user/picture.js +++ b/src/socket.io/user/picture.js @@ -101,7 +101,7 @@ module.exports = function (SocketUser) { }, next); }, function (next) { - plugins.fireHook('action:user.removeUploadedPicture', {callerUid: socket.uid, uid: data.uid}); + plugins.fireHook('action:user.removeUploadedPicture', {callerUid: socket.uid, uid: data.uid}, next); } ], callback); }; From d29361f4c9fcdcf74469f476ced8739b90fcb4fb Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 11 Jan 2017 15:06:28 -0500 Subject: [PATCH 069/316] added additional visibility masks for profile menu hook, also added isPrivileged user method, closes #5306 --- src/controllers/accounts/helpers.js | 35 +++++++++++++++++++++++++---- src/middleware/index.js | 24 ++++++++++++++++++++ src/user.js | 10 +++++++++ 3 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/controllers/accounts/helpers.js b/src/controllers/accounts/helpers.js index 2ef476a4ef..e2bc008de8 100644 --- a/src/controllers/accounts/helpers.js +++ b/src/controllers/accounts/helpers.js @@ -119,7 +119,13 @@ helpers.getUserDataByUserSlug = function (userslug, callerUID, callback) { userData['reputation:disabled'] = parseInt(meta.config['reputation:disabled'], 10) === 1; userData['downvote:disabled'] = parseInt(meta.config['downvote:disabled'], 10) === 1; userData['email:confirmed'] = !!parseInt(userData['email:confirmed'], 10); - userData.profile_links = filterLinks(results.profile_links.concat(results.profile_menu.links), isSelf); + userData.profile_links = filterLinks(results.profile_links.concat(results.profile_menu.links), { + self: isSelf, + other: !isSelf, + moderator: isModerator, + globalMod: isGlobalModerator, + admin: isAdmin + }); userData.sso = results.sso.associations; userData.status = user.getStatus(userData); @@ -154,9 +160,30 @@ helpers.getBaseUser = function (userslug, callerUID, callback) { helpers.getUserDataByUserSlug(userslug, callerUID, callback); }; -function filterLinks(links, self) { - return links.filter(function (link) { - return link && (link.public || self); +function filterLinks(links, states) { + return links.filter(function (link, index) { + // "public" is the old property, if visibility is defined, discard `public` + if (link.hasOwnProperty('public') && !link.hasOwnProperty('visibility')) { + winston.warn('[account/profileMenu (' + link.id + ')] Use of the `.public` property is deprecated, use `visibility` now'); + return link && (link.public || states.self); + } + + // Default visibility + link.visibility = Object.assign({ + self: true, + other: true, + moderator: true, + globalMod: true, + admin: true + }, link.visibility); + + // Iterate through states and permit if every test passes (or is not defined) + var permit = Object.keys(states).some(function (state) { + return states[state] === link.visibility[state]; + }); + + links[index].public = permit; + return permit; }); } diff --git a/src/middleware/index.js b/src/middleware/index.js index f48aaef748..85e29a879a 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -73,6 +73,30 @@ middleware.ensureSelfOrGlobalPrivilege = function (req, res, next) { } }; +middleware.ensureSelfOrPrivileged = function (req, res, next) { + /* + The "self" part of this middleware hinges on you having used + middleware.exposeUid prior to invoking this middleware. + */ + if (req.user) { + if (req.user.uid === res.locals.uid) { + return next(); + } + + user.isPrivileged(req.uid, function (err, ok) { + if (err) { + return next(err); + } else if (ok) { + return next(); + } else { + controllers.helpers.notAllowed(req, res); + } + }); + } else { + controllers.helpers.notAllowed(req, res); + } +}; + middleware.pageView = function (req, res, next) { analytics.pageView({ ip: req.ip, diff --git a/src/user.js b/src/user.js index 00e5824690..2ae5a4baf5 100644 --- a/src/user.js +++ b/src/user.js @@ -256,6 +256,16 @@ var meta = require('./meta'); privileges.users.isGlobalModerator(uid, callback); }; + User.isPrivileged = function (uid, callback) { + async.parallel([ + async.apply(User.isAdministrator, uid), + async.apply(User.isGlobalModerator, uid), + async.apply(User.isModeratorOfAnyCategory, uid) + ], function (err, results) { + callback(err, results ? results.some(Boolean) : false); + }); + }; + User.isAdminOrGlobalMod = function (uid, callback) { async.parallel({ isAdmin: async.apply(User.isAdministrator, uid), From 1e137636c9321524fa2c7a5bc5bb32def9c023f0 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 12 Jan 2017 14:31:28 +0300 Subject: [PATCH 070/316] closes #5359 --- src/topics/delete.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/topics/delete.js b/src/topics/delete.js index 91c1bf53e3..d7148f55c3 100644 --- a/src/topics/delete.js +++ b/src/topics/delete.js @@ -137,15 +137,18 @@ module.exports = function (Topics) { function (next) { reduceCounters(tid, next); } - ], next); - } - ], function (err) { - if (err) { - return callback(err); + ], function (err) { + next(err); + }); + }, + function (next) { + Topics.getTopicData(tid, next); + }, + function (topicData, next) { + plugins.fireHook('action:topic.purge', {topic: topicData, uid: uid}); + db.delete('topic:' + tid, next); } - plugins.fireHook('action:topic.purge', tid); - db.delete('topic:' + tid, callback); - }); + ], callback); }; function deleteFromFollowersIgnorers(tid, callback) { From 274448426d40a3566109c0f07cee7d63e55f68a1 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 12 Jan 2017 14:31:35 +0300 Subject: [PATCH 071/316] helpers.setupAdminPageRoute --- src/routes/helpers.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/routes/helpers.js b/src/routes/helpers.js index 052d99292c..3f3ff06472 100644 --- a/src/routes/helpers.js +++ b/src/routes/helpers.js @@ -9,4 +9,10 @@ helpers.setupPageRoute = function (router, name, middleware, middlewares, contro router.get('/api' + name, middlewares, controller); }; +helpers.setupAdminPageRoute = function (router, name, middleware, middlewares, controller) { + router.get(name, middleware.admin.buildHeader, middlewares, controller); + router.get('/api' + name, middlewares, controller); +}; + + module.exports = helpers; \ No newline at end of file From 938022a0f70fe2ace34d4a1c1b1364e90585cb4b Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 12 Jan 2017 14:32:28 +0300 Subject: [PATCH 072/316] add postData to action:post.delete, closes #5360 --- src/posts/delete.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/posts/delete.js b/src/posts/delete.js index d3f02a95da..a21f2bb81b 100644 --- a/src/posts/delete.js +++ b/src/posts/delete.js @@ -29,6 +29,7 @@ module.exports = function (Posts) { topics.getTopicFields(_post.tid, ['tid', 'cid', 'pinned'], next); }, function (topicData, next) { + postData.cid = topicData.cid; async.parallel([ function (next) { updateTopicTimestamp(topicData, next); @@ -42,7 +43,7 @@ module.exports = function (Posts) { ], next); }, function (results, next) { - plugins.fireHook('action:post.delete', {pid: pid, uid: uid}); + plugins.fireHook('action:post.delete', {post: _.clone(postData), uid: uid}); next(null, postData); } ], callback); From 8f916343f0e849d33069ac2a46b526ef87dfb2b1 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 12 Jan 2017 14:41:35 +0300 Subject: [PATCH 073/316] closes #5361 --- src/posts/delete.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/posts/delete.js b/src/posts/delete.js index a21f2bb81b..d54846c2fd 100644 --- a/src/posts/delete.js +++ b/src/posts/delete.js @@ -147,12 +147,15 @@ module.exports = function (Posts) { db.sortedSetsRemove(['posts:pid', 'posts:flagged'], pid, next); } ], function (err) { - if (err) { - return next(err); - } - plugins.fireHook('action:post.purge', {pid: pid, uid: uid}); - db.delete('post:' + pid, next); + next(err); }); + }, + function (next) { + Posts.getPostData(pid, next); + }, + function (postData, next) { + plugins.fireHook('action:post.purge', {post: postData, uid: uid}); + db.delete('post:' + pid, next); } ], callback); }; From 6c04e393b523607b6f352d5e5ffe72b364f20f10 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 12 Jan 2017 10:03:45 -0500 Subject: [PATCH 074/316] fire hooks for flag update --- src/flags.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/flags.js b/src/flags.js index 168eb5a11e..dd7b038b92 100644 --- a/src/flags.js +++ b/src/flags.js @@ -470,9 +470,13 @@ Flags.update = function (flagId, uid, changeset, callback) { // Save new object to db (upsert) tasks.push(async.apply(db.setObject, 'flag:' + flagId, changeset)); + // Append history tasks.push(async.apply(Flags.appendHistory, flagId, uid, changeset)); + // Fire plugin hook + tasks.push(async.apply(plugins.fireHook, 'action:flag.update', { changeset: changeset, uid: uid })); + async.parallel(tasks, function (err, data) { return next(err); }); From bef3da5b48e89ce769e13c1af71c78415e22e61c Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 12 Jan 2017 10:09:47 -0500 Subject: [PATCH 075/316] pass flagId into flag.update hook --- src/flags.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/flags.js b/src/flags.js index dd7b038b92..a50ea54973 100644 --- a/src/flags.js +++ b/src/flags.js @@ -475,7 +475,7 @@ Flags.update = function (flagId, uid, changeset, callback) { tasks.push(async.apply(Flags.appendHistory, flagId, uid, changeset)); // Fire plugin hook - tasks.push(async.apply(plugins.fireHook, 'action:flag.update', { changeset: changeset, uid: uid })); + tasks.push(async.apply(plugins.fireHook, 'action:flag.update', { flagId: flagId, changeset: changeset, uid: uid })); async.parallel(tasks, function (err, data) { return next(err); From 10f270d01666a8a1c05238d574ad2ad5547678de Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 12 Jan 2017 11:31:59 -0500 Subject: [PATCH 076/316] adding byPid zset for flags --- src/flags.js | 5 ++++- test/flags.js | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/flags.js b/src/flags.js index a50ea54973..02e7fdeaad 100644 --- a/src/flags.js +++ b/src/flags.js @@ -336,7 +336,10 @@ Flags.create = function (type, id, uid, reason, timestamp, callback) { tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byTargetUid:' + targetUid, timestamp, flagId)); // by target uid } if (targetCid) { - tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byCid:' + targetCid, timestamp, flagId)); // by target uid + tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byCid:' + targetCid, timestamp, flagId)); // by target cid + } + if (type === 'post') { + tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byPid:' + id, timestamp, flagId)); // by target pid } async.parallel(tasks, function (err, data) { diff --git a/test/flags.js b/test/flags.js index 9df1a11a2b..af53b61271 100644 --- a/test/flags.js +++ b/test/flags.js @@ -80,6 +80,14 @@ describe('Flags', function () { done(); }); }); + + it('should add the flag to the byPid zset for pid 1 if it is of type post', function (done) { + db.isSortedSetMember('flags:byPid:' + 1, 1, function (err, isMember) { + assert.ifError(err); + assert.ok(isMember); + done(); + }); + }); }); describe('.exists()', function () { From 7f9381b0870988d642941855672f893c6475b3d9 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 12 Jan 2017 14:22:30 -0500 Subject: [PATCH 077/316] closes #5346 --- public/src/admin/general/dashboard.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/src/admin/general/dashboard.js b/public/src/admin/general/dashboard.js index f9e2589f65..b58da4126c 100644 --- a/public/src/admin/general/dashboard.js +++ b/public/src/admin/general/dashboard.js @@ -81,7 +81,6 @@ define('admin/general/dashboard', ['semver', 'Chart', 'translator'], function (s setupRealtimeButton(); setupGraphs(); - initiateDashboard(); }; Admin.updateRoomUsage = function (err, data) { @@ -299,6 +298,8 @@ define('admin/general/dashboard', ['semver', 'Chart', 'translator'], function (s } updateTrafficGraph($(this).attr('data-units'), until); }); + + initiateDashboard(); }); } From 65cf2dbe5591753d2d32055a507212e57d0a691c Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 12 Jan 2017 14:26:03 -0500 Subject: [PATCH 078/316] move updateRoomUsage to after graph setup #5346 --- public/src/admin/general/dashboard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/admin/general/dashboard.js b/public/src/admin/general/dashboard.js index b58da4126c..9a5faede09 100644 --- a/public/src/admin/general/dashboard.js +++ b/public/src/admin/general/dashboard.js @@ -37,7 +37,6 @@ define('admin/general/dashboard', ['semver', 'Chart', 'translator'], function (s Admin.init = function () { app.enterRoom('admin'); - socket.emit('admin.rooms.getAll', Admin.updateRoomUsage); isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); @@ -299,6 +298,7 @@ define('admin/general/dashboard', ['semver', 'Chart', 'translator'], function (s updateTrafficGraph($(this).attr('data-units'), until); }); + socket.emit('admin.rooms.getAll', Admin.updateRoomUsage); initiateDashboard(); }); } From 8f0f959d53efbc0725dff682dd70cbab08e9ad55 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 13 Jan 2017 12:04:12 +0300 Subject: [PATCH 079/316] add `filter:categories.copyPrivilegesFrom` https://github.com/Schamper/nodebb-plugin-poll/issues/77 --- src/categories/create.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/categories/create.js b/src/categories/create.js index 508d8ab348..cebcbcd82b 100644 --- a/src/categories/create.js +++ b/src/categories/create.js @@ -139,9 +139,20 @@ module.exports = function (Categories) { }; Categories.copyPrivilegesFrom = function (fromCid, toCid, callback) { - async.each(privileges.privilegeList, function (privilege, next) { - copyPrivilege(privilege, fromCid, toCid, next); - }, callback); + async.waterfall([ + function (next) { + plugins.fireHook('filter:categories.copyPrivilegesFrom', { + privileges: privileges.privilegeList, + fromCid: fromCid, + toCid: toCid + }, next); + }, + function (data, next) { + async.each(data.privileges, function (privilege, next) { + copyPrivilege(privilege, data.fromCid, data.toCid, next); + }, next); + } + ], callback); }; function copyPrivilege(privilege, fromCid, toCid, callback) { From 82e9e1a05dfc0c05663c4ed0c3f918f42919cc6d Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 17 Jan 2017 14:05:19 -0500 Subject: [PATCH 080/316] closes #5189 --- public/src/client/infinitescroll.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/public/src/client/infinitescroll.js b/public/src/client/infinitescroll.js index 6ae9b5cbbc..ed43e4da22 100644 --- a/public/src/client/infinitescroll.js +++ b/public/src/client/infinitescroll.js @@ -23,7 +23,9 @@ define('forum/infinitescroll', function () { }; function onScroll() { - if (loadingMore) { + var bsEnv = utils.findBootstrapEnvironment(); + var mobileComposerOpen = (bsEnv === 'xs' || bsEnv === 'sm') && $('html').hasClass('composing'); + if (loadingMore || mobileComposerOpen) { return; } var currentScrollTop = $(window).scrollTop(); From ef72762ba7c40c7c358b0d94b20d7a30f95b5835 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 18 Jan 2017 20:21:13 +0300 Subject: [PATCH 081/316] use groups.isMemberOfGroups --- src/groups.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/groups.js b/src/groups.js index 51c1d97dc8..15b42b368b 100644 --- a/src/groups.js +++ b/src/groups.js @@ -432,12 +432,8 @@ var utils = require('../public/src/utils'); db.getSortedSetRevRange(set, 0, -1, next); }, function (groupNames, next) { - var groupSets = groupNames.map(function (name) { - return 'group:' + name + ':members'; - }); - async.map(uids, function (uid, next) { - db.isMemberOfSortedSets(groupSets, uid, function (err, isMembers) { + Groups.isMemberOfGroups(uid, groupNames, function (err, isMembers) { if (err) { return next(err); } From 0b95eab71e141f72765cc8ae1e8406ea6ea5ec2e Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 23 Jan 2017 13:02:35 +0300 Subject: [PATCH 082/316] use roomName if availabe in taskbar if room is renamed update title on taskbar --- public/src/modules/chat.js | 9 ++++++--- public/src/modules/taskbar.js | 8 ++++++-- src/messaging/notifications.js | 16 ++++++++++++---- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index f231fd765d..9ef9d21c77 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -70,7 +70,7 @@ define('chat', [ sounds.play('chat-incoming'); taskbar.push('chat', modal.attr('UUID'), { - title: username, + title: data.roomName || username, touid: data.message.fromUser.uid, roomId: data.roomId }); @@ -102,7 +102,10 @@ define('chat', [ }); socket.on('event:chats.roomRename', function (data) { - module.getModal(data.roomId).find('[component="chat/room/name"]').val($('
    ').html(data.newName).text()); + var newTitle = $('
    ').html(data.newName).text(); + var modal = module.getModal(data.roomId); + modal.find('[component="chat/room/name"]').val(newTitle); + taskbar.updateTitle('chat', modal.attr('UUID'), newTitle); }); ChatsMessages.onChatMessageEdit(); @@ -197,7 +200,7 @@ define('chat', [ handle: '.modal-header' }); }); - + scrollStop.apply(chatModal.find('[component="chat/messages"]')); chatModal.find('#chat-close-btn').on('click', function () { diff --git a/public/src/modules/taskbar.js b/public/src/modules/taskbar.js index 7a0b3c39ea..6cd735f15f 100644 --- a/public/src/modules/taskbar.js +++ b/public/src/modules/taskbar.js @@ -39,7 +39,7 @@ define('taskbar', function () { taskbar.discard = function (module, uuid) { var btnEl = taskbar.tasklist.find('[data-module="' + module + '"][data-uuid="' + uuid + '"]'); btnEl.remove(); - + update(); }; @@ -115,7 +115,7 @@ define('taskbar', function () { .html('' + (data.options.icon ? ' ' : '') + (data.options.image ? ' ' : '') + - '' + title + '' + + '' + title + '' + '') .attr({ 'data-module': data.module, @@ -136,5 +136,9 @@ define('taskbar', function () { $(window).trigger('action:taskbar.pushed', data); } + taskbar.updateTitle = function(module, uuid, newTitle) { + taskbar.tasklist.find('[data-module="' + module + '"][data-uuid="' + uuid + '"] [component="taskbar/title"]').text(newTitle); + }; + return taskbar; }); diff --git a/src/messaging/notifications.js b/src/messaging/notifications.js index 6d9500c4ba..2737725462 100644 --- a/src/messaging/notifications.js +++ b/src/messaging/notifications.js @@ -15,7 +15,14 @@ module.exports = function (Messaging) { Messaging.notifyQueue = {}; // Only used to notify a user of a new chat message, see Messaging.notifyUser Messaging.notifyUsersInRoom = function (fromUid, roomId, messageObj) { - Messaging.getUidsInRoom(roomId, 0, -1, function (err, uids) { + async.parallel({ + uids: function (next) { + Messaging.getUidsInRoom(roomId, 0, -1, next); + }, + roomData: function (next) { + Messaging.getRoomData(roomId, next); + } + }, function (err, results) { if (err) { return; } @@ -23,9 +30,10 @@ module.exports = function (Messaging) { var data = { roomId: roomId, fromUid: fromUid, - message: messageObj + message: messageObj, + roomName: results.roomData.roomName }; - uids.forEach(function (uid) { + results.uids.forEach(function (uid) { data.self = parseInt(uid, 10) === parseInt(fromUid) ? 1 : 0; Messaging.pushUnreadCount(uid); sockets.in('uid_' + uid).emit('event:chats.receive', data); @@ -43,7 +51,7 @@ module.exports = function (Messaging) { } queueObj.timeout = setTimeout(function () { - sendNotifications(fromUid, uids, roomId, queueObj.message, function (err) { + sendNotifications(fromUid, results.uids, roomId, queueObj.message, function (err) { if (!err) { delete Messaging.notifyQueue[fromUid + ':' + roomId]; } From 33e452cc8e24af49dba2813d37600fd4598df9fd Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 23 Jan 2017 13:09:51 +0300 Subject: [PATCH 083/316] fix lint --- public/src/modules/taskbar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/modules/taskbar.js b/public/src/modules/taskbar.js index 6cd735f15f..50f8dfd29f 100644 --- a/public/src/modules/taskbar.js +++ b/public/src/modules/taskbar.js @@ -136,7 +136,7 @@ define('taskbar', function () { $(window).trigger('action:taskbar.pushed', data); } - taskbar.updateTitle = function(module, uuid, newTitle) { + taskbar.updateTitle = function (module, uuid, newTitle) { taskbar.tasklist.find('[data-module="' + module + '"][data-uuid="' + uuid + '"] [component="taskbar/title"]').text(newTitle); }; From a7aaf8ea96ce11bb5a8121c007784e26e5a7aef2 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 23 Jan 2017 11:25:05 -0500 Subject: [PATCH 084/316] fixing bug that caused plugin language files to not be parsed --- src/meta/languages.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meta/languages.js b/src/meta/languages.js index b7c79db5ba..fd4c9bb0fc 100644 --- a/src/meta/languages.js +++ b/src/meta/languages.js @@ -28,7 +28,7 @@ function getTranslationTree(callback) { }); // Filter out plugins with invalid paths - async.filter(paths, file.exists, function (paths) { + async.filter(paths, file.exists, function (err, paths) { next(null, paths); }); }, From ea7d61e2e4b66d4c4ec06b2512ea443670605baa Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 23 Jan 2017 11:40:51 -0500 Subject: [PATCH 085/316] handling err in src/meta/languages.js --- src/meta/languages.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/meta/languages.js b/src/meta/languages.js index fd4c9bb0fc..bac7a2ba33 100644 --- a/src/meta/languages.js +++ b/src/meta/languages.js @@ -28,9 +28,7 @@ function getTranslationTree(callback) { }); // Filter out plugins with invalid paths - async.filter(paths, file.exists, function (err, paths) { - next(null, paths); - }); + async.filter(paths, file.exists, next); }, function (paths, next) { async.map(paths, Plugins.loadPluginInfo, next); From 8dc5e20f37ef75be03fcefa9359f421589edee3f Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 23 Jan 2017 12:38:05 -0500 Subject: [PATCH 086/316] Up composer version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c57e96b200..e7aefe1f7a 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "morgan": "^1.3.2", "mousetrap": "^1.5.3", "nconf": "~0.8.2", - "nodebb-plugin-composer-default": "4.3.6", + "nodebb-plugin-composer-default": "4.3.7", "nodebb-plugin-dbsearch": "1.0.5", "nodebb-plugin-emoji-extended": "1.1.1", "nodebb-plugin-emoji-one": "1.1.5", From 72092f7109ed3eb78fe4cda09a9ae77f348fba7a Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 23 Jan 2017 14:53:11 -0500 Subject: [PATCH 087/316] bump mentions again --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 56eb8d0702..799e5e687d 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "nodebb-plugin-emoji-extended": "1.1.1", "nodebb-plugin-emoji-one": "1.1.5", "nodebb-plugin-markdown": "7.0.1", - "nodebb-plugin-mentions": "1.1.4", + "nodebb-plugin-mentions": "2.0.0", "nodebb-plugin-soundpack-default": "0.1.6", "nodebb-plugin-spam-be-gone": "0.4.10", "nodebb-rewards-essentials": "0.0.9", From dbb71423a8a830f176fcd37cf44a53ee8c5f7690 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 23 Jan 2017 15:17:53 -0500 Subject: [PATCH 088/316] executing core middlewares first before secondary or plugin middlewares --- src/routes/helpers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/helpers.js b/src/routes/helpers.js index 3f3ff06472..fd466c78f1 100644 --- a/src/routes/helpers.js +++ b/src/routes/helpers.js @@ -3,7 +3,7 @@ var helpers = {}; helpers.setupPageRoute = function (router, name, middleware, middlewares, controller) { - middlewares = middlewares.concat([middleware.registrationComplete, middleware.pageView, middleware.pluginHooks]); + middlewares = [middleware.registrationComplete, middleware.pageView, middleware.pluginHooks].concat(middlewares); router.get(name, middleware.busyCheck, middleware.buildHeader, middlewares, controller); router.get('/api' + name, middlewares, controller); From f0c941e5aeef86f3b31e6512643bd15d14afd839 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Mon, 23 Jan 2017 17:09:32 -0700 Subject: [PATCH 089/316] No 404 responses for missing language files --- src/routes/index.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/routes/index.js b/src/routes/index.js index 99ff3a7f27..d33fd316b5 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -157,6 +157,11 @@ module.exports = function (app, middleware, hotswapIds) { res.redirect(relativePath + '/assets/language' + req.path + '.json?' + meta.config['cache-buster']); }); + // stop 404's on `/assets/language` route + app.use(relativePath + '/assets/language', function (req, res) { + res.json({}); + }); + app.use(relativePath, express.static(path.join(__dirname, '../../', 'public'), { maxAge: app.enabled('cache') ? 5184000000 : 0 })); From aeed8f53f3eda192444b809eefd602af8c41db60 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 24 Jan 2017 12:45:36 +0300 Subject: [PATCH 090/316] closes #5393 --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 799e5e687d..2b9e9ccd5f 100644 --- a/package.json +++ b/package.json @@ -81,9 +81,9 @@ "semver": "^5.1.0", "serve-favicon": "^2.1.5", "sitemap": "^1.4.0", - "socket.io": "1.7.1", - "socket.io-client": "1.7.1", - "socket.io-redis": "2.0.0", + "socket.io": "1.7.2", + "socket.io-client": "1.7.2", + "socket.io-redis": "3.1.0", "socketio-wildcard": "~0.3.0", "string": "^3.0.0", "templates.js": "0.3.6", From ae279fe57f65517cac13ade727d03b9bbd39a849 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 24 Jan 2017 12:33:22 -0500 Subject: [PATCH 091/316] casting some values as int, in ensureSelfOrPrivileged middleware --- src/middleware/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middleware/index.js b/src/middleware/index.js index 8f8f8a8002..b32b9147a4 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -79,7 +79,7 @@ middleware.ensureSelfOrPrivileged = function (req, res, next) { middleware.exposeUid prior to invoking this middleware. */ if (req.user) { - if (req.user.uid === res.locals.uid) { + if (parseInt(req.user.uid, 10) === parseInt(res.locals.uid, 10)) { return next(); } From 2a33991f0de40a38f3988664d078b3cf35e7ff2d Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 25 Jan 2017 01:32:24 +0300 Subject: [PATCH 092/316] read usercount from global object --- src/controllers/admin/users.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/controllers/admin/users.js b/src/controllers/admin/users.js index 0e67c7d64c..422de5b02d 100644 --- a/src/controllers/admin/users.js +++ b/src/controllers/admin/users.js @@ -128,7 +128,11 @@ function getUsers(set, section, min, max, req, res, next) { if (byScore) { db.sortedSetCount(set, min, max, next); } else { - db.sortedSetCard(set, next); + if (set === 'users:banned' || set === 'users:notvalidated') { + db.sortedSetCard(set, next); + } else { + db.getObjectField('global', 'userCount', next); + } } }, users: function (next) { From 4df8832c8db5f2728f3183f2192d913cf45b5382 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 25 Jan 2017 11:02:23 -0500 Subject: [PATCH 093/316] adding conditional to upgrade script to handle cases where an upgrade is performed on a flag that is already in database --- src/upgrade.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/upgrade.js b/src/upgrade.js index 8dce35b901..12939ad99d 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -385,7 +385,14 @@ Upgrade.upgrade = function (callback) { setImmediate(next); } } - ], next); + ], function (err) { + if (err && err.message === '[[error:already-flagged]]') { + // Already flagged, no need to parse, but not an error + next(); + } else { + next(err); + } + }); }); }, next); }); From a143a15f5d2e081e3d4bb18580db5b27885230a7 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 26 Jan 2017 20:44:26 +0300 Subject: [PATCH 094/316] closes #5397 --- src/posts/create.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/posts/create.js b/src/posts/create.js index 3484bc424e..6cb925dc4c 100644 --- a/src/posts/create.js +++ b/src/posts/create.js @@ -108,7 +108,7 @@ module.exports = function (Posts) { }, function (postData, next) { postData.isMain = isMain; - plugins.fireHook('action:post.save', _.clone(postData)); + plugins.fireHook('action:post.save', {post: _.clone(postData)}); next(null, postData); } ], callback); From 3fb7f9fce5f70ce51fc0610999be593722662e68 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 26 Jan 2017 22:18:16 +0300 Subject: [PATCH 095/316] closes #5398 --- src/categories/create.js | 2 +- src/categories/delete.js | 2 +- src/controllers/authentication.js | 2 +- src/groups/create.js | 2 +- src/groups/delete.js | 3 +-- src/plugins/install.js | 4 ++-- src/posts.js | 4 ++-- src/socket.io/admin.js | 3 ++- src/topics/create.js | 7 +++--- src/topics/tools.js | 37 ++++++++++++------------------- src/user/create.js | 2 +- 11 files changed, 30 insertions(+), 38 deletions(-) diff --git a/src/categories/create.js b/src/categories/create.js index cebcbcd82b..ba12fc31b0 100644 --- a/src/categories/create.js +++ b/src/categories/create.js @@ -73,7 +73,7 @@ module.exports = function (Categories) { next(null, category); }, function (category, next) { - plugins.fireHook('action:category.create', category); + plugins.fireHook('action:category.create', {category: category}); next(null, category); } ], callback); diff --git a/src/categories/delete.js b/src/categories/delete.js index 06d4931132..cb0d30a09d 100644 --- a/src/categories/delete.js +++ b/src/categories/delete.js @@ -31,7 +31,7 @@ module.exports = function (Categories) { purgeCategory(cid, next); }, function (next) { - plugins.fireHook('action:category.delete', cid); + plugins.fireHook('action:category.delete', {cid: cid, uid: uid}); next(); } ], callback); diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js index 90a1715cbb..4aa1b028f6 100644 --- a/src/controllers/authentication.js +++ b/src/controllers/authentication.js @@ -332,7 +332,7 @@ authenticationController.onSuccessfulLogin = function (req, uid, callback) { // Force session check for all connected socket.io clients with the same session id sockets.in('sess_' + req.sessionID).emit('checkSession', uid); - plugins.fireHook('action:user.loggedIn', uid); + plugins.fireHook('action:user.loggedIn', {uid: uid, req: req}); callback(); }); }; diff --git a/src/groups/create.js b/src/groups/create.js index a59567afe6..e4533e1ab0 100644 --- a/src/groups/create.js +++ b/src/groups/create.js @@ -70,7 +70,7 @@ module.exports = function (Groups) { async.series(tasks, next); }, function (results, next) { - plugins.fireHook('action:group.create', groupData); + plugins.fireHook('action:group.create', {group: groupData}); next(null, groupData); } ], callback); diff --git a/src/groups/delete.js b/src/groups/delete.js index 0838dd2407..3c2e02ae73 100644 --- a/src/groups/delete.js +++ b/src/groups/delete.js @@ -17,8 +17,6 @@ module.exports = function (Groups) { } var groupObj = groupsData[0]; - plugins.fireHook('action:group.destroy', groupObj); - async.parallel([ async.apply(db.delete, 'group:' + groupName), async.apply(db.sortedSetRemove, 'groups:createtime', groupName), @@ -45,6 +43,7 @@ module.exports = function (Groups) { return callback(err); } Groups.resetCache(); + plugins.fireHook('action:group.destroy', {group: groupObj}); callback(); }); }); diff --git a/src/plugins/install.js b/src/plugins/install.js index a914a09ca6..5c6e4dddac 100644 --- a/src/plugins/install.js +++ b/src/plugins/install.js @@ -50,7 +50,7 @@ module.exports = function (Plugins) { }, function (next) { meta.reloadRequired = true; - Plugins.fireHook(isActive ? 'action:plugin.deactivate' : 'action:plugin.activate', id); + Plugins.fireHook(isActive ? 'action:plugin.deactivate' : 'action:plugin.activate', {id: id}); next(); } ], function (err) { @@ -95,7 +95,7 @@ module.exports = function (Plugins) { Plugins.get(id, next); }, function (pluginData, next) { - Plugins.fireHook('action:plugin.' + type, id); + Plugins.fireHook('action:plugin.' + type, {id: id, version: version}); next(null, pluginData); } ], callback); diff --git a/src/posts.js b/src/posts.js index ed22e51ed1..b8eb93fa1a 100644 --- a/src/posts.js +++ b/src/posts.js @@ -156,7 +156,7 @@ var plugins = require('./plugins'); pid: pid }; data[field] = value; - plugins.fireHook('action:post.setFields', data); + plugins.fireHook('action:post.setFields', {data: data}); callback(); }); }; @@ -167,7 +167,7 @@ var plugins = require('./plugins'); return callback(err); } data.pid = pid; - plugins.fireHook('action:post.setFields', data); + plugins.fireHook('action:post.setFields', {data: data}); callback(); }); }; diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js index b16bafa296..320832e363 100644 --- a/src/socket.io/admin.js +++ b/src/socket.io/admin.js @@ -168,10 +168,11 @@ SocketAdmin.config.setMultiple = function (socket, data, callback) { key: field, value: data[field] }; - plugins.fireHook('action:config.set', setting); + logger.monitorConfig({io: index.server}, setting); } } + plugins.fireHook('action:config.set', {settings: data}); setImmediate(next); } ], callback); diff --git a/src/topics/create.js b/src/topics/create.js index bf62266ecf..2026f006da 100644 --- a/src/topics/create.js +++ b/src/topics/create.js @@ -2,6 +2,7 @@ 'use strict'; var async = require('async'); +var _ = require('underscore'); var validator = require('validator'); var S = require('string'); var db = require('../database'); @@ -82,7 +83,7 @@ module.exports = function (Topics) { ], next); }, function (results, next) { - plugins.fireHook('action:topic.save', topicData); + plugins.fireHook('action:topic.save', {topic: _.clone(topicData)}); next(null, topicData.tid); } ], callback); @@ -174,7 +175,7 @@ module.exports = function (Topics) { data.postData.index = 0; analytics.increment(['topics', 'topics:byCid:' + data.topicData.cid]); - plugins.fireHook('action:topic.post', data.topicData); + plugins.fireHook('action:topic.post', {topic: data.topicData, post: data.postData}); if (parseInt(uid, 10)) { user.notifications.sendTopicNotificationToFollowers(uid, data.topicData, data.postData); @@ -269,7 +270,7 @@ module.exports = function (Topics) { Topics.notifyFollowers(postData, uid); analytics.increment(['posts', 'posts:byCid:' + cid]); - plugins.fireHook('action:topic.reply', postData); + plugins.fireHook('action:topic.reply', {post: _.clone(postData)}); next(null, postData); } diff --git a/src/topics/tools.js b/src/topics/tools.js index a8c00e08c0..295e2a028b 100644 --- a/src/topics/tools.js +++ b/src/topics/tools.js @@ -114,18 +114,18 @@ module.exports = function (Topics) { function toggleLock(tid, uid, lock, callback) { callback = callback || function () {}; - var cid; + var topicData; async.waterfall([ function (next) { - Topics.getTopicField(tid, 'cid', next); + Topics.getTopicFields(tid, ['tid', 'uid', 'cid'], next); }, - function (_cid, next) { - cid = _cid; - if (!cid) { + function (_topicData, next) { + topicData = _topicData; + if (!topicData || !topicData.cid) { return next(new Error('[[error:no-topic]]')); } - privileges.categories.isAdminOrMod(cid, uid, next); + privileges.categories.isAdminOrMod(topicData.cid, uid, next); }, function (isAdminOrMod, next) { if (!isAdminOrMod) { @@ -135,16 +135,11 @@ module.exports = function (Topics) { Topics.setTopicField(tid, 'locked', lock ? 1 : 0, next); }, function (next) { - var data = { - tid: tid, - isLocked: lock, - uid: uid, - cid: cid - }; + topicData.isLocked = lock; - plugins.fireHook('action:topic.lock', data); + plugins.fireHook('action:topic.lock', {topic: _.clone(topicData), uid: uid}); - next(null, data); + next(null, topicData); } ], callback); } @@ -167,7 +162,7 @@ module.exports = function (Topics) { if (!exists) { return callback(new Error('[[error:no-topic]]')); } - Topics.getTopicFields(tid, ['cid', 'lastposttime', 'postcount'], next); + Topics.getTopicFields(tid, ['uid', 'tid', 'cid', 'lastposttime', 'postcount'], next); }, function (_topicData, next) { topicData = _topicData; @@ -198,16 +193,12 @@ module.exports = function (Topics) { ], next); }, function (results, next) { - var data = { - tid: tid, - isPinned: pin, - uid: uid, - cid: topicData.cid - }; - plugins.fireHook('action:topic.pin', data); + topicData.isPinned = pin; - next(null, data); + plugins.fireHook('action:topic.pin', {topic: _.clone(topicData), uid: uid}); + + next(null, topicData); } ], callback); } diff --git a/src/user/create.js b/src/user/create.js index 67d6baf3cd..c6cd92460a 100644 --- a/src/user/create.js +++ b/src/user/create.js @@ -141,7 +141,7 @@ module.exports = function (User) { if (userNameChanged) { User.notifications.sendNameChangeNotification(userData.uid, userData.username); } - plugins.fireHook('action:user.create', userData); + plugins.fireHook('action:user.create', {user: userData}); next(null, userData.uid); } ], callback); From 46cde0444ed17a3fbb45374a9119751ddf31adee Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 27 Jan 2017 12:40:52 +0300 Subject: [PATCH 096/316] up dbsearch --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2b9e9ccd5f..5835a9f1f3 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "mousetrap": "^1.5.3", "nconf": "~0.8.2", "nodebb-plugin-composer-default": "4.3.7", - "nodebb-plugin-dbsearch": "1.0.5", + "nodebb-plugin-dbsearch": "2.0.0", "nodebb-plugin-emoji-extended": "1.1.1", "nodebb-plugin-emoji-one": "1.1.5", "nodebb-plugin-markdown": "7.0.1", From a89ed01f4f7d0969625328bd94b419e36be74f72 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 27 Jan 2017 13:14:05 +0300 Subject: [PATCH 097/316] up composer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5835a9f1f3..ce4e35e96a 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "mousetrap": "^1.5.3", "nconf": "~0.8.2", "nodebb-plugin-composer-default": "4.3.7", - "nodebb-plugin-dbsearch": "2.0.0", + "nodebb-plugin-dbsearch": "2.0.1", "nodebb-plugin-emoji-extended": "1.1.1", "nodebb-plugin-emoji-one": "1.1.5", "nodebb-plugin-markdown": "7.0.1", From f1f00b63fb39d52f1912cef35d4b8a1c317ea9d8 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 27 Jan 2017 20:35:50 +0300 Subject: [PATCH 098/316] closes #5400 --- public/src/modules/translator.js | 6 +++--- test/translator.js | 17 ++++++++++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/public/src/modules/translator.js b/public/src/modules/translator.js index eac467f413..db3953229e 100644 --- a/public/src/modules/translator.js +++ b/public/src/modules/translator.js @@ -155,7 +155,7 @@ while (cursor + 2 <= len) { sliced = str.slice(cursor, cursor + 2); - // found some text after the double bracket, + // found some text after the double bracket, // so this is probably a translation string if (!textBeforeColonFound && validTextRegex.test(sliced[0])) { textBeforeColonFound = true; @@ -174,7 +174,7 @@ cursor += 1; // a space or comma was found before the name // this isn't a translation string, so back out - } else if (!(textBeforeColonFound && colonFound && textAfterColonFound && commaAfterNameFound) && + } else if (!(textBeforeColonFound && colonFound && textAfterColonFound && commaAfterNameFound) && invalidTextRegex.test(sliced[0])) { cursor += 1; lastBreak -= 2; @@ -272,7 +272,7 @@ } var out = translated; translatedArgs.forEach(function (arg, i) { - var escaped = arg.replace(/%/g, '%').replace(/\\,/g, ','); + var escaped = arg.replace(/%(?=\d)/g, '%').replace(/\\,/g, ','); out = out.replace(new RegExp('%' + (i + 1), 'g'), escaped); }); return out; diff --git a/test/translator.js b/test/translator.js index 8198814164..17affe1700 100644 --- a/test/translator.js +++ b/test/translator.js @@ -4,6 +4,7 @@ var assert = require('assert'); var shim = require('../public/src/modules/translator.js'); var Translator = shim.Translator; +var db = require('./mocks/databasemock'); require('../src/languages').init(function () {}); @@ -118,10 +119,20 @@ describe('new Translator(language)', function () { it('should properly escape and ignore % and \\, in arguments', function () { var translator = Translator.create('en-GB'); - var title = 'Test 1\\, 2\\, 3 % salmon'; + var title = 'Test 1\\, 2\\, 3 %2 salmon'; var key = "[[topic:composer.replying_to, " + title + "]]"; return translator.translate(key).then(function (translated) { - assert.strictEqual(translated, 'Replying to Test 1, 2, 3 % salmon'); + assert.strictEqual(translated, 'Replying to Test 1, 2, 3 %2 salmon'); + }); + }); + + it('should not escape regular %', function () { + var translator = Translator.create('en-GB'); + + var title = '3 % salmon'; + var key = "[[topic:composer.replying_to, " + title + "]]"; + return translator.translate(key).then(function (translated) { + assert.strictEqual(translated, 'Replying to 3 % salmon'); }); }); @@ -173,7 +184,7 @@ describe('Translator.create()', function () { describe('Translator modules', function () { it('should work before registered', function () { var translator = Translator.create(); - + Translator.registerModule('test-custom-integer-format', function (lang) { return function (key, args) { var num = parseInt(args[0], 10) || 0; From 366ab103a460fcf53990ee44f20fe05bfe7b5c8a Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 27 Jan 2017 20:37:54 +0300 Subject: [PATCH 099/316] closes #5405 --- src/controllers/unread.js | 10 +++++++- src/socket.io/topics/infinitescroll.js | 2 +- src/socket.io/topics/unread.js | 2 +- src/topics/unread.js | 33 ++++++++++++-------------- test/topics.js | 8 +++---- 5 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/controllers/unread.js b/src/controllers/unread.js index 83de85a67e..8be52f955c 100644 --- a/src/controllers/unread.js +++ b/src/controllers/unread.js @@ -40,7 +40,15 @@ unreadController.get = function (req, res, next) { settings = results.settings; var start = Math.max(0, (page - 1) * settings.topicsPerPage); var stop = start + settings.topicsPerPage - 1; - topics.getUnreadTopics(cid, req.uid, start, stop, filter, next); + var cutoff = req.session.unreadCutoff ? req.session.unreadCutoff : topics.unreadCutoff(); + topics.getUnreadTopics({ + cid: cid, + uid: req.uid, + start: start, + stop: stop, + filter: filter, + cutoff: cutoff + }, next); } ], function (err, data) { if (err) { diff --git a/src/socket.io/topics/infinitescroll.js b/src/socket.io/topics/infinitescroll.js index a68d220609..3efa621656 100644 --- a/src/socket.io/topics/infinitescroll.js +++ b/src/socket.io/topics/infinitescroll.js @@ -98,7 +98,7 @@ module.exports = function (SocketTopics) { var start = parseInt(data.after, 10); var stop = start + 9; - topics.getUnreadTopics(data.cid, socket.uid, start, stop, data.filter, callback); + topics.getUnreadTopics({cid: data.cid, uid: socket.uid, start: start, stop: stop, filter: data.filter}, callback); }; SocketTopics.loadMoreRecentTopics = function (socket, data, callback) { diff --git a/src/socket.io/topics/unread.js b/src/socket.io/topics/unread.js index 39c6485a26..d5a010af14 100644 --- a/src/socket.io/topics/unread.js +++ b/src/socket.io/topics/unread.js @@ -51,7 +51,7 @@ module.exports = function (SocketTopics) { SocketTopics.markCategoryTopicsRead = function (socket, cid, callback) { async.waterfall([ function (next) { - topics.getUnreadTids(cid, socket.uid, '', next); + topics.getUnreadTids({cid: cid, uid: socket.uid, filter: ''}, next); }, function (tids, next) { SocketTopics.markAsRead(socket, tids, next); diff --git a/src/topics/unread.js b/src/topics/unread.js index e61ca46d59..a754fa0f99 100644 --- a/src/topics/unread.js +++ b/src/topics/unread.js @@ -18,14 +18,13 @@ module.exports = function (Topics) { callback = filter; filter = ''; } - Topics.getUnreadTids(0, uid, filter, function (err, tids) { + Topics.getUnreadTids({cid: 0, uid: uid, filter: filter}, function (err, tids) { callback(err, Array.isArray(tids) ? tids.length : 0); }); }; - Topics.getUnreadTopics = function (cid, uid, start, stop, filter, callback) { - + Topics.getUnreadTopics = function (params, callback) { var unreadTopics = { showSelect: true, nextStart : 0, @@ -34,7 +33,7 @@ module.exports = function (Topics) { async.waterfall([ function (next) { - Topics.getUnreadTids(cid, uid, filter, next); + Topics.getUnreadTids(params, next); }, function (tids, next) { unreadTopics.topicCount = tids.length; @@ -43,13 +42,13 @@ module.exports = function (Topics) { return next(null, []); } - if (stop === -1) { - tids = tids.slice(start); + if (params.stop === -1) { + tids = tids.slice(params.start); } else { - tids = tids.slice(start, stop + 1); + tids = tids.slice(params.start, params.stop + 1); } - Topics.getTopicsByTids(tids, uid, next); + Topics.getTopicsByTids(tids, params.uid, next); }, function (topicData, next) { if (!Array.isArray(topicData) || !topicData.length) { @@ -57,7 +56,7 @@ module.exports = function (Topics) { } unreadTopics.topics = topicData; - unreadTopics.nextStart = stop + 1; + unreadTopics.nextStart = params.stop + 1; next(null, unreadTopics); } ], callback); @@ -67,21 +66,19 @@ module.exports = function (Topics) { return Date.now() - (parseInt(meta.config.unreadCutoff, 10) || 2) * 86400000; }; - Topics.getUnreadTids = function (cid, uid, filter, callback) { - uid = parseInt(uid, 10); + Topics.getUnreadTids = function (params, callback) { + var uid = parseInt(params.uid, 10); if (uid === 0) { return callback(null, []); } - - var cutoff = Topics.unreadCutoff(); - + var cutoff = params.cutoff || Topics.unreadCutoff(); var ignoredCids; async.waterfall([ function (next) { async.parallel({ ignoredCids: function (next) { - if (filter === 'watched') { + if (params.filter === 'watched') { return next(null, []); } user.getIgnoredCategories(uid, next); @@ -121,7 +118,7 @@ module.exports = function (Topics) { if (results.ignoredTids.indexOf(recentTopic.value.toString()) !== -1) { return false; } - switch (filter) { + switch (params.filter) { case 'new': return !userRead[recentTopic.value]; default: @@ -133,7 +130,7 @@ module.exports = function (Topics) { return array.indexOf(tid) === index; }); - if (filter === 'watched') { + if (params.filter === 'watched') { Topics.filterWatchedTids(tids, uid, next); } else { next(null, tids); @@ -143,7 +140,7 @@ module.exports = function (Topics) { tids = tids.slice(0, 200); - filterTopics(uid, tids, cid, ignoredCids, filter, next); + filterTopics(uid, tids, params.cid, ignoredCids, params.filter, next); } ], callback); }; diff --git a/test/topics.js b/test/topics.js index 3a192b9e94..38881c4d58 100644 --- a/test/topics.js +++ b/test/topics.js @@ -445,7 +445,7 @@ describe('Topic\'s', function () { assert.equal(pinnedTids[1], tid2); done(); }); - }); + }); }); }); @@ -482,7 +482,7 @@ describe('Topic\'s', function () { topics.ignore( newTid, uid, done ); }, function (done) { - topics.getUnreadTopics(0, uid, 0, -1, '', done ); + topics.getUnreadTopics({cid: 0, uid: uid, start: 0, stop: -1, filter: ''}, done ); }, function (results, done) { var topics = results.topics; @@ -526,7 +526,7 @@ describe('Topic\'s', function () { topics.follow( newTid, uid, done ); }, function (done) { - topics.getUnreadTopics(0, uid, 0, -1, '', done ); + topics.getUnreadTopics({cid: 0, uid: uid, start: 0, stop: -1, filter: ''}, done ); }, function (results, done) { var topics = results.topics; @@ -546,7 +546,7 @@ describe('Topic\'s', function () { topics.follow( newTid, uid, done ); }, function (done) { - topics.getUnreadTopics(0, uid, 0, -1, '', done ); + topics.getUnreadTopics({cid: 0, uid: uid, start: 0, stop: -1, filter: ''}, done ); }, function (results, done) { var topics = results.topics; From 54ac23cc7a708cda27cb7b6e9393b3ba66dbcd89 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Mon, 30 Jan 2017 15:22:20 -0700 Subject: [PATCH 100/316] Revert "No 404 responses for missing language files" This reverts commit f0c941e5aeef86f3b31e6512643bd15d14afd839. --- src/routes/index.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/routes/index.js b/src/routes/index.js index d33fd316b5..99ff3a7f27 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -157,11 +157,6 @@ module.exports = function (app, middleware, hotswapIds) { res.redirect(relativePath + '/assets/language' + req.path + '.json?' + meta.config['cache-buster']); }); - // stop 404's on `/assets/language` route - app.use(relativePath + '/assets/language', function (req, res) { - res.json({}); - }); - app.use(relativePath, express.static(path.join(__dirname, '../../', 'public'), { maxAge: app.enabled('cache') ? 5184000000 : 0 })); From b37d52c089b29204eacb6c1dcf75166666f2dab9 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 31 Jan 2017 18:38:01 +0300 Subject: [PATCH 101/316] closes #5407 --- src/plugins/hooks.js | 4 +++- src/plugins/install.js | 20 ++++++++++++-------- src/plugins/load.js | 2 +- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/plugins/hooks.js b/src/plugins/hooks.js index b5a32ba1b7..a6a014e776 100644 --- a/src/plugins/hooks.js +++ b/src/plugins/hooks.js @@ -8,7 +8,9 @@ module.exports = function (Plugins) { 'filter:user.custom_fields': null, // remove in v1.1.0 'filter:post.save': 'filter:post.create', 'filter:user.profileLinks': 'filter:user.profileMenu', - 'action:post.flag': 'action:flag.create' + 'action:post.flag': 'action:flag.create', + 'action:plugin.activate': null, + 'action:plugin.install': null }; /* `data` is an object consisting of (* is required): diff --git a/src/plugins/install.js b/src/plugins/install.js index 5c6e4dddac..927316a21c 100644 --- a/src/plugins/install.js +++ b/src/plugins/install.js @@ -50,8 +50,11 @@ module.exports = function (Plugins) { }, function (next) { meta.reloadRequired = true; - Plugins.fireHook(isActive ? 'action:plugin.deactivate' : 'action:plugin.activate', {id: id}); - next(); + if (isActive) { + Plugins.fireHook('action:plugin.deactivate', {id: id}); + } + + setImmediate(next); } ], function (err) { if (err) { @@ -68,7 +71,6 @@ module.exports = function (Plugins) { }; function toggleInstall(id, version, callback) { - var type; var installed; async.waterfall([ function (next) { @@ -76,7 +78,6 @@ module.exports = function (Plugins) { }, function (_installed, next) { installed = _installed; - type = installed ? 'uninstall' : 'install'; Plugins.isActive(id, next); }, function (active, next) { @@ -86,17 +87,20 @@ module.exports = function (Plugins) { }); return; } - next(); + setImmediate(next); }, function (next) { - runNpmCommand(type, id, version || 'latest', next); + runNpmCommand(installed ? 'uninstall' : 'install', id, version || 'latest', next); }, function (next) { Plugins.get(id, next); }, function (pluginData, next) { - Plugins.fireHook('action:plugin.' + type, {id: id, version: version}); - next(null, pluginData); + if (installed) { + Plugins.fireHook('action:plugin.uninstall', {id: id, version: version}); + } + + setImmediate(next, null, pluginData); } ], callback); } diff --git a/src/plugins/load.js b/src/plugins/load.js index 43c3d96f4c..c1d1edf6eb 100644 --- a/src/plugins/load.js +++ b/src/plugins/load.js @@ -88,7 +88,7 @@ module.exports = function (Plugins) { }, function (next) { mapClientModules(pluginData, next); - }, + } ], function (err) { if (err) { winston.verbose('[plugins] Could not load plugin : ' + pluginData.id); From cd652e6730479133afa3dc14ca9acfaeaadfc5bf Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 31 Jan 2017 11:45:20 -0500 Subject: [PATCH 102/316] up mentions --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 14abe3d6da..b2693c9aab 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "nodebb-plugin-emoji-extended": "1.1.1", "nodebb-plugin-emoji-one": "1.1.5", "nodebb-plugin-markdown": "7.0.1", - "nodebb-plugin-mentions": "2.0.0", + "nodebb-plugin-mentions": "2.0.1", "nodebb-plugin-soundpack-default": "0.1.6", "nodebb-plugin-spam-be-gone": "0.4.10", "nodebb-rewards-essentials": "0.0.9", From 9a0bd29d677d27372e2f9ec8463559c44b1a42ad Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 31 Jan 2017 12:12:02 -0500 Subject: [PATCH 103/316] up vanilla --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b2693c9aab..58346fe76c 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "3.0.15", "nodebb-theme-persona": "4.1.95", - "nodebb-theme-vanilla": "5.1.58", + "nodebb-theme-vanilla": "5.1.59", "nodebb-widget-essentials": "2.0.13", "nodemailer": "2.6.4", "nodemailer-sendmail-transport": "1.0.0", From 6b9bafc6cd417204dc0453c4f82de8cc7438118e Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 2 Feb 2017 11:53:37 -0500 Subject: [PATCH 104/316] adding missing flag strings --- public/language/ar/pages.json | 4 +++- public/language/bg/pages.json | 4 +++- public/language/bn/pages.json | 4 +++- public/language/cs/pages.json | 4 +++- public/language/da/pages.json | 4 +++- public/language/de/pages.json | 4 +++- public/language/el/pages.json | 4 +++- public/language/en-US/pages.json | 4 +++- public/language/en-x-pirate/pages.json | 4 +++- public/language/es/pages.json | 4 +++- public/language/et/pages.json | 4 +++- public/language/fa-IR/pages.json | 4 +++- public/language/fi/pages.json | 4 +++- public/language/fr/pages.json | 4 +++- public/language/gl/pages.json | 4 +++- public/language/he/pages.json | 4 +++- public/language/hu/pages.json | 4 +++- public/language/id/pages.json | 4 +++- public/language/it/pages.json | 4 +++- public/language/ja/pages.json | 4 +++- public/language/ko/pages.json | 4 +++- public/language/lt/pages.json | 4 +++- public/language/ms/pages.json | 4 +++- public/language/nb/pages.json | 4 +++- public/language/nl/pages.json | 4 +++- public/language/pl/pages.json | 4 +++- public/language/pt-BR/pages.json | 4 +++- public/language/pt-PT/pages.json | 4 +++- public/language/ro/pages.json | 4 +++- public/language/ru/pages.json | 4 +++- public/language/rw/pages.json | 4 +++- public/language/sc/pages.json | 4 +++- public/language/sk/pages.json | 4 +++- public/language/sl/pages.json | 4 +++- public/language/sr/pages.json | 4 +++- public/language/sv/pages.json | 4 +++- public/language/th/pages.json | 4 +++- public/language/tr/pages.json | 4 +++- public/language/vi/pages.json | 4 +++- public/language/zh-CN/pages.json | 4 +++- public/language/zh-TW/pages.json | 4 +++- 41 files changed, 123 insertions(+), 41 deletions(-) diff --git a/public/language/ar/pages.json b/public/language/ar/pages.json index 678658b058..c978c623b3 100644 --- a/public/language/ar/pages.json +++ b/public/language/ar/pages.json @@ -6,7 +6,7 @@ "popular-month": "المواضيع الشائعة هذا الشهر", "popular-alltime": "المواضيع الشائعة منذ القدم", "recent": "المواضيع الحديثة", - "flagged-posts": "Flagged Posts", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "اﻷعضاء المتصلون", "users/latest": "أحدث اﻷعضاء", @@ -27,6 +27,8 @@ "group": "%1 مجموعة", "chats": "محادثات", "chat": "Chatting with %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Editing \"%1\"", "account/edit/password": "Editing password of \"%1\"", "account/edit/username": "Editing username of \"%1\"", diff --git a/public/language/bg/pages.json b/public/language/bg/pages.json index 1425f091da..43c87dde63 100644 --- a/public/language/bg/pages.json +++ b/public/language/bg/pages.json @@ -6,7 +6,7 @@ "popular-month": "Популярните теми този месец", "popular-alltime": "Популярните теми за всички времена", "recent": "Скорошни теми", - "flagged-posts": "Докладвани публикации", + "flagged-content": "Flagged Content", "ip-blacklist": "Черен списък за IP адреси", "users/online": "Потребители на линия", "users/latest": "Последни потребители", @@ -27,6 +27,8 @@ "group": "Група %1", "chats": "Разговори", "chat": "Разговаря с %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Редактиране на „%1“", "account/edit/password": "Редактиране на паролата на „%1“", "account/edit/username": "Редактиране на потребителското име на „%1“", diff --git a/public/language/bn/pages.json b/public/language/bn/pages.json index c68042e529..e08fb8f743 100644 --- a/public/language/bn/pages.json +++ b/public/language/bn/pages.json @@ -6,7 +6,7 @@ "popular-month": "Popular topics this month", "popular-alltime": "All time popular topics", "recent": "সাম্প্রতিক টপিক", - "flagged-posts": "Flagged Posts", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "Online Users", "users/latest": "Latest Users", @@ -27,6 +27,8 @@ "group": "%1 group", "chats": "Chats", "chat": "Chatting with %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Editing \"%1\"", "account/edit/password": "Editing password of \"%1\"", "account/edit/username": "Editing username of \"%1\"", diff --git a/public/language/cs/pages.json b/public/language/cs/pages.json index 64902c40c0..97a19aebaa 100644 --- a/public/language/cs/pages.json +++ b/public/language/cs/pages.json @@ -6,7 +6,7 @@ "popular-month": "Oblíbená témata pro tento měsíc", "popular-alltime": "Oblíbená témata za celou dobu", "recent": "Aktuální témata", - "flagged-posts": "Označené příspěvky", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "Uživatelé online", "users/latest": "Nejnovější uživatelé", @@ -27,6 +27,8 @@ "group": "%1 skupina", "chats": "Chaty", "chat": "Chatovat s %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Editing \"%1\"", "account/edit/password": "Editing password of \"%1\"", "account/edit/username": "Editing username of \"%1\"", diff --git a/public/language/da/pages.json b/public/language/da/pages.json index ea3ccbf48c..40c5478269 100644 --- a/public/language/da/pages.json +++ b/public/language/da/pages.json @@ -6,7 +6,7 @@ "popular-month": "Populære tråde denne måned", "popular-alltime": "Top populære tråde", "recent": "Seneste tråde", - "flagged-posts": "Anmeldte Indlæg", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "Online brugere", "users/latest": "Seneste brugere", @@ -27,6 +27,8 @@ "group": "%1 gruppe", "chats": "Chats", "chat": "Chatter med %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Redigere \"%1\"", "account/edit/password": "Redigerer adgangskode for \"%1\"", "account/edit/username": "Redigerer brugernavn for \"%1\"", diff --git a/public/language/de/pages.json b/public/language/de/pages.json index 5c47502a3c..0af7092482 100644 --- a/public/language/de/pages.json +++ b/public/language/de/pages.json @@ -6,7 +6,7 @@ "popular-month": "Beliebte Themen dieses Monats", "popular-alltime": "Beliebteste Themen", "recent": "Neueste Themen", - "flagged-posts": "Gemeldete Beiträge", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "Benutzer online", "users/latest": "Neuste Benutzer", @@ -27,6 +27,8 @@ "group": "%1 Gruppe", "chats": "Chats", "chat": "Chatte mit %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Bearbeite %1", "account/edit/password": "Bearbeite Passwort von \"%1\"", "account/edit/username": "Bearbeite Benutzernamen von \"%1\"", diff --git a/public/language/el/pages.json b/public/language/el/pages.json index 18a7dd1ef8..243f8511da 100644 --- a/public/language/el/pages.json +++ b/public/language/el/pages.json @@ -6,7 +6,7 @@ "popular-month": "Popular topics this month", "popular-alltime": "All time popular topics", "recent": "Πρόσφατα Θέματα", - "flagged-posts": "Flagged Posts", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "Online Users", "users/latest": "Latest Users", @@ -27,6 +27,8 @@ "group": "%1 group", "chats": "Chats", "chat": "Chatting with %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Editing \"%1\"", "account/edit/password": "Editing password of \"%1\"", "account/edit/username": "Editing username of \"%1\"", diff --git a/public/language/en-US/pages.json b/public/language/en-US/pages.json index af720e865c..dcbb38b5b9 100644 --- a/public/language/en-US/pages.json +++ b/public/language/en-US/pages.json @@ -6,7 +6,7 @@ "popular-month": "Popular topics this month", "popular-alltime": "All time popular topics", "recent": "Recent Topics", - "flagged-posts": "Flagged Posts", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "Online Users", "users/latest": "Latest Users", @@ -27,6 +27,8 @@ "group": "%1 group", "chats": "Chats", "chat": "Chatting with %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Editing \"%1\"", "account/edit/password": "Editing password of \"%1\"", "account/edit/username": "Editing username of \"%1\"", diff --git a/public/language/en-x-pirate/pages.json b/public/language/en-x-pirate/pages.json index af720e865c..dcbb38b5b9 100644 --- a/public/language/en-x-pirate/pages.json +++ b/public/language/en-x-pirate/pages.json @@ -6,7 +6,7 @@ "popular-month": "Popular topics this month", "popular-alltime": "All time popular topics", "recent": "Recent Topics", - "flagged-posts": "Flagged Posts", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "Online Users", "users/latest": "Latest Users", @@ -27,6 +27,8 @@ "group": "%1 group", "chats": "Chats", "chat": "Chatting with %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Editing \"%1\"", "account/edit/password": "Editing password of \"%1\"", "account/edit/username": "Editing username of \"%1\"", diff --git a/public/language/es/pages.json b/public/language/es/pages.json index 0f56b570df..59b1e2624d 100644 --- a/public/language/es/pages.json +++ b/public/language/es/pages.json @@ -6,7 +6,7 @@ "popular-month": "Temas populares del mes", "popular-alltime": "Temas populares de siempre", "recent": "Temas recientes", - "flagged-posts": "Posts reportados", + "flagged-content": "Flagged Content", "ip-blacklist": "Lista negra de IPS", "users/online": "Conectados", "users/latest": "Últimos usuarios", @@ -27,6 +27,8 @@ "group": "Grupo de %1", "chats": "Chats", "chat": "Chatear con %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Editar \"%1\"", "account/edit/password": "Editar contraseña de \"%1\"", "account/edit/username": "Editar nombre de usuario de \"%1\"", diff --git a/public/language/et/pages.json b/public/language/et/pages.json index 938fc79e13..e3ca3603dc 100644 --- a/public/language/et/pages.json +++ b/public/language/et/pages.json @@ -6,7 +6,7 @@ "popular-month": "Populaarsed teemad sel kuul", "popular-alltime": "Populaarseimad teemad üldse", "recent": "Hiljutised teemad", - "flagged-posts": "Märgistatud postitused", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "Sisseloginud kasutajad", "users/latest": "Hiljutised kasutajad", @@ -27,6 +27,8 @@ "group": "Kasutaja %1 grupp", "chats": "Vestlused", "chat": "Vestlus kasutajaga %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Muudan \"%1\"", "account/edit/password": "Redigeerid \"%1\" parooli", "account/edit/username": "Redigeerid \"%1\" kasutajanime", diff --git a/public/language/fa-IR/pages.json b/public/language/fa-IR/pages.json index 2b0c51f60c..9b55e53351 100644 --- a/public/language/fa-IR/pages.json +++ b/public/language/fa-IR/pages.json @@ -6,7 +6,7 @@ "popular-month": "موضوعات پربازدید این ماه", "popular-alltime": "پربازدیدترین موضوعات", "recent": "موضوع‌های تازه", - "flagged-posts": "پست نشانه گذاری شده", + "flagged-content": "Flagged Content", "ip-blacklist": "لیست سیاه آی‌پی", "users/online": "کاربران آنلاین", "users/latest": "آخرین کاربران", @@ -27,6 +27,8 @@ "group": "%1 گروه", "chats": "چتها", "chat": "چت با %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "ویرایش \"%1\"", "account/edit/password": "ویرایش کلمه ی عبورِ \"%1\"", "account/edit/username": "ویرایش نام کاربریِ \"%1\"", diff --git a/public/language/fi/pages.json b/public/language/fi/pages.json index 4ac31974c4..a5e8b87187 100644 --- a/public/language/fi/pages.json +++ b/public/language/fi/pages.json @@ -6,7 +6,7 @@ "popular-month": "Suositut aiheet tässä kuussa", "popular-alltime": "Suositut aiheet koko ajalta", "recent": "Viimeisimmät aiheet", - "flagged-posts": "Liputetut viestit", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "Paikalla olevat käyttäjät", "users/latest": "Viimeisimmat käyttäjät", @@ -27,6 +27,8 @@ "group": "%1 group", "chats": "Keskustelut", "chat": "Chatting with %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Editing \"%1\"", "account/edit/password": "Editing password of \"%1\"", "account/edit/username": "Editing username of \"%1\"", diff --git a/public/language/fr/pages.json b/public/language/fr/pages.json index 898d6c84ce..a28d0193c4 100644 --- a/public/language/fr/pages.json +++ b/public/language/fr/pages.json @@ -6,7 +6,7 @@ "popular-month": "Sujets populaires ce mois-ci", "popular-alltime": "Sujets populaires depuis toujours", "recent": "Sujets récents", - "flagged-posts": "Messages signalés", + "flagged-content": "Flagged Content", "ip-blacklist": "Liste noire d'adresses IP", "users/online": "Utilisateurs en ligne", "users/latest": "Derniers inscrits", @@ -27,6 +27,8 @@ "group": "%1 groupe", "chats": "Discussions", "chat": "Conversation avec %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Édition de \"%1\"", "account/edit/password": "Édition du mot de passe de \"%1\"", "account/edit/username": "Édition du nom d'utilisateur de \"%1\"", diff --git a/public/language/gl/pages.json b/public/language/gl/pages.json index c0c26c54e4..ce26b384ea 100644 --- a/public/language/gl/pages.json +++ b/public/language/gl/pages.json @@ -6,7 +6,7 @@ "popular-month": "Temas populares do mes", "popular-alltime": "Temas populares de tódolos tempos", "recent": "Temas recentes", - "flagged-posts": "Publicacions Marcadas.", + "flagged-content": "Flagged Content", "ip-blacklist": "Lista negra de IPs", "users/online": "Usuarios conectados", "users/latest": "Últimos usuarios", @@ -27,6 +27,8 @@ "group": "%1 grupo", "chats": "Charlas", "chat": "Falando con %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Editando \"%1\"", "account/edit/password": "Editando contrasinal \"%1\"", "account/edit/username": "Editando nome de usuario \"%1\"", diff --git a/public/language/he/pages.json b/public/language/he/pages.json index 2ae5abf720..8832d5915a 100644 --- a/public/language/he/pages.json +++ b/public/language/he/pages.json @@ -6,7 +6,7 @@ "popular-month": "נושאים חמים החודש", "popular-alltime": "הנושאים החמים בכל הזמנים", "recent": "נושאים אחרונים", - "flagged-posts": "פוסטים מסומנים", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "משתמשים מחוברים", "users/latest": "משתמשים אחרונים", @@ -27,6 +27,8 @@ "group": "קבוצת %1", "chats": "הודעות פרטיות", "chat": "שלחו הודעה פרטית ל%1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "לערוך את \"%1\"", "account/edit/password": "עורך את הסיסמה של \"%1\"", "account/edit/username": "עורך את שם המשתמש של \"%1\"", diff --git a/public/language/hu/pages.json b/public/language/hu/pages.json index 8a5faaf35d..e3c175a2b5 100644 --- a/public/language/hu/pages.json +++ b/public/language/hu/pages.json @@ -6,7 +6,7 @@ "popular-month": "Havi népszerű témakörök", "popular-alltime": "Mindenkori legnépszerűbb témakörök", "recent": "Legfrissebb témakörök", - "flagged-posts": "Megjelölt Üzenetek", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "Aktiv Felhasználok", "users/latest": "Latest Users", @@ -27,6 +27,8 @@ "group": "%1 csoport", "chats": "Chats", "chat": "Chatting with %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Editing \"%1\"", "account/edit/password": "Editing password of \"%1\"", "account/edit/username": "Editing username of \"%1\"", diff --git a/public/language/id/pages.json b/public/language/id/pages.json index b8aa021b71..e3b340e0de 100644 --- a/public/language/id/pages.json +++ b/public/language/id/pages.json @@ -6,7 +6,7 @@ "popular-month": "Popular topics this month", "popular-alltime": "All time popular topics", "recent": "Topik Terkini", - "flagged-posts": "Flagged Posts", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "Online Users", "users/latest": "Latest Users", @@ -27,6 +27,8 @@ "group": "%1 group", "chats": "Chats", "chat": "Chatting with %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Editing \"%1\"", "account/edit/password": "Editing password of \"%1\"", "account/edit/username": "Editing username of \"%1\"", diff --git a/public/language/it/pages.json b/public/language/it/pages.json index 465cdd5b05..818fca88e0 100644 --- a/public/language/it/pages.json +++ b/public/language/it/pages.json @@ -6,7 +6,7 @@ "popular-month": "Discussioni popolari questo mese", "popular-alltime": "Discussioni più popolari di sempre", "recent": "Discussioni Recenti", - "flagged-posts": "Post Segnalati", + "flagged-content": "Flagged Content", "ip-blacklist": "Lista nera degli IP", "users/online": "Utenti Online", "users/latest": "Ultimi Utenti", @@ -27,6 +27,8 @@ "group": "Gruppo %1", "chats": "Chat", "chat": "In chat con %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Modificando \"%1\"", "account/edit/password": "Modificando la password di \"%1\"", "account/edit/username": "Modificando il nome utente di \"%1\"", diff --git a/public/language/ja/pages.json b/public/language/ja/pages.json index 79ad63add2..39b7f3619e 100644 --- a/public/language/ja/pages.json +++ b/public/language/ja/pages.json @@ -6,7 +6,7 @@ "popular-month": "今月人気のトピック", "popular-alltime": "人気のトピック", "recent": "最新スレッド", - "flagged-posts": "投稿にフラグを立てました", + "flagged-content": "Flagged Content", "ip-blacklist": "IPブラックリスト", "users/online": "オンラインのユーザー", "users/latest": "最近のユーザー", @@ -27,6 +27,8 @@ "group": "%1 グループ", "chats": "チャット", "chat": "%1とチャットします", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "編集中 \"%1\"", "account/edit/password": "\"%1\"のパスワードを編集中", "account/edit/username": "\"%1\"のユーザー名を編集中", diff --git a/public/language/ko/pages.json b/public/language/ko/pages.json index 553595268e..2d258932ac 100644 --- a/public/language/ko/pages.json +++ b/public/language/ko/pages.json @@ -6,7 +6,7 @@ "popular-month": "인기있는 주제 (월간)", "popular-alltime": "인기있는 주제", "recent": "최근 주제", - "flagged-posts": "플래그된 게시물", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "온라인 사용자", "users/latest": "최근 사용자", @@ -27,6 +27,8 @@ "group": "%1 그룹", "chats": "대화", "chat": "%1 님과 대화", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "\"%1\" 편집", "account/edit/password": "\"%1\" 의 패스워드 변경", "account/edit/username": "\"%1\" 의 사용자명 변경", diff --git a/public/language/lt/pages.json b/public/language/lt/pages.json index 26a2b0167a..62fe318c71 100644 --- a/public/language/lt/pages.json +++ b/public/language/lt/pages.json @@ -6,7 +6,7 @@ "popular-month": "Popular topics this month", "popular-alltime": "All time popular topics", "recent": "Paskutinės temos", - "flagged-posts": "Flagged Posts", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "Online Users", "users/latest": "Latest Users", @@ -27,6 +27,8 @@ "group": "%1 group", "chats": "Chats", "chat": "Chatting with %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Editing \"%1\"", "account/edit/password": "Editing password of \"%1\"", "account/edit/username": "Editing username of \"%1\"", diff --git a/public/language/ms/pages.json b/public/language/ms/pages.json index d10ea46388..bfade44f86 100644 --- a/public/language/ms/pages.json +++ b/public/language/ms/pages.json @@ -6,7 +6,7 @@ "popular-month": "Topik Popular Bulan Ini", "popular-alltime": "Topik Popular Sepanjang Masa", "recent": "Topik Baru", - "flagged-posts": "Kiriman Dipalang", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "Pengguna Atas Talian", "users/latest": "Pengguna Terkini", @@ -27,6 +27,8 @@ "group": "%1 Kumpulan", "chats": "Borak", "chat": "Borak Dengan %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Menyunting \"%1\"", "account/edit/password": "Mengemaskini kata laluan \"%1\"", "account/edit/username": "Mengemaskini nama pengguna \"%1\"", diff --git a/public/language/nb/pages.json b/public/language/nb/pages.json index c15ff8a9a3..39f5bd7800 100644 --- a/public/language/nb/pages.json +++ b/public/language/nb/pages.json @@ -6,7 +6,7 @@ "popular-month": "Populære emner denne måneden", "popular-alltime": "Mest populære emner for all tid", "recent": "Nylige emner", - "flagged-posts": "Flagged Posts", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "Påloggede Brukere", "users/latest": "Nyeste Brukere", @@ -27,6 +27,8 @@ "group": "%1 gruppe", "chats": "Samtaler", "chat": "Samtale med %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Endrer \"%1\"", "account/edit/password": "Editing password of \"%1\"", "account/edit/username": "Editing username of \"%1\"", diff --git a/public/language/nl/pages.json b/public/language/nl/pages.json index 700b5de892..816f62be22 100644 --- a/public/language/nl/pages.json +++ b/public/language/nl/pages.json @@ -6,7 +6,7 @@ "popular-month": "De populaire onderwerpen van deze maand", "popular-alltime": "De populaire onderwerpen", "recent": "Recente onderwerpen", - "flagged-posts": "Ongepaste berichten", + "flagged-content": "Flagged Content", "ip-blacklist": "IP zwarte lijst", "users/online": "Online Gebruikers", "users/latest": "Meest recente gebruikers", @@ -27,6 +27,8 @@ "group": "%1's groep", "chats": "Chats", "chat": "Chatten met %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "\"%1\" aanpassen", "account/edit/password": "Wachtwoord van \"%1\" aanpassen", "account/edit/username": "Gebruikersnaam van \"%1\" aanpassen", diff --git a/public/language/pl/pages.json b/public/language/pl/pages.json index dfd76118bf..167e5696f5 100644 --- a/public/language/pl/pages.json +++ b/public/language/pl/pages.json @@ -6,7 +6,7 @@ "popular-month": "Tematy popularne w tym miesiącu", "popular-alltime": "Wszystkie popularne tematy", "recent": "Ostatnie Tematy", - "flagged-posts": "Oflagowane posty", + "flagged-content": "Flagged Content", "ip-blacklist": "Czarna lista adresów IP", "users/online": "Dostępni Użytkownicy", "users/latest": "Nowi Użytkownicy", @@ -27,6 +27,8 @@ "group": "Grupa %1", "chats": "Rozmowy", "chat": "Rozmowa z %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Edytowanie \"%1\"", "account/edit/password": "Edytowanie hasła \"%1\"", "account/edit/username": "Edytowanie nazwy \"%1\"", diff --git a/public/language/pt-BR/pages.json b/public/language/pt-BR/pages.json index 1d8c4f87bb..7a27b39a3c 100644 --- a/public/language/pt-BR/pages.json +++ b/public/language/pt-BR/pages.json @@ -6,7 +6,7 @@ "popular-month": "Tópicos populares deste mês", "popular-alltime": "Tópicos populares de todos os tempos", "recent": "Tópicos Recentes", - "flagged-posts": "Posts Sinalizados", + "flagged-content": "Flagged Content", "ip-blacklist": "Lista negra de IPs", "users/online": "Usuários Online", "users/latest": "Últimos Usuários", @@ -27,6 +27,8 @@ "group": "%1 grupo", "chats": "Chats", "chat": "Conversando com %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Editando \"%1\"", "account/edit/password": "Editando senha de \"%1\"", "account/edit/username": "Editando nome de usuário de \"%1\"", diff --git a/public/language/pt-PT/pages.json b/public/language/pt-PT/pages.json index b0c9ea6b93..d22a86e4d1 100644 --- a/public/language/pt-PT/pages.json +++ b/public/language/pt-PT/pages.json @@ -6,7 +6,7 @@ "popular-month": "Tópicos populares este mês", "popular-alltime": "Tópicos populares desde sempre", "recent": "Tópicos recentes", - "flagged-posts": "Publicações sinalizadas", + "flagged-content": "Flagged Content", "ip-blacklist": "Lista negra de IPs", "users/online": "Utilizadores online", "users/latest": "Últimos utilizadores", @@ -27,6 +27,8 @@ "group": "%1 group", "chats": "Conversas", "chat": "Conversando com %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Editando \"%1\"", "account/edit/password": "Editando palavra-passe de \"%1\"", "account/edit/username": "Editando o nome de utilizador de \"%1\"", diff --git a/public/language/ro/pages.json b/public/language/ro/pages.json index 36eafbd6ef..4816853bef 100644 --- a/public/language/ro/pages.json +++ b/public/language/ro/pages.json @@ -6,7 +6,7 @@ "popular-month": "Subiecte populare în luna asta", "popular-alltime": "All time popular topics", "recent": "Subiecte Noi", - "flagged-posts": "Flagged Posts", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "Utilizatori online", "users/latest": "Ultimii membrii", @@ -27,6 +27,8 @@ "group": "%1 group", "chats": "Chats", "chat": "Chatting with %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Editing \"%1\"", "account/edit/password": "Editing password of \"%1\"", "account/edit/username": "Editing username of \"%1\"", diff --git a/public/language/ru/pages.json b/public/language/ru/pages.json index ec4fccb5f1..86cfc9da3e 100644 --- a/public/language/ru/pages.json +++ b/public/language/ru/pages.json @@ -6,7 +6,7 @@ "popular-month": "Популярные темы этого месяца", "popular-alltime": "Популярные темы за всё время", "recent": "Последние темы", - "flagged-posts": "Отмеченные сообщения", + "flagged-content": "Flagged Content", "ip-blacklist": "Чёрный список IP", "users/online": "В сети", "users/latest": "Новые участники", @@ -27,6 +27,8 @@ "group": "Группа %1", "chats": "Чаты", "chat": "Чат с участником %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Редактирование \"%1\"", "account/edit/password": "Сменить пароль \"%1\"", "account/edit/username": "Изменить имя пользователя \"%1\"", diff --git a/public/language/rw/pages.json b/public/language/rw/pages.json index 1905438799..6b1fcdccca 100644 --- a/public/language/rw/pages.json +++ b/public/language/rw/pages.json @@ -6,7 +6,7 @@ "popular-month": "Ibiganiro bikunzwe uku kwezi", "popular-alltime": "Ibiganiro byakunzwe ibihe byose", "recent": "Ibiganiro Biheruka", - "flagged-posts": "Ibyatambikanywe", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "Abariho", "users/latest": "Abashya", @@ -27,6 +27,8 @@ "group": "Itsinda %1 ", "chats": "Mu Gikari", "chat": "Ukuganira na %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Uguhindura \"%1\"", "account/edit/password": "Uguhindura ijambobanga rya \"%1\"", "account/edit/username": "Uguhindura izina rya \"%1\"", diff --git a/public/language/sc/pages.json b/public/language/sc/pages.json index 2602c1834f..25d7ccb5ce 100644 --- a/public/language/sc/pages.json +++ b/public/language/sc/pages.json @@ -6,7 +6,7 @@ "popular-month": "Popular topics this month", "popular-alltime": "All time popular topics", "recent": "Ùrtimas Arresonadas", - "flagged-posts": "Flagged Posts", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "Online Users", "users/latest": "Latest Users", @@ -27,6 +27,8 @@ "group": "%1 group", "chats": "Chats", "chat": "Chatting with %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Editing \"%1\"", "account/edit/password": "Editing password of \"%1\"", "account/edit/username": "Editing username of \"%1\"", diff --git a/public/language/sk/pages.json b/public/language/sk/pages.json index 095ee00be2..7e6d0bc63b 100644 --- a/public/language/sk/pages.json +++ b/public/language/sk/pages.json @@ -6,7 +6,7 @@ "popular-month": "Populárne témy za tento mesiac", "popular-alltime": "Populárne témy za celé obdobie", "recent": "Nedávne témy", - "flagged-posts": "Označené príspevky", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "Online užívatelia", "users/latest": "Najnovší užívatelia", @@ -27,6 +27,8 @@ "group": "%1 skupina", "chats": "Konverzácie", "chat": "Rozprávate sa s %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Úprava \"%1\"", "account/edit/password": "Úprava hesla \"%1\"", "account/edit/username": "Úprava užívateľského mena \"%1\"", diff --git a/public/language/sl/pages.json b/public/language/sl/pages.json index 9970c374cd..88cd0f8afc 100644 --- a/public/language/sl/pages.json +++ b/public/language/sl/pages.json @@ -6,7 +6,7 @@ "popular-month": "Priljubljene teme v tem mesecu", "popular-alltime": "Vse priljubljene teme", "recent": "Zadnje teme", - "flagged-posts": "Označene teme", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "Dosegljivi uporabniki", "users/latest": "Zadnji uporabniki", @@ -27,6 +27,8 @@ "group": "Skupina %1", "chats": "Klepeti", "chat": "Klepet z osebo %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Urejanje \"%1\"", "account/edit/password": "Urejanje gesla za \"%1\"", "account/edit/username": "Urejanje uporabniškega imena za \"%1\"", diff --git a/public/language/sr/pages.json b/public/language/sr/pages.json index 24fafe64ba..5e81f13a6e 100644 --- a/public/language/sr/pages.json +++ b/public/language/sr/pages.json @@ -6,7 +6,7 @@ "popular-month": "Популарне теме овог месеца", "popular-alltime": "Популарне теме свих времена", "recent": "Недавне теме", - "flagged-posts": "Означене поруке", + "flagged-content": "Flagged Content", "ip-blacklist": "Црна листа IP адреса", "users/online": "Корисници на мрежи", "users/latest": "Најновији корисници", @@ -27,6 +27,8 @@ "group": "%1 група", "chats": "Ћаскања", "chat": "Ћаскање са %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Уређивање \"%1\"", "account/edit/password": "Уређивање лозинке од \"%1\"", "account/edit/username": "Уређивање корисничког имена од \"%1\"", diff --git a/public/language/sv/pages.json b/public/language/sv/pages.json index 61f6c23a62..cdc9019082 100644 --- a/public/language/sv/pages.json +++ b/public/language/sv/pages.json @@ -6,7 +6,7 @@ "popular-month": "Populära ämnen denna månad", "popular-alltime": "Populäraste ämnena genom tiderna", "recent": "Senaste ämnena", - "flagged-posts": "Flaggade inlägg", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Svartlista", "users/online": "Användare online", "users/latest": "Senaste Användare", @@ -27,6 +27,8 @@ "group": "%1 grupp", "chats": "Chattar", "chat": "Chattar med %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Redigerar \"%1\"", "account/edit/password": "Redigerar lösenord för \"%1\"", "account/edit/username": "Redigerar användarnamn för \"%1\"", diff --git a/public/language/th/pages.json b/public/language/th/pages.json index e2d1215bcb..7a92d461eb 100644 --- a/public/language/th/pages.json +++ b/public/language/th/pages.json @@ -6,7 +6,7 @@ "popular-month": "Popular topics this month", "popular-alltime": "All time popular topics", "recent": "กระทู้ล่าสุด", - "flagged-posts": "Flagged Posts", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "Online Users", "users/latest": "Latest Users", @@ -27,6 +27,8 @@ "group": "%1 group", "chats": "Chats", "chat": "Chatting with %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Editing \"%1\"", "account/edit/password": "Editing password of \"%1\"", "account/edit/username": "Editing username of \"%1\"", diff --git a/public/language/tr/pages.json b/public/language/tr/pages.json index 73900a5eb1..7eec286875 100644 --- a/public/language/tr/pages.json +++ b/public/language/tr/pages.json @@ -6,7 +6,7 @@ "popular-month": "Bu ayki popüler başlıklar", "popular-alltime": "En popüler başlıklar", "recent": "Güncel Konular", - "flagged-posts": "Bayraklanmış İletiler", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Karaliste", "users/online": "Çevrimiçi Kullanıcılar", "users/latest": "En Yeni Kullanıcılar", @@ -27,6 +27,8 @@ "group": "%1 grubu", "chats": "Sohbetler", "chat": "%1 ile sohbet", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "\"%1\" düzenleniyor", "account/edit/password": "\"%1\" parolayı düzenliyor", "account/edit/username": "\"%1\" kullanıcı adını düzenliyor", diff --git a/public/language/vi/pages.json b/public/language/vi/pages.json index 4ac7cf00a9..ba094bf22e 100644 --- a/public/language/vi/pages.json +++ b/public/language/vi/pages.json @@ -6,7 +6,7 @@ "popular-month": "Chủ đề nổi bật tháng này", "popular-alltime": "Chủ đề nổi bật mọi thời đại", "recent": "Chủ đề gần đây", - "flagged-posts": "Bài viết bị gắn cờ", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "Thành viên đang online", "users/latest": "Thành viên mới nhất", @@ -27,6 +27,8 @@ "group": "Nhóm %1", "chats": "Chat", "chat": "Chat với %1", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "Chỉnh sửa \"%1\"", "account/edit/password": "Chỉnh sửa mật khẩu của \"%1\"", "account/edit/username": "Chỉnh sửa tên đăng nhập của \"%1\"", diff --git a/public/language/zh-CN/pages.json b/public/language/zh-CN/pages.json index af1f0bedfa..428ded20bd 100644 --- a/public/language/zh-CN/pages.json +++ b/public/language/zh-CN/pages.json @@ -6,7 +6,7 @@ "popular-month": "当月热门话题", "popular-alltime": "热门主题", "recent": "最新主题", - "flagged-posts": "已举报的帖子", + "flagged-content": "Flagged Content", "ip-blacklist": "IP 黑名单", "users/online": "在线会员", "users/latest": "最新会员", @@ -27,6 +27,8 @@ "group": "%1 的用户组", "chats": "聊天", "chat": "与 %1 聊天", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "正在编辑 \"%1\"", "account/edit/password": "正在编辑 \"%1\" 的密码", "account/edit/username": "正在编辑 \"%1\" 的用户名", diff --git a/public/language/zh-TW/pages.json b/public/language/zh-TW/pages.json index 0f3a3a5e1e..708850a33d 100644 --- a/public/language/zh-TW/pages.json +++ b/public/language/zh-TW/pages.json @@ -6,7 +6,7 @@ "popular-month": "本月受歡迎的主題", "popular-alltime": "所有時間受歡迎的主題", "recent": "近期的主題", - "flagged-posts": "標記的張貼", + "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", "users/online": "線上使用者", "users/latest": "最近使用者", @@ -27,6 +27,8 @@ "group": "%1 群組", "chats": "聊天", "chat": "與 %1 聊天", + "flags": "Flags", + "flag-details": "Flag %1 Details", "account/edit": "編輯 \"%1\"", "account/edit/password": "編輯 \"%1\" 的密碼", "account/edit/username": "編輯\"%1\"的帳號", From 5bb0c3ae073562380abb67263111fa405cbb83a4 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 3 Feb 2017 10:24:19 -0500 Subject: [PATCH 105/316] increased file watch interval to reduce high CPU usage when using grunt --- Gruntfile.js | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index ca5d4dad49..85ea76938f 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -71,10 +71,16 @@ module.exports = function (grunt) { 'node_modules/nodebb-*/*.less', 'node_modules/nodebb-*/**/*.less', '!node_modules/nodebb-*/node_modules/**', '!node_modules/nodebb-*/.git/**' - ] + ], + options: { + interval: 1000 + } }, lessUpdated_Admin: { - files: ['public/**/*.less'] + files: ['public/**/*.less'], + options: { + interval: 1000 + } }, clientUpdated: { files: [ @@ -83,10 +89,16 @@ module.exports = function (grunt) { '!node_modules/nodebb-*/node_modules/**', 'node_modules/templates.js/lib/templates.js', '!node_modules/nodebb-*/.git/**' - ] + ], + options: { + interval: 1000 + } }, serverUpdated: { - files: ['*.js', 'install/*.js', 'src/**/*.js'] + files: ['*.js', 'install/*.js', 'src/**/*.js'], + options: { + interval: 1000 + } }, templatesUpdated: { files: [ @@ -94,7 +106,10 @@ module.exports = function (grunt) { 'node_modules/nodebb-*/*.tpl', 'node_modules/nodebb-*/**/*.tpl', '!node_modules/nodebb-*/node_modules/**', '!node_modules/nodebb-*/.git/**' - ] + ], + options: { + interval: 1000 + } }, langUpdated: { files: [ @@ -107,6 +122,9 @@ module.exports = function (grunt) { '!node_modules/nodebb-*/package.json', '!node_modules/nodebb-*/theme.json', ], + options: { + interval: 1000 + } }, } }); From 387110fecc89f535055249411d8f4421554836e0 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 8 Feb 2017 18:23:34 +0300 Subject: [PATCH 106/316] closes #5315 --- .../language/en-GB/admin/settings/general.json | 3 ++- public/language/en-GB/search.json | 1 + public/src/client/search.js | 4 ++-- src/controllers/search.js | 3 ++- src/search.js | 4 ++-- src/views/admin/settings/general.tpl | 17 +++++++++++++++++ 6 files changed, 26 insertions(+), 6 deletions(-) diff --git a/public/language/en-GB/admin/settings/general.json b/public/language/en-GB/admin/settings/general.json index c26740ee4f..72ecfe641f 100644 --- a/public/language/en-GB/admin/settings/general.json +++ b/public/language/en-GB/admin/settings/general.json @@ -26,5 +26,6 @@ "touch-icon.upload": "Upload", "touch-icon.help": "Recommended size and format: 192x192, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "outgoing-links": "Outgoing Links", - "outgoing-links.warning-page": "Use Outgoing Links Warning Page" + "outgoing-links.warning-page": "Use Outgoing Links Warning Page", + "search-default-sort-by": "Search default sort by" } \ No newline at end of file diff --git a/public/language/en-GB/search.json b/public/language/en-GB/search.json index 98c1afcea2..51a0a76ebb 100644 --- a/public/language/en-GB/search.json +++ b/public/language/en-GB/search.json @@ -12,6 +12,7 @@ "reply-count": "Reply Count", "at-least": "At least", "at-most": "At most", + "relevance": "Relevance", "post-time": "Post time", "newer-than": "Newer than", "older-than": "Older than", diff --git a/public/src/client/search.js b/public/src/client/search.js index 48cf358c0c..28a9ac94cd 100644 --- a/public/src/client/search.js +++ b/public/src/client/search.js @@ -105,8 +105,8 @@ define('forum/search', ['search', 'autocomplete'], function (searchModule, autoc $('#post-time-filter').val(formData.timeFilter); } - if (formData.sortBy) { - $('#post-sort-by').val(formData.sortBy); + if (formData.sortBy || ajaxify.data.searchDefaultSortBy) { + $('#post-sort-by').val(formData.sortBy || ajaxify.data.searchDefaultSortBy); $('#post-sort-direction').val(formData.sortDirection); } diff --git a/src/controllers/search.js b/src/controllers/search.js index 2261e4f3b7..db3adf0682 100644 --- a/src/controllers/search.js +++ b/src/controllers/search.js @@ -38,7 +38,7 @@ searchController.search = function (req, res, next) { repliesFilter: req.query.repliesFilter, timeRange: req.query.timeRange, timeFilter: req.query.timeFilter, - sortBy: req.query.sortBy, + sortBy: req.query.sortBy || meta.config.searchDefaultSortBy || '', sortDirection: req.query.sortDirection, page: page, uid: req.uid, @@ -67,6 +67,7 @@ searchController.search = function (req, res, next) { searchData.title = '[[global:header.search]]'; searchData.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[global:search]]'}]); searchData.expandSearch = !req.query.term; + searchData.searchDefaultSortBy = meta.config.searchDefaultSortBy || ''; res.render('search', searchData); }); diff --git a/src/search.js b/src/search.js index d77b1f58f0..bd1fde947d 100644 --- a/src/search.js +++ b/src/search.js @@ -147,7 +147,7 @@ function getMatchedPosts(pids, data, callback) { topicFields.push('postcount'); } - if (data.sortBy) { + if (data.sortBy && data.sortBy !== 'relevance') { if (data.sortBy.startsWith('category')) { topicFields.push('cid'); } else if (data.sortBy.startsWith('topic.')) { @@ -328,7 +328,7 @@ function filterByTags(posts, hasTags) { } function sortPosts(posts, data) { - if (!posts.length || !data.sortBy) { + if (!posts.length || !data.sortBy || data.sortBy === 'relevance') { return; } diff --git a/src/views/admin/settings/general.tpl b/src/views/admin/settings/general.tpl index 990509e41f..68705951da 100644 --- a/src/views/admin/settings/general.tpl +++ b/src/views/admin/settings/general.tpl @@ -113,6 +113,23 @@
    +
    +
    [[admin/settings/general:search-default-sort-by]]
    +
    + +
    +
    +
    [[admin/settings/general:outgoing-links]]
    From 83cd960ffaadd3ef96792a5f4dec438290ed8e33 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Wed, 8 Feb 2017 14:46:39 -0500 Subject: [PATCH 107/316] improved threaded view design --- public/language/en-GB/topic.json | 3 +- public/src/client/topic/replies.js | 8 +++-- src/topics/posts.js | 47 +++++++++++++++++++++++++++++- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/public/language/en-GB/topic.json b/public/language/en-GB/topic.json index 29a85c15cc..933a829fa7 100644 --- a/public/language/en-GB/topic.json +++ b/public/language/en-GB/topic.json @@ -16,7 +16,8 @@ "notify_me": "Be notified of new replies in this topic", "quote": "Quote", "reply": "Reply", - "replies_to_this_post": "Replies: %1", + "replies_to_this_post": "%1 Replies", + "last_reply_time": "Last reply", "reply-as-topic": "Reply as topic", "guest-login-reply": "Log in to reply", "edit": "Edit", diff --git a/public/src/client/topic/replies.js b/public/src/client/topic/replies.js index ba7fd81f33..d025091d1e 100644 --- a/public/src/client/topic/replies.js +++ b/public/src/client/topic/replies.js @@ -21,9 +21,9 @@ define('forum/topic/replies', ['navigator', 'components', 'forum/topic/posts'], function onRepliesClicked(button, tid) { var post = button.closest('[data-pid]'); var pid = post.data('pid'); - var open = button.children('[component="post/replies/open"]'); - var loading = button.children('[component="post/replies/loading"]'); - var close = button.children('[component="post/replies/close"]'); + var open = button.find('[component="post/replies/open"]'); + var loading = button.find('[component="post/replies/loading"]'); + var close = button.find('[component="post/replies/close"]'); if (open.is(':not(.hidden)') && loading.is('.hidden')) { open.addClass('hidden'); @@ -86,10 +86,12 @@ define('forum/topic/replies', ['navigator', 'components', 'forum/topic/posts'], function incrementCount(post, inc) { var replyCount = $('[component="post"][data-pid="' + post.toPid + '"]').find('[component="post/reply-count"]').first(); var countEl = replyCount.find('[component="post/reply-count/text"]'); + var avatars = replyCount.find('[component="post/reply-count/avatars"]'); var count = Math.max(0, parseInt(countEl.attr('data-replies'), 10) + inc); countEl.attr('data-replies', count); replyCount.toggleClass('hidden', !count); countEl.translateText('[[topic:replies_to_this_post, ' + count + ']]'); + avatars.addClass('hasMore'); } return Replies; diff --git a/src/topics/posts.js b/src/topics/posts.js index aadf6bd76b..7f936b9b0d 100644 --- a/src/topics/posts.js +++ b/src/topics/posts.js @@ -10,6 +10,7 @@ var user = require('../user'); var posts = require('../posts'); var meta = require('../meta'); var plugins = require('../plugins'); +var utils = require('../../public/src/utils'); module.exports = function (Topics) { @@ -109,6 +110,9 @@ module.exports = function (Topics) { }, parents: function (next) { Topics.addParentPosts(postData, next); + }, + replies: function (next) { + getPostReplies(pids, uid, next); } }, function (err, results) { if (err) { @@ -123,8 +127,8 @@ module.exports = function (Topics) { postObj.bookmarked = results.bookmarks[i]; postObj.upvoted = results.voteData.upvotes[i]; postObj.downvoted = results.voteData.downvotes[i]; + postObj.replies = results.replies[i]; postObj.votes = postObj.votes || 0; - postObj.replies = postObj.replies || 0; postObj.selfPost = !!parseInt(uid, 10) && parseInt(uid, 10) === parseInt(postObj.uid, 10); // Username override for guests, if enabled @@ -384,4 +388,45 @@ module.exports = function (Topics) { db.getObjectField('topic:' + tid, 'postcount', callback); }; + function getPostReplies(pids, callerUid, callback) { + async.map(pids, function (pid, next) { + db.getSortedSetRange('pid:' + pid + ':replies', 0, -1, function (err, replyPids) { + var uids = [], count = 0; + + async.until(function () { + return count === replyPids.length || uids.length === 6; + }, function (next) { + posts.getPostField(replyPids[count], 'uid', function (err, uid) { + uid = parseInt(uid, 10); + if (uids.indexOf(uid) === -1) { + uids.push(uid); + } + count++; + next(err); + }); + }, function (err) { + async.parallel({ + "users": function (next) { + user.getUsersWithFields(uids, ['uid', 'username', 'userslug', 'picture'], callerUid, next); + }, + "timestampISO": function (next) { + posts.getPostField(replyPids[0], 'timestamp', function (err, timestamp) { + next(err, utils.toISOString(timestamp)); + }); + }, + "count": function (next) { + db.sortedSetCard('pid:' + pid + ':replies', next); + } + }, function (err, replies) { + if (replies.users.length > 5) { + replies.users.shift(); + replies.hasMore = true; + } + + next(err, replies); + }); + }); + }); + }, callback); + } }; From 575a3c7b9d0d69dd555268d1defc09675d3e4740 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Wed, 8 Feb 2017 16:39:44 -0500 Subject: [PATCH 108/316] no need for a call to count replies --- src/topics/posts.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/topics/posts.js b/src/topics/posts.js index 7f936b9b0d..96b1992fc9 100644 --- a/src/topics/posts.js +++ b/src/topics/posts.js @@ -413,9 +413,6 @@ module.exports = function (Topics) { posts.getPostField(replyPids[0], 'timestamp', function (err, timestamp) { next(err, utils.toISOString(timestamp)); }); - }, - "count": function (next) { - db.sortedSetCard('pid:' + pid + ':replies', next); } }, function (err, replies) { if (replies.users.length > 5) { @@ -423,6 +420,8 @@ module.exports = function (Topics) { replies.hasMore = true; } + replies.count = replyPids.length; + next(err, replies); }); }); From 4f02775bdfb7a356a594d9733c4344304c475659 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 9 Feb 2017 14:30:02 +0300 Subject: [PATCH 109/316] replies change --- src/topics/posts.js | 76 +++++++++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 31 deletions(-) diff --git a/src/topics/posts.js b/src/topics/posts.js index 96b1992fc9..9d7ea65828 100644 --- a/src/topics/posts.js +++ b/src/topics/posts.js @@ -389,43 +389,57 @@ module.exports = function (Topics) { }; function getPostReplies(pids, callerUid, callback) { - async.map(pids, function (pid, next) { - db.getSortedSetRange('pid:' + pid + ':replies', 0, -1, function (err, replyPids) { - var uids = [], count = 0; - - async.until(function () { - return count === replyPids.length || uids.length === 6; - }, function (next) { - posts.getPostField(replyPids[count], 'uid', function (err, uid) { - uid = parseInt(uid, 10); - if (uids.indexOf(uid) === -1) { - uids.push(uid); - } - count++; - next(err); - }); - }, function (err) { + var uids = []; + var count = 0; + var replyPids; + async.map(pids, function (pid, _next) { + async.waterfall([ + function (next) { + db.getSortedSetRange('pid:' + pid + ':replies', 0, -1, next); + }, + function (_replyPids, next) { + replyPids = _replyPids; + if (!replyPids.length) { + return _next(null, {count: 0}); + } + async.until(function () { + return count === replyPids.length || uids.length === 6; + }, function (next) { + posts.getPostField(replyPids[count], 'uid', function (err, uid) { + if (err) { + return next(err); + } + uid = parseInt(uid, 10); + if (uids.indexOf(uid) === -1) { + uids.push(uid); + } + count++; + next(); + }); + }, next); + }, + function (next) { async.parallel({ - "users": function (next) { + users: function (next) { user.getUsersWithFields(uids, ['uid', 'username', 'userslug', 'picture'], callerUid, next); }, - "timestampISO": function (next) { - posts.getPostField(replyPids[0], 'timestamp', function (err, timestamp) { - next(err, utils.toISOString(timestamp)); - }); - } - }, function (err, replies) { - if (replies.users.length > 5) { - replies.users.shift(); - replies.hasMore = true; + timestampISO: function (next) { + posts.getPostField(replyPids[replyPids.length - 1], 'timestamp', next); } + }, next); + }, + function (replies, next) { + if (replies.users.length > 5) { + replies.users.shift(); + replies.hasMore = true; + } - replies.count = replyPids.length; + replies.count = replyPids.length; + replies.timestampISO = utils.toISOString(replies.timestampISO); - next(err, replies); - }); - }); - }); + next(null, replies); + } + ], next); }, callback); } }; From 21861fa9311338b963dd67e0721094386751cce0 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 9 Feb 2017 14:31:29 +0300 Subject: [PATCH 110/316] fix next --- src/topics/posts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/topics/posts.js b/src/topics/posts.js index 9d7ea65828..797450b8f5 100644 --- a/src/topics/posts.js +++ b/src/topics/posts.js @@ -439,7 +439,7 @@ module.exports = function (Topics) { next(null, replies); } - ], next); + ], _next); }, callback); } }; From ae5deff3d5b514db30bf292fb120e906e65fb0b3 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 9 Feb 2017 14:36:00 +0300 Subject: [PATCH 111/316] fix uids --- src/topics/posts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/topics/posts.js b/src/topics/posts.js index 797450b8f5..0a5fc4c671 100644 --- a/src/topics/posts.js +++ b/src/topics/posts.js @@ -389,10 +389,10 @@ module.exports = function (Topics) { }; function getPostReplies(pids, callerUid, callback) { - var uids = []; var count = 0; var replyPids; async.map(pids, function (pid, _next) { + var uids = []; async.waterfall([ function (next) { db.getSortedSetRange('pid:' + pid + ':replies', 0, -1, next); From 2c527cdfeb5acbdc41e79911418824a3ac531f26 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 9 Feb 2017 14:38:06 +0300 Subject: [PATCH 112/316] more fixes --- src/topics/posts.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/topics/posts.js b/src/topics/posts.js index 0a5fc4c671..37ea83b2cf 100644 --- a/src/topics/posts.js +++ b/src/topics/posts.js @@ -389,10 +389,10 @@ module.exports = function (Topics) { }; function getPostReplies(pids, callerUid, callback) { - var count = 0; - var replyPids; async.map(pids, function (pid, _next) { + var replyPids; var uids = []; + var count = 0; async.waterfall([ function (next) { db.getSortedSetRange('pid:' + pid + ':replies', 0, -1, next); From b9244110d90e783dc0ffd6bb8a03759f26054410 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 9 Feb 2017 17:29:28 +0300 Subject: [PATCH 113/316] up dbsearch --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a37e9435bc..75a330a271 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "mousetrap": "^1.5.3", "nconf": "~0.8.2", "nodebb-plugin-composer-default": "4.4.0", - "nodebb-plugin-dbsearch": "2.0.1", + "nodebb-plugin-dbsearch": "2.0.2", "nodebb-plugin-emoji-extended": "1.1.1", "nodebb-plugin-emoji-one": "1.1.5", "nodebb-plugin-markdown": "7.1.0", From b1511a18709c0e4d32031bb6b78f0bee961a5a5c Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 9 Feb 2017 21:15:43 +0300 Subject: [PATCH 114/316] change client route --- src/controllers/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/index.js b/src/controllers/index.js index 7b97c878d6..c595b2e1da 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -353,7 +353,7 @@ Controllers.ping = function (req, res) { Controllers.handle404 = function (req, res) { var relativePath = nconf.get('relative_path'); - var isClientScript = new RegExp('^' + relativePath + '\\/src\\/.+\\.js'); + var isClientScript = new RegExp('^' + relativePath + '\\/assets\\/src\\/.+\\.js'); if (plugins.hasListeners('action:meta.override404')) { return plugins.fireHook('action:meta.override404', { From 9e354a09d18c5fa57f28f396399859878969af8f Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 13 Feb 2017 15:28:26 -0500 Subject: [PATCH 115/316] prettier stats on dashboard --- public/less/admin/general/dashboard.less | 13 +++++++++++++ public/src/admin/general/dashboard.js | 16 ++++++++-------- src/views/admin/general/dashboard.tpl | 11 +++++------ 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/public/less/admin/general/dashboard.less b/public/less/admin/general/dashboard.less index c8b704707b..12477d9142 100644 --- a/public/less/admin/general/dashboard.less +++ b/public/less/admin/general/dashboard.less @@ -136,4 +136,17 @@ .motd textarea { width: 100%; } + + .stats { + .formatted-number { + font-size: 22px; + } + + .stat { + text-transform: uppercase; + font-weight: 600; + font-size: 10px; + color: #999; + } + } } \ No newline at end of file diff --git a/public/src/admin/general/dashboard.js b/public/src/admin/general/dashboard.js index 9a5faede09..07e566ca68 100644 --- a/public/src/admin/general/dashboard.js +++ b/public/src/admin/general/dashboard.js @@ -94,20 +94,20 @@ define('admin/general/dashboard', ['semver', 'Chart', 'translator'], function (s graphData.rooms = data; var html = '
    ' + - '
    ' + data.onlineRegisteredCount + '
    ' + - '
    [[admin/general/dashboard:active-users.users]]
    ' + + '' + data.onlineRegisteredCount + '' + + '
    [[admin/general/dashboard:active-users.users]]
    ' + '
    ' + '
    ' + - '
    ' + data.onlineGuestCount + '
    ' + - '
    [[admin/general/dashboard:active-users.guests]]
    ' + + '' + data.onlineGuestCount + '' + + '
    [[admin/general/dashboard:active-users.guests]]
    ' + '
    ' + '
    ' + - '
    ' + (data.onlineRegisteredCount + data.onlineGuestCount) + '
    ' + - '
    [[admin/general/dashboard:active-users.total]]
    ' + + '' + (data.onlineRegisteredCount + data.onlineGuestCount) + '' + + '
    [[admin/general/dashboard:active-users.total]]
    ' + '
    ' + '
    ' + - '
    ' + data.socketCount + '
    ' + - '
    [[admin/general/dashboard:active-users.connections]]
    ' + + '' + data.socketCount + '' + + '
    [[admin/general/dashboard:active-users.connections]]
    ' + '
    '; updateRegisteredGraph(data.onlineRegisteredCount, data.onlineGuestCount); diff --git a/src/views/admin/general/dashboard.tpl b/src/views/admin/general/dashboard.tpl index cc1d8d97ce..62502ea005 100644 --- a/src/views/admin/general/dashboard.tpl +++ b/src/views/admin/general/dashboard.tpl @@ -29,25 +29,25 @@
    -
    +
    {stats.name}
    {stats.day} -
    [[admin/general/dashboard:stats.day]]
    +
    [[admin/general/dashboard:stats.day]]
    {stats.week} -
    [[admin/general/dashboard:stats.week]]
    +
    [[admin/general/dashboard:stats.week]]
    {stats.month} -
    [[admin/general/dashboard:stats.month]]
    +
    [[admin/general/dashboard:stats.month]]
    {stats.alltime} -
    [[admin/general/dashboard:stats.all]]
    +
    [[admin/general/dashboard:stats.all]]
    @@ -115,7 +115,6 @@
    [[admin/general/dashboard:active-users]]
    -
    From 5e35170805d5f2e6b831250ab6166313406681ee Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 13 Feb 2017 15:28:56 -0500 Subject: [PATCH 116/316] ghfw :rage1: --- src/views/admin/general/dashboard.tpl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/admin/general/dashboard.tpl b/src/views/admin/general/dashboard.tpl index 62502ea005..00f3b31149 100644 --- a/src/views/admin/general/dashboard.tpl +++ b/src/views/admin/general/dashboard.tpl @@ -115,6 +115,7 @@
    [[admin/general/dashboard:active-users]]
    +
    From 62df8edb77188979c4f070564d194dc426aa5b70 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 13 Feb 2017 15:36:20 -0500 Subject: [PATCH 117/316] better positioning of dashboard legend --- public/less/admin/general/dashboard.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/less/admin/general/dashboard.less b/public/less/admin/general/dashboard.less index 12477d9142..72fbe49e2c 100644 --- a/public/less/admin/general/dashboard.less +++ b/public/less/admin/general/dashboard.less @@ -55,8 +55,8 @@ .box-header-font; display: block; position: absolute; - top: 20px; - left: 35px; + top: 19px; + left: 50px; list-style-type: none; padding: 0; From 3d1425605a080a40c1dabde7670f4f96b7901568 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 13 Feb 2017 15:40:20 -0500 Subject: [PATCH 118/316] prettification of pageview stats --- public/less/admin/general/dashboard.less | 6 +++++- src/views/admin/general/dashboard.tpl | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/public/less/admin/general/dashboard.less b/public/less/admin/general/dashboard.less index 72fbe49e2c..585d81f1d2 100644 --- a/public/less/admin/general/dashboard.less +++ b/public/less/admin/general/dashboard.less @@ -129,8 +129,12 @@ } } - .monthly-pageviews { + .pageview-stats { width:33%; + + strong { + font-size: 22px; + } } .motd textarea { diff --git a/src/views/admin/general/dashboard.tpl b/src/views/admin/general/dashboard.tpl index 00f3b31149..ee617ed0e7 100644 --- a/src/views/admin/general/dashboard.tpl +++ b/src/views/admin/general/dashboard.tpl @@ -11,15 +11,15 @@

    -
    + -
    + -
    + From 0667870286b49d5d3177f8086c4055ef25c59d65 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 13 Feb 2017 15:50:12 -0500 Subject: [PATCH 119/316] set an active state for pageview options --- public/less/admin/general/dashboard.less | 4 ++++ public/src/admin/general/dashboard.js | 2 ++ src/views/admin/general/dashboard.tpl | 6 +++--- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/public/less/admin/general/dashboard.less b/public/less/admin/general/dashboard.less index 585d81f1d2..12e542e32a 100644 --- a/public/less/admin/general/dashboard.less +++ b/public/less/admin/general/dashboard.less @@ -153,4 +153,8 @@ color: #999; } } + + .updatePageviewsGraph.active { + font-weight: bold; + } } \ No newline at end of file diff --git a/public/src/admin/general/dashboard.js b/public/src/admin/general/dashboard.js index 07e566ca68..5a582abbda 100644 --- a/public/src/admin/general/dashboard.js +++ b/public/src/admin/general/dashboard.js @@ -296,6 +296,8 @@ define('admin/general/dashboard', ['semver', 'Chart', 'translator'], function (s until = lastMonth.getTime(); } updateTrafficGraph($(this).attr('data-units'), until); + $('[data-action="updateGraph"]').removeClass('active'); + $(this).addClass('active'); }); socket.emit('admin.rooms.getAll', Admin.updateRoomUsage); diff --git a/src/views/admin/general/dashboard.tpl b/src/views/admin/general/dashboard.tpl index ee617ed0e7..d904637fe4 100644 --- a/src/views/admin/general/dashboard.tpl +++ b/src/views/admin/general/dashboard.tpl @@ -13,15 +13,15 @@
    From 36bf54a4b8a3e5eabfff8eb6d35c10e6536d62d3 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 16 Feb 2017 18:28:37 -0500 Subject: [PATCH 120/316] bump tjs --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 75a330a271..a7aaedbec4 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "socket.io-redis": "3.1.0", "socketio-wildcard": "~0.3.0", "string": "^3.0.0", - "templates.js": "0.3.6", + "templates.js": "0.3.10", "toobusy-js": "^0.5.1", "uglify-js": "^2.6.0", "underscore": "^1.8.3", From 5b8c9503c314aac3eece30c6abca98c634895a98 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 22 Feb 2017 13:20:47 -0500 Subject: [PATCH 121/316] refactor upgrade scripts to use individual files in src/upgrades/ as source of schema changes --- app.js | 16 +- nodebb | 12 + src/upgrade.js | 463 ++---------------- src/upgrades/20160922-category-recent-tids.js | 33 ++ .../20161008-favourites-to-bookmarks.js | 52 ++ .../20161014-sorted-sets-for-post-replies.js | 34 ++ .../20161122-global-and-user-language-keys.js | 63 +++ .../20161125-sorted-set-for-pinned-topics.js | 37 ++ src/upgrades/20161207-flags-refactor.js | 89 ++++ 9 files changed, 377 insertions(+), 422 deletions(-) create mode 100644 src/upgrades/20160922-category-recent-tids.js create mode 100644 src/upgrades/20161008-favourites-to-bookmarks.js create mode 100644 src/upgrades/20161014-sorted-sets-for-post-replies.js create mode 100644 src/upgrades/20161122-global-and-user-language-keys.js create mode 100644 src/upgrades/20161125-sorted-set-for-pinned-topics.js create mode 100644 src/upgrades/20161207-flags-refactor.js diff --git a/app.js b/app.js index 57c57d61b6..271e16fee5 100644 --- a/app.js +++ b/app.js @@ -189,13 +189,17 @@ function upgrade() { var meta = require('./src/meta'); var upgrade = require('./src/upgrade'); var build = require('./src/meta/build'); + var tasks = [db.init, meta.configs.init, upgrade.run, build.buildAll]; - async.series([ - async.apply(db.init), - async.apply(meta.configs.init), - async.apply(upgrade.upgrade), - async.apply(build.buildAll) - ], function (err) { + if (nconf.get('upgrade') !== true) { + // Likely an upgrade script name passed in + tasks[2] = async.apply(upgrade.runSingle, nconf.get('upgrade')); + + // Skip build + tasks.pop(); + } + + async.series(tasks, function (err) { if (err) { winston.error(err.stack); process.exit(1); diff --git a/nodebb b/nodebb index ee87b24288..8ee1628855 100755 --- a/nodebb +++ b/nodebb @@ -406,6 +406,18 @@ var commands = { description: 'Run NodeBB upgrade scripts, ensure packages are up-to-date', usage: 'Usage: ' + './nodebb upgrade'.yellow, handler: function () { + if (process.argv[3]) { + process.stdout.write('\nUpdating NodeBB data store schema...\n'.yellow); + var arr = ['--upgrade'].concat(process.argv.slice(3)); + var upgradeProc = fork(arr); + + return upgradeProc.on('close', function (err) { + if (err) { + process.stdout.write('\nError'.red + ': ' + err.message + '\n'); + } + }); + } + async.series([ function (next) { process.stdout.write('1. '.bold + 'Bringing base dependencies up to date... '.yellow); diff --git a/src/upgrade.js b/src/upgrade.js index 12939ad99d..1aee44d060 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -1,443 +1,74 @@ -"use strict"; +/* jslint node: true */ +'use strict'; -/* globals console, require */ - -var db = require('./database'); var async = require('async'); -var winston = require('winston'); +var path = require('path'); -var Upgrade = {}; +var utils = require('../public/src/utils'); -var minSchemaDate = Date.UTC(2016, 8, 7); // This value gets updated every new MAJOR version -var schemaDate; -var thisSchemaDate; +var Upgrade = {}; -// IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema -var latestSchema = Date.UTC(2016, 11, 7); +Upgrade.run = function (callback) { + process.stdout.write('\nParsing upgrade scripts... '); -Upgrade.check = function (callback) { - db.get('schemaDate', function (err, value) { + utils.walk(path.join(__dirname, './upgrades'), function (err, files) { if (err) { return callback(err); } - if (!value) { - db.set('schemaDate', latestSchema, function (err) { - if (err) { - return callback(err); - } - callback(null); - }); - return; - } - - var schema_ok = parseInt(value, 10) >= latestSchema; - callback(!schema_ok ? new Error('schema-out-of-date') : null); + Upgrade.process(files, callback); }); }; -Upgrade.update = function (schemaDate, callback) { - db.set('schemaDate', schemaDate, callback); -}; - -Upgrade.upgrade = function (callback) { - var updatesMade = false; - - winston.info('Beginning database schema update'); - - async.series([ - function (next) { - // Prepare for upgrade & check to make sure the upgrade is possible - db.get('schemaDate', function (err, value) { - if (err) { - return next(err); - } - - if(!value) { - db.set('schemaDate', latestSchema, function () { - next(); - }); - schemaDate = latestSchema; - } else { - schemaDate = parseInt(value, 10); - } - - if (schemaDate >= minSchemaDate) { - next(); - } else { - next(new Error('upgrade-not-possible')); - } - }); - }, - function (next) { - thisSchemaDate = Date.UTC(2016, 8, 22); - - if (schemaDate < thisSchemaDate) { - updatesMade = true; - winston.info('[2016/09/22] Setting category recent tids'); - - - db.getSortedSetRange('categories:cid', 0, -1, function (err, cids) { - if (err) { - return next(err); - } - - async.eachSeries(cids, function (cid, next) { - db.getSortedSetRevRange('cid:' + cid + ':pids', 0, 0, function (err, pid) { - if (err || !pid) { - return next(err); - } - db.getObjectFields('post:' + pid, ['tid', 'timestamp'], function (err, postData) { - if (err || !postData || !postData.tid) { - return next(err); - } - db.sortedSetAdd('cid:' + cid + ':recent_tids', postData.timestamp, postData.tid, next); - }); - }); - }, function (err) { - if (err) { - return next(err); - } - - winston.info('[2016/09/22] Setting category recent tids - done'); - Upgrade.update(thisSchemaDate, next); - }); - }); - } else { - winston.info('[2016/09/22] Setting category recent tids - skipped!'); - next(); - } - }, - function (next) { - function upgradePosts(next) { - var batch = require('./batch'); - - batch.processSortedSet('posts:pid', function (ids, next) { - async.each(ids, function (id, next) { - console.log('processing pid ' + id); - async.waterfall([ - function (next) { - db.rename('pid:' + id + ':users_favourited', 'pid:' + id + ':users_bookmarked', next); - }, - function (next) { - db.getObjectField('post:' + id, 'reputation', next); - }, - function (reputation, next) { - if (parseInt(reputation, 10)) { - db.setObjectField('post:' + id, 'bookmarks', reputation, next); - } else { - next(); - } - }, - function (next) { - db.deleteObjectField('post:' + id, 'reputation', next); - } - ], next); - }, next); - }, {}, next); - } - - function upgradeUsers(next) { - var batch = require('./batch'); - - batch.processSortedSet('users:joindate', function (ids, next) { - async.each(ids, function (id, next) { - console.log('processing uid ' + id); - db.rename('uid:' + id + ':favourites', 'uid:' + id + ':bookmarks', next); - }, next); - }, {}, next); - } - - thisSchemaDate = Date.UTC(2016, 9, 8); - - if (schemaDate < thisSchemaDate) { - updatesMade = true; - winston.info('[2016/10/8] favourite -> bookmark refactor'); - async.series([upgradePosts, upgradeUsers], function (err) { - if (err) { - return next(err); - } - winston.info('[2016/08/05] favourite- bookmark refactor done!'); - Upgrade.update(thisSchemaDate, next); - }); - } else { - winston.info('[2016/10/8] favourite -> bookmark refactor - skipped!'); - next(); - } - }, - function (next) { - thisSchemaDate = Date.UTC(2016, 9, 14); - - if (schemaDate < thisSchemaDate) { - updatesMade = true; - winston.info('[2016/10/14] Creating sorted sets for post replies'); - - var posts = require('./posts'); - var batch = require('./batch'); - batch.processSortedSet('posts:pid', function (ids, next) { - posts.getPostsFields(ids, ['pid', 'toPid', 'timestamp'], function (err, data) { - if (err) { - return next(err); - } - - async.eachSeries(data, function (postData, next) { - if (!parseInt(postData.toPid, 10)) { - return next(null); - } - console.log('processing pid: ' + postData.pid + ' toPid: ' + postData.toPid); - async.parallel([ - async.apply(db.sortedSetAdd, 'pid:' + postData.toPid + ':replies', postData.timestamp, postData.pid), - async.apply(db.incrObjectField, 'post:' + postData.toPid, 'replies') - ], next); - }, next); - }); - }, function (err) { - if (err) { - return next(err); - } - - winston.info('[2016/10/14] Creating sorted sets for post replies - done'); - Upgrade.update(thisSchemaDate, next); - }); - } else { - winston.info('[2016/10/14] Creating sorted sets for post replies - skipped!'); - next(); - } - }, - function (next) { - thisSchemaDate = Date.UTC(2016, 10, 22); - - if (schemaDate < thisSchemaDate) { - updatesMade = true; - winston.info('[2016/11/22] Update global and user language keys'); - - var user = require('./user'); - var meta = require('./meta'); - var batch = require('./batch'); - var newLanguage; - var i = 0; - var j = 0; - async.parallel([ - function (next) { - meta.configs.get('defaultLang', function (err, defaultLang) { - if (err) { - return next(err); - } - - if (!defaultLang) { - return setImmediate(next); - } - - newLanguage = defaultLang.replace('_', '-').replace('@', '-x-'); - if (newLanguage !== defaultLang) { - meta.configs.set('defaultLang', newLanguage, next); - } else { - setImmediate(next); - } - }); - }, - function (next) { - batch.processSortedSet('users:joindate', function (ids, next) { - async.each(ids, function (uid, next) { - async.waterfall([ - async.apply(db.getObjectField, 'user:' + uid + ':settings', 'userLang'), - function (language, next) { - ++i; - if (!language) { - return setImmediate(next); - } - - newLanguage = language.replace('_', '-').replace('@', '-x-'); - if (newLanguage !== language) { - ++j; - user.setSetting(uid, 'userLang', newLanguage, next); - } else { - setImmediate(next); - } - } - ], next); - }, next); - }, next); - } - ], function (err) { - if (err) { - return next(err); - } - - winston.info('[2016/11/22] Update global and user language keys - done (' + i + ' processed, ' + j + ' updated)'); - Upgrade.update(thisSchemaDate, next); - }); - } else { - winston.info('[2016/11/22] Update global and user language keys - skipped!'); - next(); - } - }, - function (next) { - thisSchemaDate = Date.UTC(2016, 10, 25); - - if (schemaDate < thisSchemaDate) { - updatesMade = true; - winston.info('[2016/11/25] Creating sorted sets for pinned topics'); - - var topics = require('./topics'); - var batch = require('./batch'); - batch.processSortedSet('topics:tid', function (ids, next) { - topics.getTopicsFields(ids, ['tid', 'cid', 'pinned', 'lastposttime'], function (err, data) { - if (err) { - return next(err); - } - - data = data.filter(function (topicData) { - return parseInt(topicData.pinned, 10) === 1; - }); - - async.eachSeries(data, function (topicData, next) { - console.log('processing tid: ' + topicData.tid); - - async.parallel([ - async.apply(db.sortedSetAdd, 'cid:' + topicData.cid + ':tids:pinned', Date.now(), topicData.tid), - async.apply(db.sortedSetRemove, 'cid:' + topicData.cid + ':tids', topicData.tid), - async.apply(db.sortedSetRemove, 'cid:' + topicData.cid + ':tids:posts', topicData.tid) - ], next); - }, next); - }); - }, function (err) { - if (err) { - return next(err); - } - - winston.info('[2016/11/25] Creating sorted sets for pinned topics - done'); - Upgrade.update(thisSchemaDate, next); - }); - } else { - winston.info('[2016/11/25] Creating sorted sets for pinned topics - skipped!'); - next(); - } - }, - function (next) { - thisSchemaDate = Date.UTC(2016, 11, 7); +Upgrade.runSingle = function (query, callback) { + process.stdout.write('\nParsing upgrade scripts... '); - if (schemaDate < thisSchemaDate) { - updatesMade = true; - winston.info('[2016/12/07] Migrating flags to new schema (#5232)'); - - var batch = require('./batch'); - var posts = require('./posts'); - var flags = require('./flags'); - var migrated = 0; - - batch.processSortedSet('posts:pid', function (ids, next) { - posts.getPostsByPids(ids, 1, function (err, posts) { - if (err) { - return next(err); - } + async.waterfall([ + async.apply(utils.walk, path.join(__dirname, './upgrades')), + function (files, next) { + next(null, files.filter(function (file) { + return file.search(new RegExp(query)) !== -1; + })); + } + ], function (err, files) { + if (err) { + return callback(err); + } - posts = posts.filter(function (post) { - return post.hasOwnProperty('flags'); - }); - - async.each(posts, function (post, next) { - async.parallel({ - uids: async.apply(db.getSortedSetRangeWithScores, 'pid:' + post.pid + ':flag:uids', 0, -1), - reasons: async.apply(db.getSortedSetRange, 'pid:' + post.pid + ':flag:uid:reason', 0, -1) - }, function (err, data) { - if (err) { - return next(err); - } + Upgrade.process(files, callback); + }); +}; - // Adding in another check here in case a post was improperly dismissed (flag count > 1 but no flags in db) - if (!data.uids.length || !data.reasons.length) { - return setImmediate(next); - } +Upgrade.process = function (files, callback) { + process.stdout.write('OK'.green + String(' ' + files.length).cyan + ' script(s) found\n'.cyan); - // Just take the first entry - var datetime = data.uids[0].score; - var reason = data.reasons[0].split(':')[1]; - var flagObj; + // Do I need to sort the files here? we'll see. + // sort(); - async.waterfall([ - async.apply(flags.create, 'post', post.pid, data.uids[0].value, reason, datetime), - function (_flagObj, next) { - flagObj = _flagObj; - if (post['flag:state'] || post['flag:assignee']) { - flags.update(flagObj.flagId, 1, { - state: post['flag:state'], - assignee: post['flag:assignee'], - datetime: datetime - }, next); - } else { - setImmediate(next); - } - }, - function (next) { - if (post.hasOwnProperty('flag:notes') && post['flag:notes'].length) { - try { - var history = JSON.parse(post['flag:history']); - history = history.filter(function (event) { - return event.type === 'notes'; - })[0]; + async.eachSeries(files, function (file, next) { + var scriptExport = require(file); + var date = new Date(scriptExport.timestamp); - flags.appendNote(flagObj.flagId, history.uid, post['flag:notes'], history.timestamp, next); - } catch (e) { - next(e); - } - } else { - setImmediate(next); - } - } - ], function (err) { - if (err && err.message === '[[error:already-flagged]]') { - // Already flagged, no need to parse, but not an error - next(); - } else { - next(err); - } - }); - }); - }, next); - }); - }, function (err) { - if (err) { - return next(err); - } + process.stdout.write(' → '.white + String('[' + [date.getFullYear(), date.getMonth() + 1, date.getDate() + 1].join('/') + '] ').gray + String(scriptExport.name).reset + '... '); - winston.info('[2016/12/07] Migrating flags to new schema (#5232) - done'); - Upgrade.update(thisSchemaDate, next); - }); - } else { - winston.info('[2016/12/07] Migrating flags to new schema (#5232) - skipped!'); - next(); - } - } - // Add new schema updates here - // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 24!!! - ], function (err) { - if (!err) { - if(updatesMade) { - winston.info('[upgrade] Schema update complete!'); - } else { - winston.info('[upgrade] Schema already up to date!'); + // Do the upgrade... + scriptExport.method(function (err) { + if (err) { + process.stdout.write('error\n'.red); + return next(err); } - } else { - switch(err.message) { - case 'upgrade-not-possible': - winston.error('[upgrade] NodeBB upgrade could not complete, as your database schema is too far out of date.'); - winston.error('[upgrade] Please ensure that you did not skip any minor version upgrades.'); - winston.error('[upgrade] (e.g. v0.1.x directly to v0.3.x)'); - break; - default: - winston.error('[upgrade] Errors were encountered while updating the NodeBB schema: ' + err.message); - break; - } + process.stdout.write('OK\n'.green); + next(); + }); + }, function (err) { + if (err) { + return callback(err); } - if (typeof callback === 'function') { - callback(err); - } else { - process.exit(); - } + process.stdout.write('Upgrade complete!\n\n'.green); + callback(); }); }; -module.exports = Upgrade; +module.exports = Upgrade; \ No newline at end of file diff --git a/src/upgrades/20160922-category-recent-tids.js b/src/upgrades/20160922-category-recent-tids.js new file mode 100644 index 0000000000..ccb6f5c818 --- /dev/null +++ b/src/upgrades/20160922-category-recent-tids.js @@ -0,0 +1,33 @@ +/* jslint node: true */ +'use strict'; + +var db = require('../database'); + +var async = require('async'); +var winston = require('winston'); + +module.exports = { + name: 'Category recent tids', + timestamp: Date.UTC(2016, 8, 22), + method: function (callback) { + db.getSortedSetRange('categories:cid', 0, -1, function (err, cids) { + if (err) { + return callback(err); + } + + async.eachSeries(cids, function (cid, next) { + db.getSortedSetRevRange('cid:' + cid + ':pids', 0, 0, function (err, pid) { + if (err || !pid) { + return next(err); + } + db.getObjectFields('post:' + pid, ['tid', 'timestamp'], function (err, postData) { + if (err || !postData || !postData.tid) { + return next(err); + } + db.sortedSetAdd('cid:' + cid + ':recent_tids', postData.timestamp, postData.tid, next); + }); + }); + }, callback); + }); + } +}; \ No newline at end of file diff --git a/src/upgrades/20161008-favourites-to-bookmarks.js b/src/upgrades/20161008-favourites-to-bookmarks.js new file mode 100644 index 0000000000..288d32c511 --- /dev/null +++ b/src/upgrades/20161008-favourites-to-bookmarks.js @@ -0,0 +1,52 @@ +/* jslint node: true */ +'use strict'; + +var db = require('../database'); + +var async = require('async'); +var winston = require('winston'); + +module.exports = { + name: 'Favourites to Bookmarks', + timestamp: Date.UTC(2016, 9, 8), + method: function (callback) { + function upgradePosts(next) { + var batch = require('../batch'); + + batch.processSortedSet('posts:pid', function (ids, next) { + async.each(ids, function (id, next) { + async.waterfall([ + function (next) { + db.rename('pid:' + id + ':users_favourited', 'pid:' + id + ':users_bookmarked', next); + }, + function (next) { + db.getObjectField('post:' + id, 'reputation', next); + }, + function (reputation, next) { + if (parseInt(reputation, 10)) { + db.setObjectField('post:' + id, 'bookmarks', reputation, next); + } else { + next(); + } + }, + function (next) { + db.deleteObjectField('post:' + id, 'reputation', next); + } + ], next); + }, next); + }, {}, next); + } + + function upgradeUsers(next) { + var batch = require('../batch'); + + batch.processSortedSet('users:joindate', function (ids, next) { + async.each(ids, function (id, next) { + db.rename('uid:' + id + ':favourites', 'uid:' + id + ':bookmarks', next); + }, next); + }, {}, next); + } + + async.series([upgradePosts, upgradeUsers], callback); + } +}; \ No newline at end of file diff --git a/src/upgrades/20161014-sorted-sets-for-post-replies.js b/src/upgrades/20161014-sorted-sets-for-post-replies.js new file mode 100644 index 0000000000..7442b614d4 --- /dev/null +++ b/src/upgrades/20161014-sorted-sets-for-post-replies.js @@ -0,0 +1,34 @@ +/* jslint node: true */ +'use strict'; + +var db = require('../database'); + +var async = require('async'); +var winston = require('winston'); + +module.exports = { + name: 'Sorted sets for post replies', + timestamp: Date.UTC(2016, 9, 14), + method: function (callback) { + var posts = require('../posts'); + var batch = require('../batch'); + batch.processSortedSet('posts:pid', function (ids, next) { + posts.getPostsFields(ids, ['pid', 'toPid', 'timestamp'], function (err, data) { + if (err) { + return next(err); + } + + async.eachSeries(data, function (postData, next) { + if (!parseInt(postData.toPid, 10)) { + return next(null); + } + winston.verbose('processing pid: ' + postData.pid + ' toPid: ' + postData.toPid); + async.parallel([ + async.apply(db.sortedSetAdd, 'pid:' + postData.toPid + ':replies', postData.timestamp, postData.pid), + async.apply(db.incrObjectField, 'post:' + postData.toPid, 'replies') + ], next); + }, next); + }); + }, callback); + } +}; \ No newline at end of file diff --git a/src/upgrades/20161122-global-and-user-language-keys.js b/src/upgrades/20161122-global-and-user-language-keys.js new file mode 100644 index 0000000000..d08b3fea74 --- /dev/null +++ b/src/upgrades/20161122-global-and-user-language-keys.js @@ -0,0 +1,63 @@ +/* jslint node: true */ +'use strict'; + +var db = require('../database'); + +var async = require('async'); +var winston = require('winston'); + +module.exports = { + name: 'Update global and user language keys', + timestamp: Date.UTC(2016, 10, 22), + method: function (callback) { + var user = require('../user'); + var meta = require('../meta'); + var batch = require('../batch'); + var newLanguage; + var i = 0; + var j = 0; + async.parallel([ + function (next) { + meta.configs.get('defaultLang', function (err, defaultLang) { + if (err) { + return next(err); + } + + if (!defaultLang) { + return setImmediate(next); + } + + newLanguage = defaultLang.replace('_', '-').replace('@', '-x-'); + if (newLanguage !== defaultLang) { + meta.configs.set('defaultLang', newLanguage, next); + } else { + setImmediate(next); + } + }); + }, + function (next) { + batch.processSortedSet('users:joindate', function (ids, next) { + async.each(ids, function (uid, next) { + async.waterfall([ + async.apply(db.getObjectField, 'user:' + uid + ':settings', 'userLang'), + function (language, next) { + ++i; + if (!language) { + return setImmediate(next); + } + + newLanguage = language.replace('_', '-').replace('@', '-x-'); + if (newLanguage !== language) { + ++j; + user.setSetting(uid, 'userLang', newLanguage, next); + } else { + setImmediate(next); + } + } + ], next); + }, next); + }, next); + } + ], callback); + } +}; \ No newline at end of file diff --git a/src/upgrades/20161125-sorted-set-for-pinned-topics.js b/src/upgrades/20161125-sorted-set-for-pinned-topics.js new file mode 100644 index 0000000000..d10a6ee902 --- /dev/null +++ b/src/upgrades/20161125-sorted-set-for-pinned-topics.js @@ -0,0 +1,37 @@ +/* jslint node: true */ +'use strict'; + +var db = require('../database'); + +var async = require('async'); +var winston = require('winston'); + +module.exports = { + name: 'Sorted set for pinned topics', + timestamp: Date.UTC(2016, 10, 25), + method: function (callback) { + var topics = require('../topics'); + var batch = require('../batch'); + batch.processSortedSet('topics:tid', function (ids, next) { + topics.getTopicsFields(ids, ['tid', 'cid', 'pinned', 'lastposttime'], function (err, data) { + if (err) { + return next(err); + } + + data = data.filter(function (topicData) { + return parseInt(topicData.pinned, 10) === 1; + }); + + async.eachSeries(data, function (topicData, next) { + winston.verbose('processing tid: ' + topicData.tid); + + async.parallel([ + async.apply(db.sortedSetAdd, 'cid:' + topicData.cid + ':tids:pinned', Date.now(), topicData.tid), + async.apply(db.sortedSetRemove, 'cid:' + topicData.cid + ':tids', topicData.tid), + async.apply(db.sortedSetRemove, 'cid:' + topicData.cid + ':tids:posts', topicData.tid) + ], next); + }, next); + }); + }, callback); + } +}; \ No newline at end of file diff --git a/src/upgrades/20161207-flags-refactor.js b/src/upgrades/20161207-flags-refactor.js new file mode 100644 index 0000000000..c61a3a4747 --- /dev/null +++ b/src/upgrades/20161207-flags-refactor.js @@ -0,0 +1,89 @@ +/* jslint node: true */ +'use strict'; + +var db = require('../database'); + +var async = require('async'); + +module.exports = { + name: 'Migrating flags to new schema', + timestamp: Date.UTC(2016, 11, 7), + method: function (callback) { + var batch = require('../batch'); + var posts = require('../posts'); + var flags = require('../flags'); + var migrated = 0; + + batch.processSortedSet('posts:pid', function (ids, next) { + posts.getPostsByPids(ids, 1, function (err, posts) { + if (err) { + return next(err); + } + + posts = posts.filter(function (post) { + return post.hasOwnProperty('flags'); + }); + + async.each(posts, function (post, next) { + async.parallel({ + uids: async.apply(db.getSortedSetRangeWithScores, 'pid:' + post.pid + ':flag:uids', 0, -1), + reasons: async.apply(db.getSortedSetRange, 'pid:' + post.pid + ':flag:uid:reason', 0, -1) + }, function (err, data) { + if (err) { + return next(err); + } + + // Adding in another check here in case a post was improperly dismissed (flag count > 1 but no flags in db) + if (!data.uids.length || !data.reasons.length) { + return setImmediate(next); + } + + // Just take the first entry + var datetime = data.uids[0].score; + var reason = data.reasons[0].split(':')[1]; + var flagObj; + + async.waterfall([ + async.apply(flags.create, 'post', post.pid, data.uids[0].value, reason, datetime), + function (_flagObj, next) { + flagObj = _flagObj; + if (post['flag:state'] || post['flag:assignee']) { + flags.update(flagObj.flagId, 1, { + state: post['flag:state'], + assignee: post['flag:assignee'], + datetime: datetime + }, next); + } else { + setImmediate(next); + } + }, + function (next) { + if (post.hasOwnProperty('flag:notes') && post['flag:notes'].length) { + try { + var history = JSON.parse(post['flag:history']); + history = history.filter(function (event) { + return event.type === 'notes'; + })[0]; + + flags.appendNote(flagObj.flagId, history.uid, post['flag:notes'], history.timestamp, next); + } catch (e) { + next(e); + } + } else { + setImmediate(next); + } + } + ], function (err) { + if (err && err.message === '[[error:already-flagged]]') { + // Already flagged, no need to parse, but not an error + next(); + } else { + next(err); + } + }); + }); + }, next); + }); + }, callback); + } +}; \ No newline at end of file From 9eed3abb1d9a47fa21741a0a7a6e09ad525e7374 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 22 Feb 2017 13:28:22 -0500 Subject: [PATCH 122/316] added template to upgrades folder --- src/upgrades/TEMPLATE | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/upgrades/TEMPLATE diff --git a/src/upgrades/TEMPLATE b/src/upgrades/TEMPLATE new file mode 100644 index 0000000000..5b79aa6148 --- /dev/null +++ b/src/upgrades/TEMPLATE @@ -0,0 +1,15 @@ +/* jslint node: true */ +'use strict'; + +var db = require('../database'); + +var async = require('async'); +var winston = require('winston'); + +module.exports = { + name: 'User_friendly_upgrade_script_name', + timestamp: Date.UTC(2017, 0, 1), + method: function (callback) { + // Do stuff here... + } +}; \ No newline at end of file From 6ea0fc4e2f358cc7b9e5350b13a4f44023e5825f Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 22 Feb 2017 15:57:31 -0500 Subject: [PATCH 123/316] changed file schema to be name only, storing them in object sets referencing their applicable versions --- src/upgrade.js | 50 ++++++++++++++++--- ...recent-tids.js => category_recent_tids.js} | 0 ...ookmarks.js => favourites_to_bookmarks.js} | 0 ...07-flags-refactor.js => flags_refactor.js} | 0 ...ys.js => global_and_user_language_keys.js} | 0 ...ics.js => sorted_set_for_pinned_topics.js} | 0 ...ies.js => sorted_sets_for_post_replies.js} | 0 7 files changed, 44 insertions(+), 6 deletions(-) rename src/upgrades/{20160922-category-recent-tids.js => category_recent_tids.js} (100%) rename src/upgrades/{20161008-favourites-to-bookmarks.js => favourites_to_bookmarks.js} (100%) rename src/upgrades/{20161207-flags-refactor.js => flags_refactor.js} (100%) rename src/upgrades/{20161122-global-and-user-language-keys.js => global_and_user_language_keys.js} (100%) rename src/upgrades/{20161125-sorted-set-for-pinned-topics.js => sorted_set_for_pinned_topics.js} (100%) rename src/upgrades/{20161014-sorted-sets-for-post-replies.js => sorted_sets_for_post_replies.js} (100%) diff --git a/src/upgrade.js b/src/upgrade.js index 1aee44d060..be303b4876 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -4,19 +4,54 @@ var async = require('async'); var path = require('path'); +var db = require('./database'); var utils = require('../public/src/utils'); -var Upgrade = {}; +var Upgrade = { + available: [ + { + version: "1.2.0", + upgrades: ['category_recent_tids'] + }, + { + version: "1.3.0", + upgrades: ['favourites_to_bookmarks', 'sorted_sets_for_post_replies'] + }, + { + version: "1.4.0", + upgrades: ['global_and_user_language_keys', 'sorted_set_for_pinned_topics'] + }, + { + version: "1.5.0", + upgrades: ['flags_refactor'] + } + ] +}; Upgrade.run = function (callback) { process.stdout.write('\nParsing upgrade scripts... '); + var queue = []; + var skipped = 0; - utils.walk(path.join(__dirname, './upgrades'), function (err, files) { + // Retrieve list of upgrades that have already been run + db.getSortedSetRange('schemaLog', 0, -1, function (err, completed) { if (err) { return callback(err); } - Upgrade.process(files, callback); + queue = Upgrade.available.reduce(function (memo, cur) { + cur.upgrades.forEach(function (filename) { + if (completed.indexOf(filename) === -1) { + memo.push(path.join(__dirname, './upgrades', filename)); + } else { + ++skipped; + } + }); + + return memo; + }, queue); + + Upgrade.process(queue, skipped, callback); }); }; @@ -35,12 +70,12 @@ Upgrade.runSingle = function (query, callback) { return callback(err); } - Upgrade.process(files, callback); + Upgrade.process(files, 0, callback); }); }; -Upgrade.process = function (files, callback) { - process.stdout.write('OK'.green + String(' ' + files.length).cyan + ' script(s) found\n'.cyan); +Upgrade.process = function (files, skipCount, callback) { + process.stdout.write('OK'.green + ' | '.reset + String(files.length).cyan + ' script(s) found'.cyan + (skipCount > 0 ? ', '.cyan + String(skipCount).cyan + ' skipped'.cyan : '') + '\n'.reset); // Do I need to sort the files here? we'll see. // sort(); @@ -58,6 +93,9 @@ Upgrade.process = function (files, callback) { return next(err); } + // Record success in schemaLog + db.sortedSetAdd('schemaLog', Date.now(), path.basename(file, '.js')); + process.stdout.write('OK\n'.green); next(); }); diff --git a/src/upgrades/20160922-category-recent-tids.js b/src/upgrades/category_recent_tids.js similarity index 100% rename from src/upgrades/20160922-category-recent-tids.js rename to src/upgrades/category_recent_tids.js diff --git a/src/upgrades/20161008-favourites-to-bookmarks.js b/src/upgrades/favourites_to_bookmarks.js similarity index 100% rename from src/upgrades/20161008-favourites-to-bookmarks.js rename to src/upgrades/favourites_to_bookmarks.js diff --git a/src/upgrades/20161207-flags-refactor.js b/src/upgrades/flags_refactor.js similarity index 100% rename from src/upgrades/20161207-flags-refactor.js rename to src/upgrades/flags_refactor.js diff --git a/src/upgrades/20161122-global-and-user-language-keys.js b/src/upgrades/global_and_user_language_keys.js similarity index 100% rename from src/upgrades/20161122-global-and-user-language-keys.js rename to src/upgrades/global_and_user_language_keys.js diff --git a/src/upgrades/20161125-sorted-set-for-pinned-topics.js b/src/upgrades/sorted_set_for_pinned_topics.js similarity index 100% rename from src/upgrades/20161125-sorted-set-for-pinned-topics.js rename to src/upgrades/sorted_set_for_pinned_topics.js diff --git a/src/upgrades/20161014-sorted-sets-for-post-replies.js b/src/upgrades/sorted_sets_for_post_replies.js similarity index 100% rename from src/upgrades/20161014-sorted-sets-for-post-replies.js rename to src/upgrades/sorted_sets_for_post_replies.js From a4c00663ffc28f4dbf4cb6ef3e064bf0a8f8f7b5 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 23 Feb 2017 13:13:59 -0500 Subject: [PATCH 124/316] Revert "adding origin to new local ioConfig var, as per @barisusakli, re: #5472" This reverts commit c1b1ff5c6ec5a35ed1e127edc5ccfc06dd04c5c2. --- src/socket.io/index.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/socket.io/index.js b/src/socket.io/index.js index 318831d0fb..58f12ea374 100644 --- a/src/socket.io/index.js +++ b/src/socket.io/index.js @@ -22,10 +22,6 @@ Sockets.init = function (server) { var SocketIO = require('socket.io'); var socketioWildcard = require('socketio-wildcard')(); - var ioConfig = { - transports: nconf.get('socket.io:transports') - }; - io = new SocketIO({ path: nconf.get('relative_path') + '/socket.io' }); @@ -51,15 +47,17 @@ Sockets.init = function (server) { } if (!override) { - ioConfig.origins = parsedUrl.protocol + '//' + domain + ':*'; + io.set('origins', parsedUrl.protocol + '//' + domain + ':*'); winston.info('[socket.io] Restricting access to origin: ' + parsedUrl.protocol + '//' + domain + ':*'); } else { - ioConfig.origins = override; + io.set('origins', override); winston.info('[socket.io] Restricting access to origin: ' + override); } } - io.listen(server, ioConfig); + io.listen(server, { + transports: nconf.get('socket.io:transports') + }); Sockets.server = io; }; From ddea9f534e85a3a2ed2cfecf1abf5ba297d7e8b0 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 24 Feb 2017 12:47:46 -0500 Subject: [PATCH 125/316] running eslint again --- public/src/client/account/header.js | 2 +- public/src/client/flags/detail.js | 58 ++-- public/src/client/flags/list.js | 34 +- public/src/client/topic/postTools.js | 2 +- src/categories/create.js | 6 +- src/categories/delete.js | 2 +- src/controllers/accounts/helpers.js | 4 +- src/controllers/admin/users.js | 8 +- src/controllers/authentication.js | 2 +- src/controllers/mods.js | 10 +- src/flags.js | 433 ++++++++++++------------- src/groups/create.js | 2 +- src/groups/delete.js | 2 +- src/meta/minifier.js | 4 +- src/middleware/index.js | 3 +- src/posts.js | 4 +- src/posts/create.js | 2 +- src/posts/edit.js | 4 +- src/socket.io/flags.js | 16 +- src/socket.io/topics/infinitescroll.js | 2 +- src/socket.io/topics/unread.js | 2 +- src/topics/create.js | 6 +- src/upgrade.js | 10 +- src/user.js | 2 +- src/user/create.js | 2 +- test/flags.js | 93 +++--- 26 files changed, 356 insertions(+), 359 deletions(-) diff --git a/public/src/client/account/header.js b/public/src/client/account/header.js index 2c49279237..ce340c2c90 100644 --- a/public/src/client/account/header.js +++ b/public/src/client/account/header.js @@ -182,7 +182,7 @@ define('forum/account/header', [ require(['flags'], function (flags) { flags.showFlagModal({ type: 'user', - id: ajaxify.data.uid + id: ajaxify.data.uid, }); }); } diff --git a/public/src/client/flags/detail.js b/public/src/client/flags/detail.js index e20f05dba9..d01b908fc5 100644 --- a/public/src/client/flags/detail.js +++ b/public/src/client/flags/detail.js @@ -14,34 +14,32 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator'], f var action = this.getAttribute('data-action'); switch (action) { - case 'update': - socket.emit('flags.update', { - flagId: ajaxify.data.flagId, - data: $('#attributes').serializeArray() - }, function (err, history) { - if (err) { - return app.alertError(err.message); - } else { - app.alertSuccess('[[flags:updated]]'); - Flags.reloadHistory(history); - } - }); - break; - - case 'appendNote': - socket.emit('flags.appendNote', { - flagId: ajaxify.data.flagId, - note: document.getElementById('note').value - }, function (err, payload) { - if (err) { - return app.alertError(err.message); - } else { - app.alertSuccess('[[flags:note-added]]'); - Flags.reloadNotes(payload.notes); - Flags.reloadHistory(payload.history); - } - }); - break; + case 'update': + socket.emit('flags.update', { + flagId: ajaxify.data.flagId, + data: $('#attributes').serializeArray(), + }, function (err, history) { + if (err) { + return app.alertError(err.message); + } + app.alertSuccess('[[flags:updated]]'); + Flags.reloadHistory(history); + }); + break; + + case 'appendNote': + socket.emit('flags.appendNote', { + flagId: ajaxify.data.flagId, + note: document.getElementById('note').value, + }, function (err, payload) { + if (err) { + return app.alertError(err.message); + } + app.alertSuccess('[[flags:note-added]]'); + Flags.reloadNotes(payload.notes); + Flags.reloadHistory(payload.history); + }); + break; } }); @@ -50,7 +48,7 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator'], f Flags.reloadNotes = function (notes) { templates.parse('flags/detail', 'notes', { - notes: notes + notes: notes, }, function (html) { var wrapperEl = components.get('flag/notes'); wrapperEl.empty(); @@ -62,7 +60,7 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator'], f Flags.reloadHistory = function (history) { templates.parse('flags/detail', 'history', { - history: history + history: history, }, function (html) { translator.translate(html, function (translated) { var wrapperEl = components.get('flag/history'); diff --git a/public/src/client/flags/list.js b/public/src/client/flags/list.js index 12cc19093f..754dce8746 100644 --- a/public/src/client/flags/list.js +++ b/public/src/client/flags/list.js @@ -47,17 +47,17 @@ define('forum/flags/list', ['components', 'Chart'], function (components, Chart) labels: dailyLabels, datasets: [ { - label: "", - backgroundColor: "rgba(151,187,205,0.2)", - borderColor: "rgba(151,187,205,1)", - pointBackgroundColor: "rgba(151,187,205,1)", - pointHoverBackgroundColor: "#fff", - pointBorderColor: "#fff", - pointHoverBorderColor: "rgba(151,187,205,1)", - data: ajaxify.data.analytics - } - ] - } + label: '', + backgroundColor: 'rgba(151,187,205,0.2)', + borderColor: 'rgba(151,187,205,1)', + pointBackgroundColor: 'rgba(151,187,205,1)', + pointHoverBackgroundColor: '#fff', + pointBorderColor: '#fff', + pointHoverBorderColor: 'rgba(151,187,205,1)', + data: ajaxify.data.analytics, + }, + ], + }, }; dailyCanvas.width = $(dailyCanvas).parent().width(); @@ -68,17 +68,17 @@ define('forum/flags/list', ['components', 'Chart'], function (components, Chart) responsive: true, animation: false, legend: { - display: false + display: false, }, scales: { yAxes: [{ ticks: { beginAtZero: true, - stepSize: 1 - } - }] - } - } + stepSize: 1, + }, + }], + }, + }, }); }; diff --git a/public/src/client/topic/postTools.js b/public/src/client/topic/postTools.js index 735c411b98..90de1cf810 100644 --- a/public/src/client/topic/postTools.js +++ b/public/src/client/topic/postTools.js @@ -119,7 +119,7 @@ define('forum/topic/postTools', [ require(['flags'], function (flags) { flags.showFlagModal({ type: 'post', - id: pid + id: pid, }); }); }); diff --git a/src/categories/create.js b/src/categories/create.js index 140b23dc9d..fc1aee1d1a 100644 --- a/src/categories/create.js +++ b/src/categories/create.js @@ -72,7 +72,7 @@ module.exports = function (Categories) { next(null, category); }, function (category, next) { - plugins.fireHook('action:category.create', {category: category}); + plugins.fireHook('action:category.create', { category: category }); next(null, category); }, ], callback); @@ -143,14 +143,14 @@ module.exports = function (Categories) { plugins.fireHook('filter:categories.copyPrivilegesFrom', { privileges: privileges.privilegeList, fromCid: fromCid, - toCid: toCid + toCid: toCid, }, next); }, function (data, next) { async.each(data.privileges, function (privilege, next) { copyPrivilege(privilege, data.fromCid, data.toCid, next); }, next); - } + }, ], callback); }; diff --git a/src/categories/delete.js b/src/categories/delete.js index b9ddeca6a4..7cfbfccf27 100644 --- a/src/categories/delete.js +++ b/src/categories/delete.js @@ -30,7 +30,7 @@ module.exports = function (Categories) { purgeCategory(cid, next); }, function (next) { - plugins.fireHook('action:category.delete', {cid: cid, uid: uid}); + plugins.fireHook('action:category.delete', { cid: cid, uid: uid }); next(); }, ], callback); diff --git a/src/controllers/accounts/helpers.js b/src/controllers/accounts/helpers.js index 2130fbf078..2f5a2af3bb 100644 --- a/src/controllers/accounts/helpers.js +++ b/src/controllers/accounts/helpers.js @@ -124,7 +124,7 @@ helpers.getUserDataByUserSlug = function (userslug, callerUID, callback) { other: !isSelf, moderator: isModerator, globalMod: isGlobalModerator, - admin: isAdmin + admin: isAdmin, }); userData.sso = results.sso.associations; @@ -174,7 +174,7 @@ function filterLinks(links, states) { other: true, moderator: true, globalMod: true, - admin: true + admin: true, }, link.visibility); // Iterate through states and permit if every test passes (or is not defined) diff --git a/src/controllers/admin/users.js b/src/controllers/admin/users.js index 0eae3b82c6..7dd08994cc 100644 --- a/src/controllers/admin/users.js +++ b/src/controllers/admin/users.js @@ -127,12 +127,10 @@ function getUsers(set, section, min, max, req, res, next) { count: function (next) { if (byScore) { db.sortedSetCount(set, min, max, next); + } else if (set === 'users:banned' || set === 'users:notvalidated') { + db.sortedSetCard(set, next); } else { - if (set === 'users:banned' || set === 'users:notvalidated') { - db.sortedSetCard(set, next); - } else { - db.getObjectField('global', 'userCount', next); - } + db.getObjectField('global', 'userCount', next); } }, users: function (next) { diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js index 2319332d83..a88f3c2bf4 100644 --- a/src/controllers/authentication.js +++ b/src/controllers/authentication.js @@ -330,7 +330,7 @@ authenticationController.onSuccessfulLogin = function (req, uid, callback) { // Force session check for all connected socket.io clients with the same session id sockets.in('sess_' + req.sessionID).emit('checkSession', uid); - plugins.fireHook('action:user.loggedIn', {uid: uid, req: req}); + plugins.fireHook('action:user.loggedIn', { uid: uid, req: req }); callback(); }); }; diff --git a/src/controllers/mods.js b/src/controllers/mods.js index a34ebacb8b..f55b852860 100644 --- a/src/controllers/mods.js +++ b/src/controllers/mods.js @@ -8,7 +8,7 @@ var flags = require('../flags'); var analytics = require('../analytics'); var modsController = { - flags: {} + flags: {}, }; modsController.flags.list = function (req, res, next) { @@ -58,7 +58,7 @@ modsController.flags.list = function (req, res, next) { async.parallel({ flags: async.apply(flags.list, filters, req.uid), analytics: async.apply(analytics.getDailyStatsForSet, 'analytics:flags', Date.now(), 30), - categories: async.apply(categories.buildForSelect, req.uid) + categories: async.apply(categories.buildForSelect, req.uid), }, function (err, data) { if (err) { return next(err); @@ -92,7 +92,7 @@ modsController.flags.list = function (req, res, next) { categories: data.categories, hasFilter: hasFilter, filters: filters, - title: '[[pages:flags]]' + title: '[[pages:flags]]', }); }); }); @@ -103,7 +103,7 @@ modsController.flags.detail = function (req, res, next) { isAdminOrGlobalMod: async.apply(user.isAdminOrGlobalMod, req.uid), moderatedCids: async.apply(user.getModeratedCids, req.uid), flagData: async.apply(flags.get, req.params.flagId), - assignees: async.apply(user.getAdminsandGlobalModsandModerators) + assignees: async.apply(user.getAdminsandGlobalModsandModerators), }, function (err, results) { if (err || !results.flagData) { return next(err || new Error('[[error:invalid-data]]')); @@ -122,7 +122,7 @@ modsController.flags.detail = function (req, res, next) { return memo; }, {}), - title: '[[pages:flag-details, ' + req.params.flagId + ']]' + title: '[[pages:flag-details, ' + req.params.flagId + ']]', })); }); }; diff --git a/src/flags.js b/src/flags.js index 02e7fdeaad..57dd90a8f6 100644 --- a/src/flags.js +++ b/src/flags.js @@ -24,13 +24,13 @@ Flags.get = function (flagId, callback) { async.apply(async.parallel, { base: async.apply(db.getObject.bind(db), 'flag:' + flagId), history: async.apply(Flags.getHistory, flagId), - notes: async.apply(Flags.getNotes, flagId) + notes: async.apply(Flags.getNotes, flagId), }), function (data, next) { // Second stage async.parallel({ userObj: async.apply(user.getUserFields, data.base.uid, ['username', 'userslug', 'picture']), - targetObj: async.apply(Flags.getTarget, data.base.type, data.base.targetId, data.base.uid) + targetObj: async.apply(Flags.getTarget, data.base.type, data.base.targetId, data.base.uid), }, function (err, payload) { // Final object return construction next(err, Object.assign(data.base, { @@ -39,10 +39,10 @@ Flags.get = function (flagId, callback) { target: payload.targetObj, history: data.history, notes: data.notes, - reporter: payload.userObj + reporter: payload.userObj, })); }); - } + }, ], callback); }; @@ -63,44 +63,44 @@ Flags.list = function (filters, uid, callback) { }); } else { // Empty array, do nothing - return; + } }; if (Object.keys(filters).length > 0) { for (var type in filters) { switch (type) { - case 'type': - prepareSets('flags:byType:', filters[type]); - break; + case 'type': + prepareSets('flags:byType:', filters[type]); + break; - case 'state': - prepareSets('flags:byState:', filters[type]); - break; - - case 'reporterId': - prepareSets('flags:byReporter:', filters[type]); - break; - - case 'assignee': - prepareSets('flags:byAssignee:', filters[type]); - break; + case 'state': + prepareSets('flags:byState:', filters[type]); + break; - case 'targetUid': - prepareSets('flags:byTargetUid:', filters[type]); - break; + case 'reporterId': + prepareSets('flags:byReporter:', filters[type]); + break; - case 'cid': - prepareSets('flags:byCid:', filters[type]); - break; + case 'assignee': + prepareSets('flags:byAssignee:', filters[type]); + break; - case 'quick': - switch (filters.quick) { - case 'mine': - sets.push('flags:byAssignee:' + uid); - break; - } + case 'targetUid': + prepareSets('flags:byTargetUid:', filters[type]); + break; + + case 'cid': + prepareSets('flags:byCid:', filters[type]); + break; + + case 'quick': + switch (filters.quick) { + case 'mine': + sets.push('flags:byAssignee:' + uid); break; + } + break; } } } @@ -147,38 +147,38 @@ Flags.list = function (filters, uid, callback) { username: userObj.username, picture: userObj.picture, 'icon:bgColor': userObj['icon:bgColor'], - 'icon:text': userObj['icon:text'] - } + 'icon:text': userObj['icon:text'], + }, })); }); - } + }, ], function (err, flagObj) { if (err) { return next(err); } - switch(flagObj.state) { - case 'open': - flagObj.labelClass = 'info'; - break; - case 'wip': - flagObj.labelClass = 'warning'; - break; - case 'resolved': - flagObj.labelClass = 'success'; - break; - case 'rejected': - flagObj.labelClass = 'danger'; - break; + switch (flagObj.state) { + case 'open': + flagObj.labelClass = 'info'; + break; + case 'wip': + flagObj.labelClass = 'warning'; + break; + case 'resolved': + flagObj.labelClass = 'success'; + break; + case 'rejected': + flagObj.labelClass = 'danger'; + break; } next(null, Object.assign(flagObj, { target_readable: flagObj.type.charAt(0).toUpperCase() + flagObj.type.slice(1) + ' ' + flagObj.targetId, - datetimeISO: new Date(parseInt(flagObj.datetime, 10)).toISOString() + datetimeISO: new Date(parseInt(flagObj.datetime, 10)).toISOString(), })); }); - }, next); - } + }, next); + }, ], callback); }; @@ -186,7 +186,7 @@ Flags.validate = function (payload, callback) { async.parallel({ targetExists: async.apply(Flags.targetExists, payload.type, payload.id), target: async.apply(Flags.getTarget, payload.type, payload.id, payload.uid), - reporter: async.apply(user.getUserData, payload.uid) + reporter: async.apply(user.getUserData, payload.uid), }, function (err, data) { if (err) { return callback(err); @@ -199,42 +199,42 @@ Flags.validate = function (payload, callback) { } switch (payload.type) { - case 'post': - privileges.posts.canEdit(payload.id, payload.uid, function (err, editable) { - if (err) { - return callback(err); - } + case 'post': + privileges.posts.canEdit(payload.id, payload.uid, function (err, editable) { + if (err) { + return callback(err); + } - var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 1; + var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 1; // Check if reporter meets rep threshold (or can edit the target post, in which case threshold does not apply) - if (!editable.flag && parseInt(data.reporter.reputation, 10) < minimumReputation) { - return callback(new Error('[[error:not-enough-reputation-to-flag]]')); - } + if (!editable.flag && parseInt(data.reporter.reputation, 10) < minimumReputation) { + return callback(new Error('[[error:not-enough-reputation-to-flag]]')); + } - callback(); - }); - break; - - case 'user': - privileges.users.canEdit(payload.uid, payload.id, function (err, editable) { - if (err) { - return callback(err); - } + callback(); + }); + break; - var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 1; + case 'user': + privileges.users.canEdit(payload.uid, payload.id, function (err, editable) { + if (err) { + return callback(err); + } + + var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 1; // Check if reporter meets rep threshold (or can edit the target user, in which case threshold does not apply) - if (!editable && parseInt(data.reporter.reputation, 10) < minimumReputation) { - return callback(new Error('[[error:not-enough-reputation-to-flag]]')); - } + if (!editable && parseInt(data.reporter.reputation, 10) < minimumReputation) { + return callback(new Error('[[error:not-enough-reputation-to-flag]]')); + } - callback(); - }); - break; - - default: - callback(new Error('[[error:invalid-data]]')); - break; - } + callback(); + }); + break; + + default: + callback(new Error('[[error:invalid-data]]')); + break; + } }); }; @@ -252,7 +252,7 @@ Flags.getNotes = function (flagId, callback) { uid: noteObj[0], content: noteObj[1], datetime: note.score, - datetimeISO: new Date(parseInt(note.score, 10)).toISOString() + datetimeISO: new Date(parseInt(note.score, 10)).toISOString(), }; } catch (e) { return next(e); @@ -271,7 +271,7 @@ Flags.getNotes = function (flagId, callback) { return note; })); }); - } + }, ], callback); }; @@ -296,7 +296,7 @@ Flags.create = function (type, id, uid, reason, timestamp, callback) { // Extra data for zset insertion async.apply(Flags.getTargetUid, type, id), - async.apply(Flags.getTargetCid, type, id) + async.apply(Flags.getTargetCid, type, id), ], function (err, checks) { if (err) { return next(err); @@ -309,9 +309,8 @@ Flags.create = function (type, id, uid, reason, timestamp, callback) { return next(new Error('[[error:already-flagged]]')); } else if (!checks[1]) { return next(new Error('[[error:invalid-data]]')); - } else { - next(); } + next(); }); }, async.apply(db.incrObjectField, 'global', 'nextFlagId'), @@ -323,13 +322,13 @@ Flags.create = function (type, id, uid, reason, timestamp, callback) { targetId: id, description: reason, uid: uid, - datetime: timestamp + datetime: timestamp, }), async.apply(db.sortedSetAdd.bind(db), 'flags:datetime', timestamp, flagId), // by time, the default async.apply(db.sortedSetAdd.bind(db), 'flags:byReporter:' + uid, timestamp, flagId), // by reporter async.apply(db.sortedSetAdd.bind(db), 'flags:byType:' + type, timestamp, flagId), // by flag type async.apply(db.sortedSetAdd.bind(db), 'flags:hash', flagId, [type, id, uid].join(':')), // save zset for duplicate checking - async.apply(analytics.increment, 'flags') // some fancy analytics + async.apply(analytics.increment, 'flags'), // some fancy analytics ]; if (targetUid) { @@ -341,20 +340,20 @@ Flags.create = function (type, id, uid, reason, timestamp, callback) { if (type === 'post') { tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byPid:' + id, timestamp, flagId)); // by target pid } - + async.parallel(tasks, function (err, data) { if (err) { return next(err); } if (doHistoryAppend) { - Flags.update(flagId, uid, { "state": "open" }); + Flags.update(flagId, uid, { state: 'open' }); } next(null, flagId); }); }, - async.apply(Flags.get) + async.apply(Flags.get), ], callback); }; @@ -368,72 +367,72 @@ Flags.getTarget = function (type, id, uid, callback) { function (exists, next) { if (exists) { switch (type) { - case 'post': - async.waterfall([ - async.apply(posts.getPostsByPids, [id], uid), - function (posts, next) { - topics.addPostData(posts, uid, next); - } - ], function (err, posts) { - next(err, posts[0]); - }); - break; + case 'post': + async.waterfall([ + async.apply(posts.getPostsByPids, [id], uid), + function (posts, next) { + topics.addPostData(posts, uid, next); + }, + ], function (err, posts) { + next(err, posts[0]); + }); + break; - case 'user': - user.getUsersData([id], function (err, users) { - next(err, users ? users[0] : undefined); - }); - break; + case 'user': + user.getUsersData([id], function (err, users) { + next(err, users ? users[0] : undefined); + }); + break; - default: - next(new Error('[[error:invalid-data]]')); - break; + default: + next(new Error('[[error:invalid-data]]')); + break; } } else { // Target used to exist (otherwise flag creation'd fail), but no longer next(null, {}); } - } + }, ], callback); }; Flags.targetExists = function (type, id, callback) { switch (type) { - case 'post': - posts.exists(id, callback); - break; - - case 'user': - user.exists(id, callback); - break; + case 'post': + posts.exists(id, callback); + break; - default: - callback(new Error('[[error:invalid-data]]')); - break; + case 'user': + user.exists(id, callback); + break; + + default: + callback(new Error('[[error:invalid-data]]')); + break; } }; Flags.getTargetUid = function (type, id, callback) { switch (type) { - case 'post': - posts.getPostField(id, 'uid', callback); - break; - - default: - setImmediate(callback, null, id); - break; + case 'post': + posts.getPostField(id, 'uid', callback); + break; + + default: + setImmediate(callback, null, id); + break; } }; Flags.getTargetCid = function (type, id, callback) { switch (type) { - case 'post': - posts.getCidByPid(id, callback); - break; + case 'post': + posts.getCidByPid(id, callback); + break; - default: - setImmediate(callback, null, id); - break; + default: + setImmediate(callback, null, id); + break; } }; @@ -453,14 +452,14 @@ Flags.update = function (flagId, uid, changeset, callback) { } else { // Add tasks as necessary switch (prop) { - case 'state': - tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byState:' + changeset[prop], now, flagId)); - tasks.push(async.apply(db.sortedSetRemove.bind(db), 'flags:byState:' + current[prop], flagId)); - break; - - case 'assignee': - tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byAssignee:' + changeset[prop], now, flagId)); - break; + case 'state': + tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byState:' + changeset[prop], now, flagId)); + tasks.push(async.apply(db.sortedSetRemove.bind(db), 'flags:byState:' + current[prop], flagId)); + break; + + case 'assignee': + tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byAssignee:' + changeset[prop], now, flagId)); + break; } } } @@ -483,7 +482,7 @@ Flags.update = function (flagId, uid, changeset, callback) { async.parallel(tasks, function (err, data) { return next(err); }); - } + }, ], callback); }; @@ -512,12 +511,12 @@ Flags.getHistory = function (flagId, callback) { uid: entry.value[0], fields: changeset, datetime: entry.score, - datetimeISO: new Date(parseInt(entry.score, 10)).toISOString() + datetimeISO: new Date(parseInt(entry.score, 10)).toISOString(), }; }); user.getUsersFields(uids, ['username', 'userslug', 'picture'], next); - } + }, ], function (err, users) { if (err) { return callback(err); @@ -564,8 +563,8 @@ Flags.appendNote = function (flagId, uid, note, datetime, callback) { async.apply(db.sortedSetAdd, 'flag:' + flagId + ':notes', datetime, payload), async.apply(Flags.appendHistory, flagId, uid, { notes: null, - datetime: datetime - }) + datetime: datetime, + }), ], callback); }; @@ -576,88 +575,88 @@ Flags.notify = function (flagObj, uid, callback) { } switch (flagObj.type) { - case 'post': - async.parallel({ - post: function (next) { - async.waterfall([ - async.apply(posts.getPostData, flagObj.targetId), - async.apply(posts.parsePost) - ], next); - }, - title: async.apply(topics.getTitleByPid, flagObj.targetId), - admins: async.apply(groups.getMembers, 'administrators', 0, -1), - globalMods: async.apply(groups.getMembers, 'Global Moderators', 0, -1), - moderators: function (next) { - async.waterfall([ - async.apply(posts.getCidByPid, flagObj.targetId), - function (cid, next) { - groups.getMembers('cid:' + cid + ':privileges:mods', 0, -1, next); - } - ], next); - } - }, function (err, results) { - if (err) { + case 'post': + async.parallel({ + post: function (next) { + async.waterfall([ + async.apply(posts.getPostData, flagObj.targetId), + async.apply(posts.parsePost), + ], next); + }, + title: async.apply(topics.getTitleByPid, flagObj.targetId), + admins: async.apply(groups.getMembers, 'administrators', 0, -1), + globalMods: async.apply(groups.getMembers, 'Global Moderators', 0, -1), + moderators: function (next) { + async.waterfall([ + async.apply(posts.getCidByPid, flagObj.targetId), + function (cid, next) { + groups.getMembers('cid:' + cid + ':privileges:mods', 0, -1, next); + }, + ], next); + }, + }, function (err, results) { + if (err) { + return callback(err); + } + + var title = S(results.title).decodeHTMLEntities().s; + var titleEscaped = title.replace(/%/g, '%').replace(/,/g, ','); + + notifications.create({ + bodyShort: '[[notifications:user_flagged_post_in, ' + flagObj.reporter.username + ', ' + titleEscaped + ']]', + bodyLong: flagObj.description, + pid: flagObj.targetId, + path: '/post/' + flagObj.targetId, + nid: 'flag:post:' + flagObj.targetId + ':uid:' + uid, + from: uid, + mergeId: 'notifications:user_flagged_post_in|' + flagObj.targetId, + topicTitle: results.title, + }, function (err, notification) { + if (err || !notification) { return callback(err); } - var title = S(results.title).decodeHTMLEntities().s; - var titleEscaped = title.replace(/%/g, '%').replace(/,/g, ','); - - notifications.create({ - bodyShort: '[[notifications:user_flagged_post_in, ' + flagObj.reporter.username + ', ' + titleEscaped + ']]', - bodyLong: flagObj.description, - pid: flagObj.targetId, - path: '/post/' + flagObj.targetId, - nid: 'flag:post:' + flagObj.targetId + ':uid:' + uid, - from: uid, - mergeId: 'notifications:user_flagged_post_in|' + flagObj.targetId, - topicTitle: results.title - }, function (err, notification) { - if (err || !notification) { - return callback(err); - } - - plugins.fireHook('action:flag.create', { - flag: flagObj - }); - notifications.push(notification, results.admins.concat(results.moderators).concat(results.globalMods), callback); + plugins.fireHook('action:flag.create', { + flag: flagObj, }); + notifications.push(notification, results.admins.concat(results.moderators).concat(results.globalMods), callback); }); - break; + }); + break; + + case 'user': + async.parallel({ + admins: async.apply(groups.getMembers, 'administrators', 0, -1), + globalMods: async.apply(groups.getMembers, 'Global Moderators', 0, -1), + }, function (err, results) { + if (err) { + return callback(err); + } - case 'user': - async.parallel({ - admins: async.apply(groups.getMembers, 'administrators', 0, -1), - globalMods: async.apply(groups.getMembers, 'Global Moderators', 0, -1), - }, function (err, results) { - if (err) { + notifications.create({ + bodyShort: '[[notifications:user_flagged_user, ' + flagObj.reporter.username + ', ' + flagObj.target.username + ']]', + bodyLong: flagObj.description, + path: '/uid/' + flagObj.targetId, + nid: 'flag:user:' + flagObj.targetId + ':uid:' + uid, + from: uid, + mergeId: 'notifications:user_flagged_user|' + flagObj.targetId, + }, function (err, notification) { + if (err || !notification) { return callback(err); } - notifications.create({ - bodyShort: '[[notifications:user_flagged_user, ' + flagObj.reporter.username + ', ' + flagObj.target.username + ']]', - bodyLong: flagObj.description, - path: '/uid/' + flagObj.targetId, - nid: 'flag:user:' + flagObj.targetId + ':uid:' + uid, - from: uid, - mergeId: 'notifications:user_flagged_user|' + flagObj.targetId - }, function (err, notification) { - if (err || !notification) { - return callback(err); - } - - plugins.fireHook('action:flag.create', { - flag: flagObj - }); - notifications.push(notification, results.admins.concat(results.globalMods), callback); + plugins.fireHook('action:flag.create', { + flag: flagObj, }); + notifications.push(notification, results.admins.concat(results.globalMods), callback); }); - break; - - default: - callback(new Error('[[error:invalid-data]]')); - break; + }); + break; + + default: + callback(new Error('[[error:invalid-data]]')); + break; } }; -module.exports = Flags; \ No newline at end of file +module.exports = Flags; diff --git a/src/groups/create.js b/src/groups/create.js index c7d57b1ebb..df534a3f39 100644 --- a/src/groups/create.js +++ b/src/groups/create.js @@ -69,7 +69,7 @@ module.exports = function (Groups) { async.series(tasks, next); }, function (results, next) { - plugins.fireHook('action:group.create', {group: groupData}); + plugins.fireHook('action:group.create', { group: groupData }); next(null, groupData); }, ], callback); diff --git a/src/groups/delete.js b/src/groups/delete.js index 28dcce8340..607e387d6e 100644 --- a/src/groups/delete.js +++ b/src/groups/delete.js @@ -42,7 +42,7 @@ module.exports = function (Groups) { return callback(err); } Groups.resetCache(); - plugins.fireHook('action:group.destroy', {group: groupObj}); + plugins.fireHook('action:group.destroy', { group: groupObj }); callback(); }); }); diff --git a/src/meta/minifier.js b/src/meta/minifier.js index c3ac1899fb..43761c9d33 100644 --- a/src/meta/minifier.js +++ b/src/meta/minifier.js @@ -20,7 +20,7 @@ Minifier.js.minify = function (scripts, minify, callback) { if (err) { return next(err); } - + if (!exists) { console.warn('[minifier] file not found, ' + script); } @@ -30,7 +30,7 @@ Minifier.js.minify = function (scripts, minify, callback) { if (err) { return callback(err); } - + if (minify) { minifyScripts(scripts, callback); } else { diff --git a/src/middleware/index.js b/src/middleware/index.js index 17d365aebb..5a3f9aa5ba 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -86,9 +86,8 @@ middleware.ensureSelfOrPrivileged = function (req, res, next) { return next(err); } else if (ok) { return next(); - } else { - controllers.helpers.notAllowed(req, res); } + controllers.helpers.notAllowed(req, res); }); } else { controllers.helpers.notAllowed(req, res); diff --git a/src/posts.js b/src/posts.js index 10f65a5e29..2f9132a25b 100644 --- a/src/posts.js +++ b/src/posts.js @@ -155,7 +155,7 @@ var plugins = require('./plugins'); pid: pid, }; data[field] = value; - plugins.fireHook('action:post.setFields', {data: data}); + plugins.fireHook('action:post.setFields', { data: data }); callback(); }); }; @@ -166,7 +166,7 @@ var plugins = require('./plugins'); return callback(err); } data.pid = pid; - plugins.fireHook('action:post.setFields', {data: data}); + plugins.fireHook('action:post.setFields', { data: data }); callback(); }); }; diff --git a/src/posts/create.js b/src/posts/create.js index 7ea3ef0dbb..d2a46f0e82 100644 --- a/src/posts/create.js +++ b/src/posts/create.js @@ -106,7 +106,7 @@ module.exports = function (Posts) { }, function (postData, next) { postData.isMain = isMain; - plugins.fireHook('action:post.save', {post: _.clone(postData)}); + plugins.fireHook('action:post.save', { post: _.clone(postData) }); next(null, postData); }, ], callback); diff --git a/src/posts/edit.js b/src/posts/edit.js index a92d865509..3481e1dc16 100644 --- a/src/posts/edit.js +++ b/src/posts/edit.js @@ -65,7 +65,7 @@ module.exports = function (Posts) { postData.cid = results.topic.cid; postData.topic = results.topic; - plugins.fireHook('action:post.edit', {post: _.clone(postData), uid: data.uid}); + plugins.fireHook('action:post.edit', { post: _.clone(postData), uid: data.uid }); cache.del(String(postData.pid)); pubsub.publish('post:edit', String(postData.pid)); @@ -136,7 +136,7 @@ module.exports = function (Posts) { function (tags, next) { topicData.tags = data.tags; topicData.oldTitle = results.topic.title; - plugins.fireHook('action:topic.edit', {topic: topicData, uid: data.uid}); + plugins.fireHook('action:topic.edit', { topic: topicData, uid: data.uid }); next(null, { tid: tid, cid: results.topic.cid, diff --git a/src/socket.io/flags.js b/src/socket.io/flags.js index c25bd662fa..d7964a1035 100644 --- a/src/socket.io/flags.js +++ b/src/socket.io/flags.js @@ -29,12 +29,12 @@ SocketFlags.create = function (socket, data, callback) { async.apply(flags.validate, { uid: socket.uid, type: data.type, - id: data.id + id: data.id, }), function (next) { // If we got here, then no errors occurred flags.create(data.type, data.id, socket.uid, data.reason, next); - } + }, ], function (err, flagObj) { if (err) { return callback(err); @@ -56,7 +56,7 @@ SocketFlags.update = function (socket, data, callback) { function (next) { async.parallel([ async.apply(user.isAdminOrGlobalMod, socket.uid), - async.apply(user.isModeratorOfAnyCategory, socket.uid) + async.apply(user.isModeratorOfAnyCategory, socket.uid), ], function (err, results) { next(err, results[0] || results[1]); }); @@ -74,7 +74,7 @@ SocketFlags.update = function (socket, data, callback) { flags.update(data.flagId, socket.uid, payload, next); }, - async.apply(flags.getHistory, data.flagId) + async.apply(flags.getHistory, data.flagId), ], callback); }; @@ -87,7 +87,7 @@ SocketFlags.appendNote = function (socket, data, callback) { function (next) { async.parallel([ async.apply(user.isAdminOrGlobalMod, socket.uid), - async.apply(user.isModeratorOfAnyCategory, socket.uid) + async.apply(user.isModeratorOfAnyCategory, socket.uid), ], function (err, results) { next(err, results[0] || results[1]); }); @@ -101,10 +101,10 @@ SocketFlags.appendNote = function (socket, data, callback) { }, function (next) { async.parallel({ - "notes": async.apply(flags.getNotes, data.flagId), - "history": async.apply(flags.getHistory, data.flagId) + notes: async.apply(flags.getNotes, data.flagId), + history: async.apply(flags.getHistory, data.flagId), }, next); - } + }, ], callback); }; diff --git a/src/socket.io/topics/infinitescroll.js b/src/socket.io/topics/infinitescroll.js index 71b11ce9e9..023ad6266e 100644 --- a/src/socket.io/topics/infinitescroll.js +++ b/src/socket.io/topics/infinitescroll.js @@ -95,7 +95,7 @@ module.exports = function (SocketTopics) { var start = parseInt(data.after, 10); var stop = start + 9; - topics.getUnreadTopics({cid: data.cid, uid: socket.uid, start: start, stop: stop, filter: data.filter}, callback); + topics.getUnreadTopics({ cid: data.cid, uid: socket.uid, start: start, stop: stop, filter: data.filter }, callback); }; SocketTopics.loadMoreRecentTopics = function (socket, data, callback) { diff --git a/src/socket.io/topics/unread.js b/src/socket.io/topics/unread.js index c46aef0a12..29ebe8b189 100644 --- a/src/socket.io/topics/unread.js +++ b/src/socket.io/topics/unread.js @@ -50,7 +50,7 @@ module.exports = function (SocketTopics) { SocketTopics.markCategoryTopicsRead = function (socket, cid, callback) { async.waterfall([ function (next) { - topics.getUnreadTids({cid: cid, uid: socket.uid, filter: ''}, next); + topics.getUnreadTids({ cid: cid, uid: socket.uid, filter: '' }, next); }, function (tids, next) { SocketTopics.markAsRead(socket, tids, next); diff --git a/src/topics/create.js b/src/topics/create.js index 5cee9e3d7c..d0e2c37e7b 100644 --- a/src/topics/create.js +++ b/src/topics/create.js @@ -82,7 +82,7 @@ module.exports = function (Topics) { ], next); }, function (results, next) { - plugins.fireHook('action:topic.save', {topic: _.clone(topicData)}); + plugins.fireHook('action:topic.save', { topic: _.clone(topicData) }); next(null, topicData.tid); }, ], callback); @@ -174,7 +174,7 @@ module.exports = function (Topics) { data.postData.index = 0; analytics.increment(['topics', 'topics:byCid:' + data.topicData.cid]); - plugins.fireHook('action:topic.post', {topic: data.topicData, post: data.postData}); + plugins.fireHook('action:topic.post', { topic: data.topicData, post: data.postData }); if (parseInt(uid, 10)) { user.notifications.sendTopicNotificationToFollowers(uid, data.topicData, data.postData); @@ -269,7 +269,7 @@ module.exports = function (Topics) { Topics.notifyFollowers(postData, uid); analytics.increment(['posts', 'posts:byCid:' + cid]); - plugins.fireHook('action:topic.reply', {post: _.clone(postData)}); + plugins.fireHook('action:topic.reply', { post: _.clone(postData) }); next(null, postData); }, diff --git a/src/upgrade.js b/src/upgrade.js index e345448bda..c37026ecd9 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -334,11 +334,11 @@ Upgrade.upgrade = function (callback) { posts = posts.filter(function (post) { return post.hasOwnProperty('flags'); }); - + async.each(posts, function (post, next) { async.parallel({ uids: async.apply(db.getSortedSetRangeWithScores, 'pid:' + post.pid + ':flag:uids', 0, -1), - reasons: async.apply(db.getSortedSetRange, 'pid:' + post.pid + ':flag:uid:reason', 0, -1) + reasons: async.apply(db.getSortedSetRange, 'pid:' + post.pid + ':flag:uid:reason', 0, -1), }, function (err, data) { if (err) { return next(err); @@ -362,7 +362,7 @@ Upgrade.upgrade = function (callback) { flags.update(flagObj.flagId, 1, { state: post['flag:state'], assignee: post['flag:assignee'], - datetime: datetime + datetime: datetime, }, next); } else { setImmediate(next); @@ -383,7 +383,7 @@ Upgrade.upgrade = function (callback) { } else { setImmediate(next); } - } + }, ], function (err) { if (err && err.message === '[[error:already-flagged]]') { // Already flagged, no need to parse, but not an error @@ -407,7 +407,7 @@ Upgrade.upgrade = function (callback) { winston.info('[2016/12/07] Migrating flags to new schema (#5232) - skipped!'); next(); } - } + }, // Add new schema updates here // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 24!!! ], function (err) { diff --git a/src/user.js b/src/user.js index ccaea5a9c6..6cd1490520 100644 --- a/src/user.js +++ b/src/user.js @@ -258,7 +258,7 @@ var meta = require('./meta'); async.parallel([ async.apply(User.isAdministrator, uid), async.apply(User.isGlobalModerator, uid), - async.apply(User.isModeratorOfAnyCategory, uid) + async.apply(User.isModeratorOfAnyCategory, uid), ], function (err, results) { callback(err, results ? results.some(Boolean) : false); }); diff --git a/src/user/create.js b/src/user/create.js index 40ba2d5540..701d3cccbf 100644 --- a/src/user/create.js +++ b/src/user/create.js @@ -140,7 +140,7 @@ module.exports = function (User) { if (userNameChanged) { User.notifications.sendNameChangeNotification(userData.uid, userData.username); } - plugins.fireHook('action:user.create', {user: userData}); + plugins.fireHook('action:user.create', { user: userData }); next(null, userData.uid); }, ], callback); diff --git a/test/flags.js b/test/flags.js index af53b61271..8623dbc269 100644 --- a/test/flags.js +++ b/test/flags.js @@ -1,5 +1,6 @@ 'use strict'; -/*globals require, before, after, describe, it*/ + +/* globals require, before, after, describe, it*/ var assert = require('assert'); var async = require('async'); @@ -17,10 +18,10 @@ describe('Flags', function () { before(function (done) { // Create some stuff to flag async.waterfall([ - async.apply(User.create, {username: 'testUser', password: 'abcdef', email: 'b@c.com'}), + async.apply(User.create, { username: 'testUser', password: 'abcdef', email: 'b@c.com' }), function (uid, next) { Categories.create({ - name: 'test category' + name: 'test category', }, function (err, category) { if (err) { return done(err); @@ -30,13 +31,13 @@ describe('Flags', function () { cid: category.cid, uid: uid, title: 'Topic to flag', - content: 'This is flaggable content' + content: 'This is flaggable content', }, next); }); }, function (topicData, next) { User.create({ - username: 'testUser2', password: 'abcdef', email: 'c@d.com' + username: 'testUser2', password: 'abcdef', email: 'c@d.com', }, next); }, function (uid, next) { @@ -44,9 +45,9 @@ describe('Flags', function () { }, function (next) { User.create({ - username: 'unprivileged', password: 'abcdef', email: 'd@e.com' + username: 'unprivileged', password: 'abcdef', email: 'd@e.com', }, next); - } + }, ], done); }); @@ -59,10 +60,10 @@ describe('Flags', function () { uid: 1, targetId: 1, type: 'post', - description: 'Test flag' + description: 'Test flag', }; - for(var key in compare) { + for (var key in compare) { if (compare.hasOwnProperty(key)) { assert.ok(flagData[key]); assert.equal(flagData[key], compare[key]); @@ -136,10 +137,10 @@ describe('Flags', function () { targetId: 1, type: 'post', description: 'Test flag', - state: 'open' + state: 'open', }; - for(var key in compare) { + for (var key in compare) { if (compare.hasOwnProperty(key)) { assert.ok(flagData[key]); assert.equal(flagData[key], compare[key]); @@ -157,7 +158,7 @@ describe('Flags', function () { assert.ifError(err); assert.ok(Array.isArray(flags)); assert.equal(flags.length, 1); - + Flags.get(flags[0].flagId, function (err, flagData) { assert.ifError(err); assert.equal(flags[0].flagId, flagData.flagId); @@ -170,7 +171,7 @@ describe('Flags', function () { describe('(with filters)', function () { it('should return a filtered list of flags if said filters are passed in', function (done) { Flags.list({ - state: 'open' + state: 'open', }, 1, function (err, flags) { assert.ifError(err); assert.ok(Array.isArray(flags)); @@ -181,7 +182,7 @@ describe('Flags', function () { it('should return no flags if a filter with no matching flags is used', function (done) { Flags.list({ - state: 'rejected' + state: 'rejected', }, 1, function (err, flags) { assert.ifError(err); assert.ok(Array.isArray(flags)); @@ -192,7 +193,7 @@ describe('Flags', function () { it('should return a flag when filtered by cid 1', function (done) { Flags.list({ - cid: 1 + cid: 1, }, 1, function (err, flags) { assert.ifError(err); assert.ok(Array.isArray(flags)); @@ -203,7 +204,7 @@ describe('Flags', function () { it('shouldn\'t return a flag when filtered by cid 2', function (done) { Flags.list({ - cid: 2 + cid: 2, }, 1, function (err, flags) { assert.ifError(err); assert.ok(Array.isArray(flags)); @@ -214,7 +215,7 @@ describe('Flags', function () { it('should return a flag when filtered by both cid 1 and 2', function (done) { Flags.list({ - cid: [1, 2] + cid: [1, 2], }, 1, function (err, flags) { assert.ifError(err); assert.ok(Array.isArray(flags)); @@ -226,7 +227,7 @@ describe('Flags', function () { it('should return one flag if filtered by both cid 1 and 2 and open state', function (done) { Flags.list({ cid: [1, 2], - state: 'open' + state: 'open', }, 1, function (err, flags) { assert.ifError(err); assert.ok(Array.isArray(flags)); @@ -238,7 +239,7 @@ describe('Flags', function () { it('should return no flag if filtered by both cid 1 and 2 and non-open state', function (done) { Flags.list({ cid: [1, 2], - state: 'resolved' + state: 'resolved', }, 1, function (err, flags) { assert.ifError(err); assert.ok(Array.isArray(flags)); @@ -252,8 +253,8 @@ describe('Flags', function () { describe('.update()', function () { it('should alter a flag\'s various attributes and persist them to the database', function (done) { Flags.update(1, 1, { - "state": "wip", - "assignee": 1 + state: 'wip', + assignee: 1, }, function (err) { assert.ifError(err); db.getObjectFields('flag:1', ['state', 'assignee'], function (err, data) { @@ -277,13 +278,13 @@ describe('Flags', function () { history.forEach(function (change) { switch (change.attribute) { - case 'state': - assert.strictEqual('[[flags:state-wip]]', change.value); - break; - - case 'assignee': - assert.strictEqual(1, change.value); - break; + case 'state': + assert.strictEqual('[[flags:state-wip]]', change.value); + break; + + case 'assignee': + assert.strictEqual(1, change.value); + break; } }); @@ -299,10 +300,10 @@ describe('Flags', function () { var compare = { uid: 1, pid: 1, - content: 'This is flaggable content' + content: 'This is flaggable content', }; - for(var key in compare) { + for (var key in compare) { if (compare.hasOwnProperty(key)) { assert.ok(data[key]); assert.equal(data[key], compare[key]); @@ -319,10 +320,10 @@ describe('Flags', function () { var compare = { uid: 1, username: 'testUser', - email: 'b@c.com' + email: 'b@c.com', }; - for(var key in compare) { + for (var key in compare) { if (compare.hasOwnProperty(key)) { assert.ok(data[key]); assert.equal(data[key], compare[key]); @@ -352,7 +353,7 @@ describe('Flags', function () { Flags.validate({ type: 'post', id: 1, - uid: 1 + uid: 1, }, function (err) { assert.ok(err); assert.strictEqual('[[error:post-deleted]]', err.message); @@ -368,7 +369,7 @@ describe('Flags', function () { Flags.validate({ type: 'post', id: 1, - uid: 3 + uid: 3, }, function (err) { assert.ok(err); assert.strictEqual('[[error:not-enough-reputation-to-flag]]', err.message); @@ -382,7 +383,7 @@ describe('Flags', function () { it('should add a note to a flag', function (done) { Flags.appendNote(1, 1, 'this is my note', function (err) { assert.ifError(err); - + db.getSortedSetRange('flag:1:notes', 0, -1, function (err, notes) { if (err) { throw err; @@ -422,11 +423,11 @@ describe('Flags', function () { assert.ifError(err); var compare = { uid: 1, - content: 'this is my note' + content: 'this is my note', }; - + var data = notes[1]; - for(var key in compare) { + for (var key in compare) { if (compare.hasOwnProperty(key)) { assert.ok(data[key]); assert.strictEqual(data[key], compare[key]); @@ -458,7 +459,7 @@ describe('Flags', function () { it('should add a new entry into a flag\'s history', function (done) { Flags.appendHistory(1, 1, { - state: 'rejected' + state: 'rejected', }, function (err) { assert.ifError(err); @@ -486,14 +487,16 @@ describe('Flags', function () { describe('(websockets)', function () { var SocketFlags = require('../src/socket.io/flags.js'); - var tid, pid, flag; + var tid, + pid, + flag; before(function (done) { Topics.post({ cid: 1, uid: 1, title: 'Another topic', - content: 'This is flaggable content' + content: 'This is flaggable content', }, function (err, topic) { tid = topic.postData.tid; pid = topic.postData.pid; @@ -507,7 +510,7 @@ describe('Flags', function () { SocketFlags.create({ uid: 2 }, { type: 'post', id: pid, - reason: 'foobar' + reason: 'foobar', }, function (err, flagObj) { flag = flagObj; assert.ifError(err); @@ -527,8 +530,8 @@ describe('Flags', function () { flagId: 2, data: [{ name: 'state', - value: 'wip' - }] + value: 'wip', + }], }, function (err, history) { assert.ifError(err); assert(Array.isArray(history)); @@ -543,7 +546,7 @@ describe('Flags', function () { it('should append a note to the flag', function (done) { SocketFlags.appendNote({ uid: 2 }, { flagId: 2, - note: 'lorem ipsum dolor sit amet' + note: 'lorem ipsum dolor sit amet', }, function (err, data) { assert.ifError(err); assert(data.hasOwnProperty('notes')); From 6e7b505ea762e40ca9a1c30949d022bf340ac297 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 24 Feb 2017 12:55:07 -0500 Subject: [PATCH 126/316] more linting --- public/src/client/flags/list.js | 4 +- src/controllers/mods.js | 16 ++++---- src/file.js | 2 +- src/flags.js | 67 +++++++++++++++++---------------- src/posts/delete.js | 1 - src/socket.io/flags.js | 9 ----- src/upgrade.js | 1 - src/user/admin.js | 2 - test/flags.js | 6 +-- 9 files changed, 48 insertions(+), 60 deletions(-) diff --git a/public/src/client/flags/list.js b/public/src/client/flags/list.js index 754dce8746..4c516eac38 100644 --- a/public/src/client/flags/list.js +++ b/public/src/client/flags/list.js @@ -16,7 +16,9 @@ define('forum/flags/list', ['components', 'Chart'], function (components, Chart) // Parse ajaxify data to set form values to reflect current filters for (var filter in ajaxify.data.filters) { - filtersEl.find('[name="' + filter + '"]').val(ajaxify.data.filters[filter]); + if (ajaxify.data.filters.hasOwnProperty(filter)) { + filtersEl.find('[name="' + filter + '"]').val(ajaxify.data.filters[filter]); + } } filtersEl.find('button').on('click', function () { diff --git a/src/controllers/mods.js b/src/controllers/mods.js index f55b852860..6365650323 100644 --- a/src/controllers/mods.js +++ b/src/controllers/mods.js @@ -42,16 +42,14 @@ modsController.flags.list = function (req, res, next) { if (!filters.cid) { // If mod and no cid filter, add filter for their modded categories filters.cid = res.locals.cids; - } else { + } else if (Array.isArray(filters.cid)) { // Remove cids they do not moderate - if (Array.isArray(filters.cid)) { - filters.cid = filters.cid.filter(function (cid) { - return res.locals.cids.indexOf(String(cid)) !== -1; - }); - } else if (res.locals.cids.indexOf(String(filters.cid)) === -1) { - filters.cid = res.locals.cids; - hasFilter = false; - } + filters.cid = filters.cid.filter(function (cid) { + return res.locals.cids.indexOf(String(cid)) !== -1; + }); + } else if (res.locals.cids.indexOf(String(filters.cid)) === -1) { + filters.cid = res.locals.cids; + hasFilter = false; } } diff --git a/src/file.js b/src/file.js index 572e2cf622..c48d24dc5e 100644 --- a/src/file.js +++ b/src/file.js @@ -92,7 +92,7 @@ file.allowedExtensions = function () { }; file.exists = function (path, callback) { - fs.stat(path, function (err, stat) { + fs.stat(path, function (err) { if (err) { if (err.code === 'ENOENT') { return callback(null, false); diff --git a/src/flags.js b/src/flags.js index 57dd90a8f6..33175d933f 100644 --- a/src/flags.js +++ b/src/flags.js @@ -1,7 +1,6 @@ 'use strict'; var async = require('async'); -var winston = require('winston'); var db = require('./database'); var user = require('./user'); var groups = require('./groups'); @@ -69,38 +68,40 @@ Flags.list = function (filters, uid, callback) { if (Object.keys(filters).length > 0) { for (var type in filters) { - switch (type) { - case 'type': - prepareSets('flags:byType:', filters[type]); - break; - - case 'state': - prepareSets('flags:byState:', filters[type]); - break; - - case 'reporterId': - prepareSets('flags:byReporter:', filters[type]); - break; - - case 'assignee': - prepareSets('flags:byAssignee:', filters[type]); - break; - - case 'targetUid': - prepareSets('flags:byTargetUid:', filters[type]); - break; - - case 'cid': - prepareSets('flags:byCid:', filters[type]); - break; - - case 'quick': - switch (filters.quick) { - case 'mine': - sets.push('flags:byAssignee:' + uid); + if (filters.hasOwnProperty(type)) { + switch (type) { + case 'type': + prepareSets('flags:byType:', filters[type]); + break; + + case 'state': + prepareSets('flags:byState:', filters[type]); + break; + + case 'reporterId': + prepareSets('flags:byReporter:', filters[type]); + break; + + case 'assignee': + prepareSets('flags:byAssignee:', filters[type]); + break; + + case 'targetUid': + prepareSets('flags:byTargetUid:', filters[type]); + break; + + case 'cid': + prepareSets('flags:byCid:', filters[type]); + break; + + case 'quick': + switch (filters.quick) { + case 'mine': + sets.push('flags:byAssignee:' + uid); + break; + } break; } - break; } } } @@ -341,7 +342,7 @@ Flags.create = function (type, id, uid, reason, timestamp, callback) { tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byPid:' + id, timestamp, flagId)); // by target pid } - async.parallel(tasks, function (err, data) { + async.parallel(tasks, function (err) { if (err) { return next(err); } @@ -479,7 +480,7 @@ Flags.update = function (flagId, uid, changeset, callback) { // Fire plugin hook tasks.push(async.apply(plugins.fireHook, 'action:flag.update', { flagId: flagId, changeset: changeset, uid: uid })); - async.parallel(tasks, function (err, data) { + async.parallel(tasks, function (err) { return next(err); }); }, diff --git a/src/posts/delete.js b/src/posts/delete.js index af281be698..a2874770af 100644 --- a/src/posts/delete.js +++ b/src/posts/delete.js @@ -8,7 +8,6 @@ var topics = require('../topics'); var user = require('../user'); var notifications = require('../notifications'); var plugins = require('../plugins'); -var flags = require('../flags'); module.exports = function (Posts) { Posts.delete = function (pid, uid, callback) { diff --git a/src/socket.io/flags.js b/src/socket.io/flags.js index d7964a1035..2cc2881832 100644 --- a/src/socket.io/flags.js +++ b/src/socket.io/flags.js @@ -1,17 +1,8 @@ 'use strict'; var async = require('async'); -var S = require('string'); var user = require('../user'); -var groups = require('../groups'); -var posts = require('../posts'); -var topics = require('../topics'); -var privileges = require('../privileges'); -var notifications = require('../notifications'); -var plugins = require('../plugins'); -var meta = require('../meta'); -var utils = require('../../public/src/utils'); var flags = require('../flags'); var SocketFlags = {}; diff --git a/src/upgrade.js b/src/upgrade.js index c37026ecd9..01c767315e 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -323,7 +323,6 @@ Upgrade.upgrade = function (callback) { var batch = require('./batch'); var posts = require('./posts'); var flags = require('./flags'); - var migrated = 0; batch.processSortedSet('posts:pid', function (ids, next) { posts.getPostsByPids(ids, 1, function (err, posts) { diff --git a/src/user/admin.js b/src/user/admin.js index cf3a2004db..28668cff80 100644 --- a/src/user/admin.js +++ b/src/user/admin.js @@ -3,10 +3,8 @@ var async = require('async'); var db = require('../database'); -var posts = require('../posts'); var plugins = require('../plugins'); var winston = require('winston'); -var flags = require('../flags'); module.exports = function (User) { User.logIP = function (uid, ip) { diff --git a/test/flags.js b/test/flags.js index 8623dbc269..0426541cd8 100644 --- a/test/flags.js +++ b/test/flags.js @@ -487,9 +487,9 @@ describe('Flags', function () { describe('(websockets)', function () { var SocketFlags = require('../src/socket.io/flags.js'); - var tid, - pid, - flag; + var tid; + var pid; + var flag; before(function (done) { Topics.post({ From 15542f1382d98a5e8145c2495b6e9cb59f515d53 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 24 Feb 2017 13:30:07 -0500 Subject: [PATCH 127/316] removing old tests that got added back in due to git being silly --- test/posts.js | 215 ---------------------------------------------- test/socket.io.js | 8 -- 2 files changed, 223 deletions(-) diff --git a/test/posts.js b/test/posts.js index 39c66701eb..0ea8074d28 100644 --- a/test/posts.js +++ b/test/posts.js @@ -501,221 +501,6 @@ describe('Post\'s', function () { }); }); - describe('flagging a post', function () { - var meta = require('../src/meta'); - var socketPosts = require('../src/socket.io/posts'); - it('should fail to flag a post due to low reputation', function (done) { - meta.config['privileges:flag'] = 10; - flagPost(function (err) { - assert.equal(err.message, '[[error:not-enough-reputation-to-flag]]'); - done(); - }); - }); - - it('should flag a post', function (done) { - meta.config['privileges:flag'] = -1; - flagPost(function (err) { - assert.ifError(err); - done(); - }); - }); - - it('should return nothing without a uid or a reason', function (done) { - socketPosts.flag({ uid: 0 }, { pid: postData.pid, reason: 'reason' }, function (err) { - assert.equal(err.message, '[[error:not-logged-in]]'); - socketPosts.flag({ uid: voteeUid }, {}, function (err) { - assert.equal(err.message, '[[error:invalid-data]]'); - done(); - }); - }); - }); - - it('should return an error without an existing post', function (done) { - socketPosts.flag({ uid: voteeUid }, { pid: 12312312, reason: 'reason' }, function (err) { - assert.equal(err.message, '[[error:no-post]]'); - done(); - }); - }); - - it('should return an error if the flag already exists', function (done) { - socketPosts.flag({ uid: voteeUid }, { pid: postData.pid, reason: 'reason' }, function (err) { - assert.equal(err.message, '[[error:already-flagged]]'); - done(); - }); - }); - }); - - function flagPost(next) { - var socketPosts = require('../src/socket.io/posts'); - socketPosts.flag({ uid: voteeUid }, { pid: postData.pid, reason: 'reason' }, next); - } - - describe('get flag data', function () { - it('should see the flagged post', function (done) { - posts.isFlaggedByUser(postData.pid, voteeUid, function (err, hasFlagged) { - assert.ifError(err); - assert(hasFlagged); - done(); - }); - }); - - it('should return the flagged post data', function (done) { - posts.getFlags('posts:flagged', cid, voteeUid, 0, -1, function (err, flagData) { - assert.ifError(err); - assert(flagData.posts); - assert(flagData.count); - assert.equal(flagData.count, 1); - assert.equal(flagData.posts.length, 1); - assert(flagData.posts[0].flagReasons); - assert.equal(flagData.posts[0].flagReasons.length, 1); - assert.strictEqual(flagData.posts[0].flagReasons[0].reason, 'reason'); - assert(flagData.posts[0].flagData); - assert.strictEqual(flagData.posts[0].flagData.state, 'open'); - done(); - }); - }); - }); - - describe('updating a flag', function () { - var socketPosts = require('../src/socket.io/posts'); - - it('should update a flag', function (done) { - async.waterfall([ - function (next) { - socketPosts.updateFlag({ uid: globalModUid }, { - pid: postData.pid, - data: [ - { name: 'assignee', value: `${globalModUid}` }, - { name: 'notes', value: 'notes' }, - ], - }, function (err) { - assert.ifError(err); - posts.getFlags('posts:flagged', cid, globalModUid, 0, -1, function (err, flagData) { - assert.ifError(err); - assert(flagData.posts); - assert.equal(flagData.posts.length, 1); - assert.deepEqual({ - assignee: flagData.posts[0].flagData.assignee, - notes: flagData.posts[0].flagData.notes, - state: flagData.posts[0].flagData.state, - labelClass: flagData.posts[0].flagData.labelClass, - }, { - assignee: `${globalModUid}`, - notes: 'notes', - state: 'open', - labelClass: 'info', - }); - next(); - }); - }); - }, function (next) { - posts.updateFlagData(globalModUid, postData.pid, { - state: 'rejected', - }, function (err) { - assert.ifError(err); - posts.getFlags('posts:flagged', cid, globalModUid, 0, -1, function (err, flagData) { - assert.ifError(err); - assert(flagData.posts); - assert.equal(flagData.posts.length, 1); - assert.deepEqual({ - state: flagData.posts[0].flagData.state, - labelClass: flagData.posts[0].flagData.labelClass, - }, { - state: 'rejected', - labelClass: 'danger', - }); - next(); - }); - }); - }, function (next) { - posts.updateFlagData(globalModUid, postData.pid, { - state: 'wip', - }, function (err) { - assert.ifError(err); - posts.getFlags('posts:flagged', cid, globalModUid, 0, -1, function (err, flagData) { - assert.ifError(err); - assert(flagData.posts); - assert.equal(flagData.posts.length, 1); - assert.deepEqual({ - state: flagData.posts[0].flagData.state, - labelClass: flagData.posts[0].flagData.labelClass, - }, { - state: 'wip', - labelClass: 'warning', - }); - next(); - }); - }); - }, function (next) { - posts.updateFlagData(globalModUid, postData.pid, { - state: 'resolved', - }, function (err) { - assert.ifError(err); - posts.getFlags('posts:flagged', cid, globalModUid, 0, -1, function (err, flagData) { - assert.ifError(err); - assert(flagData.posts); - assert.equal(flagData.posts.length, 1); - assert.deepEqual({ - state: flagData.posts[0].flagData.state, - labelClass: flagData.posts[0].flagData.labelClass, - }, { - state: 'resolved', - labelClass: 'success', - }); - next(); - }); - }); - }, - ], done); - }); - }); - - describe('dismissing a flag', function () { - var socketPosts = require('../src/socket.io/posts'); - - it('should dismiss a flag', function (done) { - socketPosts.dismissFlag({ uid: globalModUid }, postData.pid, function (err) { - assert.ifError(err); - posts.isFlaggedByUser(postData.pid, voteeUid, function (err, hasFlagged) { - assert.ifError(err); - assert(!hasFlagged); - flagPost(function (err) { - assert.ifError(err); - done(); - }); - }); - }); - }); - - it('should dismiss all of a user\'s flags', function (done) { - posts.dismissUserFlags(voteeUid, function (err) { - assert.ifError(err); - posts.isFlaggedByUser(postData.pid, voteeUid, function (err, hasFlagged) { - assert.ifError(err); - assert(!hasFlagged); - flagPost(function (err) { - assert.ifError(err); - done(); - }); - }); - }); - }); - - it('should dismiss all flags', function (done) { - socketPosts.dismissAllFlags({ uid: globalModUid }, {}, function (err) { - assert.ifError(err); - posts.isFlaggedByUser(postData.pid, voteeUid, function (err, hasFlagged) { - assert.ifError(err); - assert(!hasFlagged); - flagPost(function (err) { - assert.ifError(err); - done(); - }); - }); - }); - }); - }); - describe('getPostSummaryByPids', function () { it('should return empty array for empty pids', function (done) { posts.getPostSummaryByPids([], 0, {}, function (err, data) { diff --git a/test/socket.io.js b/test/socket.io.js index a94aad8551..575983a18a 100644 --- a/test/socket.io.js +++ b/test/socket.io.js @@ -244,14 +244,6 @@ describe('socket.io', function () { }); }); - it('should reset flags', function (done) { - var socketAdmin = require('../src/socket.io/admin'); - socketAdmin.user.resetFlags({ uid: adminUid }, [regularUid], function (err) { - assert.ifError(err); - done(); - }); - }); - describe('validation emails', function () { var socketAdmin = require('../src/socket.io/admin'); var meta = require('../src/meta'); From 35248c543b5175ee2559aa1631514bad29b6cc28 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Sat, 25 Feb 2017 15:40:05 -0500 Subject: [PATCH 128/316] linting nodebb executable --- nodebb | 49 +++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/nodebb b/nodebb index 8ee1628855..f0fac9d0cf 100755 --- a/nodebb +++ b/nodebb @@ -29,7 +29,7 @@ if (args.dev) { function getRunningPid(callback) { fs.readFile(__dirname + '/pidfile', { - encoding: 'utf-8' + encoding: 'utf-8', }, function (err, pid) { if (err) { return callback(err); @@ -38,7 +38,7 @@ function getRunningPid(callback) { try { process.kill(parseInt(pid, 10), 0); callback(null, parseInt(pid, 10)); - } catch(e) { + } catch (e) { callback(e); } }); @@ -52,7 +52,7 @@ function getCurrentVersion(callback) { try { pkg = JSON.parse(pkg); return callback(null, pkg.version); - } catch(err) { + } catch (err) { return callback(err); } }); @@ -60,20 +60,21 @@ function getCurrentVersion(callback) { function fork(args) { return cproc.fork('app.js', args, { cwd: __dirname, - silent: false + silent: false, }); } function getInstalledPlugins(callback) { async.parallel({ files: async.apply(fs.readdir, path.join(__dirname, 'node_modules')), - deps: async.apply(fs.readFile, path.join(__dirname, 'package.json'), { encoding: 'utf-8' }) + deps: async.apply(fs.readFile, path.join(__dirname, 'package.json'), { encoding: 'utf-8' }), }, function (err, payload) { if (err) { return callback(err); } - var isNbbModule = /^nodebb-(?:plugin|theme|widget|rewards)-[\w\-]+$/, - moduleName, isGitRepo; + var isNbbModule = /^nodebb-(?:plugin|theme|widget|rewards)-[\w\-]+$/; + var moduleName; + var isGitRepo; payload.files = payload.files.filter(function (file) { return isNbbModule.test(file); @@ -98,7 +99,7 @@ function getInstalledPlugins(callback) { try { fs.accessSync(path.join(__dirname, 'node_modules/' + moduleName, '.git')); isGitRepo = true; - } catch(e) { + } catch (e) { isGitRepo = false; } @@ -144,7 +145,7 @@ function checkPlugins(standalone, callback) { async.waterfall([ async.apply(async.parallel, { plugins: async.apply(getInstalledPlugins), - version: async.apply(getCurrentVersion) + version: async.apply(getCurrentVersion), }), function (payload, next) { var toCheck = Object.keys(payload.plugins); @@ -157,7 +158,7 @@ function checkPlugins(standalone, callback) { request({ method: 'GET', url: 'https://packages.nodebb.org/api/v1/suggest?version=' + payload.version + '&package[]=' + toCheck.join('&package[]='), - json: true + json: true, }, function (err, res, body) { if (err) { process.stdout.write('error'.red + '\n'.reset); @@ -169,7 +170,8 @@ function checkPlugins(standalone, callback) { body = [body]; } - var current, suggested, + var current, + suggested, upgradable = body.map(function (suggestObj) { current = payload.plugins[suggestObj.package]; suggested = suggestObj.version; @@ -178,16 +180,15 @@ function checkPlugins(standalone, callback) { return { name: suggestObj.package, current: current, - suggested: suggested + suggested: suggested, }; - } else { - return null; } + return null; }).filter(Boolean); next(null, upgradable); }); - } + }, ], callback); } function upgradePlugins(callback) { @@ -223,7 +224,7 @@ function upgradePlugins(callback) { prompt.get({ name: 'upgrade', description: 'Proceed with upgrade (y|n)?'.reset, - type: 'string' + type: 'string', }, function (err, result) { if (err) { return callback(err); @@ -280,7 +281,7 @@ var commands = { // Spawn a new NodeBB process cproc.fork(__dirname + '/loader.js', { - env: process.env + env: process.env, }); }, }, @@ -320,7 +321,7 @@ var commands = { process.stdout.write('\n\n'.reset); cproc.spawn('tail', ['-F', './logs/output.log'], { cwd: __dirname, - stdio: 'inherit' + stdio: 'inherit', }); }, }, @@ -334,11 +335,11 @@ var commands = { // Spawn a new NodeBB process cproc.fork(__dirname + '/loader.js', { - env: process.env + env: process.env, }); cproc.spawn('tail', ['-F', './logs/output.log'], { cwd: __dirname, - stdio: 'inherit' + stdio: 'inherit', }); }, }, @@ -348,13 +349,13 @@ var commands = { handler: function () { process.env.NODE_ENV = 'development'; cproc.fork(__dirname + '/loader.js', ['--no-daemon', '--no-silent'], { - env: process.env + env: process.env, }); }, }, build: { description: 'Compile static assets (CSS, Javascript, etc)', - usage: 'Usage: ' + './nodebb build'.yellow + ' [js,clientCSS,acpCSS,tpl,lang]'.red + '\n' + + usage: 'Usage: ' + './nodebb build'.yellow + ' [js,clientCSS,acpCSS,tpl,lang]'.red + '\n' + ' e.g. ' + './nodebb build js,tpl'.yellow + '\tbuilds JS and templates\n' + ' ' + './nodebb build'.yellow + '\t\tbuilds all targets\n', handler: function () { @@ -434,7 +435,7 @@ var commands = { var upgradeProc = fork(arr); upgradeProc.on('close', next); - } + }, ], function (err) { if (err) { process.stdout.write('\nError'.red + ': ' + err.message + '\n'); @@ -442,7 +443,7 @@ var commands = { var message = 'NodeBB Upgrade Complete!'; // some consoles will return undefined/zero columns, so just use 2 spaces in upgrade script if we can't get our column count var columns = process.stdout.columns; - var spaces = columns ? new Array(Math.floor(columns / 2) - (message.length / 2) + 1).join(' ') : " "; + var spaces = columns ? new Array(Math.floor(columns / 2) - (message.length / 2) + 1).join(' ') : ' '; process.stdout.write('OK\n'.green); process.stdout.write('\n' + spaces + message.green.bold + '\n\n'.reset); From b385655dba522a2a1f3e1725395b00313cffe6a5 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Sat, 25 Feb 2017 15:40:22 -0500 Subject: [PATCH 129/316] adding upgrade scripts from all of v1.x.x, #5467 --- src/upgrade.js | 16 ++- src/upgrades/assign_topic_read_privilege.js | 73 ++++++++++++ src/upgrades/chat_room_hashes.js | 41 +++++++ src/upgrades/chat_upgrade.js | 87 ++++++++++++++ .../dismiss_flags_from_deleted_topics.js | 43 +++++++ .../edit_delete_deletetopic_privileges.js | 109 ++++++++++++++++++ src/upgrades/global_moderators.js | 34 ++++++ src/upgrades/group_title_update.js | 34 ++++++ src/upgrades/remove_negative_best_posts.js | 22 ++++ src/upgrades/separate_upvote_downvote.js | 50 ++++++++ src/upgrades/social_post_sharing.js | 23 ++++ src/upgrades/theme_to_active_plugins.js | 18 +++ src/upgrades/upload_privileges.js | 40 +++++++ src/upgrades/user_best_posts.js | 31 +++++ src/upgrades/user_post_count_per_tid.js | 50 ++++++++ src/upgrades/users_notvalidated.js | 31 +++++ 16 files changed, 700 insertions(+), 2 deletions(-) create mode 100644 src/upgrades/assign_topic_read_privilege.js create mode 100644 src/upgrades/chat_room_hashes.js create mode 100644 src/upgrades/chat_upgrade.js create mode 100644 src/upgrades/dismiss_flags_from_deleted_topics.js create mode 100644 src/upgrades/edit_delete_deletetopic_privileges.js create mode 100644 src/upgrades/global_moderators.js create mode 100644 src/upgrades/group_title_update.js create mode 100644 src/upgrades/remove_negative_best_posts.js create mode 100644 src/upgrades/separate_upvote_downvote.js create mode 100644 src/upgrades/social_post_sharing.js create mode 100644 src/upgrades/theme_to_active_plugins.js create mode 100644 src/upgrades/upload_privileges.js create mode 100644 src/upgrades/user_best_posts.js create mode 100644 src/upgrades/user_post_count_per_tid.js create mode 100644 src/upgrades/users_notvalidated.js diff --git a/src/upgrade.js b/src/upgrade.js index 3558a2b30a..33aaa831ca 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -10,9 +10,21 @@ var utils = require('../public/src/utils'); var Upgrade = { available: [ + { + version: '1.0.0', + upgrades: ['chat_upgrade', 'chat_room_hashes', 'theme_to_active_plugins', 'user_best_posts', 'users_notvalidated', 'global_moderators', 'social_post_sharing'], + }, + { + version: '1.1.0', + upgrades: ['group_title_update', 'user_post_count_per_tid', 'dismiss_flags_from_deleted_topics', 'assign_topic_read_privilege', 'separate_upvote_downvote'], + }, + { + version: '1.1.1', + upgrades: ['upload_privileges', 'remove_negative_best_posts'], + }, { version: '1.2.0', - upgrades: ['category_recent_tids'], + upgrades: ['category_recent_tids', 'edit_delete_deletetopic_privileges'], }, { version: '1.3.0', @@ -85,7 +97,7 @@ Upgrade.process = function (files, skipCount, callback) { var scriptExport = require(file); var date = new Date(scriptExport.timestamp); - process.stdout.write(' → '.white + String('[' + [date.getFullYear(), date.getMonth() + 1, date.getDate() + 1].join('/') + '] ').gray + String(scriptExport.name).reset + '... '); + process.stdout.write(' → '.white + String('[' + [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()].join('/') + '] ').gray + String(scriptExport.name).reset + '... '); // Do the upgrade... scriptExport.method(function (err) { diff --git a/src/upgrades/assign_topic_read_privilege.js b/src/upgrades/assign_topic_read_privilege.js new file mode 100644 index 0000000000..1e65d0d6f7 --- /dev/null +++ b/src/upgrades/assign_topic_read_privilege.js @@ -0,0 +1,73 @@ +/* jslint node: true */ + +'use strict'; + +var db = require('../database'); + +var async = require('async'); +var winston = require('winston'); + +module.exports = { + name: 'Giving topics:read privs to any group that was previously allowed to Find & Access Category', + timestamp: Date.UTC(2016, 4, 28), + method: function (callback) { + var groupsAPI = require('../groups'); + var privilegesAPI = require('../privileges'); + + db.getSortedSetRange('categories:cid', 0, -1, function (err, cids) { + if (err) { + return callback(err); + } + + async.eachSeries(cids, function (cid, next) { + privilegesAPI.categories.list(cid, function (err, data) { + if (err) { + return next(err); + } + + var groups = data.groups; + var users = data.users; + + async.waterfall([ + function (next) { + async.eachSeries(groups, function (group, next) { + if (group.privileges['groups:read']) { + return groupsAPI.join('cid:' + cid + ':privileges:groups:topics:read', group.name, function (err) { + if (!err) { + winston.verbose('cid:' + cid + ':privileges:groups:topics:read granted to gid: ' + group.name); + } + + return next(err); + }); + } + + next(null); + }, next); + }, + function (next) { + async.eachSeries(users, function (user, next) { + if (user.privileges.read) { + return groupsAPI.join('cid:' + cid + ':privileges:topics:read', user.uid, function (err) { + if (!err) { + winston.verbose('cid:' + cid + ':privileges:topics:read granted to uid: ' + user.uid); + } + + return next(err); + }); + } + + next(null); + }, next); + }, + ], function (err) { + if (!err) { + winston.verbose('-- cid ' + cid + ' upgraded'); + } + + next(err); + }); + }); + }, callback); + }); + }, +}; diff --git a/src/upgrades/chat_room_hashes.js b/src/upgrades/chat_room_hashes.js new file mode 100644 index 0000000000..bccf85282c --- /dev/null +++ b/src/upgrades/chat_room_hashes.js @@ -0,0 +1,41 @@ +/* jslint node: true */ + +'use strict'; + +var db = require('../database'); + +var async = require('async'); + +module.exports = { + name: 'Chat room hashes', + timestamp: Date.UTC(2015, 11, 23), + method: function (callback) { + db.getObjectField('global', 'nextChatRoomId', function (err, nextChatRoomId) { + if (err) { + return callback(err); + } + var currentChatRoomId = 1; + async.whilst(function () { + return currentChatRoomId <= nextChatRoomId; + }, function (next) { + db.getSortedSetRange('chat:room:' + currentChatRoomId + ':uids', 0, 0, function (err, uids) { + if (err) { + return next(err); + } + if (!Array.isArray(uids) || !uids.length || !uids[0]) { + currentChatRoomId += 1; + return next(); + } + + db.setObject('chat:room:' + currentChatRoomId, { owner: uids[0], roomId: currentChatRoomId }, function (err) { + if (err) { + return next(err); + } + currentChatRoomId += 1; + next(); + }); + }); + }, callback); + }); + }, +}; diff --git a/src/upgrades/chat_upgrade.js b/src/upgrades/chat_upgrade.js new file mode 100644 index 0000000000..ce49b501de --- /dev/null +++ b/src/upgrades/chat_upgrade.js @@ -0,0 +1,87 @@ +/* jslint node: true */ + +'use strict'; + +var db = require('../database'); + +var async = require('async'); +var winston = require('winston'); + +module.exports = { + name: 'Upgrading chats', + timestamp: Date.UTC(2015, 11, 15), + method: function (callback) { + db.getObjectFields('global', ['nextMid', 'nextChatRoomId'], function (err, globalData) { + if (err) { + return callback(err); + } + + var rooms = {}; + var roomId = globalData.nextChatRoomId || 1; + var currentMid = 1; + + async.whilst(function () { + return currentMid <= globalData.nextMid; + }, function (next) { + db.getObject('message:' + currentMid, function (err, message) { + var msgTime; + + function addMessageToUids(roomId, callback) { + async.parallel([ + function (next) { + db.sortedSetAdd('uid:' + message.fromuid + ':chat:room:' + roomId + ':mids', msgTime, currentMid, next); + }, + function (next) { + db.sortedSetAdd('uid:' + message.touid + ':chat:room:' + roomId + ':mids', msgTime, currentMid, next); + }, + ], callback); + } + + if (err || !message) { + winston.verbose('skipping chat message ', currentMid); + currentMid += 1; + return next(err); + } + + var pairID = [parseInt(message.fromuid, 10), parseInt(message.touid, 10)].sort().join(':'); + msgTime = parseInt(message.timestamp, 10); + + if (rooms[pairID]) { + winston.verbose('adding message ' + currentMid + ' to existing roomID ' + roomId); + addMessageToUids(rooms[pairID], function (err) { + if (err) { + return next(err); + } + currentMid += 1; + next(); + }); + } else { + winston.verbose('adding message ' + currentMid + ' to new roomID ' + roomId); + async.parallel([ + function (next) { + db.sortedSetAdd('uid:' + message.fromuid + ':chat:rooms', msgTime, roomId, next); + }, + function (next) { + db.sortedSetAdd('uid:' + message.touid + ':chat:rooms', msgTime, roomId, next); + }, + function (next) { + db.sortedSetAdd('chat:room:' + roomId + ':uids', [msgTime, msgTime + 1], [message.fromuid, message.touid], next); + }, + function (next) { + addMessageToUids(roomId, next); + }, + ], function (err) { + if (err) { + return next(err); + } + rooms[pairID] = roomId; + roomId += 1; + currentMid += 1; + db.setObjectField('global', 'nextChatRoomId', roomId, next); + }); + } + }); + }, callback); + }); + }, +}; diff --git a/src/upgrades/dismiss_flags_from_deleted_topics.js b/src/upgrades/dismiss_flags_from_deleted_topics.js new file mode 100644 index 0000000000..cf4af73436 --- /dev/null +++ b/src/upgrades/dismiss_flags_from_deleted_topics.js @@ -0,0 +1,43 @@ +/* jslint node: true */ + +'use strict'; + +var db = require('../database'); + +var async = require('async'); +var winston = require('winston'); + +module.exports = { + name: 'Dismiss flags from deleted topics', + timestamp: Date.UTC(2016, 3, 29), + method: function (callback) { + var posts = require('../posts'); + var topics = require('../topics'); + + var pids; + var tids; + + async.waterfall([ + async.apply(db.getSortedSetRange, 'posts:flagged', 0, -1), + function (_pids, next) { + pids = _pids; + posts.getPostsFields(pids, ['tid'], next); + }, + function (_tids, next) { + tids = _tids.map(function (a) { + return a.tid; + }); + + topics.getTopicsFields(tids, ['deleted'], next); + }, + function (state, next) { + var toDismiss = state.map(function (a, idx) { + return parseInt(a.deleted, 10) === 1 ? pids[idx] : null; + }).filter(Boolean); + + winston.verbose('[2016/04/29] ' + toDismiss.length + ' dismissable flags found'); + async.each(toDismiss, posts.dismissFlag, next); + }, + ], callback); + }, +}; diff --git a/src/upgrades/edit_delete_deletetopic_privileges.js b/src/upgrades/edit_delete_deletetopic_privileges.js new file mode 100644 index 0000000000..5ba6f604c5 --- /dev/null +++ b/src/upgrades/edit_delete_deletetopic_privileges.js @@ -0,0 +1,109 @@ +/* jslint node: true */ + +'use strict'; + +var db = require('../database'); + +var async = require('async'); +var winston = require('winston'); + +module.exports = { + name: 'Granting edit/delete/delete topic on existing categories', + timestamp: Date.UTC(2016, 7, 7), + method: function (callback) { + var groupsAPI = require('../groups'); + var privilegesAPI = require('../privileges'); + + db.getSortedSetRange('categories:cid', 0, -1, function (err, cids) { + if (err) { + return callback(err); + } + + async.eachSeries(cids, function (cid, next) { + privilegesAPI.categories.list(cid, function (err, data) { + if (err) { + return next(err); + } + + var groups = data.groups; + var users = data.users; + + async.waterfall([ + function (next) { + async.eachSeries(groups, function (group, next) { + if (group.privileges['groups:topics:reply']) { + return async.parallel([ + async.apply(groupsAPI.join, 'cid:' + cid + ':privileges:groups:posts:edit', group.name), + async.apply(groupsAPI.join, 'cid:' + cid + ':privileges:groups:posts:delete', group.name), + ], function (err) { + if (!err) { + winston.verbose('cid:' + cid + ':privileges:groups:posts:edit, cid:' + cid + ':privileges:groups:posts:delete granted to gid: ' + group.name); + } + + return next(err); + }); + } + + next(null); + }, next); + }, + function (next) { + async.eachSeries(groups, function (group, next) { + if (group.privileges['groups:topics:create']) { + return groupsAPI.join('cid:' + cid + ':privileges:groups:topics:delete', group.name, function (err) { + if (!err) { + winston.verbose('cid:' + cid + ':privileges:groups:topics:delete granted to gid: ' + group.name); + } + + return next(err); + }); + } + + next(null); + }, next); + }, + function (next) { + async.eachSeries(users, function (user, next) { + if (user.privileges['topics:reply']) { + return async.parallel([ + async.apply(groupsAPI.join, 'cid:' + cid + ':privileges:posts:edit', user.uid), + async.apply(groupsAPI.join, 'cid:' + cid + ':privileges:posts:delete', user.uid), + ], function (err) { + if (!err) { + winston.verbose('cid:' + cid + ':privileges:posts:edit, cid:' + cid + ':privileges:posts:delete granted to uid: ' + user.uid); + } + + return next(err); + }); + } + + next(null); + }, next); + }, + function (next) { + async.eachSeries(users, function (user, next) { + if (user.privileges['topics:create']) { + return groupsAPI.join('cid:' + cid + ':privileges:topics:delete', user.uid, function (err) { + if (!err) { + winston.verbose('cid:' + cid + ':privileges:topics:delete granted to uid: ' + user.uid); + } + + return next(err); + }); + } + + next(null); + }, next); + }, + ], function (err) { + if (!err) { + winston.verbose('-- cid ' + cid + ' upgraded'); + } + + next(err); + }); + }); + }, callback); + }); + }, +}; diff --git a/src/upgrades/global_moderators.js b/src/upgrades/global_moderators.js new file mode 100644 index 0000000000..bfe909a11b --- /dev/null +++ b/src/upgrades/global_moderators.js @@ -0,0 +1,34 @@ +/* jslint node: true */ + +'use strict'; + +var async = require('async'); + +module.exports = { + name: 'Creating Global moderators group', + timestamp: Date.UTC(2016, 0, 23), + method: function (callback) { + var groups = require('../groups'); + async.waterfall([ + function (next) { + groups.exists('Global Moderators', next); + }, + function (exists, next) { + if (exists) { + return next(null, null); + } + groups.create({ + name: 'Global Moderators', + userTitle: 'Global Moderator', + description: 'Forum wide moderators', + hidden: 0, + private: 1, + disableJoinRequests: 1, + }, next); + }, + function (groupData, next) { + groups.show('Global Moderators', next); + }, + ], callback); + }, +}; diff --git a/src/upgrades/group_title_update.js b/src/upgrades/group_title_update.js new file mode 100644 index 0000000000..5f82e97bfe --- /dev/null +++ b/src/upgrades/group_title_update.js @@ -0,0 +1,34 @@ +/* jslint node: true */ + +'use strict'; + +var db = require('../database'); + +var async = require('async'); +var winston = require('winston'); + +module.exports = { + name: 'Group title from settings to user profile', + timestamp: Date.UTC(2016, 3, 14), + method: function (callback) { + var user = require('../user'); + var batch = require('../batch'); + var count = 0; + batch.processSortedSet('users:joindate', function (uids, next) { + winston.verbose('upgraded ' + count + ' users'); + user.getMultipleUserSettings(uids, function (err, settings) { + if (err) { + return next(err); + } + count += uids.length; + settings = settings.filter(function (setting) { + return setting && setting.groupTitle; + }); + + async.each(settings, function (setting, next) { + db.setObjectField('user:' + setting.uid, 'groupTitle', setting.groupTitle, next); + }, next); + }); + }, {}, callback); + }, +}; diff --git a/src/upgrades/remove_negative_best_posts.js b/src/upgrades/remove_negative_best_posts.js new file mode 100644 index 0000000000..604d1d8234 --- /dev/null +++ b/src/upgrades/remove_negative_best_posts.js @@ -0,0 +1,22 @@ +/* jslint node: true */ + +'use strict'; + +var db = require('../database'); + +var async = require('async'); +var winston = require('winston'); + +module.exports = { + name: 'Removing best posts with negative scores', + timestamp: Date.UTC(2016, 7, 5), + method: function (callback) { + var batch = require('../batch'); + batch.processSortedSet('users:joindate', function (ids, next) { + async.each(ids, function (id, next) { + winston.verbose('processing uid ' + id); + db.sortedSetsRemoveRangeByScore(['uid:' + id + ':posts:votes'], '-inf', 0, next); + }, next); + }, {}, callback); + }, +}; diff --git a/src/upgrades/separate_upvote_downvote.js b/src/upgrades/separate_upvote_downvote.js new file mode 100644 index 0000000000..b313c7439d --- /dev/null +++ b/src/upgrades/separate_upvote_downvote.js @@ -0,0 +1,50 @@ +/* jslint node: true */ + +'use strict'; + +var db = require('../database'); + +var async = require('async'); +var winston = require('winston'); + +module.exports = { + name: 'Store upvotes/downvotes separately', + timestamp: Date.UTC(2016, 5, 13), + method: function (callback) { + var batch = require('../batch'); + var posts = require('../posts'); + var count = 0; + batch.processSortedSet('posts:pid', function (pids, next) { + winston.verbose('upgraded ' + count + ' posts'); + count += pids.length; + async.each(pids, function (pid, next) { + async.parallel({ + upvotes: function (next) { + db.setCount('pid:' + pid + ':upvote', next); + }, + downvotes: function (next) { + db.setCount('pid:' + pid + ':downvote', next); + }, + }, function (err, results) { + if (err) { + return next(err); + } + var data = {}; + + if (parseInt(results.upvotes, 10) > 0) { + data.upvotes = results.upvotes; + } + if (parseInt(results.downvotes, 10) > 0) { + data.downvotes = results.downvotes; + } + + if (Object.keys(data).length) { + posts.setPostFields(pid, data, next); + } else { + next(); + } + }, next); + }, next); + }, {}, callback); + }, +}; diff --git a/src/upgrades/social_post_sharing.js b/src/upgrades/social_post_sharing.js new file mode 100644 index 0000000000..22c4a8c7cf --- /dev/null +++ b/src/upgrades/social_post_sharing.js @@ -0,0 +1,23 @@ +/* jslint node: true */ + +'use strict'; + +var db = require('../database'); + +var async = require('async'); + +module.exports = { + name: 'Social: Post Sharing', + timestamp: Date.UTC(2016, 1, 25), + method: function (callback) { + var social = require('../social'); + async.parallel([ + function (next) { + social.setActivePostSharingNetworks(['facebook', 'google', 'twitter'], next); + }, + function (next) { + db.deleteObjectField('config', 'disableSocialButtons', next); + }, + ], callback); + }, +}; diff --git a/src/upgrades/theme_to_active_plugins.js b/src/upgrades/theme_to_active_plugins.js new file mode 100644 index 0000000000..3adbca16dc --- /dev/null +++ b/src/upgrades/theme_to_active_plugins.js @@ -0,0 +1,18 @@ +/* jslint node: true */ + +'use strict'; + +var db = require('../database'); + +var async = require('async'); + +module.exports = { + name: 'Adding theme to active plugins sorted set', + timestamp: Date.UTC(2015, 11, 23), + method: function (callback) { + async.waterfall([ + async.apply(db.getObjectField, 'config', 'theme:id'), + async.apply(db.sortedSetAdd, 'plugins:active', 0), + ], callback); + }, +}; diff --git a/src/upgrades/upload_privileges.js b/src/upgrades/upload_privileges.js new file mode 100644 index 0000000000..b3268a2022 --- /dev/null +++ b/src/upgrades/upload_privileges.js @@ -0,0 +1,40 @@ +/* jslint node: true */ + +'use strict'; + +var db = require('../database'); + +var async = require('async'); + +module.exports = { + name: 'Giving upload privileges', + timestamp: Date.UTC(2016, 6, 12), + method: function (callback) { + var privilegesAPI = require('../privileges'); + var meta = require('../meta'); + + db.getSortedSetRange('categories:cid', 0, -1, function (err, cids) { + if (err) { + return callback(err); + } + + async.eachSeries(cids, function (cid, next) { + privilegesAPI.categories.list(cid, function (err, data) { + if (err) { + return next(err); + } + async.eachSeries(data.groups, function (group, next) { + if (group.name === 'guests' && parseInt(meta.config.allowGuestUploads, 10) !== 1) { + return next(); + } + if (group.privileges['groups:read']) { + privilegesAPI.categories.give(['upload:post:image'], cid, group.name, next); + } else { + next(); + } + }, next); + }); + }, callback); + }); + }, +}; diff --git a/src/upgrades/user_best_posts.js b/src/upgrades/user_best_posts.js new file mode 100644 index 0000000000..126539c2b7 --- /dev/null +++ b/src/upgrades/user_best_posts.js @@ -0,0 +1,31 @@ +/* jslint node: true */ + +'use strict'; + +var db = require('../database'); + +var async = require('async'); +var winston = require('winston'); + +module.exports = { + name: 'Creating user best post sorted sets', + timestamp: Date.UTC(2016, 0, 14), + method: function (callback) { + var batch = require('../batch'); + + batch.processSortedSet('posts:pid', function (ids, next) { + async.eachSeries(ids, function (id, next) { + db.getObjectFields('post:' + id, ['pid', 'uid', 'votes'], function (err, postData) { + if (err) { + return next(err); + } + if (!postData || !parseInt(postData.votes, 10) || !parseInt(postData.uid, 10)) { + return next(); + } + winston.verbose('processing pid: ' + postData.pid + ' uid: ' + postData.uid + ' votes: ' + postData.votes); + db.sortedSetAdd('uid:' + postData.uid + ':posts:votes', postData.votes, postData.pid, next); + }); + }, next); + }, {}, callback); + }, +}; diff --git a/src/upgrades/user_post_count_per_tid.js b/src/upgrades/user_post_count_per_tid.js new file mode 100644 index 0000000000..c0e86c4f52 --- /dev/null +++ b/src/upgrades/user_post_count_per_tid.js @@ -0,0 +1,50 @@ +/* jslint node: true */ + +'use strict'; + +var db = require('../database'); + +var async = require('async'); +var winston = require('winston'); + +module.exports = { + name: 'Users post count per tid', + timestamp: Date.UTC(2016, 3, 19), + method: function (callback) { + var batch = require('../batch'); + var topics = require('../topics'); + var count = 0; + batch.processSortedSet('topics:tid', function (tids, next) { + winston.verbose('upgraded ' + count + ' topics'); + count += tids.length; + async.each(tids, function (tid, next) { + db.delete('tid:' + tid + ':posters', function (err) { + if (err) { + return next(err); + } + topics.getPids(tid, function (err, pids) { + if (err) { + return next(err); + } + + if (!pids.length) { + return next(); + } + + async.eachSeries(pids, function (pid, next) { + db.getObjectField('post:' + pid, 'uid', function (err, uid) { + if (err) { + return next(err); + } + if (!parseInt(uid, 10)) { + return next(); + } + db.sortedSetIncrBy('tid:' + tid + ':posters', 1, uid, next); + }); + }, next); + }); + }); + }, next); + }, {}, callback); + }, +}; diff --git a/src/upgrades/users_notvalidated.js b/src/upgrades/users_notvalidated.js new file mode 100644 index 0000000000..9c23b6a539 --- /dev/null +++ b/src/upgrades/users_notvalidated.js @@ -0,0 +1,31 @@ +/* jslint node: true */ + +'use strict'; + +var db = require('../database'); + +var async = require('async'); +var winston = require('winston'); + +module.exports = { + name: 'Creating users:notvalidated', + timestamp: Date.UTC(2016, 0, 20), + method: function (callback) { + var batch = require('../batch'); + var now = Date.now(); + batch.processSortedSet('users:joindate', function (ids, next) { + async.eachSeries(ids, function (id, next) { + db.getObjectFields('user:' + id, ['uid', 'email:confirmed'], function (err, userData) { + if (err) { + return next(err); + } + if (!userData || !parseInt(userData.uid, 10) || parseInt(userData['email:confirmed'], 10) === 1) { + return next(); + } + winston.verbose('processing uid: ' + userData.uid + ' email:confirmed: ' + userData['email:confirmed']); + db.sortedSetAdd('users:notvalidated', now, userData.uid, next); + }); + }, next); + }, callback); + }, +}; From b8ee09aacb4fe0a924d2175e7136cabfd38ec655 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Sat, 25 Feb 2017 15:45:45 -0500 Subject: [PATCH 130/316] re: #5467 -- requiring exact filename match sans extension, for a single upgrade to work --- src/upgrade.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/upgrade.js b/src/upgrade.js index 33aaa831ca..a5e4d89158 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -75,7 +75,7 @@ Upgrade.runSingle = function (query, callback) { async.apply(utils.walk, path.join(__dirname, './upgrades')), function (files, next) { next(null, files.filter(function (file) { - return file.search(new RegExp(query)) !== -1; + return path.basename(file, '.js') === query; })); }, ], function (err, files) { From 3b1b2d39c6a03f846f040a9d779ad37fc809c651 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Sat, 25 Feb 2017 21:43:29 -0500 Subject: [PATCH 131/316] added some instructions to upgrade script file --- src/upgrade.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/upgrade.js b/src/upgrade.js index a5e4d89158..06b4ec50ed 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -8,6 +8,16 @@ var path = require('path'); var db = require('./database'); var utils = require('../public/src/utils'); +/* + * Need to write an upgrade script for NodeBB? Cool. + * + * 1. Copy TEMPLATE to a file name of your choice. Try to be succinct. + * 2. Open up that file and change the user-friendly name (can be longer/more descriptive than the file name) + * and timestamp + * 3. Add your script under the "method" property + * 4. Append your filename to the array below for the next NodeBB version. + */ + var Upgrade = { available: [ { @@ -35,7 +45,7 @@ var Upgrade = { upgrades: ['global_and_user_language_keys', 'sorted_set_for_pinned_topics'], }, { - version: '1.5.0', + version: 'develop', // rename this to whatever the next NodeBB version is upgrades: ['flags_refactor'], }, ], From d75cc60e7644a755f784126b77d7b7befc2139d3 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Sun, 26 Feb 2017 15:53:25 -0500 Subject: [PATCH 132/316] added upgrade.check back into upgrade.js --- src/start.js | 12 ++++++------ src/upgrade.js | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/start.js b/src/start.js index 87c0d2bf70..731e939911 100644 --- a/src/start.js +++ b/src/start.js @@ -58,16 +58,16 @@ start.start = function () { if (err) { switch (err.message) { case 'schema-out-of-date': - winston.warn('Your NodeBB schema is out-of-date. Please run the following command to bring your dataset up to spec:'); - winston.warn(' ./nodebb upgrade'); + winston.error('Your NodeBB schema is out-of-date. Please run the following command to bring your dataset up to spec:'); + winston.error(' ./nodebb upgrade'); break; case 'dependencies-out-of-date': - winston.warn('One or more of NodeBB\'s dependent packages are out-of-date. Please run the following command to update them:'); - winston.warn(' ./nodebb upgrade'); + winston.error('One or more of NodeBB\'s dependent packages are out-of-date. Please run the following command to update them:'); + winston.error(' ./nodebb upgrade'); break; case 'dependencies-missing': - winston.warn('One or more of NodeBB\'s dependent packages are missing. Please run the following command to update them:'); - winston.warn(' ./nodebb upgrade'); + winston.error('One or more of NodeBB\'s dependent packages are missing. Please run the following command to update them:'); + winston.error(' ./nodebb upgrade'); break; default: winston.error(err); diff --git a/src/upgrade.js b/src/upgrade.js index 06b4ec50ed..0f7e55c438 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -51,6 +51,22 @@ var Upgrade = { ], }; +Upgrade.check = function (callback) { + // Throw 'schema-out-of-date' if not all upgrade scripts have run + var all = Upgrade.available.reduce(function (memo, current) { + memo = memo.concat(current.upgrades); + return memo; + }, []); + + db.getSortedSetRange('schemaLog', 0, -1, function (err, executed) { + var remainder = all.filter(function (name) { + return executed.indexOf(name) === -1; + }); + + callback(remainder.length > 1 ? new Error('schema-out-of-date') : null); + }); +}; + Upgrade.run = function (callback) { process.stdout.write('\nParsing upgrade scripts... '); var queue = []; From a2662f8b696918f52a712a0623fe0b2a55b13573 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Sun, 26 Feb 2017 16:03:26 -0500 Subject: [PATCH 133/316] linting for tests to pass --- src/upgrade.js | 4 ++++ src/upgrades/category_recent_tids.js | 6 +++--- src/upgrades/favourites_to_bookmarks.js | 8 ++++---- src/upgrades/flags_refactor.js | 14 +++++++------- src/upgrades/global_and_user_language_keys.js | 14 +++++--------- src/upgrades/sorted_set_for_pinned_topics.js | 7 ++++--- src/upgrades/sorted_sets_for_post_replies.js | 7 ++++--- 7 files changed, 31 insertions(+), 29 deletions(-) diff --git a/src/upgrade.js b/src/upgrade.js index 0f7e55c438..00f599fbdb 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -59,6 +59,10 @@ Upgrade.check = function (callback) { }, []); db.getSortedSetRange('schemaLog', 0, -1, function (err, executed) { + if (err) { + return callback(err); + } + var remainder = all.filter(function (name) { return executed.indexOf(name) === -1; }); diff --git a/src/upgrades/category_recent_tids.js b/src/upgrades/category_recent_tids.js index ccb6f5c818..18d27266f6 100644 --- a/src/upgrades/category_recent_tids.js +++ b/src/upgrades/category_recent_tids.js @@ -1,10 +1,10 @@ /* jslint node: true */ + 'use strict'; var db = require('../database'); var async = require('async'); -var winston = require('winston'); module.exports = { name: 'Category recent tids', @@ -29,5 +29,5 @@ module.exports = { }); }, callback); }); - } -}; \ No newline at end of file + }, +}; diff --git a/src/upgrades/favourites_to_bookmarks.js b/src/upgrades/favourites_to_bookmarks.js index 288d32c511..05b1e5b5f9 100644 --- a/src/upgrades/favourites_to_bookmarks.js +++ b/src/upgrades/favourites_to_bookmarks.js @@ -1,10 +1,10 @@ /* jslint node: true */ + 'use strict'; var db = require('../database'); var async = require('async'); -var winston = require('winston'); module.exports = { name: 'Favourites to Bookmarks', @@ -31,7 +31,7 @@ module.exports = { }, function (next) { db.deleteObjectField('post:' + id, 'reputation', next); - } + }, ], next); }, next); }, {}, next); @@ -48,5 +48,5 @@ module.exports = { } async.series([upgradePosts, upgradeUsers], callback); - } -}; \ No newline at end of file + }, +}; diff --git a/src/upgrades/flags_refactor.js b/src/upgrades/flags_refactor.js index c61a3a4747..deb1befc6c 100644 --- a/src/upgrades/flags_refactor.js +++ b/src/upgrades/flags_refactor.js @@ -1,4 +1,5 @@ /* jslint node: true */ + 'use strict'; var db = require('../database'); @@ -12,7 +13,6 @@ module.exports = { var batch = require('../batch'); var posts = require('../posts'); var flags = require('../flags'); - var migrated = 0; batch.processSortedSet('posts:pid', function (ids, next) { posts.getPostsByPids(ids, 1, function (err, posts) { @@ -23,11 +23,11 @@ module.exports = { posts = posts.filter(function (post) { return post.hasOwnProperty('flags'); }); - + async.each(posts, function (post, next) { async.parallel({ uids: async.apply(db.getSortedSetRangeWithScores, 'pid:' + post.pid + ':flag:uids', 0, -1), - reasons: async.apply(db.getSortedSetRange, 'pid:' + post.pid + ':flag:uid:reason', 0, -1) + reasons: async.apply(db.getSortedSetRange, 'pid:' + post.pid + ':flag:uid:reason', 0, -1), }, function (err, data) { if (err) { return next(err); @@ -51,7 +51,7 @@ module.exports = { flags.update(flagObj.flagId, 1, { state: post['flag:state'], assignee: post['flag:assignee'], - datetime: datetime + datetime: datetime, }, next); } else { setImmediate(next); @@ -72,7 +72,7 @@ module.exports = { } else { setImmediate(next); } - } + }, ], function (err) { if (err && err.message === '[[error:already-flagged]]') { // Already flagged, no need to parse, but not an error @@ -85,5 +85,5 @@ module.exports = { }, next); }); }, callback); - } -}; \ No newline at end of file + }, +}; diff --git a/src/upgrades/global_and_user_language_keys.js b/src/upgrades/global_and_user_language_keys.js index d08b3fea74..14f71c1fce 100644 --- a/src/upgrades/global_and_user_language_keys.js +++ b/src/upgrades/global_and_user_language_keys.js @@ -1,10 +1,10 @@ /* jslint node: true */ + 'use strict'; var db = require('../database'); var async = require('async'); -var winston = require('winston'); module.exports = { name: 'Update global and user language keys', @@ -14,8 +14,6 @@ module.exports = { var meta = require('../meta'); var batch = require('../batch'); var newLanguage; - var i = 0; - var j = 0; async.parallel([ function (next) { meta.configs.get('defaultLang', function (err, defaultLang) { @@ -41,23 +39,21 @@ module.exports = { async.waterfall([ async.apply(db.getObjectField, 'user:' + uid + ':settings', 'userLang'), function (language, next) { - ++i; if (!language) { return setImmediate(next); } newLanguage = language.replace('_', '-').replace('@', '-x-'); if (newLanguage !== language) { - ++j; user.setSetting(uid, 'userLang', newLanguage, next); } else { setImmediate(next); } - } + }, ], next); }, next); }, next); - } + }, ], callback); - } -}; \ No newline at end of file + }, +}; diff --git a/src/upgrades/sorted_set_for_pinned_topics.js b/src/upgrades/sorted_set_for_pinned_topics.js index d10a6ee902..faa12f3cc8 100644 --- a/src/upgrades/sorted_set_for_pinned_topics.js +++ b/src/upgrades/sorted_set_for_pinned_topics.js @@ -1,4 +1,5 @@ /* jslint node: true */ + 'use strict'; var db = require('../database'); @@ -28,10 +29,10 @@ module.exports = { async.parallel([ async.apply(db.sortedSetAdd, 'cid:' + topicData.cid + ':tids:pinned', Date.now(), topicData.tid), async.apply(db.sortedSetRemove, 'cid:' + topicData.cid + ':tids', topicData.tid), - async.apply(db.sortedSetRemove, 'cid:' + topicData.cid + ':tids:posts', topicData.tid) + async.apply(db.sortedSetRemove, 'cid:' + topicData.cid + ':tids:posts', topicData.tid), ], next); }, next); }); }, callback); - } -}; \ No newline at end of file + }, +}; diff --git a/src/upgrades/sorted_sets_for_post_replies.js b/src/upgrades/sorted_sets_for_post_replies.js index 7442b614d4..78fc96c7a1 100644 --- a/src/upgrades/sorted_sets_for_post_replies.js +++ b/src/upgrades/sorted_sets_for_post_replies.js @@ -1,4 +1,5 @@ /* jslint node: true */ + 'use strict'; var db = require('../database'); @@ -25,10 +26,10 @@ module.exports = { winston.verbose('processing pid: ' + postData.pid + ' toPid: ' + postData.toPid); async.parallel([ async.apply(db.sortedSetAdd, 'pid:' + postData.toPid + ':replies', postData.timestamp, postData.pid), - async.apply(db.incrObjectField, 'post:' + postData.toPid, 'replies') + async.apply(db.incrObjectField, 'post:' + postData.toPid, 'replies'), ], next); }, next); }); }, callback); - } -}; \ No newline at end of file + }, +}; From 93c96da0b9433a90dadd2ac5b582c2abe454ab0f Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Sun, 26 Feb 2017 16:26:09 -0500 Subject: [PATCH 134/316] adjusted upgrade check logic for instances with no schemaLog --- src/install.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/install.js b/src/install.js index a06a138f26..e3abfdca2f 100644 --- a/src/install.js +++ b/src/install.js @@ -507,12 +507,11 @@ install.setup = function (callback) { setCopyrightWidget, function (next) { var upgrade = require('./upgrade'); - upgrade.check(function (err, uptodate) { - if (err) { + upgrade.check(function (err) { + if (err && err.message === 'schema-out-of-date') { + upgrade.run(next); + } else if (err) { return next(err); - } - if (!uptodate) { - upgrade.upgrade(next); } else { next(); } From 8f5d1ca4da68c6e23f2fe41a801bf98a4cf1b50a Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 27 Feb 2017 10:15:44 -0500 Subject: [PATCH 135/316] added null check for #5482 --- src/upgrades/sound_settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/upgrades/sound_settings.js b/src/upgrades/sound_settings.js index a31a573808..f925a1ea2e 100644 --- a/src/upgrades/sound_settings.js +++ b/src/upgrades/sound_settings.js @@ -25,7 +25,7 @@ module.exports = { var keys = ['chat-incoming', 'chat-outgoing', 'notification']; db.getObject('settings:sounds', function (err, settings) { - if (err) { + if (err || !settings) { return cb(err); } From 86a8b8ab9304e89da6c69e1dfec73d95cebf7d0c Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 27 Feb 2017 15:27:07 -0500 Subject: [PATCH 136/316] posts:votes sorted set --- src/posts.js | 3 +++ src/upgrade.js | 46 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/posts.js b/src/posts.js index 2f9132a25b..83b8e39a1f 100644 --- a/src/posts.js +++ b/src/posts.js @@ -248,6 +248,9 @@ var plugins = require('./plugins'); }, ], next); }, + function (next) { + db.sortedSetAdd('posts:votes', postData.votes, postData.pid, next); + }, function (next) { Posts.setPostFields(postData.pid, { upvotes: postData.upvotes, downvotes: postData.downvotes }, next); }, diff --git a/src/upgrade.js b/src/upgrade.js index 46075ff5e9..14b394efb1 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -12,7 +12,7 @@ var schemaDate; var thisSchemaDate; // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema -var latestSchema = Date.UTC(2017, 1, 25); +var latestSchema = Date.UTC(2017, 1, 27); Upgrade.check = function (callback) { db.get('schemaDate', function (err, value) { @@ -479,6 +479,50 @@ Upgrade.upgrade = function (callback) { next(); } }, + function (next) { + thisSchemaDate = Date.UTC(2017, 1, 27); + var schemaName = '[2017/2/27] New sorted set posts:votes'; + + if (schemaDate < thisSchemaDate) { + updatesMade = true; + winston.verbose(schemaName); + + db.getSortedSetRange('categories:cid', 0, -1, function (err, cids) { + if (err) { + return next(err); + } + + async.eachSeries(cids, function (cid, next) { + db.getSortedSetRevRange('cid:' + cid + ':pids', 0, -1, function (err, pids) { + if (err || !pids) { + return next(err); + } + + async.each(pids, function(pid, next) { + db.getObjectFields('post:' + pid, ['upvotes', 'downvotes'], function (err, postData) { + if (err || !postData) { + return next(err); + } + + var votes = parseInt(postData.upvotes || 0, 10) - parseInt(postData.downvotes || 0, 10); + db.sortedSetAdd('posts:votes', votes, pid, next); + }); + }, next); + }); + }, function (err) { + if (err) { + return next(err); + } + + winston.info(schemaName + ' - done'); + Upgrade.update(thisSchemaDate, next); + }); + }); + } else { + winston.info(schemaName + ' - skipped!'); + next(); + } + }, // Add new schema updates here // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 24!!! ], function (err) { From 5d9b6062d6f72cb8d9d40e0ce9555dc619cde4cd Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 27 Feb 2017 17:03:40 -0500 Subject: [PATCH 137/316] simpler method for getting pids --- src/upgrade.js | 44 +++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/src/upgrade.js b/src/upgrade.js index 14b394efb1..8976a63290 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -480,43 +480,33 @@ Upgrade.upgrade = function (callback) { } }, function (next) { - thisSchemaDate = Date.UTC(2017, 1, 27); + thisSchemaDate = Date.UTC(2017, 1, 28); var schemaName = '[2017/2/27] New sorted set posts:votes'; if (schemaDate < thisSchemaDate) { updatesMade = true; winston.verbose(schemaName); - db.getSortedSetRange('categories:cid', 0, -1, function (err, cids) { + require('./batch').processSortedSet('posts:pid', function (pids, next) { + async.each(pids, function (pid, next) { + async.each(pids, function (pid, next) { + db.getObjectFields('post:' + pid, ['upvotes', 'downvotes'], function (err, postData) { + if (err || !postData) { + return next(err); + } + + var votes = parseInt(postData.upvotes || 0, 10) - parseInt(postData.downvotes || 0, 10); + db.sortedSetAdd('posts:votes', votes, pid, next); + }); + }, next); + }, next); + }, {}, function (err) { if (err) { return next(err); } - async.eachSeries(cids, function (cid, next) { - db.getSortedSetRevRange('cid:' + cid + ':pids', 0, -1, function (err, pids) { - if (err || !pids) { - return next(err); - } - - async.each(pids, function(pid, next) { - db.getObjectFields('post:' + pid, ['upvotes', 'downvotes'], function (err, postData) { - if (err || !postData) { - return next(err); - } - - var votes = parseInt(postData.upvotes || 0, 10) - parseInt(postData.downvotes || 0, 10); - db.sortedSetAdd('posts:votes', votes, pid, next); - }); - }, next); - }); - }, function (err) { - if (err) { - return next(err); - } - - winston.info(schemaName + ' - done'); - Upgrade.update(thisSchemaDate, next); - }); + winston.info(schemaName + ' - done'); + Upgrade.update(thisSchemaDate, next); }); } else { winston.info(schemaName + ' - skipped!'); From 7bdbe7d9db6536c4e22e7b89d5ad91d3936ef3ce Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 27 Feb 2017 17:04:36 -0500 Subject: [PATCH 138/316] wrong date --- src/upgrade.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/upgrade.js b/src/upgrade.js index 8976a63290..038f7b8389 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -480,7 +480,7 @@ Upgrade.upgrade = function (callback) { } }, function (next) { - thisSchemaDate = Date.UTC(2017, 1, 28); + thisSchemaDate = Date.UTC(2017, 1, 27); var schemaName = '[2017/2/27] New sorted set posts:votes'; if (schemaDate < thisSchemaDate) { From 06eabbc5076a093740aaad1358785eafae944a7a Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 27 Feb 2017 17:07:17 -0500 Subject: [PATCH 139/316] typo --- src/upgrade.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/upgrade.js b/src/upgrade.js index 038f7b8389..2b4510b099 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -489,16 +489,14 @@ Upgrade.upgrade = function (callback) { require('./batch').processSortedSet('posts:pid', function (pids, next) { async.each(pids, function (pid, next) { - async.each(pids, function (pid, next) { - db.getObjectFields('post:' + pid, ['upvotes', 'downvotes'], function (err, postData) { - if (err || !postData) { - return next(err); - } + db.getObjectFields('post:' + pid, ['upvotes', 'downvotes'], function (err, postData) { + if (err || !postData) { + return next(err); + } - var votes = parseInt(postData.upvotes || 0, 10) - parseInt(postData.downvotes || 0, 10); - db.sortedSetAdd('posts:votes', votes, pid, next); - }); - }, next); + var votes = parseInt(postData.upvotes || 0, 10) - parseInt(postData.downvotes || 0, 10); + db.sortedSetAdd('posts:votes', votes, pid, next); + }); }, next); }, {}, function (err) { if (err) { From a8dd7946979f9cf12e0f1b4cd75fc7f1ee2afb72 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 28 Feb 2017 14:04:44 -0500 Subject: [PATCH 140/316] reapply ef93ef3dd4e26803e3c19a2c8a3268fb3e15d9e7 --- src/upgrades/sound_settings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/upgrades/sound_settings.js b/src/upgrades/sound_settings.js index 4c8359fc2e..5ba958f591 100644 --- a/src/upgrades/sound_settings.js +++ b/src/upgrades/sound_settings.js @@ -42,8 +42,8 @@ module.exports = { batch.processSortedSet('users:joindate', function (ids, next) { async.each(ids, function (uid, next) { - user.getSettings(uid, function (err, settings) { - if (err) { + db.getObject('user:' + uid + ':settings', function (err, settings) { + if (err || !settings) { return next(err); } From 15665d08725692f7aee8f8f82556317e378a0d11 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 1 Mar 2017 12:11:05 -0500 Subject: [PATCH 141/316] re-ordered upgrade scripts so schemaDate is saved properly --- src/upgrade.js | 80 +++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/src/upgrade.js b/src/upgrade.js index ba3d7a9e0e..2bedd38d00 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -407,6 +407,46 @@ Upgrade.upgrade = function (callback) { next(); } }, + function (next) { + thisSchemaDate = Date.UTC(2017, 1, 28); + var schemaName = '[2017/2/28] Update urls in config to `/assets`'; + + if (schemaDate < thisSchemaDate) { + updatesMade = true; + winston.info(schemaName); + async.waterfall([ + function (cb) { + db.getObject('config', cb); + }, + function (config, cb) { + if (!config) { + return cb(); + } + + var keys = ['brand:favicon', 'brand:touchicon', 'og:image', 'brand:logo:url', 'defaultAvatar', 'profile:defaultCovers']; + + keys.forEach(function (key) { + var oldValue = config[key]; + + if (!oldValue || typeof oldValue !== 'string') { + return; + } + + config[key] = oldValue.replace(/(?:\/assets)?\/(images|uploads)\//g, '/assets/$1/'); + }); + + db.setObject('config', config, cb); + }, + function (next) { + winston.info(schemaName + ' - done'); + Upgrade.update(thisSchemaDate, next); + }, + ], next); + } else { + winston.info(schemaName + ' - skipped!'); + next(); + } + }, function (next) { thisSchemaDate = Date.UTC(2017, 1, 25); var schemaName = '[2017/2/25] Update global and user sound settings'; @@ -479,46 +519,6 @@ Upgrade.upgrade = function (callback) { next(); } }, - function (next) { - thisSchemaDate = Date.UTC(2017, 1, 28); - var schemaName = '[2017/2/28] Update urls in config to `/assets`'; - - if (schemaDate < thisSchemaDate) { - updatesMade = true; - winston.info(schemaName); - async.waterfall([ - function (cb) { - db.getObject('config', cb); - }, - function (config, cb) { - if (!config) { - return cb(); - } - - var keys = ['brand:favicon', 'brand:touchicon', 'og:image', 'brand:logo:url', 'defaultAvatar', 'profile:defaultCovers']; - - keys.forEach(function (key) { - var oldValue = config[key]; - - if (!oldValue || typeof oldValue !== 'string') { - return; - } - - config[key] = oldValue.replace(/(?:\/assets)?\/(images|uploads)\//g, '/assets/$1/'); - }); - - db.setObject('config', config, cb); - }, - function (next) { - winston.info(schemaName + ' - done'); - Upgrade.update(thisSchemaDate, next); - }, - ], next); - } else { - winston.info(schemaName + ' - skipped!'); - next(); - } - }, function (next) { thisSchemaDate = Date.UTC(2017, 1, 27); var schemaName = '[2017/2/27] New sorted set posts:votes'; From f0d9bddd56eeb9ad1b7d8db13e194defe6ae6dbf Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 6 Mar 2017 09:58:02 -0500 Subject: [PATCH 142/316] fix merge conflict artifact that was left in --- src/user/data.js | 52 ++++++++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/src/user/data.js b/src/user/data.js index 22810999f8..3377d5370e 100644 --- a/src/user/data.js +++ b/src/user/data.js @@ -64,28 +64,24 @@ module.exports = function (User) { addField('lastonline'); } -<<<<<<< HEAD - db.getObjectsFields(keys, fields, function (err, users) { - if (err) { - return callback(err); - } - - users = uids.map(function (uid) { - return users[ref[uid]]; - }); - - modifyUserData(users, fieldsToRemove, callback); - }); -======= async.waterfall([ function (next) { - db.getObjectsFields(keys, fields, next); + db.getObjectsFields(keys, fields, function (err, users) { + if (err) { + return callback(err); + } + + users = uids.map(function (uid) { + return users[ref[uid]]; + }); + + next(null, users); + }); }, function (users, next) { modifyUserData(users, fieldsToRemove, next); }, ], callback); ->>>>>>> master }; User.getMultipleUserFields = function (uids, fields, callback) { @@ -117,28 +113,24 @@ module.exports = function (User) { return 'user:' + uid; }); -<<<<<<< HEAD - db.getObjects(keys, function (err, users) { - if (err) { - return callback(err); - } - - users = uids.map(function (uid) { - return users[ref[uid]]; - }); - - modifyUserData(users, [], callback); - }); -======= async.waterfall([ function (next) { - db.getObjects(keys, next); + db.getObjects(keys, function (err, users) { + if (err) { + return callback(err); + } + + users = uids.map(function (uid) { + return users[ref[uid]]; + }); + + next(null, users); + }); }, function (users, next) { modifyUserData(users, [], next); }, ], callback); ->>>>>>> master }; function modifyUserData(users, fieldsToRemove, callback) { From 1301fb1f340d2f2a0fb7dae43809d480aa57c66f Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 7 Mar 2017 19:51:52 +0300 Subject: [PATCH 143/316] on chat leave close the modal --- public/src/client/chats.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/public/src/client/chats.js b/public/src/client/chats.js index c42a8e9330..582c578f65 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -282,11 +282,17 @@ define('forum/chats', [ if (err) { return app.alertError(err.message); } - if (parseInt(roomId, 10) === ajaxify.data.roomId) { + if (parseInt(roomId, 10) === parseInt(ajaxify.data.roomId, 10)) { ajaxify.go('user/' + ajaxify.data.userslug + '/chats'); } else { el.remove(); } + require(['chat'], function (chat) { + var modal = chat.getModal(roomId); + if (modal.length) { + chat.close(modal); + } + }); }); }; From e7ced2457ecc1f2a781f41578eabad453154e924 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 7 Mar 2017 20:26:49 +0300 Subject: [PATCH 144/316] clone query object --- src/pagination.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pagination.js b/src/pagination.js index 3e57c5b201..3494da3047 100644 --- a/src/pagination.js +++ b/src/pagination.js @@ -1,6 +1,7 @@ 'use strict'; var qs = require('querystring'); +var _ = require('underscore'); var pagination = {}; @@ -37,10 +38,10 @@ pagination.create = function (currentPage, pageCount, queryObj) { return a - b; }); - queryObj = queryObj || {}; - delete queryObj._; + queryObj = _.clone(queryObj || {}); + var pages = pagesToShow.map(function (page) { queryObj.page = page; return { page: page, active: page === currentPage, qs: qs.stringify(queryObj) }; From 743a3fb4421672d241a0aa49eb6cdc1c238f73be Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 7 Mar 2017 20:58:43 +0300 Subject: [PATCH 145/316] fix test --- src/pagination.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pagination.js b/src/pagination.js index 3494da3047..4058e7f8f4 100644 --- a/src/pagination.js +++ b/src/pagination.js @@ -38,10 +38,10 @@ pagination.create = function (currentPage, pageCount, queryObj) { return a - b; }); - delete queryObj._; - queryObj = _.clone(queryObj || {}); + delete queryObj._; + var pages = pagesToShow.map(function (page) { queryObj.page = page; return { page: page, active: page === currentPage, qs: qs.stringify(queryObj) }; From bacbb6c75f6e6097f324a414f6332d16bbd11473 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 7 Mar 2017 20:16:59 -0500 Subject: [PATCH 146/316] holy moly it took me an hour to find this missing method that got removed in a bad merge conflict resolution... --- src/user.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/user.js b/src/user.js index 48098feadf..d521b77f0d 100644 --- a/src/user.js +++ b/src/user.js @@ -202,6 +202,16 @@ User.isGlobalModerator = function (uid, callback) { privileges.users.isGlobalModerator(uid, callback); }; +User.isPrivileged = function (uid, callback) { + async.parallel([ + async.apply(User.isAdministrator, uid), + async.apply(User.isGlobalModerator, uid), + async.apply(User.isModeratorOfAnyCategory, uid), + ], function (err, results) { + callback(err, results ? results.some(Boolean) : false); + }); +}; + User.isAdminOrGlobalMod = function (uid, callback) { async.parallel({ isAdmin: async.apply(User.isAdministrator, uid), From 04b66787ea6d04d2ec06253456d25357b8c2a135 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 7 Mar 2017 20:53:54 -0500 Subject: [PATCH 147/316] removed hardcoded stepsize of 1 for chart, so it can space the ticks out properly --- public/src/client/flags/list.js | 1 - 1 file changed, 1 deletion(-) diff --git a/public/src/client/flags/list.js b/public/src/client/flags/list.js index 4c516eac38..0806195ac4 100644 --- a/public/src/client/flags/list.js +++ b/public/src/client/flags/list.js @@ -76,7 +76,6 @@ define('forum/flags/list', ['components', 'Chart'], function (components, Chart) yAxes: [{ ticks: { beginAtZero: true, - stepSize: 1, }, }], }, From 6f4950964511de35d07925a657f4f4526a2f2053 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 8 Mar 2017 15:09:58 +0300 Subject: [PATCH 148/316] fix newSet on redis --- public/src/client/chats/messages.js | 4 +++- src/messaging.js | 2 +- src/messaging/data.js | 6 ++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/public/src/client/chats/messages.js b/public/src/client/chats/messages.js index c8300c485c..987ac46c79 100644 --- a/public/src/client/chats/messages.js +++ b/public/src/client/chats/messages.js @@ -57,8 +57,10 @@ define('forum/chats/messages', ['components', 'sounds', 'translator'], function messages.appendChatMessage = function (chatContentEl, data) { var lastSpeaker = parseInt(chatContentEl.find('.chat-message').last().attr('data-uid'), 10); + var lasttimestamp = parseInt(chatContentEl.find('.chat-message').last().attr('data-timestamp'), 10); if (!Array.isArray(data)) { - data.newSet = lastSpeaker !== data.fromuid; + data.newSet = lastSpeaker !== parseInt(data.fromuid, 10) || + parseInt(data.timestamp, 10) > parseInt(lasttimestamp, 10) + (1000 * 60 * 3); } messages.parseMessage(data, function (html) { diff --git a/src/messaging.js b/src/messaging.js index e141adff4f..e507609d81 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -102,7 +102,7 @@ Messaging.isNewSet = function (uid, roomId, timestamp, callback) { }, function (messages, next) { if (messages && messages.length) { - next(null, parseInt(timestamp, 10) > parseInt(messages[0].score, 10) + (1000 * 60 * 5)); + next(null, parseInt(timestamp, 10) > parseInt(messages[0].score, 10) + Messaging.newMessageCutoff); } else { next(null, true); } diff --git a/src/messaging/data.js b/src/messaging/data.js index e3cb67fbd8..98d4dcded8 100644 --- a/src/messaging/data.js +++ b/src/messaging/data.js @@ -8,6 +8,8 @@ var user = require('../user'); var utils = require('../../public/src/utils'); module.exports = function (Messaging) { + Messaging.newMessageCutoff = 1000 * 60 * 3; + Messaging.getMessageField = function (mid, field, callback) { Messaging.getMessageFields(mid, [field], function (err, fields) { callback(err, fields ? fields[field] : null); @@ -80,7 +82,7 @@ module.exports = function (Messaging) { // Add a spacer in between messages with time gaps between them messages = messages.map(function (message, index) { // Compare timestamps with the previous message, and check if a spacer needs to be added - if (index > 0 && parseInt(message.timestamp, 10) > parseInt(messages[index - 1].timestamp, 10) + (1000 * 60 * 5)) { + if (index > 0 && parseInt(message.timestamp, 10) > parseInt(messages[index - 1].timestamp, 10) + Messaging.newMessageCutoff) { // If it's been 5 minutes, this is a new set of messages message.newSet = true; } else if (index > 0 && message.fromuid !== messages[index - 1].fromuid) { @@ -115,7 +117,7 @@ module.exports = function (Messaging) { } if ( - (parseInt(messages[0].timestamp, 10) > parseInt(fields.timestamp, 10) + (1000 * 60 * 5)) || + (parseInt(messages[0].timestamp, 10) > parseInt(fields.timestamp, 10) + Messaging.newMessageCutoff) || (parseInt(messages[0].fromuid, 10) !== parseInt(fields.fromuid, 10)) ) { // If it's been 5 minutes, this is a new set of messages From 47af3eb7e54c843a418bb4f909e04cb400313f2c Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 8 Mar 2017 19:49:27 +0300 Subject: [PATCH 149/316] use the real count --- src/controllers/search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/search.js b/src/controllers/search.js index be98589c54..4ca7937687 100644 --- a/src/controllers/search.js +++ b/src/controllers/search.js @@ -60,7 +60,7 @@ searchController.search = function (req, res, next) { var searchData = results.search; searchData.categories = categoriesData; - searchData.categoriesCount = results.categories.length; + searchData.categoriesCount = categoriesData.length; searchData.pagination = pagination.create(page, searchData.pageCount, req.query); searchData.showAsPosts = !req.query.showAs || req.query.showAs === 'posts'; searchData.showAsTopics = req.query.showAs === 'topics'; From 32d37465fb3efc67182dce51a056ec146bbd0a22 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 8 Mar 2017 20:09:35 +0300 Subject: [PATCH 150/316] limit size of select --- src/controllers/search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/search.js b/src/controllers/search.js index 4ca7937687..bf818a39d5 100644 --- a/src/controllers/search.js +++ b/src/controllers/search.js @@ -60,7 +60,7 @@ searchController.search = function (req, res, next) { var searchData = results.search; searchData.categories = categoriesData; - searchData.categoriesCount = categoriesData.length; + searchData.categoriesCount = Math.max(10, Math.min(20, categoriesData.length)); searchData.pagination = pagination.create(page, searchData.pageCount, req.query); searchData.showAsPosts = !req.query.showAs || req.query.showAs === 'posts'; searchData.showAsTopics = req.query.showAs === 'topics'; From f092b7754e82c79b9710955fa7939e016620aa29 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 8 Mar 2017 22:43:02 +0300 Subject: [PATCH 151/316] only create 1 alert for chat spam error --- public/src/client/chats/messages.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/public/src/client/chats/messages.js b/public/src/client/chats/messages.js index 987ac46c79..2a1889cf25 100644 --- a/public/src/client/chats/messages.js +++ b/public/src/client/chats/messages.js @@ -35,7 +35,14 @@ define('forum/chats/messages', ['components', 'sounds', 'translator'], function if (err.message === '[[error:email-not-confirmed-chat]]') { return app.showEmailConfirmWarning(err); } - return app.alertError(err.message); + + return app.alert({ + alert_id: 'chat_spam_error', + title: '[[global:alert.error]]', + message: err.message, + type: 'danger', + timeout: 10000, + }); } sounds.play('chat-outgoing'); From a67d48e71994a23177cf40abd454905545054810 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 10 Mar 2017 12:50:19 -0500 Subject: [PATCH 152/316] #5513 (#5520) closes #5513 --- public/src/client/account/topics.js | 1 + public/src/client/recent.js | 1 + public/src/client/tag.js | 1 + public/src/client/topic/posts.js | 1 + public/src/client/unread.js | 1 + src/socket.io/topics/infinitescroll.js | 14 +++++++------- test/topics.js | 8 ++++---- 7 files changed, 16 insertions(+), 11 deletions(-) diff --git a/public/src/client/account/topics.js b/public/src/client/account/topics.js index 340c2e11b9..c16ffd913d 100644 --- a/public/src/client/account/topics.js +++ b/public/src/client/account/topics.js @@ -27,6 +27,7 @@ define('forum/account/topics', ['forum/account/header', 'forum/infinitescroll'], infinitescroll.loadMore('topics.loadMoreFromSet', { set: set, after: $('[component="category"]').attr('data-nextstart'), + count: config.topicsPerPage, }, function (data, done) { if (data.topics && data.topics.length) { onTopicsLoaded(data.topics, done); diff --git a/public/src/client/recent.js b/public/src/client/recent.js index 981baabd12..b8f9380198 100644 --- a/public/src/client/recent.js +++ b/public/src/client/recent.js @@ -132,6 +132,7 @@ define('forum/recent', ['forum/infinitescroll', 'components'], function (infinit infinitescroll.loadMore('topics.loadMoreRecentTopics', { after: $('[component="category"]').attr('data-nextstart'), + count: config.topicsPerPage, cid: utils.params().cid, filter: ajaxify.data.selectedFilter.filter, set: $('[component="category"]').attr('data-set') ? $('[component="category"]').attr('data-set') : 'topics:recent', diff --git a/public/src/client/tag.js b/public/src/client/tag.js index 2b74c17193..e07126cb2e 100644 --- a/public/src/client/tag.js +++ b/public/src/client/tag.js @@ -27,6 +27,7 @@ define('forum/tag', ['forum/recent', 'forum/infinitescroll'], function (recent, infinitescroll.loadMore('topics.loadMoreFromSet', { set: 'tag:' + ajaxify.data.tag + ':topics', after: $('[component="category"]').attr('data-nextstart'), + count: config.topicsPerPage, }, function (data, done) { if (data.topics && data.topics.length) { recent.onTopicsLoaded('tag', data.topics, false, done); diff --git a/public/src/client/topic/posts.js b/public/src/client/topic/posts.js index 72f580b582..fb9074b3be 100644 --- a/public/src/client/topic/posts.js +++ b/public/src/client/topic/posts.js @@ -227,6 +227,7 @@ define('forum/topic/posts', [ infinitescroll.loadMore('topics.loadMore', { tid: tid, after: after, + count: config.postsPerPage, direction: direction, topicPostSort: config.topicPostSort, }, function (data, done) { diff --git a/public/src/client/unread.js b/public/src/client/unread.js index f5b1dd73ed..01ef9cff52 100644 --- a/public/src/client/unread.js +++ b/public/src/client/unread.js @@ -92,6 +92,7 @@ define('forum/unread', ['forum/recent', 'topicSelect', 'forum/infinitescroll', ' var cid = params.cid; infinitescroll.loadMore('topics.loadMoreUnreadTopics', { after: $('[component="category"]').attr('data-nextstart'), + count: config.topicsPerPage, cid: cid, filter: ajaxify.data.selectedFilter.filter, }, function (data, done) { diff --git a/src/socket.io/topics/infinitescroll.js b/src/socket.io/topics/infinitescroll.js index 023ad6266e..cf7c37e050 100644 --- a/src/socket.io/topics/infinitescroll.js +++ b/src/socket.io/topics/infinitescroll.js @@ -40,19 +40,19 @@ module.exports = function (SocketTopics) { var reverse = data.topicPostSort === 'newest_to_oldest' || data.topicPostSort === 'most_votes'; var start = Math.max(0, parseInt(data.after, 10)); - var infScrollPostsPerPage = 10; + var infScrollPostsPerPage = Math.max(0, Math.min(meta.config.postsPerPage, parseInt(data.postsPerPage, 10) || meta.config.postsPerPage) - 1); if (data.direction > 0) { if (reverse) { start = results.topic.postcount - start; } } else if (reverse) { - start = results.topic.postcount - start - infScrollPostsPerPage - 1; + start = results.topic.postcount - start - infScrollPostsPerPage; } else { - start = start - infScrollPostsPerPage - 1; + start = start - infScrollPostsPerPage; } - var stop = start + (infScrollPostsPerPage - 1); + var stop = start + (infScrollPostsPerPage); start = Math.max(0, start); stop = Math.max(0, stop); @@ -93,7 +93,7 @@ module.exports = function (SocketTopics) { } var start = parseInt(data.after, 10); - var stop = start + 9; + var stop = start + Math.max(0, Math.min(meta.config.topicsPerPage, parseInt(data.topicsPerPage, 10) || meta.config.topicsPerPage) - 1); topics.getUnreadTopics({ cid: data.cid, uid: socket.uid, start: start, stop: stop, filter: data.filter }, callback); }; @@ -104,7 +104,7 @@ module.exports = function (SocketTopics) { } var start = parseInt(data.after, 10); - var stop = start + 9; + var stop = start + Math.max(0, Math.min(meta.config.topicsPerPage, parseInt(data.topicsPerPage, 10) || meta.config.topicsPerPage) - 1); topics.getRecentTopics(data.cid, socket.uid, start, stop, data.filter, callback); }; @@ -115,7 +115,7 @@ module.exports = function (SocketTopics) { } var start = parseInt(data.after, 10); - var stop = start + 9; + var stop = start + Math.max(0, Math.min(meta.config.topicsPerPage, parseInt(data.topicsPerPage, 10) || meta.config.topicsPerPage) - 1); topics.getTopicsFromSet(data.set, socket.uid, start, stop, callback); }; diff --git a/test/topics.js b/test/topics.js index 097ffb8c37..ad481ade1e 100644 --- a/test/topics.js +++ b/test/topics.js @@ -902,7 +902,7 @@ describe('Topic\'s', function () { }); it('should infinite load topic posts', function (done) { - socketTopics.loadMore({ uid: adminUid }, { tid: tid, after: 0 }, function (err, data) { + socketTopics.loadMore({ uid: adminUid }, { tid: tid, after: 0, count: 10 }, function (err, data) { assert.ifError(err); assert(data.mainPost); assert(data.posts); @@ -921,7 +921,7 @@ describe('Topic\'s', function () { it('should load more unread topics', function (done) { socketTopics.markUnread({ uid: adminUid }, tid, function (err) { assert.ifError(err); - socketTopics.loadMoreUnreadTopics({ uid: adminUid }, { cid: topic.categoryId, after: 0 }, function (err, data) { + socketTopics.loadMoreUnreadTopics({ uid: adminUid }, { cid: topic.categoryId, after: 0, count: 10 }, function (err, data) { assert.ifError(err); assert(data); assert(Array.isArray(data.topics)); @@ -939,7 +939,7 @@ describe('Topic\'s', function () { it('should load more recent topics', function (done) { - socketTopics.loadMoreRecentTopics({ uid: adminUid }, { cid: topic.categoryId, after: 0 }, function (err, data) { + socketTopics.loadMoreRecentTopics({ uid: adminUid }, { cid: topic.categoryId, after: 0, count: 10 }, function (err, data) { assert.ifError(err); assert(data); assert(Array.isArray(data.topics)); @@ -955,7 +955,7 @@ describe('Topic\'s', function () { }); it('should load more from custom set', function (done) { - socketTopics.loadMoreFromSet({ uid: adminUid }, { set: 'uid:' + adminUid + ':topics', after: 0 }, function (err, data) { + socketTopics.loadMoreFromSet({ uid: adminUid }, { set: 'uid:' + adminUid + ':topics', after: 0, count: 10 }, function (err, data) { assert.ifError(err); assert(data); assert(Array.isArray(data.topics)); From 33d1cc6d0f849a165e268517d4e2a15cee3d282b Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 10 Mar 2017 15:56:45 -0500 Subject: [PATCH 153/316] replace erroring line with operator-assignment --- src/socket.io/topics/infinitescroll.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/socket.io/topics/infinitescroll.js b/src/socket.io/topics/infinitescroll.js index cf7c37e050..753b050699 100644 --- a/src/socket.io/topics/infinitescroll.js +++ b/src/socket.io/topics/infinitescroll.js @@ -49,7 +49,7 @@ module.exports = function (SocketTopics) { } else if (reverse) { start = results.topic.postcount - start - infScrollPostsPerPage; } else { - start = start - infScrollPostsPerPage; + start -= infScrollPostsPerPage; } var stop = start + (infScrollPostsPerPage); From 4ba795b5f2263adb2f9c04eb1e11f0184170ab2c Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 10 Mar 2017 16:29:40 -0500 Subject: [PATCH 154/316] adding fallbacks for meta/config.topicsPerPage --- src/socket.io/topics/infinitescroll.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/socket.io/topics/infinitescroll.js b/src/socket.io/topics/infinitescroll.js index 753b050699..ec82038e0d 100644 --- a/src/socket.io/topics/infinitescroll.js +++ b/src/socket.io/topics/infinitescroll.js @@ -40,7 +40,7 @@ module.exports = function (SocketTopics) { var reverse = data.topicPostSort === 'newest_to_oldest' || data.topicPostSort === 'most_votes'; var start = Math.max(0, parseInt(data.after, 10)); - var infScrollPostsPerPage = Math.max(0, Math.min(meta.config.postsPerPage, parseInt(data.postsPerPage, 10) || meta.config.postsPerPage) - 1); + var infScrollPostsPerPage = Math.max(0, Math.min(meta.config.postsPerPage || 20, parseInt(data.postsPerPage, 10) || meta.config.postsPerPage || 20) - 1); if (data.direction > 0) { if (reverse) { @@ -93,7 +93,7 @@ module.exports = function (SocketTopics) { } var start = parseInt(data.after, 10); - var stop = start + Math.max(0, Math.min(meta.config.topicsPerPage, parseInt(data.topicsPerPage, 10) || meta.config.topicsPerPage) - 1); + var stop = start + Math.max(0, Math.min(meta.config.topicsPerPage || 20, parseInt(data.topicsPerPage, 10) || meta.config.topicsPerPage || 20) - 1); topics.getUnreadTopics({ cid: data.cid, uid: socket.uid, start: start, stop: stop, filter: data.filter }, callback); }; @@ -104,7 +104,7 @@ module.exports = function (SocketTopics) { } var start = parseInt(data.after, 10); - var stop = start + Math.max(0, Math.min(meta.config.topicsPerPage, parseInt(data.topicsPerPage, 10) || meta.config.topicsPerPage) - 1); + var stop = start + Math.max(0, Math.min(meta.config.topicsPerPage || 20, parseInt(data.topicsPerPage, 10) || meta.config.topicsPerPage || 20) - 1); topics.getRecentTopics(data.cid, socket.uid, start, stop, data.filter, callback); }; @@ -115,7 +115,7 @@ module.exports = function (SocketTopics) { } var start = parseInt(data.after, 10); - var stop = start + Math.max(0, Math.min(meta.config.topicsPerPage, parseInt(data.topicsPerPage, 10) || meta.config.topicsPerPage) - 1); + var stop = start + Math.max(0, Math.min(meta.config.topicsPerPage || 20, parseInt(data.topicsPerPage, 10) || meta.config.topicsPerPage || 20) - 1); topics.getTopicsFromSet(data.set, socket.uid, start, stop, callback); }; From fc13776f26e93e9f4cbd33d168612e79ace638f8 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 10 Mar 2017 16:36:22 -0500 Subject: [PATCH 155/316] moving files from src/upgrades to individual folders by version --- src/upgrades/{ => 1.0.0}/chat_room_hashes.js | 0 src/upgrades/{ => 1.0.0}/chat_upgrade.js | 0 src/upgrades/{ => 1.0.0}/global_moderators.js | 0 src/upgrades/{ => 1.0.0}/social_post_sharing.js | 0 src/upgrades/{ => 1.0.0}/theme_to_active_plugins.js | 0 src/upgrades/{ => 1.0.0}/user_best_posts.js | 0 src/upgrades/{ => 1.0.0}/users_notvalidated.js | 0 src/upgrades/{ => 1.1.0}/assign_topic_read_privilege.js | 0 src/upgrades/{ => 1.1.0}/dismiss_flags_from_deleted_topics.js | 0 src/upgrades/{ => 1.1.0}/group_title_update.js | 0 src/upgrades/{ => 1.1.0}/separate_upvote_downvote.js | 0 src/upgrades/{ => 1.1.0}/user_post_count_per_tid.js | 0 src/upgrades/{ => 1.1.1}/remove_negative_best_posts.js | 0 src/upgrades/{ => 1.1.1}/upload_privileges.js | 0 src/upgrades/{ => 1.2.0}/category_recent_tids.js | 0 src/upgrades/{ => 1.2.0}/edit_delete_deletetopic_privileges.js | 0 src/upgrades/{ => 1.3.0}/favourites_to_bookmarks.js | 0 src/upgrades/{ => 1.3.0}/sorted_sets_for_post_replies.js | 0 src/upgrades/{ => 1.4.0}/global_and_user_language_keys.js | 0 src/upgrades/{ => 1.4.0}/sorted_set_for_pinned_topics.js | 0 src/upgrades/{ => develop}/flags_refactor.js | 0 src/upgrades/{ => develop}/post_votes_zset.js | 0 src/upgrades/{ => master}/config_urls_update.js | 0 src/upgrades/{ => master}/sound_settings.js | 0 24 files changed, 0 insertions(+), 0 deletions(-) rename src/upgrades/{ => 1.0.0}/chat_room_hashes.js (100%) rename src/upgrades/{ => 1.0.0}/chat_upgrade.js (100%) rename src/upgrades/{ => 1.0.0}/global_moderators.js (100%) rename src/upgrades/{ => 1.0.0}/social_post_sharing.js (100%) rename src/upgrades/{ => 1.0.0}/theme_to_active_plugins.js (100%) rename src/upgrades/{ => 1.0.0}/user_best_posts.js (100%) rename src/upgrades/{ => 1.0.0}/users_notvalidated.js (100%) rename src/upgrades/{ => 1.1.0}/assign_topic_read_privilege.js (100%) rename src/upgrades/{ => 1.1.0}/dismiss_flags_from_deleted_topics.js (100%) rename src/upgrades/{ => 1.1.0}/group_title_update.js (100%) rename src/upgrades/{ => 1.1.0}/separate_upvote_downvote.js (100%) rename src/upgrades/{ => 1.1.0}/user_post_count_per_tid.js (100%) rename src/upgrades/{ => 1.1.1}/remove_negative_best_posts.js (100%) rename src/upgrades/{ => 1.1.1}/upload_privileges.js (100%) rename src/upgrades/{ => 1.2.0}/category_recent_tids.js (100%) rename src/upgrades/{ => 1.2.0}/edit_delete_deletetopic_privileges.js (100%) rename src/upgrades/{ => 1.3.0}/favourites_to_bookmarks.js (100%) rename src/upgrades/{ => 1.3.0}/sorted_sets_for_post_replies.js (100%) rename src/upgrades/{ => 1.4.0}/global_and_user_language_keys.js (100%) rename src/upgrades/{ => 1.4.0}/sorted_set_for_pinned_topics.js (100%) rename src/upgrades/{ => develop}/flags_refactor.js (100%) rename src/upgrades/{ => develop}/post_votes_zset.js (100%) rename src/upgrades/{ => master}/config_urls_update.js (100%) rename src/upgrades/{ => master}/sound_settings.js (100%) diff --git a/src/upgrades/chat_room_hashes.js b/src/upgrades/1.0.0/chat_room_hashes.js similarity index 100% rename from src/upgrades/chat_room_hashes.js rename to src/upgrades/1.0.0/chat_room_hashes.js diff --git a/src/upgrades/chat_upgrade.js b/src/upgrades/1.0.0/chat_upgrade.js similarity index 100% rename from src/upgrades/chat_upgrade.js rename to src/upgrades/1.0.0/chat_upgrade.js diff --git a/src/upgrades/global_moderators.js b/src/upgrades/1.0.0/global_moderators.js similarity index 100% rename from src/upgrades/global_moderators.js rename to src/upgrades/1.0.0/global_moderators.js diff --git a/src/upgrades/social_post_sharing.js b/src/upgrades/1.0.0/social_post_sharing.js similarity index 100% rename from src/upgrades/social_post_sharing.js rename to src/upgrades/1.0.0/social_post_sharing.js diff --git a/src/upgrades/theme_to_active_plugins.js b/src/upgrades/1.0.0/theme_to_active_plugins.js similarity index 100% rename from src/upgrades/theme_to_active_plugins.js rename to src/upgrades/1.0.0/theme_to_active_plugins.js diff --git a/src/upgrades/user_best_posts.js b/src/upgrades/1.0.0/user_best_posts.js similarity index 100% rename from src/upgrades/user_best_posts.js rename to src/upgrades/1.0.0/user_best_posts.js diff --git a/src/upgrades/users_notvalidated.js b/src/upgrades/1.0.0/users_notvalidated.js similarity index 100% rename from src/upgrades/users_notvalidated.js rename to src/upgrades/1.0.0/users_notvalidated.js diff --git a/src/upgrades/assign_topic_read_privilege.js b/src/upgrades/1.1.0/assign_topic_read_privilege.js similarity index 100% rename from src/upgrades/assign_topic_read_privilege.js rename to src/upgrades/1.1.0/assign_topic_read_privilege.js diff --git a/src/upgrades/dismiss_flags_from_deleted_topics.js b/src/upgrades/1.1.0/dismiss_flags_from_deleted_topics.js similarity index 100% rename from src/upgrades/dismiss_flags_from_deleted_topics.js rename to src/upgrades/1.1.0/dismiss_flags_from_deleted_topics.js diff --git a/src/upgrades/group_title_update.js b/src/upgrades/1.1.0/group_title_update.js similarity index 100% rename from src/upgrades/group_title_update.js rename to src/upgrades/1.1.0/group_title_update.js diff --git a/src/upgrades/separate_upvote_downvote.js b/src/upgrades/1.1.0/separate_upvote_downvote.js similarity index 100% rename from src/upgrades/separate_upvote_downvote.js rename to src/upgrades/1.1.0/separate_upvote_downvote.js diff --git a/src/upgrades/user_post_count_per_tid.js b/src/upgrades/1.1.0/user_post_count_per_tid.js similarity index 100% rename from src/upgrades/user_post_count_per_tid.js rename to src/upgrades/1.1.0/user_post_count_per_tid.js diff --git a/src/upgrades/remove_negative_best_posts.js b/src/upgrades/1.1.1/remove_negative_best_posts.js similarity index 100% rename from src/upgrades/remove_negative_best_posts.js rename to src/upgrades/1.1.1/remove_negative_best_posts.js diff --git a/src/upgrades/upload_privileges.js b/src/upgrades/1.1.1/upload_privileges.js similarity index 100% rename from src/upgrades/upload_privileges.js rename to src/upgrades/1.1.1/upload_privileges.js diff --git a/src/upgrades/category_recent_tids.js b/src/upgrades/1.2.0/category_recent_tids.js similarity index 100% rename from src/upgrades/category_recent_tids.js rename to src/upgrades/1.2.0/category_recent_tids.js diff --git a/src/upgrades/edit_delete_deletetopic_privileges.js b/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js similarity index 100% rename from src/upgrades/edit_delete_deletetopic_privileges.js rename to src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js diff --git a/src/upgrades/favourites_to_bookmarks.js b/src/upgrades/1.3.0/favourites_to_bookmarks.js similarity index 100% rename from src/upgrades/favourites_to_bookmarks.js rename to src/upgrades/1.3.0/favourites_to_bookmarks.js diff --git a/src/upgrades/sorted_sets_for_post_replies.js b/src/upgrades/1.3.0/sorted_sets_for_post_replies.js similarity index 100% rename from src/upgrades/sorted_sets_for_post_replies.js rename to src/upgrades/1.3.0/sorted_sets_for_post_replies.js diff --git a/src/upgrades/global_and_user_language_keys.js b/src/upgrades/1.4.0/global_and_user_language_keys.js similarity index 100% rename from src/upgrades/global_and_user_language_keys.js rename to src/upgrades/1.4.0/global_and_user_language_keys.js diff --git a/src/upgrades/sorted_set_for_pinned_topics.js b/src/upgrades/1.4.0/sorted_set_for_pinned_topics.js similarity index 100% rename from src/upgrades/sorted_set_for_pinned_topics.js rename to src/upgrades/1.4.0/sorted_set_for_pinned_topics.js diff --git a/src/upgrades/flags_refactor.js b/src/upgrades/develop/flags_refactor.js similarity index 100% rename from src/upgrades/flags_refactor.js rename to src/upgrades/develop/flags_refactor.js diff --git a/src/upgrades/post_votes_zset.js b/src/upgrades/develop/post_votes_zset.js similarity index 100% rename from src/upgrades/post_votes_zset.js rename to src/upgrades/develop/post_votes_zset.js diff --git a/src/upgrades/config_urls_update.js b/src/upgrades/master/config_urls_update.js similarity index 100% rename from src/upgrades/config_urls_update.js rename to src/upgrades/master/config_urls_update.js diff --git a/src/upgrades/sound_settings.js b/src/upgrades/master/sound_settings.js similarity index 100% rename from src/upgrades/sound_settings.js rename to src/upgrades/master/sound_settings.js From e30917598983f595a8e1bc75e45e75ae112ecbe6 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 10 Mar 2017 16:37:16 -0500 Subject: [PATCH 156/316] eslinted template --- src/upgrades/TEMPLATE | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/upgrades/TEMPLATE b/src/upgrades/TEMPLATE index 5b79aa6148..7d451ae761 100644 --- a/src/upgrades/TEMPLATE +++ b/src/upgrades/TEMPLATE @@ -1,4 +1,5 @@ /* jslint node: true */ + 'use strict'; var db = require('../database'); @@ -11,5 +12,5 @@ module.exports = { timestamp: Date.UTC(2017, 0, 1), method: function (callback) { // Do stuff here... - } -}; \ No newline at end of file + }, +}; From f0059ec723c0c642330af9186aa35faffb0564c4 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 10 Mar 2017 19:07:05 -0500 Subject: [PATCH 157/316] fixed paths in upgrade scripts --- src/upgrades/1.0.0/chat_room_hashes.js | 2 +- src/upgrades/1.0.0/chat_upgrade.js | 2 +- src/upgrades/1.0.0/global_moderators.js | 2 +- src/upgrades/1.0.0/social_post_sharing.js | 4 ++-- src/upgrades/1.0.0/theme_to_active_plugins.js | 2 +- src/upgrades/1.0.0/user_best_posts.js | 4 ++-- src/upgrades/1.0.0/users_notvalidated.js | 4 ++-- src/upgrades/1.1.0/assign_topic_read_privilege.js | 6 +++--- src/upgrades/1.1.0/dismiss_flags_from_deleted_topics.js | 6 +++--- src/upgrades/1.1.0/group_title_update.js | 6 +++--- src/upgrades/1.1.0/separate_upvote_downvote.js | 6 +++--- src/upgrades/1.1.0/user_post_count_per_tid.js | 6 +++--- src/upgrades/1.1.1/remove_negative_best_posts.js | 4 ++-- src/upgrades/1.1.1/upload_privileges.js | 6 +++--- src/upgrades/1.2.0/category_recent_tids.js | 2 +- src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js | 6 +++--- src/upgrades/1.3.0/favourites_to_bookmarks.js | 6 +++--- src/upgrades/1.3.0/sorted_sets_for_post_replies.js | 6 +++--- src/upgrades/1.4.0/global_and_user_language_keys.js | 8 ++++---- src/upgrades/1.4.0/sorted_set_for_pinned_topics.js | 6 +++--- src/upgrades/{master => 1.4.4}/config_urls_update.js | 2 +- src/upgrades/{master => 1.4.4}/sound_settings.js | 6 +++--- src/upgrades/{develop => 1.5.0}/flags_refactor.js | 8 ++++---- src/upgrades/{develop => 1.5.0}/post_votes_zset.js | 4 ++-- src/upgrades/TEMPLATE | 2 +- 25 files changed, 58 insertions(+), 58 deletions(-) rename src/upgrades/{master => 1.4.4}/config_urls_update.js (95%) rename src/upgrades/{master => 1.4.4}/sound_settings.js (93%) rename src/upgrades/{develop => 1.5.0}/flags_refactor.js (94%) rename src/upgrades/{develop => 1.5.0}/post_votes_zset.js (83%) diff --git a/src/upgrades/1.0.0/chat_room_hashes.js b/src/upgrades/1.0.0/chat_room_hashes.js index bccf85282c..054b6c8d7e 100644 --- a/src/upgrades/1.0.0/chat_room_hashes.js +++ b/src/upgrades/1.0.0/chat_room_hashes.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); diff --git a/src/upgrades/1.0.0/chat_upgrade.js b/src/upgrades/1.0.0/chat_upgrade.js index ce49b501de..ae93ca95a5 100644 --- a/src/upgrades/1.0.0/chat_upgrade.js +++ b/src/upgrades/1.0.0/chat_upgrade.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); var winston = require('winston'); diff --git a/src/upgrades/1.0.0/global_moderators.js b/src/upgrades/1.0.0/global_moderators.js index bfe909a11b..3ba5f49955 100644 --- a/src/upgrades/1.0.0/global_moderators.js +++ b/src/upgrades/1.0.0/global_moderators.js @@ -8,7 +8,7 @@ module.exports = { name: 'Creating Global moderators group', timestamp: Date.UTC(2016, 0, 23), method: function (callback) { - var groups = require('../groups'); + var groups = require('../../groups'); async.waterfall([ function (next) { groups.exists('Global Moderators', next); diff --git a/src/upgrades/1.0.0/social_post_sharing.js b/src/upgrades/1.0.0/social_post_sharing.js index 22c4a8c7cf..03d303fe07 100644 --- a/src/upgrades/1.0.0/social_post_sharing.js +++ b/src/upgrades/1.0.0/social_post_sharing.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); @@ -10,7 +10,7 @@ module.exports = { name: 'Social: Post Sharing', timestamp: Date.UTC(2016, 1, 25), method: function (callback) { - var social = require('../social'); + var social = require('../../social'); async.parallel([ function (next) { social.setActivePostSharingNetworks(['facebook', 'google', 'twitter'], next); diff --git a/src/upgrades/1.0.0/theme_to_active_plugins.js b/src/upgrades/1.0.0/theme_to_active_plugins.js index 3adbca16dc..3a3bdd25eb 100644 --- a/src/upgrades/1.0.0/theme_to_active_plugins.js +++ b/src/upgrades/1.0.0/theme_to_active_plugins.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); diff --git a/src/upgrades/1.0.0/user_best_posts.js b/src/upgrades/1.0.0/user_best_posts.js index 126539c2b7..a832a71254 100644 --- a/src/upgrades/1.0.0/user_best_posts.js +++ b/src/upgrades/1.0.0/user_best_posts.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); var winston = require('winston'); @@ -11,7 +11,7 @@ module.exports = { name: 'Creating user best post sorted sets', timestamp: Date.UTC(2016, 0, 14), method: function (callback) { - var batch = require('../batch'); + var batch = require('../../batch'); batch.processSortedSet('posts:pid', function (ids, next) { async.eachSeries(ids, function (id, next) { diff --git a/src/upgrades/1.0.0/users_notvalidated.js b/src/upgrades/1.0.0/users_notvalidated.js index 9c23b6a539..5b5e60270b 100644 --- a/src/upgrades/1.0.0/users_notvalidated.js +++ b/src/upgrades/1.0.0/users_notvalidated.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); var winston = require('winston'); @@ -11,7 +11,7 @@ module.exports = { name: 'Creating users:notvalidated', timestamp: Date.UTC(2016, 0, 20), method: function (callback) { - var batch = require('../batch'); + var batch = require('../../batch'); var now = Date.now(); batch.processSortedSet('users:joindate', function (ids, next) { async.eachSeries(ids, function (id, next) { diff --git a/src/upgrades/1.1.0/assign_topic_read_privilege.js b/src/upgrades/1.1.0/assign_topic_read_privilege.js index 1e65d0d6f7..9f23cd7384 100644 --- a/src/upgrades/1.1.0/assign_topic_read_privilege.js +++ b/src/upgrades/1.1.0/assign_topic_read_privilege.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); var winston = require('winston'); @@ -11,8 +11,8 @@ module.exports = { name: 'Giving topics:read privs to any group that was previously allowed to Find & Access Category', timestamp: Date.UTC(2016, 4, 28), method: function (callback) { - var groupsAPI = require('../groups'); - var privilegesAPI = require('../privileges'); + var groupsAPI = require('../../groups'); + var privilegesAPI = require('../../privileges'); db.getSortedSetRange('categories:cid', 0, -1, function (err, cids) { if (err) { diff --git a/src/upgrades/1.1.0/dismiss_flags_from_deleted_topics.js b/src/upgrades/1.1.0/dismiss_flags_from_deleted_topics.js index cf4af73436..85d14f6bbc 100644 --- a/src/upgrades/1.1.0/dismiss_flags_from_deleted_topics.js +++ b/src/upgrades/1.1.0/dismiss_flags_from_deleted_topics.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); var winston = require('winston'); @@ -11,8 +11,8 @@ module.exports = { name: 'Dismiss flags from deleted topics', timestamp: Date.UTC(2016, 3, 29), method: function (callback) { - var posts = require('../posts'); - var topics = require('../topics'); + var posts = require('../../posts'); + var topics = require('../../topics'); var pids; var tids; diff --git a/src/upgrades/1.1.0/group_title_update.js b/src/upgrades/1.1.0/group_title_update.js index 5f82e97bfe..1c6065a3d4 100644 --- a/src/upgrades/1.1.0/group_title_update.js +++ b/src/upgrades/1.1.0/group_title_update.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); var winston = require('winston'); @@ -11,8 +11,8 @@ module.exports = { name: 'Group title from settings to user profile', timestamp: Date.UTC(2016, 3, 14), method: function (callback) { - var user = require('../user'); - var batch = require('../batch'); + var user = require('../../user'); + var batch = require('../../batch'); var count = 0; batch.processSortedSet('users:joindate', function (uids, next) { winston.verbose('upgraded ' + count + ' users'); diff --git a/src/upgrades/1.1.0/separate_upvote_downvote.js b/src/upgrades/1.1.0/separate_upvote_downvote.js index b313c7439d..c397a36a7b 100644 --- a/src/upgrades/1.1.0/separate_upvote_downvote.js +++ b/src/upgrades/1.1.0/separate_upvote_downvote.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); var winston = require('winston'); @@ -11,8 +11,8 @@ module.exports = { name: 'Store upvotes/downvotes separately', timestamp: Date.UTC(2016, 5, 13), method: function (callback) { - var batch = require('../batch'); - var posts = require('../posts'); + var batch = require('../../batch'); + var posts = require('../../posts'); var count = 0; batch.processSortedSet('posts:pid', function (pids, next) { winston.verbose('upgraded ' + count + ' posts'); diff --git a/src/upgrades/1.1.0/user_post_count_per_tid.js b/src/upgrades/1.1.0/user_post_count_per_tid.js index c0e86c4f52..7b27606c99 100644 --- a/src/upgrades/1.1.0/user_post_count_per_tid.js +++ b/src/upgrades/1.1.0/user_post_count_per_tid.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); var winston = require('winston'); @@ -11,8 +11,8 @@ module.exports = { name: 'Users post count per tid', timestamp: Date.UTC(2016, 3, 19), method: function (callback) { - var batch = require('../batch'); - var topics = require('../topics'); + var batch = require('../../batch'); + var topics = require('../../topics'); var count = 0; batch.processSortedSet('topics:tid', function (tids, next) { winston.verbose('upgraded ' + count + ' topics'); diff --git a/src/upgrades/1.1.1/remove_negative_best_posts.js b/src/upgrades/1.1.1/remove_negative_best_posts.js index 604d1d8234..22b6bb9ad1 100644 --- a/src/upgrades/1.1.1/remove_negative_best_posts.js +++ b/src/upgrades/1.1.1/remove_negative_best_posts.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); var winston = require('winston'); @@ -11,7 +11,7 @@ module.exports = { name: 'Removing best posts with negative scores', timestamp: Date.UTC(2016, 7, 5), method: function (callback) { - var batch = require('../batch'); + var batch = require('../../batch'); batch.processSortedSet('users:joindate', function (ids, next) { async.each(ids, function (id, next) { winston.verbose('processing uid ' + id); diff --git a/src/upgrades/1.1.1/upload_privileges.js b/src/upgrades/1.1.1/upload_privileges.js index b3268a2022..d84516efb7 100644 --- a/src/upgrades/1.1.1/upload_privileges.js +++ b/src/upgrades/1.1.1/upload_privileges.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); @@ -10,8 +10,8 @@ module.exports = { name: 'Giving upload privileges', timestamp: Date.UTC(2016, 6, 12), method: function (callback) { - var privilegesAPI = require('../privileges'); - var meta = require('../meta'); + var privilegesAPI = require('../../privileges'); + var meta = require('../../meta'); db.getSortedSetRange('categories:cid', 0, -1, function (err, cids) { if (err) { diff --git a/src/upgrades/1.2.0/category_recent_tids.js b/src/upgrades/1.2.0/category_recent_tids.js index 18d27266f6..9256cde5f5 100644 --- a/src/upgrades/1.2.0/category_recent_tids.js +++ b/src/upgrades/1.2.0/category_recent_tids.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); diff --git a/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js b/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js index 5ba6f604c5..acc87b7b19 100644 --- a/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js +++ b/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); var winston = require('winston'); @@ -11,8 +11,8 @@ module.exports = { name: 'Granting edit/delete/delete topic on existing categories', timestamp: Date.UTC(2016, 7, 7), method: function (callback) { - var groupsAPI = require('../groups'); - var privilegesAPI = require('../privileges'); + var groupsAPI = require('../../groups'); + var privilegesAPI = require('../../privileges'); db.getSortedSetRange('categories:cid', 0, -1, function (err, cids) { if (err) { diff --git a/src/upgrades/1.3.0/favourites_to_bookmarks.js b/src/upgrades/1.3.0/favourites_to_bookmarks.js index 05b1e5b5f9..0888e69653 100644 --- a/src/upgrades/1.3.0/favourites_to_bookmarks.js +++ b/src/upgrades/1.3.0/favourites_to_bookmarks.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); @@ -11,7 +11,7 @@ module.exports = { timestamp: Date.UTC(2016, 9, 8), method: function (callback) { function upgradePosts(next) { - var batch = require('../batch'); + var batch = require('../../batch'); batch.processSortedSet('posts:pid', function (ids, next) { async.each(ids, function (id, next) { @@ -38,7 +38,7 @@ module.exports = { } function upgradeUsers(next) { - var batch = require('../batch'); + var batch = require('../../batch'); batch.processSortedSet('users:joindate', function (ids, next) { async.each(ids, function (id, next) { diff --git a/src/upgrades/1.3.0/sorted_sets_for_post_replies.js b/src/upgrades/1.3.0/sorted_sets_for_post_replies.js index 78fc96c7a1..b3ecd25f0a 100644 --- a/src/upgrades/1.3.0/sorted_sets_for_post_replies.js +++ b/src/upgrades/1.3.0/sorted_sets_for_post_replies.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); var winston = require('winston'); @@ -11,8 +11,8 @@ module.exports = { name: 'Sorted sets for post replies', timestamp: Date.UTC(2016, 9, 14), method: function (callback) { - var posts = require('../posts'); - var batch = require('../batch'); + var posts = require('../../posts'); + var batch = require('../../batch'); batch.processSortedSet('posts:pid', function (ids, next) { posts.getPostsFields(ids, ['pid', 'toPid', 'timestamp'], function (err, data) { if (err) { diff --git a/src/upgrades/1.4.0/global_and_user_language_keys.js b/src/upgrades/1.4.0/global_and_user_language_keys.js index 14f71c1fce..20548e6820 100644 --- a/src/upgrades/1.4.0/global_and_user_language_keys.js +++ b/src/upgrades/1.4.0/global_and_user_language_keys.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); @@ -10,9 +10,9 @@ module.exports = { name: 'Update global and user language keys', timestamp: Date.UTC(2016, 10, 22), method: function (callback) { - var user = require('../user'); - var meta = require('../meta'); - var batch = require('../batch'); + var user = require('../../user'); + var meta = require('../../meta'); + var batch = require('../../batch'); var newLanguage; async.parallel([ function (next) { diff --git a/src/upgrades/1.4.0/sorted_set_for_pinned_topics.js b/src/upgrades/1.4.0/sorted_set_for_pinned_topics.js index faa12f3cc8..a82ef80884 100644 --- a/src/upgrades/1.4.0/sorted_set_for_pinned_topics.js +++ b/src/upgrades/1.4.0/sorted_set_for_pinned_topics.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); var winston = require('winston'); @@ -11,8 +11,8 @@ module.exports = { name: 'Sorted set for pinned topics', timestamp: Date.UTC(2016, 10, 25), method: function (callback) { - var topics = require('../topics'); - var batch = require('../batch'); + var topics = require('../../topics'); + var batch = require('../../batch'); batch.processSortedSet('topics:tid', function (ids, next) { topics.getTopicsFields(ids, ['tid', 'cid', 'pinned', 'lastposttime'], function (err, data) { if (err) { diff --git a/src/upgrades/master/config_urls_update.js b/src/upgrades/1.4.4/config_urls_update.js similarity index 95% rename from src/upgrades/master/config_urls_update.js rename to src/upgrades/1.4.4/config_urls_update.js index bd4dfc5d83..4a9d1d703c 100644 --- a/src/upgrades/master/config_urls_update.js +++ b/src/upgrades/1.4.4/config_urls_update.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); diff --git a/src/upgrades/master/sound_settings.js b/src/upgrades/1.4.4/sound_settings.js similarity index 93% rename from src/upgrades/master/sound_settings.js rename to src/upgrades/1.4.4/sound_settings.js index 610b51f043..73b15e881a 100644 --- a/src/upgrades/master/sound_settings.js +++ b/src/upgrades/1.4.4/sound_settings.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); @@ -10,8 +10,8 @@ module.exports = { name: 'Update global and user sound settings', timestamp: Date.UTC(2017, 1, 25), method: function (callback) { - var meta = require('../meta'); - var batch = require('../batch'); + var meta = require('../../meta'); + var batch = require('../../batch'); var map = { 'notification.mp3': 'Default | Deedle-dum', diff --git a/src/upgrades/develop/flags_refactor.js b/src/upgrades/1.5.0/flags_refactor.js similarity index 94% rename from src/upgrades/develop/flags_refactor.js rename to src/upgrades/1.5.0/flags_refactor.js index deb1befc6c..7aff6bcfe7 100644 --- a/src/upgrades/develop/flags_refactor.js +++ b/src/upgrades/1.5.0/flags_refactor.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); @@ -10,9 +10,9 @@ module.exports = { name: 'Migrating flags to new schema', timestamp: Date.UTC(2016, 11, 7), method: function (callback) { - var batch = require('../batch'); - var posts = require('../posts'); - var flags = require('../flags'); + var batch = require('../../batch'); + var posts = require('../../posts'); + var flags = require('../../flags'); batch.processSortedSet('posts:pid', function (ids, next) { posts.getPostsByPids(ids, 1, function (err, posts) { diff --git a/src/upgrades/develop/post_votes_zset.js b/src/upgrades/1.5.0/post_votes_zset.js similarity index 83% rename from src/upgrades/develop/post_votes_zset.js rename to src/upgrades/1.5.0/post_votes_zset.js index c1eefb6688..d989602e23 100644 --- a/src/upgrades/develop/post_votes_zset.js +++ b/src/upgrades/1.5.0/post_votes_zset.js @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); @@ -10,7 +10,7 @@ module.exports = { name: 'New sorted set posts:votes', timestamp: Date.UTC(2017, 1, 27), method: function (callback) { - require('../batch').processSortedSet('posts:pid', function (pids, next) { + require('../../batch').processSortedSet('posts:pid', function (pids, next) { async.each(pids, function (pid, next) { db.getObjectFields('post:' + pid, ['upvotes', 'downvotes'], function (err, postData) { if (err || !postData) { diff --git a/src/upgrades/TEMPLATE b/src/upgrades/TEMPLATE index 7d451ae761..83cd01586d 100644 --- a/src/upgrades/TEMPLATE +++ b/src/upgrades/TEMPLATE @@ -2,7 +2,7 @@ 'use strict'; -var db = require('../database'); +var db = require('../../database'); var async = require('async'); var winston = require('winston'); From 34d24c4fd49e821b60585cf8b01ae99fdc9f7ef9 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 10 Mar 2017 19:07:42 -0500 Subject: [PATCH 158/316] Refactored upgrade scripts once more (for great justice!) #5467 --- src/upgrade.js | 77 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/src/upgrade.js b/src/upgrade.js index 8978f5e60a..081ddc5757 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -4,6 +4,7 @@ var async = require('async'); var path = require('path'); +var semver = require('semver'); var db = require('./database'); var utils = require('../public/src/utils'); @@ -55,24 +56,47 @@ var Upgrade = { ], }; +Upgrade.getAll = function (callback) { + async.waterfall([ + async.apply(utils.walk, path.join(__dirname, './upgrades')), + function (files, next) { + // Sort the upgrade scripts based on version + var versionA; + var versionB; + setImmediate(next, null, files.filter(function (file) { + return path.basename(file) !== 'TEMPLATE'; + }).sort(function (a, b) { + versionA = path.dirname(a).split('/').pop(); + versionB = path.dirname(b).split('/').pop(); + + return semver.compare(versionA, versionB); + })); + }, + ], callback); +}; + Upgrade.check = function (callback) { // Throw 'schema-out-of-date' if not all upgrade scripts have run - var all = Upgrade.available.reduce(function (memo, current) { - memo = memo.concat(current.upgrades); - return memo; - }, []); - - db.getSortedSetRange('schemaLog', 0, -1, function (err, executed) { - if (err) { - return callback(err); - } + async.waterfall([ + async.apply(Upgrade.getAll), + function (files, next) { + db.getSortedSetRange('schemaLog', 0, -1, function (err, executed) { + if (err) { + return callback(err); + } - var remainder = all.filter(function (name) { - return executed.indexOf(name) === -1; - }); + var remainder = files.filter(function (name) { + return executed.indexOf(path.basename(name, '.js')) === -1; + }); - callback(remainder.length > 1 ? new Error('schema-out-of-date') : null); - }); + next(remainder.length > 1 ? new Error('schema-out-of-date') : null); + }); + }, + ], callback); + // var all = Upgrade.available.reduce(function (memo, current) { + // memo = memo.concat(current.upgrades); + // return memo; + // }, []); }; Upgrade.run = function (callback) { @@ -80,20 +104,22 @@ Upgrade.run = function (callback) { var queue = []; var skipped = 0; - // Retrieve list of upgrades that have already been run - db.getSortedSetRange('schemaLog', 0, -1, function (err, completed) { + async.parallel({ + // Retrieve list of upgrades that have already been run + completed: async.apply(db.getSortedSetRange, 'schemaLog', 0, -1), + // ... and those available to be run + available: Upgrade.getAll, + }, function (err, data) { if (err) { return callback(err); } - queue = Upgrade.available.reduce(function (memo, cur) { - cur.upgrades.forEach(function (filename) { - if (completed.indexOf(filename) === -1) { - memo.push(path.join(__dirname, './upgrades', filename)); - } else { - skipped += 1; - } - }); + queue = data.available.reduce(function (memo, cur) { + if (data.completed.indexOf(path.basename(cur, '.js')) === -1) { + memo.push(cur); + } else { + skipped += 1; + } return memo; }, queue); @@ -124,9 +150,6 @@ Upgrade.runSingle = function (query, callback) { Upgrade.process = function (files, skipCount, callback) { process.stdout.write('OK'.green + ' | '.reset + String(files.length).cyan + ' script(s) found'.cyan + (skipCount > 0 ? ', '.cyan + String(skipCount).cyan + ' skipped'.cyan : '') + '\n'.reset); - // Do I need to sort the files here? we'll see. - // sort(); - async.eachSeries(files, function (file, next) { var scriptExport = require(file); var date = new Date(scriptExport.timestamp); From 767705085b7bf45d0d9e253bde1ad5c048e4c2a3 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 13 Mar 2017 14:00:48 -0400 Subject: [PATCH 159/316] test against nodejs 7 as well --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 69688f4bde..e9db303b27 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,6 +22,7 @@ addons: packages: - g++-4.8 node_js: + - "7" - "6" - "4" branches: From c323a813c041f2ea5a5df0c51ce56dd18464908f Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 13 Mar 2017 16:45:05 -0400 Subject: [PATCH 160/316] added logic to handle backwards compatibility with old schemaDate method of tracking upgrade scripts --- src/upgrade.js | 58 +++++++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/src/upgrade.js b/src/upgrade.js index 081ddc5757..5d07fea666 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -150,33 +150,47 @@ Upgrade.runSingle = function (query, callback) { Upgrade.process = function (files, skipCount, callback) { process.stdout.write('OK'.green + ' | '.reset + String(files.length).cyan + ' script(s) found'.cyan + (skipCount > 0 ? ', '.cyan + String(skipCount).cyan + ' skipped'.cyan : '') + '\n'.reset); - async.eachSeries(files, function (file, next) { - var scriptExport = require(file); - var date = new Date(scriptExport.timestamp); + async.waterfall([ + async.apply(db.get, 'schemaDate'), + function (schemaDate, next) { + var schemaTime = new Date(schemaDate); - process.stdout.write(' → '.white + String('[' + [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()].join('/') + '] ').gray + String(scriptExport.name).reset + '... '); + async.eachSeries(files, function (file, next) { + var scriptExport = require(file); + var date = new Date(scriptExport.timestamp); - // Do the upgrade... - scriptExport.method(function (err) { - if (err) { - process.stdout.write('error\n'.red); - return next(err); - } + process.stdout.write(' → '.white + String('[' + [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()].join('/') + '] ').gray + String(scriptExport.name).reset + '... '); - // Record success in schemaLog - db.sortedSetAdd('schemaLog', Date.now(), path.basename(file, '.js')); + // For backwards compatibility, cross-reference with schemaDate (if found). If a script's date is older, skip it + if (scriptExport.timestamp <= schemaTime) { + process.stdout.write('skipped\n'.grey); + db.sortedSetAdd('schemaLog', Date.now(), path.basename(file, '.js')); + return next(); + } - process.stdout.write('OK\n'.green); - next(); - }); - }, function (err) { - if (err) { - return callback(err); - } + // Do the upgrade... + scriptExport.method(function (err, skipped) { + if (err) { + process.stdout.write('error\n'.red); + return next(err); + } - process.stdout.write('Upgrade complete!\n\n'.green); - callback(); - }); + // Record success in schemaLog + db.sortedSetAdd('schemaLog', Date.now(), path.basename(file, '.js')); + + process.stdout.write('OK\n'.green); + next(); + }); + }, function (err) { + if (err) { + return next(err); + } + + process.stdout.write('Upgrade complete!\n\n'.green); + next(); + }); + }, + ], callback); }; module.exports = Upgrade; From 25518bdd18ab89e16cdd1660e259e30e8c78e31f Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 13 Mar 2017 16:54:29 -0400 Subject: [PATCH 161/316] removed unused vars --- src/upgrade.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/upgrade.js b/src/upgrade.js index 5d07fea666..57f40f5fa7 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -169,7 +169,7 @@ Upgrade.process = function (files, skipCount, callback) { } // Do the upgrade... - scriptExport.method(function (err, skipped) { + scriptExport.method(function (err) { if (err) { process.stdout.write('error\n'.red); return next(err); From 5a2ff8ed0a0e4a5bb6e91b561478127d9019e31d Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 14 Mar 2017 23:03:03 +0300 Subject: [PATCH 162/316] closes #5527 --- public/language/en-GB/notifications.json | 11 ++ public/src/client/notifications.js | 27 +--- src/controllers/accounts/notifications.js | 96 +++++++++++--- src/flags.js | 4 +- src/messaging/notifications.js | 1 + src/notifications.js | 84 +++++++------ src/socket.io/helpers.js | 1 + src/socket.io/notifications.js | 25 +--- src/socket.io/user.js | 1 + src/topics/follow.js | 1 + src/user/notifications.js | 145 ++++++++++++++++------ test/notifications.js | 25 ---- 12 files changed, 247 insertions(+), 174 deletions(-) diff --git a/public/language/en-GB/notifications.json b/public/language/en-GB/notifications.json index 0838ca17eb..e04482c633 100644 --- a/public/language/en-GB/notifications.json +++ b/public/language/en-GB/notifications.json @@ -12,6 +12,17 @@ "new_notification": "New Notification", "you_have_unread_notifications": "You have unread notifications.", + "all": "All", + "topics": "Topics", + "replies": "Replies", + "chat": "Chats", + "follows": "Follows", + "upvote": "Upvotes", + "new-flags": "New Flags", + "my-flags": "Flags assigned to me", + "bans": "Bans", + + "new_message_from": "New message from %1", "upvoted_your_post_in": "%1 has upvoted your post in %2.", "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.", diff --git a/public/src/client/notifications.js b/public/src/client/notifications.js index fb61a53063..04213499e3 100644 --- a/public/src/client/notifications.js +++ b/public/src/client/notifications.js @@ -1,7 +1,7 @@ 'use strict'; -define('forum/notifications', ['components', 'notifications', 'forum/infinitescroll'], function (components, notifs, infinitescroll) { +define('forum/notifications', ['components', 'notifications'], function (components, notifs) { var Notifications = {}; Notifications.init = function () { @@ -35,32 +35,7 @@ define('forum/notifications', ['components', 'notifications', 'forum/infinitescr notifs.updateNotifCount(0); }); }); - - infinitescroll.init(loadMoreNotifications); }; - function loadMoreNotifications(direction) { - if (direction < 0) { - return; - } - var notifList = $('.notifications-list'); - infinitescroll.loadMore('notifications.loadMore', { - after: notifList.attr('data-nextstart'), - }, function (data, done) { - if (!data) { - return done(); - } - notifList.attr('data-nextstart', data.nextStart); - if (!data.notifications || !data.notifications.length) { - return done(); - } - app.parseAndTranslate('notifications', 'notifications', { notifications: data.notifications }, function (html) { - notifList.append(html); - html.find('.timeago').timeago(); - done(); - }); - }); - } - return Notifications; }); diff --git a/src/controllers/accounts/notifications.js b/src/controllers/accounts/notifications.js index 6184ec056d..9e1839b42b 100644 --- a/src/controllers/accounts/notifications.js +++ b/src/controllers/accounts/notifications.js @@ -1,23 +1,91 @@ 'use strict'; +var async = require('async'); + var user = require('../../user'); var helpers = require('../helpers'); +var plugins = require('../../plugins'); +var pagination = require('../../pagination'); -var notificationsController = {}; +var notificationsController = module.exports; notificationsController.get = function (req, res, next) { - user.notifications.getAll(req.uid, 0, 39, function (err, notifications) { - if (err) { - return next(err); - } - res.render('notifications', { - notifications: notifications, - nextStart: 40, - title: '[[pages:notifications]]', - breadcrumbs: helpers.buildBreadcrumbs([{ text: '[[pages:notifications]]' }]), - }); - }); -}; + var regularFilters = [ + { name: '[[notifications:all]]', filter: '' }, + { name: '[[notifications:topics]]', filter: 'new-topic' }, + { name: '[[notifications:replies]]', filter: 'new-reply' }, + { name: '[[notifications:chat]]', filter: 'new-chat' }, + { name: '[[notifications:follows]]', filter: 'follow' }, + { name: '[[notifications:upvote]]', filter: 'upvote' }, + ]; + + var moderatorFilters = [ + { name: '[[notifications:new-flags]]', filter: 'new-post-flag' }, + { name: '[[notifications:my-flags]]', filter: 'my-flags' }, + { name: '[[notifications:bans]]', filter: 'ban' }, + ]; + + var filter = req.query.filter || ''; + var page = Math.max(1, req.query.page || 1); + var itemsPerPage = 20; + var start = (page - 1) * itemsPerPage; + var stop = start + itemsPerPage - 1; + var selectedFilter; + var pageCount = 1; + var allFilters = []; + + async.waterfall([ + function (next) { + async.parallel({ + filters: function (next) { + plugins.fireHook('filter:notifications.addFilters', { + regularFilters: regularFilters, + moderatorFilters: moderatorFilters, + uid: req.uid, + }, next); + }, + isPrivileged: function (next) { + user.isPrivileged(req.uid, next); + }, + }, next); + }, + function (data, _next) { + allFilters = data.filters.regularFilters; + if (data.isPrivileged) { + allFilters = allFilters.concat([ + { separator: true }, + ]).concat(data.filters.moderatorFilters); + } -module.exports = notificationsController; + selectedFilter = allFilters.find(function (filterData) { + filterData.selected = filterData.filter === filter; + return filterData.selected; + }); + + if (!selectedFilter) { + return next(); + } + + user.notifications.getAll(req.uid, selectedFilter.filter, _next); + }, + function (nids, next) { + pageCount = nids.length / itemsPerPage; + nids = nids.slice(start, stop + 1); + + user.notifications.getNotifications(nids, req.uid, next); + }, + function (notifications) { + res.render('notifications', { + notifications: notifications, + pagination: pagination.create(page, pageCount, req.query), + filters: allFilters, + regularFilters: regularFilters, + moderatorFilters: moderatorFilters, + selectedFilter: selectedFilter, + title: '[[pages:notifications]]', + breadcrumbs: helpers.buildBreadcrumbs([{ text: '[[pages:notifications]]' }]), + }); + }, + ], next); +}; diff --git a/src/flags.js b/src/flags.js index 33175d933f..185ef6f6ad 100644 --- a/src/flags.js +++ b/src/flags.js @@ -60,9 +60,6 @@ Flags.list = function (filters, uid, callback) { value.forEach(function (x) { orSets.push(setPrefix + x); }); - } else { - // Empty array, do nothing - } }; @@ -604,6 +601,7 @@ Flags.notify = function (flagObj, uid, callback) { var titleEscaped = title.replace(/%/g, '%').replace(/,/g, ','); notifications.create({ + type: 'new-post-flag', bodyShort: '[[notifications:user_flagged_post_in, ' + flagObj.reporter.username + ', ' + titleEscaped + ']]', bodyLong: flagObj.description, pid: flagObj.targetId, diff --git a/src/messaging/notifications.js b/src/messaging/notifications.js index eb7a1a1a74..a3f168f49d 100644 --- a/src/messaging/notifications.js +++ b/src/messaging/notifications.js @@ -68,6 +68,7 @@ module.exports = function (Messaging) { } notifications.create({ + type: 'new-chat', bodyShort: '[[notifications:new_message_from, ' + messageObj.fromUser.username + ']]', bodyLong: messageObj.content, nid: 'chat_' + fromuid + '_' + roomId, diff --git a/src/notifications.js b/src/notifications.js index 752915d636..1e476f619e 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -28,64 +28,66 @@ var utils = require('../public/src/utils'); }; Notifications.getMultiple = function (nids, callback) { + if (!nids.length) { + return setImmediate(callback, null, []); + } var keys = nids.map(function (nid) { return 'notifications:' + nid; }); - db.getObjects(keys, function (err, notifications) { - if (err) { - return callback(err); - } - - notifications = notifications.filter(Boolean); - if (!notifications.length) { - return callback(null, []); - } + var notifications; - var userKeys = notifications.map(function (notification) { - return notification.from; - }); + async.waterfall([ + function (next) { + db.getObjects(keys, next); + }, + function (_notifications, next) { + notifications = _notifications; + var userKeys = notifications.map(function (notification) { + return notification && notification.from; + }); - User.getUsersFields(userKeys, ['username', 'userslug', 'picture'], function (err, usersData) { - if (err) { - return callback(err); - } + User.getUsersFields(userKeys, ['username', 'userslug', 'picture'], next); + }, + function (usersData, next) { notifications.forEach(function (notification, index) { - notification.datetimeISO = utils.toISOString(notification.datetime); + if (notification) { + notification.datetimeISO = utils.toISOString(notification.datetime); - if (notification.bodyLong) { - notification.bodyLong = S(notification.bodyLong).escapeHTML().s; - } + if (notification.bodyLong) { + notification.bodyLong = S(notification.bodyLong).escapeHTML().s; + } - notification.user = usersData[index]; - if (notification.user) { - notification.image = notification.user.picture || null; - if (notification.user.username === '[[global:guest]]') { - notification.bodyShort = notification.bodyShort.replace(/([\s\S]*?),[\s\S]*?,([\s\S]*?)/, '$1, [[global:guest]], $2'); + notification.user = usersData[index]; + if (notification.user) { + notification.image = notification.user.picture || null; + if (notification.user.username === '[[global:guest]]') { + notification.bodyShort = notification.bodyShort.replace(/([\s\S]*?),[\s\S]*?,([\s\S]*?)/, '$1, [[global:guest]], $2'); + } + } else if (notification.image === 'brand:logo' || !notification.image) { + notification.image = meta.config['brand:logo'] || nconf.get('relative_path') + '/logo.png'; } - } else if (notification.image === 'brand:logo' || !notification.image) { - notification.image = meta.config['brand:logo'] || nconf.get('relative_path') + '/logo.png'; } }); - callback(null, notifications); - }); - }); + next(null, notifications); + }, + ], callback); }; Notifications.filterExists = function (nids, callback) { - // Removes nids that have been pruned - db.isSortedSetMembers('notifications', nids, function (err, exists) { - if (err) { - return callback(err); - } - - nids = nids.filter(function (notifId, idx) { - return exists[idx]; - }); + async.waterfall([ + function (next) { + db.isSortedSetMembers('notifications', nids, next); + }, + function (exists, next) { + nids = nids.filter(function (notifId, idx) { + return exists[idx]; + }); - callback(null, nids); - }); + next(null, nids); + }, + ], callback); }; Notifications.findRelated = function (mergeIds, set, callback) { diff --git a/src/socket.io/helpers.js b/src/socket.io/helpers.js index ce2ee7b30c..241937d5fb 100644 --- a/src/socket.io/helpers.js +++ b/src/socket.io/helpers.js @@ -106,6 +106,7 @@ SocketHelpers.sendNotificationToPostOwner = function (pid, fromuid, command, not var titleEscaped = title.replace(/%/g, '%').replace(/,/g, ','); notifications.create({ + type: command, bodyShort: '[[' + notification + ', ' + results.username + ', ' + titleEscaped + ']]', bodyLong: results.postObj.content, pid: pid, diff --git a/src/socket.io/notifications.js b/src/socket.io/notifications.js index 66e5135ed6..8a4568810e 100644 --- a/src/socket.io/notifications.js +++ b/src/socket.io/notifications.js @@ -1,11 +1,9 @@ 'use strict'; -var async = require('async'); var user = require('../user'); var notifications = require('../notifications'); -var utils = require('../../public/src/utils'); -var SocketNotifs = {}; +var SocketNotifs = module.exports; SocketNotifs.get = function (socket, data, callback) { if (data && Array.isArray(data.nids) && socket.uid) { @@ -15,25 +13,6 @@ SocketNotifs.get = function (socket, data, callback) { } }; -SocketNotifs.loadMore = function (socket, data, callback) { - if (!data || !utils.isNumber(data.after) || parseInt(data.after, 10) < 0) { - return callback(new Error('[[error:invalid-data]]')); - } - if (!socket.uid) { - return callback(new Error('[[error:no-privileges]]')); - } - var start = parseInt(data.after, 10); - var stop = start + 20; - async.waterfall([ - function (next) { - user.notifications.getAll(socket.uid, start, stop, next); - }, - function (notifications, next) { - next(null, { notifications: notifications, nextStart: stop }); - }, - ], callback); -}; - SocketNotifs.getCount = function (socket, data, callback) { user.notifications.getUnreadCount(socket.uid, callback); }; @@ -57,5 +36,3 @@ SocketNotifs.markUnread = function (socket, nid, callback) { SocketNotifs.markAllRead = function (socket, data, callback) { notifications.markAllRead(socket.uid, callback); }; - -module.exports = SocketNotifs; diff --git a/src/socket.io/user.js b/src/socket.io/user.js index 78f696a19b..c161d13f6f 100644 --- a/src/socket.io/user.js +++ b/src/socket.io/user.js @@ -171,6 +171,7 @@ SocketUser.follow = function (socket, data, callback) { function (_userData, next) { userData = _userData; notifications.create({ + type: 'follow', bodyShort: '[[notifications:user_started_following_you, ' + userData.username + ']]', nid: 'follow:' + data.uid + ':uid:' + socket.uid, from: socket.uid, diff --git a/src/topics/follow.js b/src/topics/follow.js index a3b1041b13..5b5f368f6a 100644 --- a/src/topics/follow.js +++ b/src/topics/follow.js @@ -224,6 +224,7 @@ module.exports = function (Topics) { postData.content = posts.relativeToAbsolute(postData.content); notifications.create({ + type: 'new-reply', bodyShort: '[[notifications:user_posted_to, ' + postData.user.username + ', ' + titleEscaped + ']]', bodyLong: postData.content, pid: postData.pid, diff --git a/src/user/notifications.js b/src/user/notifications.js index f830092b07..eebb31de7b 100644 --- a/src/user/notifications.js +++ b/src/user/notifications.js @@ -32,20 +32,80 @@ var privileges = require('../privileges'); }); }; - UserNotifications.getAll = function (uid, start, stop, callback) { - getNotifications(uid, start, stop, function (err, notifs) { - if (err) { - return callback(err); - } - notifs = notifs.unread.concat(notifs.read); - notifs = notifs.filter(Boolean).sort(function (a, b) { - return b.datetime - a.datetime; - }); + function filterNotifications(nids, filter, callback) { + if (!filter) { + return setImmediate(callback, null, nids); + } + async.waterfall([ + function (next) { + var keys = nids.map(function (nid) { + return 'notifications:' + nid; + }); + db.getObjectsFields(keys, ['nid', 'type'], next); + }, + function (notifications, next) { + nids = notifications.filter(function (notification) { + return notification && notification.nid && notification.type === filter; + }).map(function (notification) { + return notification.nid; + }); + next(null, nids); + }, + ], callback); + } - callback(null, notifs); - }); + UserNotifications.getAll = function (uid, filter, callback) { + var nids; + async.waterfall([ + function (next) { + async.parallel({ + unread: function (next) { + db.getSortedSetRevRange('uid:' + uid + ':notifications:unread', 0, -1, next); + }, + read: function (next) { + db.getSortedSetRevRange('uid:' + uid + ':notifications:read', 0, -1, next); + }, + }, next); + }, + function (results, next) { + nids = results.unread.concat(results.read); + db.isSortedSetMembers('notifications', nids, next); + }, + function (exists, next) { + var deleteNids = []; + + nids = nids.filter(function (nid, index) { + if (!nid || !exists[index]) { + deleteNids.push(nid); + } + return nid && exists[index]; + }); + + deleteUserNids(deleteNids, uid, next); + }, + function (next) { + filterNotifications(nids, filter, next); + }, + ], callback); }; + function deleteUserNids(nids, uid, callback) { + callback = callback || function () {}; + if (!nids.length) { + return setImmediate(callback); + } + async.parallel([ + function (next) { + db.sortedSetRemove('uid:' + uid + ':notifications:read', nids, next); + }, + function (next) { + db.sortedSetRemove('uid:' + uid + ':notifications:unread', nids, next); + }, + ], function (err) { + callback(err); + }); + } + function getNotifications(uid, start, stop, callback) { async.parallel({ unread: function (next) { @@ -58,53 +118,55 @@ var privileges = require('../privileges'); } function getNotificationsFromSet(set, read, uid, start, stop, callback) { - var setNids; - async.waterfall([ - async.apply(db.getSortedSetRevRange, set, start, stop), + function (next) { + db.getSortedSetRevRange(set, start, stop, next); + }, function (nids, next) { if (!Array.isArray(nids) || !nids.length) { return callback(null, []); } - setNids = nids; UserNotifications.getNotifications(nids, uid, next); }, - function (notifs, next) { - var deletedNids = []; + ], callback); + } - notifs.forEach(function (notification, index) { - if (!notification) { - winston.verbose('[notifications.get] nid ' + setNids[index] + ' not found. Removing.'); - deletedNids.push(setNids[index]); - } else { - notification.read = read; + UserNotifications.getNotifications = function (nids, uid, callback) { + var notificationData = []; + async.waterfall([ + function (next) { + async.parallel({ + notifications: function (next) { + notifications.getMultiple(nids, next); + }, + hasRead: function (next) { + db.isSortedSetMember('uid:' + uid + ':notifications:read', nids, next); + }, + }, next); + }, + function (results, next) { + var deletedNids = []; + notificationData = results.notifications.filter(function (notification, index) { + if (!notification || !notification.nid) { + deletedNids.push(nids[index]); + } + if (notification) { + notification.read = results.hasRead[index]; notification.readClass = !notification.read ? 'unread' : ''; } - }); - if (deletedNids.length) { - db.sortedSetRemove(set, deletedNids); - } + return notification && notification.path; + }); - notifications.merge(notifs, next); + deleteUserNids(deletedNids, uid, next); + }, + function (next) { + notifications.merge(notificationData, next); }, ], callback); - } - - UserNotifications.getNotifications = function (nids, uid, callback) { - notifications.getMultiple(nids, function (err, notifications) { - if (err) { - return callback(err); - } - notifications = notifications.filter(function (notification) { - return notification && notification.path; - }); - callback(null, notifications); - }); }; - UserNotifications.getDailyUnread = function (uid, callback) { var yesterday = Date.now() - (1000 * 60 * 60 * 24); // Approximate, can be more or less depending on time changes, makes no difference really. @@ -222,6 +284,7 @@ var privileges = require('../privileges'); } notifications.create({ + type: 'new-topic', bodyShort: '[[notifications:user_posted_topic, ' + postData.user.username + ', ' + title + ']]', bodyLong: postData.content, pid: postData.pid, diff --git a/test/notifications.js b/test/notifications.js index f21d46cf32..d65b4c0bb5 100644 --- a/test/notifications.js +++ b/test/notifications.js @@ -232,31 +232,6 @@ describe('Notifications', function () { }); }); - it('should error with invalid data', function (done) { - socketNotifications.loadMore({ uid: uid }, { after: 'test' }, function (err) { - assert.equal(err.message, '[[error:invalid-data]]'); - done(); - }); - }); - - it('should error if not logged in', function (done) { - socketNotifications.loadMore({ uid: 0 }, { after: 10 }, function (err) { - assert.equal(err.message, '[[error:no-privileges]]'); - done(); - }); - }); - - it('should load more notifications', function (done) { - socketNotifications.loadMore({ uid: uid }, { after: 0 }, function (err, data) { - assert.ifError(err); - assert.equal(data.notifications[0].bodyShort, 'bodyShort'); - assert.equal(data.notifications[0].nid, 'notification_id'); - assert.equal(data.notifications[0].path, '/notification/path'); - done(); - }); - }); - - it('should error if not logged in', function (done) { socketNotifications.deleteAll({ uid: 0 }, null, function (err) { assert.equal(err.message, '[[error:no-privileges]]'); From cb92ebb3be7e37d3cb17dacdc77f99ecef6e0cd0 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 14 Mar 2017 23:29:26 +0300 Subject: [PATCH 163/316] fix call --- src/user/notifications.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/user/notifications.js b/src/user/notifications.js index eebb31de7b..92a65e2649 100644 --- a/src/user/notifications.js +++ b/src/user/notifications.js @@ -141,7 +141,7 @@ var privileges = require('../privileges'); notifications.getMultiple(nids, next); }, hasRead: function (next) { - db.isSortedSetMember('uid:' + uid + ':notifications:read', nids, next); + db.isSortedSetMembers('uid:' + uid + ':notifications:read', nids, next); }, }, next); }, From 250bb4b0111b6e8d443f7f8268b0183f2dcafb97 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 15 Mar 2017 10:23:25 -0400 Subject: [PATCH 164/316] fixed leftover merge conflict artifact --- public/src/admin/general/dashboard.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/public/src/admin/general/dashboard.js b/public/src/admin/general/dashboard.js index 38d324cbcd..71ad4e3738 100644 --- a/public/src/admin/general/dashboard.js +++ b/public/src/admin/general/dashboard.js @@ -307,12 +307,9 @@ define('admin/general/dashboard', ['semver', 'Chart', 'translator'], function (s $(this).addClass('active'); }); -<<<<<<< HEAD socket.emit('admin.rooms.getAll', Admin.updateRoomUsage); initiateDashboard(); -======= callback(); ->>>>>>> origin/master }); } From f7d1e58d65655a266f6edeb81b1cbd24e2a0e78b Mon Sep 17 00:00:00 2001 From: psychobunny Date: Wed, 15 Mar 2017 18:56:12 -0400 Subject: [PATCH 165/316] improved threaded view design... again --- src/topics/posts.js | 47 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/topics/posts.js b/src/topics/posts.js index 909de0caa4..1e0fd20f42 100644 --- a/src/topics/posts.js +++ b/src/topics/posts.js @@ -10,6 +10,7 @@ var user = require('../user'); var posts = require('../posts'); var meta = require('../meta'); var plugins = require('../plugins'); +var utils = require('../../public/src/utils'); module.exports = function (Topics) { Topics.onNewPostMade = function (postData, callback) { @@ -103,6 +104,9 @@ module.exports = function (Topics) { parents: function (next) { Topics.addParentPosts(postData, next); }, + replies: function (next) { + +getPostReplies(pids, uid, next); + }, }, next); }, function (results, next) { @@ -115,7 +119,7 @@ module.exports = function (Topics) { postObj.upvoted = results.voteData.upvotes[i]; postObj.downvoted = results.voteData.downvotes[i]; postObj.votes = postObj.votes || 0; - postObj.replies = postObj.replies || 0; + postObj.replies = results.replies[i]; postObj.selfPost = !!parseInt(uid, 10) && parseInt(uid, 10) === parseInt(postObj.uid, 10); // Username override for guests, if enabled @@ -385,4 +389,45 @@ module.exports = function (Topics) { Topics.getPostCount = function (tid, callback) { db.getObjectField('topic:' + tid, 'postcount', callback); }; + + function getPostReplies(pids, callerUid, callback) { + async.map(pids, function (pid, next) { + db.getSortedSetRange('pid:' + pid + ':replies', 0, -1, function (err, replyPids) { + var uids = [], +count = 0; + + async.until(function () { + return count === replyPids.length || uids.length === 6; + }, function (next) { + posts.getPostField(replyPids[count], 'uid', function (err, uid) { + uid = parseInt(uid, 10); + if (uids.indexOf(uid) === -1) { + uids.push(uid); + } + count++; + next(err); + }); + }, function (err) { + async.parallel({ + 'users': function (next) { + user.getUsersWithFields(uids, ['uid', 'username', 'userslug', 'picture'], callerUid, next); + }, + 'timestampISO': function (next) { + posts.getPostField(replyPids[0], 'timestamp', function (err, timestamp) { + next(err, utils.toISOString(timestamp)); + }); + }, + }, function (err, replies) { + if (replies.users.length > 5) { + replies.users.shift(); + replies.hasMore = true; + } + + replies.count = replyPids.length; + next(err, replies); + }); + }); + }); + }, callback); + } }; From c805d55b858c6fb315b565cd3156f937b66dd1d2 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 17 Mar 2017 13:16:16 -0400 Subject: [PATCH 166/316] creating notification for flag assignment --- public/language/en-GB/notifications.json | 1 + src/flags.js | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/public/language/en-GB/notifications.json b/public/language/en-GB/notifications.json index e04482c633..8c7ed9d07a 100644 --- a/public/language/en-GB/notifications.json +++ b/public/language/en-GB/notifications.json @@ -44,6 +44,7 @@ "user_started_following_you_multiple": "%1 and %2 others started following you.", "new_register": "%1 sent a registration request.", "new_register_multiple": "There are %1 registration requests awaiting review.", + "flag_assigned_to_you": "Flag %1 has been assigned to you", "email-confirmed": "Email Confirmed", "email-confirmed-message": "Thank you for validating your email. Your account is now fully activated.", diff --git a/src/flags.js b/src/flags.js index 185ef6f6ad..af63365a2a 100644 --- a/src/flags.js +++ b/src/flags.js @@ -439,6 +439,27 @@ Flags.update = function (flagId, uid, changeset, callback) { var fields = ['state', 'assignee']; var tasks = []; var now = changeset.datetime || Date.now(); + var notifyAssignee = function (assigneeId, next) { + if (assigneeId === '') { + // Do nothing + return next(); + } + // Notify assignee of this update + notifications.create({ + type: 'my-flags', + bodyShort: '[[notifications:flag_assigned_to_you, ' + flagId + ']]', + bodyLong: '', + path: '/flags/' + flagId, + nid: 'flags:assign:' + flagId + ':uid:' + assigneeId, + from: uid, + }, function (err, notification) { + if (err) { + return next(err); + } + + notifications.push(notification, [assigneeId], next); + }); + }; async.waterfall([ async.apply(db.getObjectFields.bind(db), 'flag:' + flagId, fields), @@ -457,6 +478,7 @@ Flags.update = function (flagId, uid, changeset, callback) { case 'assignee': tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byAssignee:' + changeset[prop], now, flagId)); + tasks.push(async.apply(notifyAssignee, changeset[prop])); break; } } From e4168f4a87c594803d74d16dd8004411a9e8245e Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 17 Mar 2017 13:35:24 -0400 Subject: [PATCH 167/316] linting --- src/topics/posts.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/topics/posts.js b/src/topics/posts.js index 1e0fd20f42..34e58c55aa 100644 --- a/src/topics/posts.js +++ b/src/topics/posts.js @@ -105,7 +105,7 @@ module.exports = function (Topics) { Topics.addParentPosts(postData, next); }, replies: function (next) { - +getPostReplies(pids, uid, next); + getPostReplies(pids, uid, next); }, }, next); }, @@ -393,8 +393,12 @@ module.exports = function (Topics) { function getPostReplies(pids, callerUid, callback) { async.map(pids, function (pid, next) { db.getSortedSetRange('pid:' + pid + ':replies', 0, -1, function (err, replyPids) { - var uids = [], -count = 0; + if (err) { + return next(err); + } + + var uids = []; + var count = 0; async.until(function () { return count === replyPids.length || uids.length === 6; @@ -404,15 +408,19 @@ count = 0; if (uids.indexOf(uid) === -1) { uids.push(uid); } - count++; + count += 1; next(err); }); }, function (err) { + if (err) { + return next(err); + } + async.parallel({ - 'users': function (next) { + users: function (next) { user.getUsersWithFields(uids, ['uid', 'username', 'userslug', 'picture'], callerUid, next); }, - 'timestampISO': function (next) { + timestampISO: function (next) { posts.getPostField(replyPids[0], 'timestamp', function (err, timestamp) { next(err, utils.toISOString(timestamp)); }); From a82a50baa629fa1b91f86c8edcee60694bac6f63 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 21 Mar 2017 21:10:20 -0400 Subject: [PATCH 168/316] fix date and name in config urls upgrade script --- src/upgrades/1.4.4/config_urls_update.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/upgrades/1.4.4/config_urls_update.js b/src/upgrades/1.4.4/config_urls_update.js index 4a9d1d703c..9f4bba93ac 100644 --- a/src/upgrades/1.4.4/config_urls_update.js +++ b/src/upgrades/1.4.4/config_urls_update.js @@ -7,8 +7,8 @@ var db = require('../../database'); var async = require('async'); module.exports = { - name: 'User_friendly_upgrade_script_name', - timestamp: Date.UTC(2017, 0, 1), + name: 'Upgrading config urls to use assets route', + timestamp: Date.UTC(2017, 1, 28), method: function (callback) { async.waterfall([ function (cb) { From ce64db8d71581af5ee72eda6dc986489cd8bffcc Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 21 Mar 2017 21:10:46 -0400 Subject: [PATCH 169/316] fix logic error that caused upgrade scripts to always be skipped --- src/upgrade.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/upgrade.js b/src/upgrade.js index 57f40f5fa7..2cf75db145 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -153,16 +153,15 @@ Upgrade.process = function (files, skipCount, callback) { async.waterfall([ async.apply(db.get, 'schemaDate'), function (schemaDate, next) { - var schemaTime = new Date(schemaDate); - async.eachSeries(files, function (file, next) { var scriptExport = require(file); var date = new Date(scriptExport.timestamp); + var version = path.dirname(file).split('/').pop(); process.stdout.write(' → '.white + String('[' + [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()].join('/') + '] ').gray + String(scriptExport.name).reset + '... '); // For backwards compatibility, cross-reference with schemaDate (if found). If a script's date is older, skip it - if (scriptExport.timestamp <= schemaTime) { + if (scriptExport.timestamp <= schemaDate && semver.lt(version, '1.5.0')) { process.stdout.write('skipped\n'.grey); db.sortedSetAdd('schemaLog', Date.now(), path.basename(file, '.js')); return next(); From fe27d40ff6b2bf4d48ea90823c403e8ac3c286d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 23 Mar 2017 10:58:17 +0300 Subject: [PATCH 170/316] closes #5542 --- public/language/en-GB/user.json | 3 +- public/src/client/account/info.js | 12 +++++++ src/controllers/accounts/info.js | 31 ++++++++++++++-- src/socket.io/user.js | 14 ++++---- src/upgrade.js | 2 +- .../1.5.0/moderation_history_refactor.js | 30 ++++++++++++++++ src/user/info.js | 35 ++++++++++++++++++- 7 files changed, 115 insertions(+), 12 deletions(-) create mode 100644 src/upgrades/1.5.0/moderation_history_refactor.js diff --git a/public/language/en-GB/user.json b/public/language/en-GB/user.json index 52d2a81252..7b79af9ce7 100644 --- a/public/language/en-GB/user.json +++ b/public/language/en-GB/user.json @@ -149,5 +149,6 @@ "info.username-history": "Username History", "info.email-history": "Email History", "info.moderation-note": "Moderation Note", - "info.moderation-note.success": "Moderation note saved" + "info.moderation-note.success": "Moderation note saved", + "info.moderation-note.add": "Add note" } diff --git a/public/src/client/account/info.js b/public/src/client/account/info.js index f366c0a3e7..ee6648aab5 100644 --- a/public/src/client/account/info.js +++ b/public/src/client/account/info.js @@ -17,7 +17,19 @@ define('forum/account/info', ['forum/account/header', 'components'], function (h if (err) { return app.alertError(err.message); } + $('[component="account/moderation-note"]').val(''); app.alertSuccess('[[user:info.moderation-note.success]]'); + var timestamp = Date.now(); + var data = [{ + note: note, + user: app.user, + timestamp: timestamp, + timestampISO: utils.toISOString(timestamp), + }]; + app.parseAndTranslate('account/info', 'moderationNotes', { moderationNotes: data }, function (html) { + $('[component="account/moderation-note/list"]').prepend(html); + html.find('.timeago').timeago(); + }); }); }); } diff --git a/src/controllers/accounts/info.js b/src/controllers/accounts/info.js index e852ebc3df..7f9edb2462 100644 --- a/src/controllers/accounts/info.js +++ b/src/controllers/accounts/info.js @@ -2,14 +2,19 @@ var async = require('async'); +var db = require('../../database'); var user = require('../../user'); var helpers = require('../helpers'); var accountHelpers = require('./helpers'); +var pagination = require('../../pagination'); -var infoController = {}; +var infoController = module.exports; infoController.get = function (req, res, callback) { var userData; + var page = Math.max(1, req.query.page || 1); + var itemsPerPage = 10; + async.waterfall([ function (next) { accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid, next); @@ -19,11 +24,27 @@ infoController.get = function (req, res, callback) { if (!userData) { return callback(); } + + var start = (page - 1) * itemsPerPage; + var stop = start + itemsPerPage - 1; async.parallel({ history: async.apply(user.getModerationHistory, userData.uid), sessions: async.apply(user.auth.getSessions, userData.uid, req.sessionID), usernames: async.apply(user.getHistory, 'user:' + userData.uid + ':usernames'), emails: async.apply(user.getHistory, 'user:' + userData.uid + ':emails'), + notes: function (next) { + if (!userData.isAdminOrGlobalModeratorOrModerator) { + return setImmediate(next); + } + async.parallel({ + notes: function (next) { + user.getModerationNotes(userData.uid, start, stop, next); + }, + count: function (next) { + db.sortedSetCard('uid:' + userData.uid + ':moderation:notes', next); + }, + }, next); + }, }, next); }, ], function (err, data) { @@ -35,11 +56,15 @@ infoController.get = function (req, res, callback) { userData.sessions = data.sessions; userData.usernames = data.usernames; userData.emails = data.emails; + + if (userData.isAdminOrGlobalModeratorOrModerator) { + userData.moderationNotes = data.notes.notes; + var pageCount = Math.ceil(data.notes.count / itemsPerPage); + userData.pagination = pagination.create(page, pageCount, req.query); + } userData.title = '[[pages:account/info]]'; userData.breadcrumbs = helpers.buildBreadcrumbs([{ text: userData.username, url: '/user/' + userData.userslug }, { text: '[[user:account_info]]' }]); res.render('account/info', userData); }); }; - -module.exports = infoController; diff --git a/src/socket.io/user.js b/src/socket.io/user.js index c161d13f6f..e3f9e7d95d 100644 --- a/src/socket.io/user.js +++ b/src/socket.io/user.js @@ -316,7 +316,7 @@ SocketUser.getUserByEmail = function (socket, email, callback) { }; SocketUser.setModerationNote = function (socket, data, callback) { - if (!socket.uid || !data || !data.uid) { + if (!socket.uid || !data || !data.uid || !data.note) { return callback(new Error('[[error:invalid-data]]')); } @@ -335,11 +335,13 @@ SocketUser.setModerationNote = function (socket, data, callback) { if (!allowed) { return next(new Error('[[error:no-privileges]]')); } - if (data.note) { - user.setUserField(data.uid, 'moderationNote', data.note, next); - } else { - db.deleteObjectField('user:' + data.uid, 'moderationNote', next); - } + + var note = { + uid: socket.uid, + note: data.note, + timestamp: Date.now(), + }; + db.sortedSetAdd('uid:' + data.uid + ':moderation:notes', note.timestamp, JSON.stringify(note), next); }, ], callback); }; diff --git a/src/upgrade.js b/src/upgrade.js index 2cf75db145..60b074a953 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -51,7 +51,7 @@ var Upgrade = { }, { version: 'develop', // rename this to whatever the next NodeBB version is (breaking) - upgrades: ['flags_refactor', 'post_votes_zset'], + upgrades: ['flags_refactor', 'post_votes_zset', 'moderation_history_refactor'], }, ], }; diff --git a/src/upgrades/1.5.0/moderation_history_refactor.js b/src/upgrades/1.5.0/moderation_history_refactor.js new file mode 100644 index 0000000000..73b4e8f6d7 --- /dev/null +++ b/src/upgrades/1.5.0/moderation_history_refactor.js @@ -0,0 +1,30 @@ +/* jslint node: true */ + +'use strict'; + +var db = require('../../database'); +var batch = require('../../batch'); + +var async = require('async'); + +module.exports = { + name: 'Update moderation notes to zset', + timestamp: Date.UTC(2017, 2, 22), + method: function (callback) { + batch.processSortedSet('users:joindate', function (ids, next) { + async.each(ids, function (uid, next) { + db.getObjectField('user:' + uid, 'moderationNote', function (err, moderationNote) { + if (err || !moderationNote) { + return next(err); + } + var note = { + uid: 1, + note: moderationNote, + timestamp: Date.now(), + }; + db.sortedSetAdd('uid:' + uid + ':moderation:notes', note.timestamp, JSON.stringify(note), next); + }); + }, next); + }, callback); + }, +}; diff --git a/src/user/info.js b/src/user/info.js index 4af1b77f35..c3071b84cf 100644 --- a/src/user/info.js +++ b/src/user/info.js @@ -7,6 +7,7 @@ var validator = require('validator'); var db = require('../database'); var posts = require('../posts'); var topics = require('../topics'); +var utils = require('../../public/src/utils'); module.exports = function (User) { User.getLatestBanInfo = function (uid, callback) { @@ -74,7 +75,7 @@ module.exports = function (User) { } callback(null, data.map(function (set) { set.timestamp = set.score; - set.timestampISO = new Date(set.score).toISOString(); + set.timestampISO = utils.toISOString(set.score); set.value = validator.escape(String(set.value.split(':')[0])); delete set.score; return set; @@ -138,4 +139,36 @@ module.exports = function (User) { return banObj; }); } + + User.getModerationNotes = function (uid, start, stop, callback) { + var noteData; + async.waterfall([ + function (next) { + db.getSortedSetRevRange('uid:' + uid + ':moderation:notes', start, stop, next); + }, + function (notes, next) { + var uids = []; + noteData = notes.map(function (note) { + try { + var data = JSON.parse(note); + uids.push(data.uid); + data.timestampISO = utils.toISOString(data.timestamp); + return data; + } catch (err) { + return next(err); + } + }); + + User.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture'], next); + }, + function (userData, next) { + noteData.forEach(function (note, index) { + if (note) { + note.user = userData[index]; + } + }); + next(null, noteData); + }, + ], callback); + }; }; From 61eafb32f3b1119474177e16e0fa1d484fc4ee96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 23 Mar 2017 16:31:06 +0300 Subject: [PATCH 171/316] fix moderation note test --- test/user.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/user.js b/test/user.js index 776dbf28de..88c2790c67 100644 --- a/test/user.js +++ b/test/user.js @@ -924,9 +924,11 @@ describe('User', function () { assert.ifError(err); socketUser.setModerationNote({ uid: adminUid }, { uid: testUid, note: 'this is a test user' }, function (err) { assert.ifError(err); - User.getUserField(testUid, 'moderationNote', function (err, note) { + db.getSortedSetRevRange('uid:' + testUid + ':moderation:notes', 0, 0, function (err, notes) { + notes = JSON.parse(notes); assert.ifError(err); - assert.equal(note, 'this is a test user'); + assert.equal(notes[0].note, 'this is a test user'); + assert(notes[0].timestamp); done(); }); }); From 46da2daab9485b5f9291a9022b33e842e1d93120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 23 Mar 2017 16:31:14 +0300 Subject: [PATCH 172/316] fix test --- test/user.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/user.js b/test/user.js index 88c2790c67..2ef1ed44e3 100644 --- a/test/user.js +++ b/test/user.js @@ -928,6 +928,7 @@ describe('User', function () { notes = JSON.parse(notes); assert.ifError(err); assert.equal(notes[0].note, 'this is a test user'); + assert.equal(notes[0].uid, adminUid); assert(notes[0].timestamp); done(); }); From a3d65d10f6bf58b7baab7f12864cb90c3d353da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 23 Mar 2017 16:45:24 +0300 Subject: [PATCH 173/316] fix test again --- test/user.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/user.js b/test/user.js index 2ef1ed44e3..f954229864 100644 --- a/test/user.js +++ b/test/user.js @@ -925,8 +925,10 @@ describe('User', function () { socketUser.setModerationNote({ uid: adminUid }, { uid: testUid, note: 'this is a test user' }, function (err) { assert.ifError(err); db.getSortedSetRevRange('uid:' + testUid + ':moderation:notes', 0, 0, function (err, notes) { - notes = JSON.parse(notes); assert.ifError(err); + notes = notes.map(function (noteData) { + return JSON.parse(noteData); + }); assert.equal(notes[0].note, 'this is a test user'); assert.equal(notes[0].uid, adminUid); assert(notes[0].timestamp); From f2653cd3809641bf3cef4003952b184a85080ffa Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 23 Mar 2017 14:35:23 -0400 Subject: [PATCH 174/316] broader error handling for empty notif creation return --- src/flags.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/flags.js b/src/flags.js index af63365a2a..7e03e9cffd 100644 --- a/src/flags.js +++ b/src/flags.js @@ -453,7 +453,7 @@ Flags.update = function (flagId, uid, changeset, callback) { nid: 'flags:assign:' + flagId + ':uid:' + assigneeId, from: uid, }, function (err, notification) { - if (err) { + if (err || !notification) { return next(err); } From 55f07d3301b542d78a2e37610b467e074d7d179f Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 29 Mar 2017 12:44:40 -0400 Subject: [PATCH 175/316] fixed flags list in account info page --- src/user/info.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/user/info.js b/src/user/info.js index c3071b84cf..d3ad7d765d 100644 --- a/src/user/info.js +++ b/src/user/info.js @@ -51,7 +51,7 @@ module.exports = function (User) { async.waterfall([ function (next) { async.parallel({ - flags: async.apply(db.getSortedSetRevRangeWithScores, 'uid:' + uid + ':flag:pids', 0, 19), + flags: async.apply(db.getSortedSetRevRangeWithScores, 'flags:byTargetUid:' + uid, 0, 19), bans: async.apply(db.getSortedSetRevRangeWithScores, 'uid:' + uid + ':bans', 0, 19), reasons: async.apply(db.getSortedSetRevRangeWithScores, 'banned:' + uid + ':reasons', 0, 19), }, next); From ef2b245a82230c0072c00b0910ad6c6519d866c4 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 30 Mar 2017 11:00:11 -0400 Subject: [PATCH 176/316] bump mentions --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fa8d7eaecd..324e2a6ee9 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "nodebb-plugin-emoji-extended": "1.1.1", "nodebb-plugin-emoji-one": "1.1.5", "nodebb-plugin-markdown": "7.1.1", - "nodebb-plugin-mentions": "2.0.1", + "nodebb-plugin-mentions": "2.0.3", "nodebb-plugin-soundpack-default": "1.0.0", "nodebb-plugin-spam-be-gone": "0.4.13", "nodebb-rewards-essentials": "0.0.9", From 1b6d96dde7dcc5ae7af2f4ea0e1c7761b348c5e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 5 Apr 2017 14:59:50 -0400 Subject: [PATCH 177/316] on post flag increment zset --- src/flags.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/flags.js b/src/flags.js index 7e03e9cffd..ede0871b83 100644 --- a/src/flags.js +++ b/src/flags.js @@ -337,6 +337,9 @@ Flags.create = function (type, id, uid, reason, timestamp, callback) { } if (type === 'post') { tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byPid:' + id, timestamp, flagId)); // by target pid + if (targetUid) { + tasks.push(async.apply(db.sortedSetIncrBy, 'users:flags', 1, targetUid)); + } } async.parallel(tasks, function (err) { From 5c005bbbdf4dd6caba9d9cd49814c2184d14dc21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 5 Apr 2017 15:18:27 -0400 Subject: [PATCH 178/316] use bind --- src/flags.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/flags.js b/src/flags.js index ede0871b83..9022af2c45 100644 --- a/src/flags.js +++ b/src/flags.js @@ -338,7 +338,7 @@ Flags.create = function (type, id, uid, reason, timestamp, callback) { if (type === 'post') { tasks.push(async.apply(db.sortedSetAdd.bind(db), 'flags:byPid:' + id, timestamp, flagId)); // by target pid if (targetUid) { - tasks.push(async.apply(db.sortedSetIncrBy, 'users:flags', 1, targetUid)); + tasks.push(async.apply(db.sortedSetIncrBy.bind(db), 'users:flags', 1, targetUid)); } } From e121a5a798c944fb1cb02a6b869c89eb4c7dd2e8 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 6 Apr 2017 17:56:54 -0400 Subject: [PATCH 179/316] closes #5574 --- public/language/en-GB/admin/settings/advanced.json | 2 +- src/middleware/headers.js | 5 ++++- src/views/admin/settings/advanced.tpl | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/public/language/en-GB/admin/settings/advanced.json b/public/language/en-GB/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/en-GB/admin/settings/advanced.json +++ b/public/language/en-GB/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/src/middleware/headers.js b/src/middleware/headers.js index ae63b19124..190de28b0e 100644 --- a/src/middleware/headers.js +++ b/src/middleware/headers.js @@ -7,11 +7,14 @@ module.exports = function (middleware) { var headers = { 'X-Powered-By': encodeURI(meta.config['powered-by'] || 'NodeBB'), 'X-Frame-Options': meta.config['allow-from-uri'] ? 'ALLOW-FROM ' + encodeURI(meta.config['allow-from-uri']) : 'SAMEORIGIN', - 'Access-Control-Allow-Origin': encodeURI(meta.config['access-control-allow-origin'] || 'null'), 'Access-Control-Allow-Methods': encodeURI(meta.config['access-control-allow-methods'] || ''), 'Access-Control-Allow-Headers': encodeURI(meta.config['access-control-allow-headers'] || ''), }; + if (meta.config['access-control-allow-origin']) { + headers['Access-Control-Allow-Origin'] = encodeURI(meta.config['access-control-allow-origin']); + } + for (var key in headers) { if (headers.hasOwnProperty(key) && headers[key]) { res.setHeader(key, headers[key]); diff --git a/src/views/admin/settings/advanced.tpl b/src/views/admin/settings/advanced.tpl index 77a5a6d2cc..1454389198 100644 --- a/src/views/admin/settings/advanced.tpl +++ b/src/views/admin/settings/advanced.tpl @@ -35,7 +35,7 @@
    -
    +

    [[admin/settings/advanced:headers.acao-help]]

    From 42b5cdaae33cdfa2ddd21d19c75cf0db340a19e8 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 12 Apr 2017 17:30:20 -0400 Subject: [PATCH 180/316] progress bars! omg --- src/upgrade.js | 31 +++++++++++++- .../1.5.0/moderation_history_refactor.js | 42 ++++++++++++------- src/upgrades/1.5.0/post_votes_zset.js | 35 ++++++++++------ 3 files changed, 80 insertions(+), 28 deletions(-) diff --git a/src/upgrade.js b/src/upgrade.js index 60b074a953..06e2e7147e 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -157,6 +157,13 @@ Upgrade.process = function (files, skipCount, callback) { var scriptExport = require(file); var date = new Date(scriptExport.timestamp); var version = path.dirname(file).split('/').pop(); + var progress = { + current: 0, + total: 0, + incr: Upgrade.incrementProgress, + script: scriptExport, + date: date, + }; process.stdout.write(' → '.white + String('[' + [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()].join('/') + '] ').gray + String(scriptExport.name).reset + '... '); @@ -168,7 +175,9 @@ Upgrade.process = function (files, skipCount, callback) { } // Do the upgrade... - scriptExport.method(function (err) { + scriptExport.method.bind({ + progress: progress, + })(function (err) { if (err) { process.stdout.write('error\n'.red); return next(err); @@ -177,6 +186,12 @@ Upgrade.process = function (files, skipCount, callback) { // Record success in schemaLog db.sortedSetAdd('schemaLog', Date.now(), path.basename(file, '.js')); + if (progress.total > 0) { + process.stdout.clearLine(); + process.stdout.cursorTo(0); + process.stdout.write(' → '.white + String('[' + [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()].join('/') + '] ').gray + String(scriptExport.name).reset + '... '); + } + process.stdout.write('OK\n'.green); next(); }); @@ -192,4 +207,18 @@ Upgrade.process = function (files, skipCount, callback) { ], callback); }; +Upgrade.incrementProgress = function () { + this.current += 1; + + // Redraw the progress bar + var percentage = Math.floor((this.current / this.total) * 100) + '%'; + var filled = Math.floor((this.current / this.total) * 15); + var unfilled = 15 - filled; + process.stdout.clearLine(); + process.stdout.cursorTo(0); + + process.stdout.write(' → '.white + String('[' + [this.date.getUTCFullYear(), this.date.getUTCMonth() + 1, this.date.getUTCDate()].join('/') + '] ').gray + String(this.script.name).reset + '... '); + process.stdout.write('[' + (filled ? new Array(filled).join('#') : '') + new Array(unfilled).join(' ') + '] (' + this.current + '/' + this.total + ') ' + percentage); +}; + module.exports = Upgrade; diff --git a/src/upgrades/1.5.0/moderation_history_refactor.js b/src/upgrades/1.5.0/moderation_history_refactor.js index 73b4e8f6d7..b167246e5f 100644 --- a/src/upgrades/1.5.0/moderation_history_refactor.js +++ b/src/upgrades/1.5.0/moderation_history_refactor.js @@ -11,20 +11,32 @@ module.exports = { name: 'Update moderation notes to zset', timestamp: Date.UTC(2017, 2, 22), method: function (callback) { - batch.processSortedSet('users:joindate', function (ids, next) { - async.each(ids, function (uid, next) { - db.getObjectField('user:' + uid, 'moderationNote', function (err, moderationNote) { - if (err || !moderationNote) { - return next(err); - } - var note = { - uid: 1, - note: moderationNote, - timestamp: Date.now(), - }; - db.sortedSetAdd('uid:' + uid + ':moderation:notes', note.timestamp, JSON.stringify(note), next); - }); - }, next); - }, callback); + var progress = this.progress; + + db.sortedSetCard('users:joindate', function (err, numPosts) { + if (err) { + return callback(err); + } + + progress.total = numPosts; + + batch.processSortedSet('users:joindate', function (ids, next) { + async.each(ids, function (uid, next) { + db.getObjectField('user:' + uid, 'moderationNote', function (err, moderationNote) { + if (err || !moderationNote) { + return next(err); + } + var note = { + uid: 1, + note: moderationNote, + timestamp: Date.now(), + }; + + progress.incr(); + db.sortedSetAdd('uid:' + uid + ':moderation:notes', note.timestamp, JSON.stringify(note), next); + }); + }, next); + }, callback); + }); }, }; diff --git a/src/upgrades/1.5.0/post_votes_zset.js b/src/upgrades/1.5.0/post_votes_zset.js index d989602e23..df5a4c522f 100644 --- a/src/upgrades/1.5.0/post_votes_zset.js +++ b/src/upgrades/1.5.0/post_votes_zset.js @@ -10,17 +10,28 @@ module.exports = { name: 'New sorted set posts:votes', timestamp: Date.UTC(2017, 1, 27), method: function (callback) { - require('../../batch').processSortedSet('posts:pid', function (pids, next) { - async.each(pids, function (pid, next) { - db.getObjectFields('post:' + pid, ['upvotes', 'downvotes'], function (err, postData) { - if (err || !postData) { - return next(err); - } - - var votes = parseInt(postData.upvotes || 0, 10) - parseInt(postData.downvotes || 0, 10); - db.sortedSetAdd('posts:votes', votes, pid, next); - }); - }, next); - }, {}, callback); + var progress = this.progress; + + db.sortedSetCard('posts:pid', function (err, numPosts) { + if (err) { + return callback(err); + } + + progress.total = numPosts; + + require('../../batch').processSortedSet('posts:pid', function (pids, next) { + async.eachSeries(pids, function (pid, next) { + db.getObjectFields('post:' + pid, ['upvotes', 'downvotes'], function (err, postData) { + if (err || !postData) { + return next(err); + } + + var votes = parseInt(postData.upvotes || 0, 10) - parseInt(postData.downvotes || 0, 10); + progress.incr(); + db.sortedSetAdd('posts:votes', votes, pid, next); + }); + }, next); + }, {}, callback); + }); }, }; From 1e3acc0caf500e6a66d5fda85e2d25b1e82325e2 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 13 Apr 2017 15:18:27 -0400 Subject: [PATCH 181/316] up persona --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c17fc7baa6..1ddbeca78e 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "nodebb-plugin-spam-be-gone": "0.4.13", "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "4.0.0", - "nodebb-theme-persona": "4.2.7", + "nodebb-theme-persona": "4.2.8", "nodebb-theme-vanilla": "5.2.1", "nodebb-widget-essentials": "2.0.13", "nodemailer": "2.6.4", From 7ce6c1d0add64c3a99b0750d2eef56ade016aaf4 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 13 Apr 2017 16:36:02 -0400 Subject: [PATCH 182/316] closes #5571 --- .eslintrc | 2 +- src/database/mongo/sorted.js | 4 ++-- src/database/redis/helpers.js | 8 +++----- src/database/redis/sorted.js | 6 +++++- test/database/sorted.js | 34 ++++++++++++++++++++++++++++++++++ 5 files changed, 45 insertions(+), 9 deletions(-) diff --git a/.eslintrc b/.eslintrc index bbba3aa94e..4f7d4e2413 100644 --- a/.eslintrc +++ b/.eslintrc @@ -54,9 +54,9 @@ "no-restricted-syntax": "off", "no-script-url": "off", "default-case": "off", + "linebreak-style": "off", // "no-multi-assign": "off", - // "linebreak-style": "off", // "one-var": "off", // "no-undef": "off", // "max-nested-callbacks": "off", diff --git a/src/database/mongo/sorted.js b/src/database/mongo/sorted.js index 59bd05b9fd..4168199bb5 100644 --- a/src/database/mongo/sorted.js +++ b/src/database/mongo/sorted.js @@ -241,7 +241,7 @@ module.exports = function (db, module) { module.sortedSetScore = function (key, value, callback) { if (!key) { - return callback(); + return callback(null, null); } value = helpers.valueToString(value); db.collection('objects').findOne({ _key: key, value: value }, { fields: { _id: 0, score: 1 } }, function (err, result) { @@ -274,7 +274,7 @@ module.exports = function (db, module) { module.sortedSetScores = function (key, values, callback) { if (!key) { - return callback(); + return callback(null, null); } values = values.map(helpers.valueToString); db.collection('objects').find({ _key: key, value: { $in: values } }, { _id: 0, value: 1, score: 1 }).toArray(function (err, result) { diff --git a/src/database/redis/helpers.js b/src/database/redis/helpers.js index 7100437177..b1c2a98092 100644 --- a/src/database/redis/helpers.js +++ b/src/database/redis/helpers.js @@ -1,6 +1,6 @@ 'use strict'; -var helpers = {}; +var helpers = module.exports; helpers.multiKeys = function (redisClient, command, keys, callback) { callback = callback || function () {}; @@ -15,7 +15,7 @@ helpers.multiKeysValue = function (redisClient, command, keys, value, callback) callback = callback || function () {}; var multi = redisClient.multi(); for (var i = 0; i < keys.length; i += 1) { - multi[command](keys[i], value); + multi[command](String(keys[i]), String(value)); } multi.exec(callback); }; @@ -24,7 +24,7 @@ helpers.multiKeyValues = function (redisClient, command, key, values, callback) callback = callback || function () {}; var multi = redisClient.multi(); for (var i = 0; i < values.length; i += 1) { - multi[command](key, values[i]); + multi[command](String(key), String(values[i])); } multi.exec(callback); }; @@ -35,5 +35,3 @@ helpers.resultsToBool = function (results) { } return results; }; - -module.exports = helpers; diff --git a/src/database/redis/sorted.js b/src/database/redis/sorted.js index 67f55bf343..9bb004550a 100644 --- a/src/database/redis/sorted.js +++ b/src/database/redis/sorted.js @@ -124,8 +124,12 @@ module.exports = function (redisClient, module) { }; module.sortedSetScore = function (key, value, callback) { + if (!key || value === undefined) { + return callback(null, null); + } + redisClient.zscore(key, value, function (err, score) { - callback(err, !err ? parseFloat(score) : undefined); + callback(err, !err ? parseFloat(score) : null); }); }; diff --git a/test/database/sorted.js b/test/database/sorted.js index 939f4b259e..b90e9cf71f 100644 --- a/test/database/sorted.js +++ b/test/database/sorted.js @@ -324,6 +324,22 @@ describe('Sorted Set methods', function () { done(); }); }); + + it('should not error if key is undefined', function (done) { + db.sortedSetScore(undefined, 1, function (err, score) { + assert.ifError(err); + assert.strictEqual(score, null); + done(); + }); + }); + + it('should not error if value is undefined', function (done) { + db.sortedSetScore('sortedSetTest1', undefined, function (err, score) { + assert.ifError(err); + assert.strictEqual(score, null); + done(); + }); + }); }); describe('sortedSetsScore()', function () { @@ -335,6 +351,15 @@ describe('Sorted Set methods', function () { done(); }); }); + + it('should return scores even if some keys are undefined', function (done) { + db.sortedSetsScore(['sortedSetTest1', undefined, 'doesnotexist'], 'value1', function (err, scores) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(scores, [1.1, null, null]); + done(); + }); + }); }); describe('sortedSetScores()', function () { @@ -358,6 +383,15 @@ describe('Sorted Set methods', function () { done(); }); }); + + it('should return scores even if some values are undefined', function (done) { + db.sortedSetScores('sortedSetTest1', ['value2', undefined, 'doesnotexist'], function (err, scores) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(scores, [1.2, null, null]); + done(); + }); + }); }); describe('isSortedSetMember()', function () { From 6d1702c6005da9389e8f25614e1064b8152b7e4a Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 13 Apr 2017 16:58:37 -0400 Subject: [PATCH 183/316] wait for calls to finish --- src/upgrade.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/upgrade.js b/src/upgrade.js index 60b074a953..50e18d90ce 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -163,8 +163,7 @@ Upgrade.process = function (files, skipCount, callback) { // For backwards compatibility, cross-reference with schemaDate (if found). If a script's date is older, skip it if (scriptExport.timestamp <= schemaDate && semver.lt(version, '1.5.0')) { process.stdout.write('skipped\n'.grey); - db.sortedSetAdd('schemaLog', Date.now(), path.basename(file, '.js')); - return next(); + db.sortedSetAdd('schemaLog', Date.now(), path.basename(file, '.js'), next); } // Do the upgrade... @@ -173,12 +172,9 @@ Upgrade.process = function (files, skipCount, callback) { process.stdout.write('error\n'.red); return next(err); } - - // Record success in schemaLog - db.sortedSetAdd('schemaLog', Date.now(), path.basename(file, '.js')); - process.stdout.write('OK\n'.green); - next(); + // Record success in schemaLog + db.sortedSetAdd('schemaLog', Date.now(), path.basename(file, '.js'), next); }); }, function (err) { if (err) { From f4b8cb721781673b853becde45fa0cda02de2740 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 13 Apr 2017 17:02:13 -0400 Subject: [PATCH 184/316] added missing return --- src/upgrade.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/upgrade.js b/src/upgrade.js index 50e18d90ce..94a37e2455 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -164,6 +164,7 @@ Upgrade.process = function (files, skipCount, callback) { if (scriptExport.timestamp <= schemaDate && semver.lt(version, '1.5.0')) { process.stdout.write('skipped\n'.grey); db.sortedSetAdd('schemaLog', Date.now(), path.basename(file, '.js'), next); + return; } // Do the upgrade... From 520d6954c13205b767c7701c6f7c6f70c03f0a06 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 13 Apr 2017 17:58:44 -0400 Subject: [PATCH 185/316] test node 7 --- src/upgrade.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/upgrade.js b/src/upgrade.js index 94a37e2455..540543cf7b 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -84,7 +84,7 @@ Upgrade.check = function (callback) { if (err) { return callback(err); } - + console.log('available vs executed', files.length, executed.length); var remainder = files.filter(function (name) { return executed.indexOf(path.basename(name, '.js')) === -1; }); From 1cf775a4b87a1f551927f0e833f88bb476b44848 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 13 Apr 2017 18:43:39 -0400 Subject: [PATCH 186/316] upgrade script fixes --- src/upgrade.js | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/upgrade.js b/src/upgrade.js index 540543cf7b..09604f809e 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -84,19 +84,15 @@ Upgrade.check = function (callback) { if (err) { return callback(err); } - console.log('available vs executed', files.length, executed.length); + var remainder = files.filter(function (name) { return executed.indexOf(path.basename(name, '.js')) === -1; }); - next(remainder.length > 1 ? new Error('schema-out-of-date') : null); + next(remainder.length > 0 ? new Error('schema-out-of-date') : null); }); }, ], callback); - // var all = Upgrade.available.reduce(function (memo, current) { - // memo = memo.concat(current.upgrades); - // return memo; - // }, []); }; Upgrade.run = function (callback) { @@ -161,7 +157,7 @@ Upgrade.process = function (files, skipCount, callback) { process.stdout.write(' → '.white + String('[' + [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()].join('/') + '] ').gray + String(scriptExport.name).reset + '... '); // For backwards compatibility, cross-reference with schemaDate (if found). If a script's date is older, skip it - if (scriptExport.timestamp <= schemaDate && semver.lt(version, '1.5.0')) { + if (!schemaDate || (scriptExport.timestamp <= schemaDate && semver.lt(version, '1.5.0'))) { process.stdout.write('skipped\n'.grey); db.sortedSetAdd('schemaLog', Date.now(), path.basename(file, '.js'), next); return; @@ -177,15 +173,15 @@ Upgrade.process = function (files, skipCount, callback) { // Record success in schemaLog db.sortedSetAdd('schemaLog', Date.now(), path.basename(file, '.js'), next); }); - }, function (err) { - if (err) { - return next(err); - } - - process.stdout.write('Upgrade complete!\n\n'.green); - next(); - }); + }, next); + }, + function (next) { + db.set('schemaDate', Date.now(), next); }, + function (next) { + process.stdout.write('Upgrade complete!\n\n'.green); + setImmediate(next); + } ], callback); }; From 05e9e88a15e6a91fe03dbcb11ca16e793f8dfe22 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 13 Apr 2017 18:53:46 -0400 Subject: [PATCH 187/316] fix lint --- src/upgrade.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/upgrade.js b/src/upgrade.js index 09604f809e..3add28d304 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -181,7 +181,7 @@ Upgrade.process = function (files, skipCount, callback) { function (next) { process.stdout.write('Upgrade complete!\n\n'.green); setImmediate(next); - } + }, ], callback); }; From 886768ba84417a8848df8826af5e85716b84bbb5 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 13 Apr 2017 19:04:22 -0400 Subject: [PATCH 188/316] check both date and upgrade count --- src/upgrade.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/upgrade.js b/src/upgrade.js index 3add28d304..9d0e846256 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -147,8 +147,13 @@ Upgrade.process = function (files, skipCount, callback) { process.stdout.write('OK'.green + ' | '.reset + String(files.length).cyan + ' script(s) found'.cyan + (skipCount > 0 ? ', '.cyan + String(skipCount).cyan + ' skipped'.cyan : '') + '\n'.reset); async.waterfall([ - async.apply(db.get, 'schemaDate'), - function (schemaDate, next) { + function (next) { + async.parallel({ + schemaDate: async.apply(db.get, 'schemaDate'), + schemaLogCount: async.apply(db.sortedSetCard, 'schemaLog'), + }, next); + }, + function (results, next) { async.eachSeries(files, function (file, next) { var scriptExport = require(file); var date = new Date(scriptExport.timestamp); @@ -157,7 +162,7 @@ Upgrade.process = function (files, skipCount, callback) { process.stdout.write(' → '.white + String('[' + [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()].join('/') + '] ').gray + String(scriptExport.name).reset + '... '); // For backwards compatibility, cross-reference with schemaDate (if found). If a script's date is older, skip it - if (!schemaDate || (scriptExport.timestamp <= schemaDate && semver.lt(version, '1.5.0'))) { + if ((!results.schemaDate && !results.schemaLogCount) || (scriptExport.timestamp <= results.schemaDate && semver.lt(version, '1.5.0'))) { process.stdout.write('skipped\n'.grey); db.sortedSetAdd('schemaLog', Date.now(), path.basename(file, '.js'), next); return; @@ -175,9 +180,6 @@ Upgrade.process = function (files, skipCount, callback) { }); }, next); }, - function (next) { - db.set('schemaDate', Date.now(), next); - }, function (next) { process.stdout.write('Upgrade complete!\n\n'.green); setImmediate(next); From b8c606cbbd7c39833566ae0e3ce736be3ec5f457 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 14 Apr 2017 10:08:50 -0400 Subject: [PATCH 189/316] closes #5519 --- public/language/en-GB/modules.json | 1 + public/src/app.js | 33 ++++++++++++++++++++++-------- src/socket.io/modules.js | 12 +++++++++++ test/messaging.js | 12 ++++++++++- 4 files changed, 48 insertions(+), 10 deletions(-) diff --git a/public/language/en-GB/modules.json b/public/language/en-GB/modules.json index 5f258128f8..8f8e4ad1ed 100644 --- a/public/language/en-GB/modules.json +++ b/public/language/en-GB/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 Months", "chat.delete_message_confirm": "Are you sure you wish to delete this message?", "chat.add-users-to-room": "Add users to room", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Compose", "composer.show_preview": "Show Preview", diff --git a/public/src/app.js b/public/src/app.js index 3e53305aac..1c20346a7a 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -340,6 +340,22 @@ app.cacheBuster = null; }; app.newChat = function (touid, callback) { + function createChat() { + socket.emit('modules.chats.newRoom', { touid: touid }, function (err, roomId) { + if (err) { + return app.alertError(err.message); + } + + if (!ajaxify.data.template.chats) { + app.openChat(roomId); + } else { + ajaxify.go('chats/' + roomId); + } + + callback(false, roomId); + }); + } + callback = callback || function () {}; if (!app.user.uid) { return app.alertError('[[error:not-logged-in]]'); @@ -348,19 +364,18 @@ app.cacheBuster = null; if (parseInt(touid, 10) === parseInt(app.user.uid, 10)) { return app.alertError('[[error:cant-chat-with-yourself]]'); } - - socket.emit('modules.chats.newRoom', { touid: touid }, function (err, roomId) { + socket.emit('modules.chats.isDnD', touid, function (err, isDnD) { if (err) { return app.alertError(err.message); } - - if (!ajaxify.data.template.chats) { - app.openChat(roomId); - } else { - ajaxify.go('chats/' + roomId); + if (!isDnD) { + return createChat(); } - - callback(false, roomId); + bootbox.confirm('[[modules:chat.confirm-chat-with-dnd-user]]', function (ok) { + if (ok) { + createChat(); + } + }); }); }; diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index 4480a9d0f7..184609129a 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -3,6 +3,7 @@ var async = require('async'); var validator = require('validator'); +var db = require('../database'); var meta = require('../meta'); var notifications = require('../notifications'); var plugins = require('../plugins'); @@ -36,6 +37,17 @@ SocketModules.chats.getRaw = function (socket, data, callback) { ], callback); }; +SocketModules.chats.isDnD = function (socket, uid, callback) { + async.waterfall([ + function (next) { + db.getObjectField('user:' + uid, 'status', next); + }, + function (status, next) { + next(null, status === 'dnd'); + }, + ], callback); +}; + SocketModules.chats.newRoom = function (socket, data, callback) { if (!data) { return callback(new Error('[[error:invalid-data]]')); diff --git a/test/messaging.js b/test/messaging.js index 4fbaad717f..436fd78a87 100644 --- a/test/messaging.js +++ b/test/messaging.js @@ -187,6 +187,17 @@ describe('Messaging Library', function () { done(); }); }); + + it('should return true if user is dnd', function (done) { + db.setObjectField('user:' + herpUid, 'status', 'dnd', function (err) { + assert.ifError(err); + socketModules.chats.isDnD({ uid: fooUid }, herpUid, function (err, isDnD) { + assert.ifError(err); + assert(isDnD); + done(); + }); + }); + }); }); describe('edit/delete', function () { @@ -303,7 +314,6 @@ describe('Messaging Library', function () { }); }); - after(function (done) { db.emptydb(done); }); From e63559b6fdd49e85274d31c22a8b1feaac748bad Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 14 Apr 2017 12:59:36 -0400 Subject: [PATCH 190/316] closes #5484 --- install/data/defaults.json | 1 + package.json | 2 +- src/controllers/uploads.js | 16 +++++++--------- src/upgrade.js | 2 +- src/upgrades/1.5.0/allowed_file_extensions.js | 18 ++++++++++++++++++ 5 files changed, 28 insertions(+), 11 deletions(-) create mode 100644 src/upgrades/1.5.0/allowed_file_extensions.js diff --git a/install/data/defaults.json b/install/data/defaults.json index c471db6b89..25fe659b27 100644 --- a/install/data/defaults.json +++ b/install/data/defaults.json @@ -17,6 +17,7 @@ "allowLocalLogin": 1, "allowAccountDelete": 1, "allowFileUploads": 0, + "allowedFileExtensions": "png,jpg,bmp", "allowUserHomePage": 1, "maximumFileSize": 2048, "minimumTitleLength": 3, diff --git a/package.json b/package.json index 1b3eeff9ef..1286233b99 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "morgan": "^1.3.2", "mousetrap": "^1.5.3", "nconf": "~0.8.2", - "nodebb-plugin-composer-default": "4.4.4", + "nodebb-plugin-composer-default": "4.4.6", "nodebb-plugin-dbsearch": "2.0.2", "nodebb-plugin-emoji-extended": "1.1.1", "nodebb-plugin-emoji-one": "1.1.5", diff --git a/src/controllers/uploads.js b/src/controllers/uploads.js index 82556ee12c..a9768755db 100644 --- a/src/controllers/uploads.js +++ b/src/controllers/uploads.js @@ -33,7 +33,7 @@ uploadsController.upload = function (req, res, filesIterator) { return res.status(500).json({ path: req.path, error: err.message }); } - res.status(200).send(images); + res.status(200).json(images); }); }; @@ -208,20 +208,18 @@ uploadsController.uploadFile = function (uid, uploadedFile, callback) { return callback(new Error('[[error:file-too-big, ' + meta.config.maximumFileSize + ']]')); } - if (meta.config.hasOwnProperty('allowedFileExtensions')) { - var allowed = file.allowedExtensions(); - var extension = file.typeToExtension(uploadedFile.type); - if (!extension || (allowed.length > 0 && allowed.indexOf(extension) === -1)) { - return callback(new Error('[[error:invalid-file-type, ' + allowed.join(', ') + ']]')); - } + var allowed = file.allowedExtensions(); + var extension = path.extname(uploadedFile.name); + if (!extension || extension === '.' || (allowed.length > 0 && allowed.indexOf(extension) === -1)) { + return callback(new Error('[[error:invalid-file-type, ' + allowed.join(', ') + ']]')); } saveFileToLocal(uploadedFile, callback); }; function saveFileToLocal(uploadedFile, callback) { - var extension = file.typeToExtension(uploadedFile.type); - if (!extension) { + var extension = path.extname(uploadedFile.name); + if (!extension || extension === '.') { return callback(new Error('[[error:invalid-extension]]')); } var filename = uploadedFile.name || 'upload'; diff --git a/src/upgrade.js b/src/upgrade.js index 9d0e846256..96a20780cf 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -51,7 +51,7 @@ var Upgrade = { }, { version: 'develop', // rename this to whatever the next NodeBB version is (breaking) - upgrades: ['flags_refactor', 'post_votes_zset', 'moderation_history_refactor'], + upgrades: ['flags_refactor', 'post_votes_zset', 'moderation_history_refactor', 'allowed_file_extensions'], }, ], }; diff --git a/src/upgrades/1.5.0/allowed_file_extensions.js b/src/upgrades/1.5.0/allowed_file_extensions.js new file mode 100644 index 0000000000..158c04ab1d --- /dev/null +++ b/src/upgrades/1.5.0/allowed_file_extensions.js @@ -0,0 +1,18 @@ +/* jslint node: true */ + +'use strict'; + +var db = require('../../database'); + +module.exports = { + name: 'Set default allowed file extensions', + timestamp: Date.UTC(2017, 3, 14), + method: function (callback) { + db.getObjectField('config', 'allowedFileExtensions', function (err, value) { + if (err || value) { + return callback(err); + } + db.setObjectField('config', 'allowedFileExtensions', 'png,jpg,bmp', callback); + }); + }, +}; From 36df4cdf97c089f2470851f8ba21d42a1a2e4d95 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 14 Apr 2017 13:37:15 -0400 Subject: [PATCH 191/316] fix cyclic dependency issue --- public/src/utils.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/src/utils.js b/public/src/utils.js index d9595dddeb..8638d06750 100644 --- a/public/src/utils.js +++ b/public/src/utils.js @@ -4,10 +4,11 @@ if (typeof module === 'object' && module.exports) { var winston = require('winston'); - var file = require('../../src/file'); + module.exports = factory(require('xregexp')); module.exports.walk = function (dir, done) { // DEPRECATED + var file = require('../../src/file'); winston.warn('[deprecated] `utils.walk` is deprecated. Use `file.walk` instead.'); file.walk(dir, done); }; From da7bc5bb7abce407a2e0467bbc0764b98f3d914f Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 14 Apr 2017 13:38:58 -0400 Subject: [PATCH 192/316] test fix --- src/groups/cover.js | 6 ++++-- test/uploads.js | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/groups/cover.js b/src/groups/cover.js index a7d7550fa0..99386879c1 100644 --- a/src/groups/cover.js +++ b/src/groups/cover.js @@ -2,6 +2,7 @@ var async = require('async'); var fs = require('fs'); +var path = require('path'); var Jimp = require('jimp'); var mime = require('mime'); var winston = require('winston'); @@ -37,8 +38,9 @@ module.exports = function (Groups) { }, function (_tempPath, next) { tempPath = _tempPath; + uploadsController.uploadGroupCover(uid, { - name: 'groupCover', + name: 'groupCover' + path.extname(tempPath), path: tempPath, type: type, }, next); @@ -52,7 +54,7 @@ module.exports = function (Groups) { }, function (next) { uploadsController.uploadGroupCover(uid, { - name: 'groupCoverThumb', + name: 'groupCoverThumb' + path.extname(tempPath), path: tempPath, type: type, }, next); diff --git a/test/uploads.js b/test/uploads.js index 0daf0407b9..a4fe95c5df 100644 --- a/test/uploads.js +++ b/test/uploads.js @@ -104,7 +104,10 @@ describe('Upload Controllers', function () { it('should upload a file to a post', function (done) { meta.config.allowFileUploads = 1; + var oldValue = meta.config.allowedFileExtensions; + meta.config.allowedFileExtensions = 'png,jpg,bmp,html'; helpers.uploadFile(nconf.get('url') + '/api/post/upload', path.join(__dirname, '../test/files/503.html'), { cid: cid }, jar, csrf_token, function (err, res, body) { + meta.config.allowedFileExtensions = oldValue; assert.ifError(err); assert.equal(res.statusCode, 200); assert(Array.isArray(body)); From 92343eb2e1f265eaf8383ff1cc3f283752acbf32 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 14 Apr 2017 13:58:32 -0400 Subject: [PATCH 193/316] fix mongo setObject if key/value is empty string --- src/database/mongo/hash.js | 4 +++- src/database/redis/hash.js | 6 ++++++ test/database/hash.js | 11 +++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/database/mongo/hash.js b/src/database/mongo/hash.js index 57c72cdc91..997f22cfc0 100644 --- a/src/database/mongo/hash.js +++ b/src/database/mongo/hash.js @@ -8,7 +8,9 @@ module.exports = function (db, module) { if (!key || !data) { return callback(); } - + if (data.hasOwnProperty('')) { + delete data['']; + } db.collection('objects').update({ _key: key }, { $set: data }, { upsert: true, w: 1 }, function (err) { callback(err); }); diff --git a/src/database/redis/hash.js b/src/database/redis/hash.js index 61d83a93f6..9dcb005528 100644 --- a/src/database/redis/hash.js +++ b/src/database/redis/hash.js @@ -8,11 +8,17 @@ module.exports = function (redisClient, module) { if (!key || !data) { return callback(); } + + if (data.hasOwnProperty('')) { + delete data['']; + } + Object.keys(data).forEach(function (key) { if (data[key] === undefined) { delete data[key]; } }); + redisClient.hmset(key, data, function (err) { callback(err); }); diff --git a/test/database/hash.js b/test/database/hash.js index 4da340b960..d8583d3a6f 100644 --- a/test/database/hash.js +++ b/test/database/hash.js @@ -42,6 +42,17 @@ describe('Hash methods', function () { }); }); }); + + it('should not error if a key is empty string', function (done) { + db.setObject('emptyField', {'': '', b: 1}, function (err) { + assert.ifError(err); + db.getObject('emptyField', function (err, data) { + assert.ifError(err); + console.log(data); + done(); + }); + }); + }); }); describe('setObjectField()', function () { From 552f850207ec9e116f42f19cdc997e4db0b0e924 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 14 Apr 2017 14:56:39 -0400 Subject: [PATCH 194/316] closes #2702 --- src/widgets/index.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/widgets/index.js b/src/widgets/index.js index 2e439c52d8..b671effa8e 100644 --- a/src/widgets/index.js +++ b/src/widgets/index.js @@ -43,13 +43,15 @@ widgets.render = function (uid, area, req, res, callback) { data: widget.data, req: req, res: res, - }, function (err, html) { - if (err || html === null) { + }, function (err, data) { + if (err || data === null) { return next(err); } - + var html = data; if (typeof html !== 'string') { - html = ''; + html = data.html; + } else { + winston.warn('[widgets.render] passing a string is deprecated!, filter:widget.render:' + widget.widget + '. Please set hookData.html in your plugin.'); } if (widget.data.container && widget.data.container.match('{body}')) { From 4d1b8d7460d581ed3f5d50a251242b64434bdc74 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 14 Apr 2017 15:15:25 -0400 Subject: [PATCH 195/316] fix lint --- test/database/hash.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/database/hash.js b/test/database/hash.js index d8583d3a6f..65e365b7f3 100644 --- a/test/database/hash.js +++ b/test/database/hash.js @@ -44,7 +44,7 @@ describe('Hash methods', function () { }); it('should not error if a key is empty string', function (done) { - db.setObject('emptyField', {'': '', b: 1}, function (err) { + db.setObject('emptyField', { '': '', b: 1 }, function (err) { assert.ifError(err); db.getObject('emptyField', function (err, data) { assert.ifError(err); From 3598af784d6b40c8c9406b7ebe89b24c90ce589f Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 14 Apr 2017 20:00:45 -0400 Subject: [PATCH 196/316] up persona --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1286233b99..f5fdeaeed9 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "nodebb-plugin-spam-be-gone": "0.4.13", "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "4.0.0", - "nodebb-theme-persona": "4.2.8", + "nodebb-theme-persona": "4.2.9", "nodebb-theme-vanilla": "5.2.1", "nodebb-widget-essentials": "2.0.13", "nodemailer": "2.6.4", From c3423b2456d8dd3c2d5f82f456195f2e43ffb0b9 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 14 Apr 2017 20:03:10 -0400 Subject: [PATCH 197/316] up persona --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f5fdeaeed9..7fc087a6d1 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "nodebb-plugin-spam-be-gone": "0.4.13", "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "4.0.0", - "nodebb-theme-persona": "4.2.9", + "nodebb-theme-persona": "4.2.10", "nodebb-theme-vanilla": "5.2.1", "nodebb-widget-essentials": "2.0.13", "nodemailer": "2.6.4", From 33082d90cc53b95aaf45d653967f497bc3f3b787 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Sun, 16 Apr 2017 13:25:01 -0400 Subject: [PATCH 198/316] updated logic to make it a bit simpler to implement per upgrade script --- src/batch.js | 12 +++++ src/upgrade.js | 19 +++++--- .../1.5.0/moderation_history_refactor.js | 44 ++++++++----------- src/upgrades/1.5.0/post_votes_zset.js | 36 +++++++-------- 4 files changed, 58 insertions(+), 53 deletions(-) diff --git a/src/batch.js b/src/batch.js index 475c4aa7ca..f15d06a453 100644 --- a/src/batch.js +++ b/src/batch.js @@ -21,6 +21,17 @@ exports.processSortedSet = function (setKey, process, options, callback) { return callback(new Error('[[error:process-not-a-function]]')); } + // Progress bar handling (upgrade scripts) + if (options.progress) { + db.sortedSetCard(setKey, function (err, total) { + if (err) { + // Unable to get total, do nothing. + } else { + options.progress.total = total; + } + }); + } + // use the fast path if possible if (db.processSortedSet && typeof options.doneIf !== 'function' && !utils.isNumber(options.alwaysStartAt)) { return db.processSortedSet(setKey, process, options.batch || DEFAULT_BATCH_SIZE, callback); @@ -53,6 +64,7 @@ exports.processSortedSet = function (setKey, process, options, callback) { } start += utils.isNumber(options.alwaysStartAt) ? options.alwaysStartAt : batch + 1; stop = start + batch; + next(); }); }); diff --git a/src/upgrade.js b/src/upgrade.js index 9b2bf8b29e..5352a091f9 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -7,7 +7,7 @@ var path = require('path'); var semver = require('semver'); var db = require('./database'); -var utils = require('../public/src/utils'); +var file = require('../src/file'); /* * Need to write an upgrade script for NodeBB? Cool. @@ -58,7 +58,7 @@ var Upgrade = { Upgrade.getAll = function (callback) { async.waterfall([ - async.apply(utils.walk, path.join(__dirname, './upgrades')), + async.apply(file.walk, path.join(__dirname, './upgrades')), function (files, next) { // Sort the upgrade scripts based on version var versionA; @@ -128,7 +128,7 @@ Upgrade.runSingle = function (query, callback) { process.stdout.write('\nParsing upgrade scripts... '); async.waterfall([ - async.apply(utils.walk, path.join(__dirname, './upgrades')), + async.apply(file.walk, path.join(__dirname, './upgrades')), function (files, next) { next(null, files.filter(function (file) { return path.basename(file, '.js') === query; @@ -207,14 +207,19 @@ Upgrade.incrementProgress = function () { this.current += 1; // Redraw the progress bar - var percentage = Math.floor((this.current / this.total) * 100) + '%'; - var filled = Math.floor((this.current / this.total) * 15); - var unfilled = 15 - filled; + var percentage = 0; + var filled = 0; + var unfilled = 15; + if (this.total) { + percentage = Math.floor((this.current / this.total) * 100) + '%'; + filled = Math.floor((this.current / this.total) * 15); + unfilled = 15 - filled; + } process.stdout.clearLine(); process.stdout.cursorTo(0); process.stdout.write(' → '.white + String('[' + [this.date.getUTCFullYear(), this.date.getUTCMonth() + 1, this.date.getUTCDate()].join('/') + '] ').gray + String(this.script.name).reset + '... '); - process.stdout.write('[' + (filled ? new Array(filled).join('#') : '') + new Array(unfilled).join(' ') + '] (' + this.current + '/' + this.total + ') ' + percentage); + process.stdout.write('[' + (filled ? new Array(filled).join('#') : '') + new Array(unfilled).join(' ') + '] (' + this.current + '/' + (this.total || '??') + ') ' + percentage); }; module.exports = Upgrade; diff --git a/src/upgrades/1.5.0/moderation_history_refactor.js b/src/upgrades/1.5.0/moderation_history_refactor.js index b167246e5f..f98e1c62db 100644 --- a/src/upgrades/1.5.0/moderation_history_refactor.js +++ b/src/upgrades/1.5.0/moderation_history_refactor.js @@ -13,30 +13,24 @@ module.exports = { method: function (callback) { var progress = this.progress; - db.sortedSetCard('users:joindate', function (err, numPosts) { - if (err) { - return callback(err); - } - - progress.total = numPosts; - - batch.processSortedSet('users:joindate', function (ids, next) { - async.each(ids, function (uid, next) { - db.getObjectField('user:' + uid, 'moderationNote', function (err, moderationNote) { - if (err || !moderationNote) { - return next(err); - } - var note = { - uid: 1, - note: moderationNote, - timestamp: Date.now(), - }; - - progress.incr(); - db.sortedSetAdd('uid:' + uid + ':moderation:notes', note.timestamp, JSON.stringify(note), next); - }); - }, next); - }, callback); - }); + batch.processSortedSet('users:joindate', function (ids, next) { + async.each(ids, function (uid, next) { + db.getObjectField('user:' + uid, 'moderationNote', function (err, moderationNote) { + if (err || !moderationNote) { + return next(err); + } + var note = { + uid: 1, + note: moderationNote, + timestamp: Date.now(), + }; + + progress.incr(); + db.sortedSetAdd('uid:' + uid + ':moderation:notes', note.timestamp, JSON.stringify(note), next); + }); + }, next); + }, { + progress: this.progress, + }, callback); }, }; diff --git a/src/upgrades/1.5.0/post_votes_zset.js b/src/upgrades/1.5.0/post_votes_zset.js index df5a4c522f..b1d5b0cae4 100644 --- a/src/upgrades/1.5.0/post_votes_zset.js +++ b/src/upgrades/1.5.0/post_votes_zset.js @@ -12,26 +12,20 @@ module.exports = { method: function (callback) { var progress = this.progress; - db.sortedSetCard('posts:pid', function (err, numPosts) { - if (err) { - return callback(err); - } - - progress.total = numPosts; - - require('../../batch').processSortedSet('posts:pid', function (pids, next) { - async.eachSeries(pids, function (pid, next) { - db.getObjectFields('post:' + pid, ['upvotes', 'downvotes'], function (err, postData) { - if (err || !postData) { - return next(err); - } - - var votes = parseInt(postData.upvotes || 0, 10) - parseInt(postData.downvotes || 0, 10); - progress.incr(); - db.sortedSetAdd('posts:votes', votes, pid, next); - }); - }, next); - }, {}, callback); - }); + require('../../batch').processSortedSet('posts:pid', function (pids, next) { + async.eachSeries(pids, function (pid, next) { + db.getObjectFields('post:' + pid, ['upvotes', 'downvotes'], function (err, postData) { + if (err || !postData) { + return next(err); + } + + progress.incr(); + var votes = parseInt(postData.upvotes || 0, 10) - parseInt(postData.downvotes || 0, 10); + db.sortedSetAdd('posts:votes', votes, pid, next); + }); + }, next); + }, { + progress: this.progress, + }, callback); }, }; From 8fc61232305a6c702a5d1325836000de2883cf3b Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Sun, 16 Apr 2017 13:29:36 -0400 Subject: [PATCH 199/316] fixing accidental eachSeries --- src/upgrades/1.5.0/post_votes_zset.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/upgrades/1.5.0/post_votes_zset.js b/src/upgrades/1.5.0/post_votes_zset.js index b1d5b0cae4..7feabe8169 100644 --- a/src/upgrades/1.5.0/post_votes_zset.js +++ b/src/upgrades/1.5.0/post_votes_zset.js @@ -13,7 +13,7 @@ module.exports = { var progress = this.progress; require('../../batch').processSortedSet('posts:pid', function (pids, next) { - async.eachSeries(pids, function (pid, next) { + async.each(pids, function (pid, next) { db.getObjectFields('post:' + pid, ['upvotes', 'downvotes'], function (err, postData) { if (err || !postData) { return next(err); From 3022a1920bb842d327c5c2796949f3884f502a72 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Sun, 16 Apr 2017 13:37:17 -0400 Subject: [PATCH 200/316] added progress bars to all upgrade scripts processing posts:pid sorted set --- src/upgrades/1.0.0/user_best_posts.js | 6 +++++- src/upgrades/1.1.0/separate_upvote_downvote.js | 8 +++++++- src/upgrades/1.3.0/favourites_to_bookmarks.js | 7 ++++++- src/upgrades/1.3.0/sorted_sets_for_post_replies.js | 6 ++++++ src/upgrades/1.5.0/flags_refactor.js | 5 +++++ 5 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/upgrades/1.0.0/user_best_posts.js b/src/upgrades/1.0.0/user_best_posts.js index a832a71254..560ef7b29c 100644 --- a/src/upgrades/1.0.0/user_best_posts.js +++ b/src/upgrades/1.0.0/user_best_posts.js @@ -12,6 +12,7 @@ module.exports = { timestamp: Date.UTC(2016, 0, 14), method: function (callback) { var batch = require('../../batch'); + var progress = this.progress; batch.processSortedSet('posts:pid', function (ids, next) { async.eachSeries(ids, function (id, next) { @@ -24,8 +25,11 @@ module.exports = { } winston.verbose('processing pid: ' + postData.pid + ' uid: ' + postData.uid + ' votes: ' + postData.votes); db.sortedSetAdd('uid:' + postData.uid + ':posts:votes', postData.votes, postData.pid, next); + progress.incr(); }); }, next); - }, {}, callback); + }, { + progress: progress, + }, callback); }, }; diff --git a/src/upgrades/1.1.0/separate_upvote_downvote.js b/src/upgrades/1.1.0/separate_upvote_downvote.js index c397a36a7b..3f78667dc5 100644 --- a/src/upgrades/1.1.0/separate_upvote_downvote.js +++ b/src/upgrades/1.1.0/separate_upvote_downvote.js @@ -14,6 +14,8 @@ module.exports = { var batch = require('../../batch'); var posts = require('../../posts'); var count = 0; + var progress = this.progress; + batch.processSortedSet('posts:pid', function (pids, next) { winston.verbose('upgraded ' + count + ' posts'); count += pids.length; @@ -43,8 +45,12 @@ module.exports = { } else { next(); } + + progress.incr(); }, next); }, next); - }, {}, callback); + }, { + progress: progress, + }, callback); }, }; diff --git a/src/upgrades/1.3.0/favourites_to_bookmarks.js b/src/upgrades/1.3.0/favourites_to_bookmarks.js index 0888e69653..bbdc09ec4c 100644 --- a/src/upgrades/1.3.0/favourites_to_bookmarks.js +++ b/src/upgrades/1.3.0/favourites_to_bookmarks.js @@ -12,9 +12,12 @@ module.exports = { method: function (callback) { function upgradePosts(next) { var batch = require('../../batch'); + var progress = this.progress; batch.processSortedSet('posts:pid', function (ids, next) { async.each(ids, function (id, next) { + progress.incr(); + async.waterfall([ function (next) { db.rename('pid:' + id + ':users_favourited', 'pid:' + id + ':users_bookmarked', next); @@ -34,7 +37,9 @@ module.exports = { }, ], next); }, next); - }, {}, next); + }, { + progress: progress, + }, next); } function upgradeUsers(next) { diff --git a/src/upgrades/1.3.0/sorted_sets_for_post_replies.js b/src/upgrades/1.3.0/sorted_sets_for_post_replies.js index b3ecd25f0a..1cce287829 100644 --- a/src/upgrades/1.3.0/sorted_sets_for_post_replies.js +++ b/src/upgrades/1.3.0/sorted_sets_for_post_replies.js @@ -13,12 +13,16 @@ module.exports = { method: function (callback) { var posts = require('../../posts'); var batch = require('../../batch'); + var progress = this.progress; + batch.processSortedSet('posts:pid', function (ids, next) { posts.getPostsFields(ids, ['pid', 'toPid', 'timestamp'], function (err, data) { if (err) { return next(err); } + progress.incr(); + async.eachSeries(data, function (postData, next) { if (!parseInt(postData.toPid, 10)) { return next(null); @@ -30,6 +34,8 @@ module.exports = { ], next); }, next); }); + }, { + progress: progress, }, callback); }, }; diff --git a/src/upgrades/1.5.0/flags_refactor.js b/src/upgrades/1.5.0/flags_refactor.js index 7aff6bcfe7..08fc1831d8 100644 --- a/src/upgrades/1.5.0/flags_refactor.js +++ b/src/upgrades/1.5.0/flags_refactor.js @@ -13,6 +13,7 @@ module.exports = { var batch = require('../../batch'); var posts = require('../../posts'); var flags = require('../../flags'); + var progress = this.progress; batch.processSortedSet('posts:pid', function (ids, next) { posts.getPostsByPids(ids, 1, function (err, posts) { @@ -80,10 +81,14 @@ module.exports = { } else { next(err); } + + progress.incr(); }); }); }, next); }); + }, { + progress: this.progress, }, callback); }, }; From b25fbd1e2e134b8a6f99a030cda597532901667e Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Sun, 16 Apr 2017 13:39:09 -0400 Subject: [PATCH 201/316] fix broken upgrade script --- src/upgrades/1.3.0/favourites_to_bookmarks.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/upgrades/1.3.0/favourites_to_bookmarks.js b/src/upgrades/1.3.0/favourites_to_bookmarks.js index bbdc09ec4c..065f02c94a 100644 --- a/src/upgrades/1.3.0/favourites_to_bookmarks.js +++ b/src/upgrades/1.3.0/favourites_to_bookmarks.js @@ -10,9 +10,10 @@ module.exports = { name: 'Favourites to Bookmarks', timestamp: Date.UTC(2016, 9, 8), method: function (callback) { + var progress = this.progress; + function upgradePosts(next) { var batch = require('../../batch'); - var progress = this.progress; batch.processSortedSet('posts:pid', function (ids, next) { async.each(ids, function (id, next) { From 82bfa752b9f5d2242dcf5c7f8d03fc98b9cda3d7 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Sun, 16 Apr 2017 15:47:32 -0400 Subject: [PATCH 202/316] Removed outdated list of available upgrade scripts --- src/upgrade.js | 37 +------------------------------------ 1 file changed, 1 insertion(+), 36 deletions(-) diff --git a/src/upgrade.js b/src/upgrade.js index 96a20780cf..e23fde22d1 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -19,42 +19,7 @@ var utils = require('../public/src/utils'); * 4. Append your filename to the array below for the next NodeBB version. */ -var Upgrade = { - available: [ - { - version: '1.0.0', - upgrades: ['chat_upgrade', 'chat_room_hashes', 'theme_to_active_plugins', 'user_best_posts', 'users_notvalidated', 'global_moderators', 'social_post_sharing'], - }, - { - version: '1.1.0', - upgrades: ['group_title_update', 'user_post_count_per_tid', 'dismiss_flags_from_deleted_topics', 'assign_topic_read_privilege', 'separate_upvote_downvote'], - }, - { - version: '1.1.1', - upgrades: ['upload_privileges', 'remove_negative_best_posts'], - }, - { - version: '1.2.0', - upgrades: ['category_recent_tids', 'edit_delete_deletetopic_privileges'], - }, - { - version: '1.3.0', - upgrades: ['favourites_to_bookmarks', 'sorted_sets_for_post_replies'], - }, - { - version: '1.4.0', - upgrades: ['global_and_user_language_keys', 'sorted_set_for_pinned_topics'], - }, - { - version: 'master', // rename this to whenever the next NodeBB version is (non-breaking) - upgrades: ['sound_settings', 'config_urls_update'], - }, - { - version: 'develop', // rename this to whatever the next NodeBB version is (breaking) - upgrades: ['flags_refactor', 'post_votes_zset', 'moderation_history_refactor', 'allowed_file_extensions'], - }, - ], -}; +var Upgrade = {}; Upgrade.getAll = function (callback) { async.waterfall([ From 56ba08f17ed846763e7725e67f5f0e69585abbcf Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 17 Apr 2017 14:31:24 -0400 Subject: [PATCH 203/316] fix require path --- src/upgrades/1.4.6/delete_sessions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/upgrades/1.4.6/delete_sessions.js b/src/upgrades/1.4.6/delete_sessions.js index dd9cbb91e4..0a78f11206 100644 --- a/src/upgrades/1.4.6/delete_sessions.js +++ b/src/upgrades/1.4.6/delete_sessions.js @@ -15,7 +15,7 @@ module.exports = { async.waterfall([ function (next) { if (isRedisSessionStore) { - var rdb = require.main.require('./database/redis'); + var rdb = require.main.require('./src/database/redis'); var client = rdb.connect(); async.waterfall([ function (next) { From fb3020f892113ae5aceae9274c936f787f6b2373 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 17 Apr 2017 18:59:46 -0400 Subject: [PATCH 204/316] closes #5604 --- public/src/client/chats.js | 7 +++++++ public/src/modules/chat.js | 2 ++ src/socket.io/modules.js | 1 + 3 files changed, 10 insertions(+) diff --git a/public/src/client/chats.js b/public/src/client/chats.js index 3dca09bc38..c7cb1921ae 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -83,6 +83,7 @@ define('forum/chats', [ Chats.addRenameHandler(ajaxify.data.roomId, $('[component="chat/room/name"]')); Chats.addScrollHandler(ajaxify.data.roomId, ajaxify.data.uid, $('.chat-content')); + Chats.addCharactersLeftHandler(components.get('chat/input')); }; Chats.addScrollHandler = function (roomId, uid, el) { @@ -123,6 +124,12 @@ define('forum/chats', [ }); }; + Chats.addCharactersLeftHandler = function (element) { + element.on('keyup', function () { + $('[component="chat/message/length"]').text(element.val().length); + }); + }; + Chats.addEditDeleteHandler = function (element, roomId) { element.on('click', '[data-action="edit"]', function () { var messageId = $(this).parents('[data-mid]').attr('data-mid'); diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index d05829c79d..d245e5ce23 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -266,6 +266,8 @@ define('chat', [ Chats.addScrollHandler(chatModal.attr('data-roomid'), data.uid, chatModal.find('.chat-content')); + Chats.addCharactersLeftHandler(chatModal.find('[component="chat/input"]')); + taskbar.push('chat', chatModal.attr('UUID'), { title: data.roomName || (data.users.length ? data.users[0].username : ''), roomId: data.roomId, diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index 184609129a..e3f7cc171b 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -145,6 +145,7 @@ SocketModules.chats.loadRoom = function (socket, data, callback) { results.roomData.groupChat = results.roomData.hasOwnProperty('groupChat') ? results.roomData.groupChat : results.users.length > 2; results.roomData.isOwner = parseInt(results.roomData.owner, 10) === socket.uid; results.roomData.maximumUsersInChatRoom = parseInt(meta.config.maximumUsersInChatRoom, 10) || 0; + results.roomData.maximumChatMessageLength = parseInt(meta.config.maximumChatMessageLength) || 1000; results.roomData.showUserInput = !results.roomData.maximumUsersInChatRoom || results.roomData.maximumUsersInChatRoom > 2; next(null, results.roomData); }, From 97f6c0008f7372163fcfebc493d895a2b29dd654 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 17 Apr 2017 19:15:37 -0400 Subject: [PATCH 205/316] add radix --- src/socket.io/modules.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index e3f7cc171b..a3a9ff4fe3 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -145,7 +145,7 @@ SocketModules.chats.loadRoom = function (socket, data, callback) { results.roomData.groupChat = results.roomData.hasOwnProperty('groupChat') ? results.roomData.groupChat : results.users.length > 2; results.roomData.isOwner = parseInt(results.roomData.owner, 10) === socket.uid; results.roomData.maximumUsersInChatRoom = parseInt(meta.config.maximumUsersInChatRoom, 10) || 0; - results.roomData.maximumChatMessageLength = parseInt(meta.config.maximumChatMessageLength) || 1000; + results.roomData.maximumChatMessageLength = parseInt(meta.config.maximumChatMessageLength, 10) || 1000; results.roomData.showUserInput = !results.roomData.maximumUsersInChatRoom || results.roomData.maximumUsersInChatRoom > 2; next(null, results.roomData); }, From 7b91da067bd6d63c5f38da223bfb1398feba5de2 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 18 Apr 2017 14:21:28 -0400 Subject: [PATCH 206/316] closes #5602 --- .../language/en-GB/admin/settings/post.json | 2 ++ src/topics/recent.js | 3 ++- src/views/admin/settings/post.tpl | 20 ++++++++++++++++--- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/public/language/en-GB/admin/settings/post.json b/public/language/en-GB/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/en-GB/admin/settings/post.json +++ b/public/language/en-GB/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/src/topics/recent.js b/src/topics/recent.js index dbecd035b4..6801c0095b 100644 --- a/src/topics/recent.js +++ b/src/topics/recent.js @@ -8,6 +8,7 @@ var plugins = require('../plugins'); var privileges = require('../privileges'); var user = require('../user'); var categories = require('../categories'); +var meta = require('../meta'); module.exports = function (Topics) { var terms = { @@ -65,7 +66,7 @@ module.exports = function (Topics) { function (tids, next) { async.parallel({ ignoredCids: function (next) { - if (filter === 'watched') { + if (filter === 'watched' || parseInt(meta.config.disableRecentCategoryFilter, 10) === 1) { return next(null, []); } user.getIgnoredCategories(uid, next); diff --git a/src/views/admin/settings/post.tpl b/src/views/admin/settings/post.tpl index 0eef74390c..18d31c0cdf 100644 --- a/src/views/admin/settings/post.tpl +++ b/src/views/admin/settings/post.tpl @@ -123,9 +123,23 @@
    - - -
    + + +
    + +
    +
    + +
    +
    [[admin/settings/post:recent]]
    +
    +
    +
    + +
    From 974106b7e2cd60567186354a2cc0b6c59fb107ba Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 19 Apr 2017 12:07:37 -0400 Subject: [PATCH 207/316] closes #5610 --- src/posts.js | 16 ++++++++++------ src/posts/create.js | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/posts.js b/src/posts.js index 8949bae7f3..32b6a4454d 100644 --- a/src/posts.js +++ b/src/posts.js @@ -94,13 +94,17 @@ var plugins = require('./plugins'); }; Posts.getPostData = function (pid, callback) { - db.getObject('post:' + pid, function (err, data) { - if (err) { - return callback(err); + async.waterfall([ + function (next) { + db.getObject('post:' + pid, next); + }, + function (data, next) { + plugins.fireHook('filter:post.getPostData', { post: data }, next); + }, + function (data, next) { + next(null, data.post); } - - plugins.fireHook('filter:post.get', data, callback); - }); + ], callback); }; Posts.getPostField = function (pid, field, callback) { diff --git a/src/posts/create.js b/src/posts/create.js index 51fb89a97e..6ec02e6fc7 100644 --- a/src/posts/create.js +++ b/src/posts/create.js @@ -101,7 +101,7 @@ module.exports = function (Posts) { if (err) { return next(err); } - plugins.fireHook('filter:post.get', postData, next); + plugins.fireHook('filter:post.get', { post: postData, uid: data.uid }, next); }); }, function (postData, next) { From e5f7eed8eccea49eefe9fc75fd072dda22d8c223 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 19 Apr 2017 12:18:00 -0400 Subject: [PATCH 208/316] adding progress bar to session deletion upgrade script --- src/upgrades/1.4.6/delete_sessions.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/upgrades/1.4.6/delete_sessions.js b/src/upgrades/1.4.6/delete_sessions.js index 0a78f11206..fd4004ec9e 100644 --- a/src/upgrades/1.4.6/delete_sessions.js +++ b/src/upgrades/1.4.6/delete_sessions.js @@ -11,6 +11,7 @@ module.exports = { method: function (callback) { var configJSON = require.main.require('./config.json'); var isRedisSessionStore = configJSON.hasOwnProperty('redis'); + var progress = this.progress; async.waterfall([ function (next) { @@ -22,7 +23,10 @@ module.exports = { client.keys('sess:*', next); }, function (sessionKeys, next) { + progress.total = sessionKeys.length; + async.eachSeries(sessionKeys, function (key, next) { + progress.incr(); client.del(key, next); }, next); }, From 0d1d8f3874331e6df4bb27a90b7c0857e3250434 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 19 Apr 2017 12:33:53 -0400 Subject: [PATCH 209/316] style changes --- src/posts.js | 458 ++++++++++++++++++++++++++------------------------- 1 file changed, 235 insertions(+), 223 deletions(-) diff --git a/src/posts.js b/src/posts.js index 32b6a4454d..492599e4cd 100644 --- a/src/posts.js +++ b/src/posts.js @@ -10,265 +10,277 @@ var topics = require('./topics'); var privileges = require('./privileges'); var plugins = require('./plugins'); -(function (Posts) { - require('./posts/create')(Posts); - require('./posts/delete')(Posts); - require('./posts/edit')(Posts); - require('./posts/parse')(Posts); - require('./posts/user')(Posts); - require('./posts/topics')(Posts); - require('./posts/category')(Posts); - require('./posts/summary')(Posts); - require('./posts/recent')(Posts); - require('./posts/tools')(Posts); - require('./posts/votes')(Posts); - require('./posts/bookmarks')(Posts); +var Posts = module.exports; - Posts.exists = function (pid, callback) { - db.isSortedSetMember('posts:pid', pid, callback); - }; +require('./posts/create')(Posts); +require('./posts/delete')(Posts); +require('./posts/edit')(Posts); +require('./posts/parse')(Posts); +require('./posts/user')(Posts); +require('./posts/topics')(Posts); +require('./posts/category')(Posts); +require('./posts/summary')(Posts); +require('./posts/recent')(Posts); +require('./posts/tools')(Posts); +require('./posts/votes')(Posts); +require('./posts/bookmarks')(Posts); - Posts.getPidsFromSet = function (set, start, stop, reverse, callback) { - if (isNaN(start) || isNaN(stop)) { - return callback(null, []); - } - db[reverse ? 'getSortedSetRevRange' : 'getSortedSetRange'](set, start, stop, callback); - }; - - Posts.getPostsByPids = function (pids, uid, callback) { - if (!Array.isArray(pids) || !pids.length) { - return callback(null, []); - } +Posts.exists = function (pid, callback) { + db.isSortedSetMember('posts:pid', pid, callback); +}; - var keys = []; +Posts.getPidsFromSet = function (set, start, stop, reverse, callback) { + if (isNaN(start) || isNaN(stop)) { + return callback(null, []); + } + db[reverse ? 'getSortedSetRevRange' : 'getSortedSetRange'](set, start, stop, callback); +}; - for (var x = 0, numPids = pids.length; x < numPids; x += 1) { - keys.push('post:' + pids[x]); - } +Posts.getPostsByPids = function (pids, uid, callback) { + if (!Array.isArray(pids) || !pids.length) { + return callback(null, []); + } - async.waterfall([ - function (next) { - db.getObjects(keys, next); - }, - function (posts, next) { - async.map(posts, function (post, next) { - if (!post) { - return next(); - } - post.upvotes = parseInt(post.upvotes, 10) || 0; - post.downvotes = parseInt(post.downvotes, 10) || 0; - post.votes = post.upvotes - post.downvotes; - post.timestampISO = utils.toISOString(post.timestamp); - post.editedISO = parseInt(post.edited, 10) !== 0 ? utils.toISOString(post.edited) : ''; - Posts.parsePost(post, next); - }, next); - }, - function (posts, next) { - plugins.fireHook('filter:post.getPosts', { posts: posts, uid: uid }, next); - }, - function (data, next) { - if (!data || !Array.isArray(data.posts)) { - return next(null, []); + async.waterfall([ + function (next) { + var keys = pids.map(function (pid) { + return 'post:' + pid; + }); + db.getObjects(keys, next); + }, + function (posts, next) { + async.map(posts, function (post, next) { + if (!post) { + return next(); } - data.posts = data.posts.filter(Boolean); - next(null, data.posts); - }, - ], callback); - }; - - Posts.getPostSummariesFromSet = function (set, uid, start, stop, callback) { - async.waterfall([ - function (next) { - db.getSortedSetRevRange(set, start, stop, next); - }, - function (pids, next) { - privileges.posts.filter('read', pids, uid, next); - }, - function (pids, next) { - Posts.getPostSummaryByPids(pids, uid, { stripTags: false }, next); - }, - function (posts, next) { - next(null, { posts: posts, nextStart: stop + 1 }); - }, - ], callback); - }; - - Posts.getPostData = function (pid, callback) { - async.waterfall([ - function (next) { - db.getObject('post:' + pid, next); - }, - function (data, next) { - plugins.fireHook('filter:post.getPostData', { post: data }, next); - }, - function (data, next) { - next(null, data.post); + post.upvotes = parseInt(post.upvotes, 10) || 0; + post.downvotes = parseInt(post.downvotes, 10) || 0; + post.votes = post.upvotes - post.downvotes; + post.timestampISO = utils.toISOString(post.timestamp); + post.editedISO = parseInt(post.edited, 10) !== 0 ? utils.toISOString(post.edited) : ''; + Posts.parsePost(post, next); + }, next); + }, + function (posts, next) { + plugins.fireHook('filter:post.getPosts', { posts: posts, uid: uid }, next); + }, + function (data, next) { + if (!data || !Array.isArray(data.posts)) { + return next(null, []); } - ], callback); - }; + data.posts = data.posts.filter(Boolean); + next(null, data.posts); + }, + ], callback); +}; - Posts.getPostField = function (pid, field, callback) { - Posts.getPostFields(pid, [field], function (err, data) { - if (err) { - return callback(err); - } +Posts.getPostSummariesFromSet = function (set, uid, start, stop, callback) { + async.waterfall([ + function (next) { + db.getSortedSetRevRange(set, start, stop, next); + }, + function (pids, next) { + privileges.posts.filter('read', pids, uid, next); + }, + function (pids, next) { + Posts.getPostSummaryByPids(pids, uid, { stripTags: false }, next); + }, + function (posts, next) { + next(null, { posts: posts, nextStart: stop + 1 }); + }, + ], callback); +}; - callback(null, data[field]); - }); - }; +Posts.getPostData = function (pid, callback) { + async.waterfall([ + function (next) { + db.getObject('post:' + pid, next); + }, + function (data, next) { + plugins.fireHook('filter:post.getPostData', { post: data }, next); + }, + function (data, next) { + next(null, data.post); + }, + ], callback); +}; - Posts.getPostFields = function (pid, fields, callback) { - db.getObjectFields('post:' + pid, fields, function (err, data) { - if (err) { - return callback(err); - } +Posts.getPostField = function (pid, field, callback) { + async.waterfall([ + function (next) { + Posts.getPostFields(pid, [field], next); + }, + function (data, next) { + next(null, data[field]); + }, + ], callback); +}; +Posts.getPostFields = function (pid, fields, callback) { + async.waterfall([ + function (next) { + db.getObjectFields('post:' + pid, fields, next); + }, + function (data, next) { data.pid = pid; - plugins.fireHook('filter:post.getFields', { posts: [data], fields: fields }, function (err, data) { - callback(err, (data && Array.isArray(data.posts) && data.posts.length) ? data.posts[0] : null); - }); - }); - }; + plugins.fireHook('filter:post.getFields', { posts: [data], fields: fields }, next); + }, + function (data, next) { + next(null, (data && Array.isArray(data.posts) && data.posts.length) ? data.posts[0] : null); + }, + ], callback); +}; - Posts.getPostsFields = function (pids, fields, callback) { - if (!Array.isArray(pids) || !pids.length) { - return callback(null, []); - } +Posts.getPostsFields = function (pids, fields, callback) { + if (!Array.isArray(pids) || !pids.length) { + return callback(null, []); + } - var keys = pids.map(function (pid) { - return 'post:' + pid; - }); + var keys = pids.map(function (pid) { + return 'post:' + pid; + }); - db.getObjectsFields(keys, fields, function (err, posts) { - if (err) { - return callback(err); - } - plugins.fireHook('filter:post.getFields', { posts: posts, fields: fields }, function (err, data) { - callback(err, (data && Array.isArray(data.posts)) ? data.posts : null); - }); - }); - }; + async.waterfall([ + function (next) { + db.getObjectsFields(keys, fields, next); + }, + function (posts, next) { + plugins.fireHook('filter:post.getFields', { posts: posts, fields: fields }, next); + }, + function (data, next) { + next(null, (data && Array.isArray(data.posts)) ? data.posts : null); + }, + ], callback); +}; - Posts.setPostField = function (pid, field, value, callback) { - db.setObjectField('post:' + pid, field, value, function (err) { - if (err) { - return callback(err); - } +Posts.setPostField = function (pid, field, value, callback) { + async.waterfall([ + function (next) { + db.setObjectField('post:' + pid, field, value, next); + }, + function (next) { var data = { pid: pid, }; data[field] = value; plugins.fireHook('action:post.setFields', { data: data }); - callback(); - }); - }; + next(); + }, + ], callback); +}; - Posts.setPostFields = function (pid, data, callback) { - db.setObject('post:' + pid, data, function (err) { - if (err) { - return callback(err); - } +Posts.setPostFields = function (pid, data, callback) { + async.waterfall([ + function (next) { + db.setObject('post:' + pid, data, next); + }, + function (next) { data.pid = pid; plugins.fireHook('action:post.setFields', { data: data }); - callback(); - }); - }; + next(); + }, + ], callback); +}; - Posts.getPidIndex = function (pid, tid, topicPostSort, callback) { - var set = topicPostSort === 'most_votes' ? 'tid:' + tid + ':posts:votes' : 'tid:' + tid + ':posts'; - db.sortedSetRank(set, pid, function (err, index) { +Posts.getPidIndex = function (pid, tid, topicPostSort, callback) { + async.waterfall([ + function (next) { + var set = topicPostSort === 'most_votes' ? 'tid:' + tid + ':posts:votes' : 'tid:' + tid + ':posts'; + db.sortedSetRank(set, pid, next); + }, + function (index, next) { if (!utils.isNumber(index)) { - return callback(err, 0); + return next(null, 0); } - callback(err, parseInt(index, 10) + 1); - }); - }; + next(null, parseInt(index, 10) + 1); + }, + ], callback); +}; - Posts.getPostIndices = function (posts, uid, callback) { - if (!Array.isArray(posts) || !posts.length) { - return callback(null, []); - } +Posts.getPostIndices = function (posts, uid, callback) { + if (!Array.isArray(posts) || !posts.length) { + return callback(null, []); + } - async.waterfall([ - function (next) { - user.getSettings(uid, next); - }, - function (settings, next) { - var byVotes = settings.topicPostSort === 'most_votes'; - var sets = posts.map(function (post) { - return byVotes ? 'tid:' + post.tid + ':posts:votes' : 'tid:' + post.tid + ':posts'; - }); + async.waterfall([ + function (next) { + user.getSettings(uid, next); + }, + function (settings, next) { + var byVotes = settings.topicPostSort === 'most_votes'; + var sets = posts.map(function (post) { + return byVotes ? 'tid:' + post.tid + ':posts:votes' : 'tid:' + post.tid + ':posts'; + }); - var uniqueSets = _.uniq(sets); - var method = 'sortedSetsRanks'; - if (uniqueSets.length === 1) { - method = 'sortedSetRanks'; - sets = uniqueSets[0]; - } + var uniqueSets = _.uniq(sets); + var method = 'sortedSetsRanks'; + if (uniqueSets.length === 1) { + method = 'sortedSetRanks'; + sets = uniqueSets[0]; + } - var pids = posts.map(function (post) { - return post.pid; - }); + var pids = posts.map(function (post) { + return post.pid; + }); - db[method](sets, pids, next); - }, - function (indices, next) { - for (var i = 0; i < indices.length; i += 1) { - indices[i] = utils.isNumber(indices[i]) ? parseInt(indices[i], 10) + 1 : 0; - } + db[method](sets, pids, next); + }, + function (indices, next) { + for (var i = 0; i < indices.length; i += 1) { + indices[i] = utils.isNumber(indices[i]) ? parseInt(indices[i], 10) + 1 : 0; + } - next(null, indices); - }, - ], callback); - }; + next(null, indices); + }, + ], callback); +}; - Posts.updatePostVoteCount = function (postData, callback) { - if (!postData || !postData.pid || !postData.tid) { - return callback(); - } - async.parallel([ - function (next) { - if (postData.uid) { - if (postData.votes > 0) { - db.sortedSetAdd('uid:' + postData.uid + ':posts:votes', postData.votes, postData.pid, next); - } else { - db.sortedSetRemove('uid:' + postData.uid + ':posts:votes', postData.pid, next); - } +Posts.updatePostVoteCount = function (postData, callback) { + if (!postData || !postData.pid || !postData.tid) { + return callback(); + } + async.parallel([ + function (next) { + if (postData.uid) { + if (postData.votes > 0) { + db.sortedSetAdd('uid:' + postData.uid + ':posts:votes', postData.votes, postData.pid, next); } else { - next(); + db.sortedSetRemove('uid:' + postData.uid + ':posts:votes', postData.pid, next); } - }, - function (next) { - async.waterfall([ - function (next) { - topics.getTopicField(postData.tid, 'mainPid', next); - }, - function (mainPid, next) { - if (parseInt(mainPid, 10) === parseInt(postData.pid, 10)) { - return next(); - } - db.sortedSetAdd('tid:' + postData.tid + ':posts:votes', postData.votes, postData.pid, next); - }, - ], next); - }, - function (next) { - db.sortedSetAdd('posts:votes', postData.votes, postData.pid, next); - }, - function (next) { - Posts.setPostFields(postData.pid, { upvotes: postData.upvotes, downvotes: postData.downvotes }, next); - }, - ], function (err) { - callback(err); - }); - }; - - Posts.modifyPostByPrivilege = function (post, isAdminOrMod) { - if (post.deleted && !(isAdminOrMod || post.selfPost)) { - post.content = '[[topic:post_is_deleted]]'; - if (post.user) { - post.user.signature = ''; + } else { + next(); } + }, + function (next) { + async.waterfall([ + function (next) { + topics.getTopicField(postData.tid, 'mainPid', next); + }, + function (mainPid, next) { + if (parseInt(mainPid, 10) === parseInt(postData.pid, 10)) { + return next(); + } + db.sortedSetAdd('tid:' + postData.tid + ':posts:votes', postData.votes, postData.pid, next); + }, + ], next); + }, + function (next) { + db.sortedSetAdd('posts:votes', postData.votes, postData.pid, next); + }, + function (next) { + Posts.setPostFields(postData.pid, { upvotes: postData.upvotes, downvotes: postData.downvotes }, next); + }, + ], function (err) { + callback(err); + }); +}; + +Posts.modifyPostByPrivilege = function (post, isAdminOrMod) { + if (post.deleted && !(isAdminOrMod || post.selfPost)) { + post.content = '[[topic:post_is_deleted]]'; + if (post.user) { + post.user.signature = ''; } - }; -}(exports)); + } +}; From d8170d5d1cb36afb364628f162ab561f99f8f270 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 19 Apr 2017 13:09:16 -0400 Subject: [PATCH 210/316] fix tests --- src/posts/create.js | 8 ++++---- test/database/hash.js | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/posts/create.js b/src/posts/create.js index 6ec02e6fc7..09c4e9c9b7 100644 --- a/src/posts/create.js +++ b/src/posts/create.js @@ -104,10 +104,10 @@ module.exports = function (Posts) { plugins.fireHook('filter:post.get', { post: postData, uid: data.uid }, next); }); }, - function (postData, next) { - postData.isMain = isMain; - plugins.fireHook('action:post.save', { post: _.clone(postData) }); - next(null, postData); + function (data, next) { + data.post.isMain = isMain; + plugins.fireHook('action:post.save', { post: _.clone(data.post) }); + next(null, data.post); }, ], callback); }; diff --git a/test/database/hash.js b/test/database/hash.js index 65e365b7f3..7f9dc305d0 100644 --- a/test/database/hash.js +++ b/test/database/hash.js @@ -48,7 +48,6 @@ describe('Hash methods', function () { assert.ifError(err); db.getObject('emptyField', function (err, data) { assert.ifError(err); - console.log(data); done(); }); }); From d500e7f2aaafa6af46ebcbc9bd3f56421f53ae97 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 19 Apr 2017 13:23:53 -0400 Subject: [PATCH 211/316] add back action:plugin.install and action:plugin activate #5407 --- src/plugins/hooks.js | 2 -- src/plugins/install.js | 14 +++++--------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/plugins/hooks.js b/src/plugins/hooks.js index e72b233940..16ceb32878 100644 --- a/src/plugins/hooks.js +++ b/src/plugins/hooks.js @@ -9,8 +9,6 @@ module.exports = function (Plugins) { 'filter:post.save': 'filter:post.create', 'filter:user.profileLinks': 'filter:user.profileMenu', 'action:post.flag': 'action:flag.create', - 'action:plugin.activate': null, - 'action:plugin.install': null, }; /* `data` is an object consisting of (* is required): diff --git a/src/plugins/install.js b/src/plugins/install.js index beb38bf3d9..a5ba2db1dd 100644 --- a/src/plugins/install.js +++ b/src/plugins/install.js @@ -49,10 +49,7 @@ module.exports = function (Plugins) { }, function (next) { meta.reloadRequired = true; - if (isActive) { - Plugins.fireHook('action:plugin.deactivate', { id: id }); - } - + Plugins.fireHook(isActive ? 'action:plugin.deactivate' : 'action:plugin.activate', { id: id }); setImmediate(next); }, ], function (err) { @@ -71,12 +68,14 @@ module.exports = function (Plugins) { function toggleInstall(id, version, callback) { var installed; + var type; async.waterfall([ function (next) { Plugins.isInstalled(id, next); }, function (_installed, next) { installed = _installed; + type = installed ? 'uninstall' : 'install'; Plugins.isActive(id, next); }, function (active, next) { @@ -89,16 +88,13 @@ module.exports = function (Plugins) { setImmediate(next); }, function (next) { - runNpmCommand(installed ? 'uninstall' : 'install', id, version || 'latest', next); + runNpmCommand(type, id, version || 'latest', next); }, function (next) { Plugins.get(id, next); }, function (pluginData, next) { - if (installed) { - Plugins.fireHook('action:plugin.uninstall', { id: id, version: version }); - } - + Plugins.fireHook('action:plugin.' + type, { id: id, version: version }); setImmediate(next, null, pluginData); }, ], callback); From 9d8ff4ea3ec59c3f7e23e5897e5db29752c7cb3c Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Wed, 19 Apr 2017 17:19:38 -0600 Subject: [PATCH 212/316] Fix images not displaying in signatures --- public/src/modules/translator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/modules/translator.js b/public/src/modules/translator.js index 2e9dcafb9c..29aed1af9c 100644 --- a/public/src/modules/translator.js +++ b/public/src/modules/translator.js @@ -415,7 +415,7 @@ * @returns {string} */ Translator.escape = function escape(text) { - return typeof text === 'string' ? text.replace(/\[/g, '[').replace(/\]/g, ']') : text; + return typeof text === 'string' ? text.replace(/\[\[/g, '[[').replace(/\]\]/g, ']]') : text; }; /** From b45111d44b972fd1fc1ead74fbf29cb2d3a40132 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 19 Apr 2017 20:56:12 -0400 Subject: [PATCH 213/316] show max message length in error message --- public/language/en-GB/error.json | 2 +- public/src/client/chats/messages.js | 2 +- src/messaging/create.js | 8 +++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/public/language/en-GB/error.json b/public/language/en-GB/error.json index b5b90c04e5..7473b8bc62 100644 --- a/public/language/en-GB/error.json +++ b/public/language/en-GB/error.json @@ -129,7 +129,7 @@ "chat-disabled": "Chat system disabled", "too-many-messages": "You have sent too many messages, please wait awhile.", "invalid-chat-message": "Invalid chat message", - "chat-message-too-long": "Chat message is too long", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "You are not allowed to edit this message", "cant-remove-last-user": "You can't remove the last user", "cant-delete-chat-message": "You are not allowed to delete this message", diff --git a/public/src/client/chats/messages.js b/public/src/client/chats/messages.js index 2a1889cf25..27f19ca33c 100644 --- a/public/src/client/chats/messages.js +++ b/public/src/client/chats/messages.js @@ -9,7 +9,7 @@ define('forum/chats/messages', ['components', 'sounds', 'translator'], function var mid = inputEl.attr('data-mid'); if (msg.length > ajaxify.data.maximumChatMessageLength) { - return app.alertError('[[error:chat-message-too-long]]'); + return app.alertError('[[error:chat-message-too-long,' + ajaxify.data.maximumChatMessageLength + ']]'); } if (!msg.length) { diff --git a/src/messaging/create.js b/src/messaging/create.js index 2c6ac84ffb..f4fe421eac 100644 --- a/src/messaging/create.js +++ b/src/messaging/create.js @@ -30,9 +30,11 @@ module.exports = function (Messaging) { if (!content) { return callback(new Error('[[error:invalid-chat-message]]')); } + content = String(content); - if (content.length > (meta.config.maximumChatMessageLength || 1000)) { - return callback(new Error('[[error:chat-message-too-long]]')); + var maximumChatMessageLength = (meta.config.maximumChatMessageLength || 1000); + if (content.length > maximumChatMessageLength) { + return callback(new Error('[[error:chat-message-too-long, ' + maximumChatMessageLength + ']]')); } callback(); }; @@ -52,7 +54,7 @@ module.exports = function (Messaging) { function (_mid, next) { mid = _mid; message = { - content: content, + content: String(content), timestamp: timestamp, fromuid: fromuid, roomId: roomId, From 30b47d39f358e07a296e535223d576f7d59ea4bd Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Wed, 19 Apr 2017 20:33:03 -0600 Subject: [PATCH 214/316] Fix #5536 --- public/src/app.js | 23 +++++----- public/src/client/category.js | 11 ++--- public/src/client/search.js | 6 +-- public/src/client/topic.js | 11 ++--- public/src/modules/search.js | 4 +- public/src/modules/sounds.js | 8 ++-- public/src/modules/storage.js | 83 +++++++++++++++++++++++++++++++++++ src/meta/js.js | 1 + 8 files changed, 117 insertions(+), 30 deletions(-) create mode 100644 public/src/modules/storage.js diff --git a/public/src/app.js b/public/src/app.js index 3e53305aac..b5b5302bd2 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -630,16 +630,17 @@ app.cacheBuster = null; }; app.showCookieWarning = function () { - if (!config.cookies.enabled || !navigator.cookieEnabled) { - // Skip warning if cookie consent subsystem disabled (obviously), or cookies not in use - return; - } else if (window.location.pathname.startsWith(config.relative_path + '/admin')) { - // No need to show cookie consent warning in ACP - return; - } else if (window.localStorage.getItem('cookieconsent') === '1') { - return; - } - require(['translator'], function (translator) { + require(['translator', 'storage'], function (translator, storage) { + if (!config.cookies.enabled || !navigator.cookieEnabled) { + // Skip warning if cookie consent subsystem disabled (obviously), or cookies not in use + return; + } else if (window.location.pathname.startsWith(config.relative_path + '/admin')) { + // No need to show cookie consent warning in ACP + return; + } else if (storage.getItem('cookieconsent') === '1') { + return; + } + config.cookies.message = translator.unescape(config.cookies.message); config.cookies.dismiss = translator.unescape(config.cookies.dismiss); config.cookies.link = translator.unescape(config.cookies.link); @@ -651,7 +652,7 @@ app.cacheBuster = null; var dismissEl = warningEl.find('button'); dismissEl.on('click', function () { // Save consent cookie and remove warning element - window.localStorage.setItem('cookieconsent', '1'); + storage.setItem('cookieconsent', '1'); warningEl.remove(); }); }); diff --git a/public/src/client/category.js b/public/src/client/category.js index e2dfcd9924..7fea7cfbcb 100644 --- a/public/src/client/category.js +++ b/public/src/client/category.js @@ -11,7 +11,8 @@ define('forum/category', [ 'translator', 'topicSelect', 'forum/pagination', -], function (infinitescroll, share, navigator, categoryTools, sort, components, translator, topicSelect, pagination) { + 'storage', +], function (infinitescroll, share, navigator, categoryTools, sort, components, translator, topicSelect, pagination, storage) { var Category = {}; $(window).on('action:ajaxify.start', function (ev, data) { @@ -51,8 +52,8 @@ define('forum/category', [ var clickedIndex = $(this).parents('[data-index]').attr('data-index'); $('[component="category/topic"]').each(function (index, el) { if ($(el).offset().top - $(window).scrollTop() > 0) { - localStorage.setItem('category:' + cid + ':bookmark', $(el).attr('data-index')); - localStorage.setItem('category:' + cid + ':bookmark:clicked', clickedIndex); + storage.setItem('category:' + cid + ':bookmark', $(el).attr('data-index')); + storage.setItem('category:' + cid + ':bookmark:clicked', clickedIndex); return false; } }); @@ -118,8 +119,8 @@ define('forum/category', [ $(window).on('action:ajaxify.contentLoaded', function () { if (ajaxify.data.template.category && ajaxify.data.cid) { - var bookmarkIndex = localStorage.getItem('category:' + ajaxify.data.cid + ':bookmark'); - var clickedIndex = localStorage.getItem('category:' + ajaxify.data.cid + ':bookmark:clicked'); + var bookmarkIndex = storage.getItem('category:' + ajaxify.data.cid + ':bookmark'); + var clickedIndex = storage.getItem('category:' + ajaxify.data.cid + ':bookmark:clicked'); bookmarkIndex = Math.max(0, parseInt(bookmarkIndex, 10) || 0); clickedIndex = Math.max(0, parseInt(clickedIndex, 10) || 0); diff --git a/public/src/client/search.js b/public/src/client/search.js index 4f65935913..de68d1e401 100644 --- a/public/src/client/search.js +++ b/public/src/client/search.js @@ -1,7 +1,7 @@ 'use strict'; -define('forum/search', ['search', 'autocomplete'], function (searchModule, autocomplete) { +define('forum/search', ['search', 'autocomplete', 'storage'], function (searchModule, autocomplete, storage) { var Search = {}; Search.init = function () { @@ -147,13 +147,13 @@ define('forum/search', ['search', 'autocomplete'], function (searchModule, autoc function handleSavePreferences() { $('#save-preferences').on('click', function () { - localStorage.setItem('search-preferences', JSON.stringify(getSearchData())); + storage.setItem('search-preferences', JSON.stringify(getSearchData())); app.alertSuccess('[[search:search-preferences-saved]]'); return false; }); $('#clear-preferences').on('click', function () { - localStorage.removeItem('search-preferences'); + storage.removeItem('search-preferences'); var query = $('#search-input').val(); $('#advanced-search')[0].reset(); $('#search-input').val(query); diff --git a/public/src/client/topic.js b/public/src/client/topic.js index d620da1e70..e4ba5a06a5 100644 --- a/public/src/client/topic.js +++ b/public/src/client/topic.js @@ -12,7 +12,8 @@ define('forum/topic', [ 'navigator', 'sort', 'components', -], function (infinitescroll, threadTools, postTools, events, posts, images, replies, navigator, sort, components) { + 'storage', +], function (infinitescroll, threadTools, postTools, events, posts, images, replies, navigator, sort, components, storage) { var Topic = {}; var currentUrl = ''; @@ -142,7 +143,7 @@ define('forum/topic', [ function handleBookmark(tid) { // use the user's bookmark data if available, fallback to local if available - var bookmark = ajaxify.data.bookmark || localStorage.getItem('topic:' + tid + ':bookmark'); + var bookmark = ajaxify.data.bookmark || storage.getItem('topic:' + tid + ':bookmark'); var postIndex = getPostIndex(); if (postIndex && window.location.search.indexOf('page=') === -1) { @@ -160,7 +161,7 @@ define('forum/topic', [ navigator.scrollToPost(parseInt(bookmark - 1, 10), true); }, closefn: function () { - localStorage.removeItem('topic:' + tid + ':bookmark'); + storage.removeItem('topic:' + tid + ':bookmark'); }, }); setTimeout(function () { @@ -273,7 +274,7 @@ define('forum/topic', [ function updateUserBookmark(index) { var bookmarkKey = 'topic:' + ajaxify.data.tid + ':bookmark'; - var currentBookmark = ajaxify.data.bookmark || localStorage.getItem(bookmarkKey); + var currentBookmark = ajaxify.data.bookmark || storage.getItem(bookmarkKey); if (ajaxify.data.postcount > ajaxify.data.bookmarkThreshold && (!currentBookmark || parseInt(index, 10) > parseInt(currentBookmark, 10))) { if (app.user.uid) { @@ -287,7 +288,7 @@ define('forum/topic', [ ajaxify.data.bookmark = index; }); } else { - localStorage.setItem(bookmarkKey, index); + storage.setItem(bookmarkKey, index); } } diff --git a/public/src/modules/search.js b/public/src/modules/search.js index 1401bf8619..5b77ab7572 100644 --- a/public/src/modules/search.js +++ b/public/src/modules/search.js @@ -1,7 +1,7 @@ 'use strict'; -define('search', ['navigator', 'translator'], function (nav, translator) { +define('search', ['navigator', 'translator', 'storage'], function (nav, translator, storage) { var Search = { current: {}, }; @@ -79,7 +79,7 @@ define('search', ['navigator', 'translator'], function (nav, translator) { Search.getSearchPreferences = function () { try { - return JSON.parse(localStorage.getItem('search-preferences') || '{}'); + return JSON.parse(storage.getItem('search-preferences') || '{}'); } catch (e) { return {}; } diff --git a/public/src/modules/sounds.js b/public/src/modules/sounds.js index 38bbaec9cb..9a5f560447 100644 --- a/public/src/modules/sounds.js +++ b/public/src/modules/sounds.js @@ -1,7 +1,7 @@ 'use strict'; -define('sounds', function () { +define('sounds', ['storage'], function (storage) { var Sounds = {}; var fileMap; @@ -67,13 +67,13 @@ define('sounds', function () { if (id) { var item = 'sounds.handled:' + id; - if (localStorage.getItem(item)) { + if (storage.getItem(item)) { return; } - localStorage.setItem(item, true); + storage.setItem(item, true); setTimeout(function () { - localStorage.removeItem(item); + storage.removeItem(item); }, 5000); } diff --git a/public/src/modules/storage.js b/public/src/modules/storage.js new file mode 100644 index 0000000000..2fdf38f8af --- /dev/null +++ b/public/src/modules/storage.js @@ -0,0 +1,83 @@ +'use strict'; + +/** + * Checks localStorage and provides a fallback if it doesn't exist or is disabled + */ +define('storage', function () { + function Storage() { + this._store = {}; + this._keys = []; + } + Storage.prototype.setItem = function (key, val) { + key = String(key); + if (this._keys.indexOf(key) === -1) { + this._keys.push(key); + } + this._store[key] = val; + }; + Storage.prototype.getItem = function (key) { + key = String(key); + if (this._keys.indexOf(key) === -1) { + return null; + } + + return this._store[key]; + }; + Storage.prototype.removeItem = function (key) { + key = String(key); + this._keys = this._keys.filter(function (x) { + return x !== key; + }); + this._store[key] = null; + }; + Storage.prototype.clear = function () { + this._keys = []; + this._store = {}; + }; + Storage.prototype.key = function (n) { + n = parseInt(n, 10) || 0; + return this._keys[n]; + }; + if (Object.defineProperty) { + Object.defineProperty(Storage.prototype, 'length', { + get: function () { + return this._keys.length; + }, + }); + } + + var storage; + var item = Date.now(); + + try { + storage = window.localStorage; + storage.setItem(item, item); + if (storage.getItem(item) !== item) { + throw Error('localStorage behaved unexpectedly'); + } + storage.removeItem(item); + + return storage; + } catch (e) { + console.warn(e); + console.warn('localStorage failed, falling back on sessionStorage'); + + // see if sessionStorage works, and if so, return that + try { + storage = window.sessionStorage; + storage.setItem(item, item); + if (storage.getItem(item) !== item) { + throw Error('sessionStorage behaved unexpectedly'); + } + storage.removeItem(item); + + return storage; + } catch (e) { + console.warn(e); + console.warn('sessionStorage failed, falling back on memory storage'); + + // return an object implementing mock methods + return new Storage(); + } + } +}); diff --git a/src/meta/js.js b/src/meta/js.js index 967b100a32..0fff87c6a1 100644 --- a/src/meta/js.js +++ b/src/meta/js.js @@ -78,6 +78,7 @@ module.exports = function (Meta) { 'public/src/modules/taskbar.js', 'public/src/modules/helpers.js', 'public/src/modules/string.js', + 'public/src/modules/storage.js', ], // modules listed below are built (/src/modules) so they can be defined anonymously From cc76a8663d095ae58a71682e4a63c1954986d0fb Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Wed, 19 Apr 2017 20:44:11 -0600 Subject: [PATCH 215/316] Add flag for detecting if the mock is being used --- public/src/modules/storage.js | 1 + 1 file changed, 1 insertion(+) diff --git a/public/src/modules/storage.js b/public/src/modules/storage.js index 2fdf38f8af..5cb6051586 100644 --- a/public/src/modules/storage.js +++ b/public/src/modules/storage.js @@ -8,6 +8,7 @@ define('storage', function () { this._store = {}; this._keys = []; } + Storage.prototype.isMock = true; Storage.prototype.setItem = function (key, val) { key = String(key); if (this._keys.indexOf(key) === -1) { From de6ca3710d6a77ce424d27b90d6fb9a99d427bdf Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Thu, 20 Apr 2017 09:22:10 +0000 Subject: [PATCH 216/316] Latest translations and fallbacks --- public/language/pt-BR/admin/development/info.json | 2 +- public/language/pt-BR/admin/manage/tags.json | 2 +- public/language/pt-BR/admin/settings/guest.json | 2 +- public/language/pt-BR/admin/settings/reputation.json | 2 +- public/language/pt-BR/category.json | 2 +- public/language/pt-BR/global.json | 8 ++++---- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/public/language/pt-BR/admin/development/info.json b/public/language/pt-BR/admin/development/info.json index fe35d78a37..6b16f9a46a 100644 --- a/public/language/pt-BR/admin/development/info.json +++ b/public/language/pt-BR/admin/development/info.json @@ -1,5 +1,5 @@ { - "you-are-on": "Info - Você está em %1:%2", + "you-are-on": "Informação - Você está em %1:%2", "host": "host", "pid": "pid", "nodejs": "nodejs", diff --git a/public/language/pt-BR/admin/manage/tags.json b/public/language/pt-BR/admin/manage/tags.json index d163e643fd..4bf5b27a3e 100644 --- a/public/language/pt-BR/admin/manage/tags.json +++ b/public/language/pt-BR/admin/manage/tags.json @@ -1,5 +1,5 @@ { - "none": "O seu fórum ainda não tem quaisquer tópicos com tags.", + "none": "O seu fórum ainda não tem tópicos com tags.", "bg-color": "Cor de Fundo", "text-color": "Cor do Text", "create-modify": "Criar & Modificar Tags", diff --git a/public/language/pt-BR/admin/settings/guest.json b/public/language/pt-BR/admin/settings/guest.json index 9f3c4d57a9..2c4d0d4565 100644 --- a/public/language/pt-BR/admin/settings/guest.json +++ b/public/language/pt-BR/admin/settings/guest.json @@ -1,5 +1,5 @@ { - "handles": "Handles para Visitantes", + "handles": "Apelidos para Visitantes", "handles.enabled": "Permitir handles de visitantes", "handles.enabled-help": "Esta opção mostra um novo campo que permite visitantes de escolher um nome para associar à cada post que eles fizerem. Se desabilitado, eles serão simplesmente chamados de \"Visitante\".", "privileges": "Privilégios de Visitantes", diff --git a/public/language/pt-BR/admin/settings/reputation.json b/public/language/pt-BR/admin/settings/reputation.json index c81dd2364f..86d774ff99 100644 --- a/public/language/pt-BR/admin/settings/reputation.json +++ b/public/language/pt-BR/admin/settings/reputation.json @@ -4,6 +4,6 @@ "disable-down-voting": "Desabilitar Baixo Votar", "votes-are-public": "All Votes Are Public", "thresholds": "Limiares de Atividade", - "min-rep-downvote": "Reputação mínima para baixovotar posts", + "min-rep-downvote": "Reputação mínima para votar negativamente em posts", "min-rep-flag": "Reputação mínima para sinalizar posts" } \ No newline at end of file diff --git a/public/language/pt-BR/category.json b/public/language/pt-BR/category.json index c064d31ade..2f925a0e37 100644 --- a/public/language/pt-BR/category.json +++ b/public/language/pt-BR/category.json @@ -15,6 +15,6 @@ "watching.description": "Mostrar tópicos em não-lido", "ignoring.description": "Não mostrar tópicos em não-lido", "watch.message": "Agora você está seguindo as atualizações desta categoria e de todas as subcategorias", - "ignore.message": "Agora você está ignorando as atualizações desta categoria e de todas as subcategorias", + "ignore.message": "Agora você está ignorando as atualizações desta categoria e de todas as suas subcategorias", "watched-categories": "Categorias acompanhadas" } \ No newline at end of file diff --git a/public/language/pt-BR/global.json b/public/language/pt-BR/global.json index c6220177a4..914a11e049 100644 --- a/public/language/pt-BR/global.json +++ b/public/language/pt-BR/global.json @@ -53,10 +53,10 @@ "topics": "Tópicos", "posts": "Posts", "best": "Melhor", - "upvoters": "Cimavotadores", - "upvoted": "Votado positivamente", - "downvoters": "Baixovotadores", - "downvoted": "Votado negativamente", + "upvoters": "Votos positivos", + "upvoted": "Votou positivamente", + "downvoters": "Votos negativos", + "downvoted": "Votou negativamente", "views": "Visualizações", "reputation": "Reputação", "read_more": "ler mais", From 3d93667e2b332ff6b68ac0672638ea082366f6f1 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 20 Apr 2017 18:10:31 -0400 Subject: [PATCH 217/316] update update script to batch --- src/upgrade.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/upgrade.js b/src/upgrade.js index 8cba76974d..0621055370 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -440,14 +440,21 @@ Upgrade.upgrade = function (callback) { function (next) { if (isRedisSessionStore) { var rdb = require('./database/redis'); + var batch = require('./batch'); var client = rdb.connect(); async.waterfall([ function (next) { client.keys('sess:*', next); }, function (sessionKeys, next) { - async.eachSeries(sessionKeys, function (key, next) { - client.del(key, next); + batch.processArray(sessionKeys, function (keys, next) { + var multi = client.multi(); + keys.forEach(function (key) { + multi.del(key); + }); + multi.exec(next); + }, { + batch: 1000, }, next); }, ], function (err) { From 2ab028a4bc129f4b5fa4087c467f8220906908f3 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 20 Apr 2017 18:15:06 -0400 Subject: [PATCH 218/316] update upgrade script to use batch --- src/upgrades/1.4.6/delete_sessions.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/upgrades/1.4.6/delete_sessions.js b/src/upgrades/1.4.6/delete_sessions.js index fd4004ec9e..3e143ffed2 100644 --- a/src/upgrades/1.4.6/delete_sessions.js +++ b/src/upgrades/1.4.6/delete_sessions.js @@ -17,6 +17,7 @@ module.exports = { function (next) { if (isRedisSessionStore) { var rdb = require.main.require('./src/database/redis'); + var batch = require.main.require('./src/batch'); var client = rdb.connect(); async.waterfall([ function (next) { @@ -25,9 +26,15 @@ module.exports = { function (sessionKeys, next) { progress.total = sessionKeys.length; - async.eachSeries(sessionKeys, function (key, next) { - progress.incr(); - client.del(key, next); + batch.processArray(sessionKeys, function (keys, next) { + var multi = client.multi(); + keys.forEach(function (key) { + progress.incr(); + multi.del(key); + }); + multi.exec(next); + }, { + batch: 1000, }, next); }, ], function (err) { From 30619a23c1a9674dac2cd6deb2c6599d60aaeba1 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Fri, 21 Apr 2017 09:22:17 +0000 Subject: [PATCH 219/316] Latest translations and fallbacks --- public/language/de/admin/general/dashboard.json | 8 ++++---- public/language/el/admin/admin.json | 8 ++++---- public/language/el/category.json | 10 +++++----- public/language/el/reset_password.json | 10 +++++----- public/language/zh-CN/admin/extend/plugins.json | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/public/language/de/admin/general/dashboard.json b/public/language/de/admin/general/dashboard.json index f0ae877ce3..c417fa64a6 100644 --- a/public/language/de/admin/general/dashboard.json +++ b/public/language/de/admin/general/dashboard.json @@ -18,16 +18,16 @@ "running-version": "Es läuft NodeBB v%1.", "keep-updated": "Stelle sicher, dass dein NodeBB immer auf dem neuesten Stand für die neuesten Sicherheits-Patches und Bug-fixes ist.", "up-to-date": "

    System ist aktuell

    ", - "upgrade-available": "

    Version (v%1) wurde veröffentlicht. Beachte um ein NodeBB Upgrade durchzuführen.

    ", - "prerelease-upgrade-available": "

    Das ist eine veraltete pre-release Version von NodeBB. Version (v%1) wurde veröffentlicht. Beachte um ein NodeBB Upgrade durchzuführen.

    ", + "upgrade-available": "

    Version (v%1) wurde veröffentlicht. Ziehen Sie einNodeBB Upgrade in Betracht.

    ", + "prerelease-upgrade-available": "

    Das ist eine veraltete pre-release Version von NodeBB. Version (v%1) wurde veröffentlicht. Ziehen Sie einNodeBB Upgrade in Betracht.

    ", "prerelease-warning": "

    Das ist eine pre-release Version von NodeBB. Es können ungewollte Fehler auftreten.

    ", "running-in-development": "Das Forum wurde im Entwicklermodus gestartet. Das Forum könnte potenziellen Gefahren ausgeliefert sein. Bitte kontaktieren Sie Ihren Systemadministrator.", "notices": "Hinweise", - "restart-not-required": "Neustart nicht benötigt", + "restart-not-required": "Kein Neustart benötigt", "restart-required": "Neustart benötigt", "search-plugin-installed": "Such-Plugin installiert", - "search-plugin-not-installed": "Such-Plugin nicht installiert", + "search-plugin-not-installed": "Kein Such-Plugin installiert", "search-plugin-tooltip": "Installieren Sie ein Such-Plugin auf der Plugin seite um die Such-Funktionalität zu aktivieren", "control-panel": "Systemsteuerung", diff --git a/public/language/el/admin/admin.json b/public/language/el/admin/admin.json index 9c01f56006..cdd706e61c 100644 --- a/public/language/el/admin/admin.json +++ b/public/language/el/admin/admin.json @@ -1,7 +1,7 @@ { - "alert.confirm-reload": "Are you sure you wish to reload NodeBB?", - "alert.confirm-restart": "Are you sure you wish to restart NodeBB?", + "alert.confirm-reload": "Είσαι σίγουρος/η πως θέλεις να επαναφορτώσεις το NodeBB?", + "alert.confirm-restart": "Είσαι σίγουρος/η πως θέλεις να επανεκκινήσεις το NodeBB?", - "acp-title": "%1 | NodeBB Admin Control Panel", - "settings-header-contents": "Contents" + "acp-title": "%1 | NodeBB Πίνακας ελέγχου", + "settings-header-contents": "Περιεχόμενα" } \ No newline at end of file diff --git a/public/language/el/category.json b/public/language/el/category.json index 33820bce02..d194f5f37e 100644 --- a/public/language/el/category.json +++ b/public/language/el/category.json @@ -1,17 +1,17 @@ { - "category": "Category", - "subcategories": "Subcategories", + "category": "Κατηγορία", + "subcategories": "Υποκατηγορίες", "new_topic_button": "Νέο Θέμα", - "guest-login-post": "Log in to post", + "guest-login-post": "Συνδέσου για να δημοσιεύσεις", "no_topics": "Δεν υπάρχουν θέματα σε αυτή την κατηγορία.
    Γιατί δεν δοκιμάζεις να δημοσιεύσεις ένα εσύ;", "browsing": "περιηγούνται", "no_replies": "Κανείς δεν έχει απαντήσει", - "no_new_posts": "No new posts.", + "no_new_posts": "Δεν υπάρχουν νέες δημοσιεύσεις", "share_this_category": "Μοιράσου αυτή την κατηγορία", "watch": "Watch", "ignore": "Αγνόηση", "watching": "Watching", - "ignoring": "Ignoring", + "ignoring": "Αγνόησε", "watching.description": "Show topics in unread", "ignoring.description": "Do not show topics in unread", "watch.message": "You are now watching updates from this category and all subcategories", diff --git a/public/language/el/reset_password.json b/public/language/el/reset_password.json index 4df4164ac3..beee413045 100644 --- a/public/language/el/reset_password.json +++ b/public/language/el/reset_password.json @@ -9,9 +9,9 @@ "repeat_password": "Επιβεβαίωση Κωδικού", "enter_email": "Παρακαλώ γράψε την διεύθυνση email σου και θα σου στείλουμε ένα email με οδηγίες για το πως να επαναφέρεις τον λογαριασμό σου.", "enter_email_address": "Εισαγωγή Διεύθυνσης Email", - "password_reset_sent": "Password Reset Sent", - "invalid_email": "Invalid Email / Email does not exist!", - "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_expired": "Your password has expired, please choose a new password" + "password_reset_sent": "Η επαναφορά κωδικού στάλθηκε", + "invalid_email": "Λάθος Email ή το Email δεν υπάρχει!", + "password_too_short": "Ο κωδικός είναι πολύ μικρός, παρακαλώ επέλεξε διαφορετικό.", + "passwords_do_not_match": "Οι κωδικοί δεν ταιριάζουν μεταξύ τους.", + "password_expired": "Ο κωδικός έληξε, παρακαλώ επίλεξε νέο κωδικό" } \ No newline at end of file diff --git a/public/language/zh-CN/admin/extend/plugins.json b/public/language/zh-CN/admin/extend/plugins.json index 21446a0de9..ab2173dda1 100644 --- a/public/language/zh-CN/admin/extend/plugins.json +++ b/public/language/zh-CN/admin/extend/plugins.json @@ -1,6 +1,6 @@ { "installed": "已安装", - "active": "激活", + "active": "已启用", "inactive": "未生效", "out-of-date": "已过期", "none-found": "无插件。", From 73700ed747685ce2470b37b1fb757cfef380a462 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 21 Apr 2017 13:48:43 -0400 Subject: [PATCH 220/316] closes #5617 --- public/src/client/category.js | 5 +-- public/src/client/topic.js | 7 +--- public/src/modules/navigator.js | 69 +++++++++++++++++++++++---------- 3 files changed, 51 insertions(+), 30 deletions(-) diff --git a/public/src/client/category.js b/public/src/client/category.js index e2dfcd9924..acbf031697 100644 --- a/public/src/client/category.js +++ b/public/src/client/category.js @@ -41,10 +41,6 @@ define('forum/category', [ sort.handleSort('categoryTopicSort', 'user.setCategorySort', 'category/' + ajaxify.data.slug); - if (!config.usePagination) { - navigator.init('[component="category/topic"]', ajaxify.data.topic_count, Category.toTop, Category.toBottom, Category.navigatorCallback); - } - enableInfiniteLoadingOrPagination(); $('[component="category"]').on('click', '[component="topic/header"]', function () { @@ -187,6 +183,7 @@ define('forum/category', [ function enableInfiniteLoadingOrPagination() { if (!config.usePagination) { + navigator.init('[component="category/topic"]', ajaxify.data.topic_count, Category.toTop, Category.toBottom, Category.navigatorCallback); infinitescroll.init($('[component="category"]'), Category.loadMoreTopics); } else { navigator.disable(); diff --git a/public/src/client/topic.js b/public/src/client/topic.js index d620da1e70..832e6e9600 100644 --- a/public/src/client/topic.js +++ b/public/src/client/topic.js @@ -150,14 +150,13 @@ define('forum/topic', [ return navigator.scrollToPostIndex(postIndex, true, 0); } } else if (bookmark && (!config.usePagination || (config.usePagination && ajaxify.data.pagination.currentPage === 1)) && ajaxify.data.postcount > ajaxify.data.bookmarkThreshold) { - navigator.update(0); app.alert({ alert_id: 'bookmark', message: '[[topic:bookmark_instructions]]', timeout: 0, type: 'info', clickfn: function () { - navigator.scrollToPost(parseInt(bookmark - 1, 10), true); + navigator.scrollToIndex(parseInt(bookmark - 1, 10), true); }, closefn: function () { localStorage.removeItem('topic:' + tid + ':bookmark'); @@ -166,8 +165,6 @@ define('forum/topic', [ setTimeout(function () { app.removeAlert('bookmark'); }, 10000); - } else { - navigator.update(0); } } @@ -204,7 +201,7 @@ define('forum/topic', [ var toPost = $('[component="post"][data-pid="' + toPid + '"]'); if (toPost.length) { e.preventDefault(); - navigator.scrollToPost(toPost.attr('data-index'), true); + navigator.scrollToIndex(toPost.attr('data-index'), true); return false; } }); diff --git a/public/src/modules/navigator.js b/public/src/modules/navigator.js index 68c0359755..9ca7e94fa9 100644 --- a/public/src/modules/navigator.js +++ b/public/src/modules/navigator.js @@ -1,7 +1,5 @@ - 'use strict'; - define('navigator', ['forum/pagination', 'components'], function (pagination, components) { var navigator = {}; var index = 1; @@ -55,6 +53,7 @@ define('navigator', ['forum/pagination', 'components'], function (pagination, co }); navigator.setCount(count); + navigator.update(0); }; function generateUrl(index) { @@ -183,7 +182,7 @@ define('navigator', ['forum/pagination', 'components'], function (pagination, co navigator.scrollTop = function (index) { if ($(navigator.selector + '[data-index="' + index + '"]').length) { - navigator.scrollToPost(index, true); + navigator.scrollToIndex(index, true); } else { ajaxify.go(generateUrl()); } @@ -193,44 +192,67 @@ define('navigator', ['forum/pagination', 'components'], function (pagination, co if (parseInt(index, 10) < 0) { return; } + if ($(navigator.selector + '[data-index="' + index + '"]').length) { - navigator.scrollToPost(index, true); + navigator.scrollToIndex(index, true); } else { index = parseInt(index, 10) + 1; ajaxify.go(generateUrl(index)); } }; - navigator.scrollToPost = function (postIndex, highlight, duration) { - if (!utils.isNumber(postIndex) || !components.get('topic').length) { + navigator.scrollToPost = function (index, highlight, duration) { + console.log('[navigator.scrollToPost] deprecated please use, navigator.scrollToIndex'); + navigator.scrollToIndex(index, highlight, duration); + }; + + navigator.scrollToIndex = function (index, highlight, duration) { + var inTopic = !!components.get('topic').length; + var inCategory = !!components.get('category').length; + + if (!utils.isNumber(index) || (!inTopic && !inCategory)) { return; } duration = duration !== undefined ? duration : 400; navigator.scrollActive = true; - if (components.get('post/anchor', postIndex).length) { - return navigator.scrollToPostIndex(postIndex, highlight, duration); + // if in topic and item already on page + if (inTopic && components.get('post/anchor', index).length) { + return navigator.scrollToPostIndex(index, highlight, duration); + } + + // if in category and item alreay on page + if (inCategory && $('[component="category/topic"][data-index="' + index + '"]').length) { + return navigator.scrollToTopicIndex(index, highlight, duration); + } + + if (!config.usePagination) { + navigator.scrollActive = false; + index = parseInt(index, 10) + 1; + ajaxify.go(generateUrl(index)); + return; } - if (config.usePagination) { - var index = postIndex; + var scrollMethod = inTopic ? navigator.scrollToPostIndex : navigator.scrollToTopicIndex; + if (inTopic) { if (config.topicPostSort === 'most_votes' || config.topicPostSort === 'newest_to_oldest') { index = ajaxify.data.postcount - index; } - var page = Math.max(1, Math.ceil(index / config.postsPerPage)); - - if (parseInt(page, 10) !== ajaxify.data.pagination.currentPage) { - pagination.loadPage(page, function () { - navigator.scrollToPostIndex(postIndex, highlight, duration); - }); - } else { - navigator.scrollToPostIndex(postIndex, highlight, duration); + } else if (inCategory) { + if (config.categoryTopicSort === 'most_posts' || config.categoryTopicSort === 'oldest_to_newest') { + index = ajaxify.data.ajaxify.data.topic_count - index; } + } + + var page = Math.max(1, Math.ceil(index / config.postsPerPage)); + + if (parseInt(page, 10) !== ajaxify.data.pagination.currentPage) { + pagination.loadPage(page, function () { + scrollMethod(index, highlight, duration); + }); } else { - navigator.scrollActive = false; - postIndex = parseInt(postIndex, 10) + 1; - ajaxify.go(generateUrl(postIndex)); + scrollMethod(index, highlight, duration); } }; @@ -239,6 +261,11 @@ define('navigator', ['forum/pagination', 'components'], function (pagination, co navigator.scrollToElement(scrollTo, highlight, duration); }; + navigator.scrollToTopicIndex = function (topicIndex, highlight, duration) { + var scrollTo = $('[component="category/topic"][data-index="' + topicIndex + '"]'); + navigator.scrollToElement(scrollTo, highlight, duration); + }; + navigator.scrollToElement = function (scrollTo, highlight, duration) { if (!scrollTo.length) { navigator.scrollActive = false; From 75e5f2da7441326cfe8f24f694f651ccc31ee669 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 21 Apr 2017 21:36:42 -0400 Subject: [PATCH 221/316] closes #5485 --- public/src/client/footer.js | 22 ++++++------ src/socket.io/user.js | 1 + src/topics/follow.js | 40 ++++++++++++---------- src/topics/unread.js | 67 ++++++++++++++++++++----------------- 4 files changed, 70 insertions(+), 60 deletions(-) diff --git a/public/src/client/footer.js b/public/src/client/footer.js index eb921f36c6..dc10e84d1f 100644 --- a/public/src/client/footer.js +++ b/public/src/client/footer.js @@ -6,14 +6,8 @@ define('forum/footer', ['notifications', 'chat', 'components', 'translator'], fu Chat.prepareDOM(); translator.prepareDOM(); - function updateUnreadTopicCount(count) { - $('#unread-count i') - .toggleClass('unread-count', count > 0) - .attr('data-content', count > 99 ? '99+' : count); - } - - function updateUnreadNewTopicCount(count) { - $('#unread-new-count i') + function updateUnreadTopicCount(url, count) { + $('#main-nav a[href="' + config.relative_path + url + '"] i') .toggleClass('unread-count', count > 0) .attr('data-content', count > 99 ? '99+' : count); } @@ -67,14 +61,20 @@ define('forum/footer', ['notifications', 'chat', 'components', 'translator'], fu return app.alert(err.message); } - updateUnreadTopicCount(data.unreadTopicCount); - updateUnreadNewTopicCount(data.unreadNewTopicCount); + updateUnreadCounters(data); + updateUnreadChatCount(data.unreadChatCount); Notifications.updateNotifCount(data.unreadNotificationCount); }); } - socket.on('event:unread.updateCount', updateUnreadTopicCount); + function updateUnreadCounters(data) { + updateUnreadTopicCount('/unread', data.unreadTopicCount); + updateUnreadTopicCount('/unread/new', data.unreadNewTopicCount); + updateUnreadTopicCount('/unread/watched', data.unreadWatchedTopicCount); + } + + socket.on('event:unread.updateCount', updateUnreadCounters); socket.on('event:unread.updateChatCount', updateUnreadChatCount); initUnreadTopics(); diff --git a/src/socket.io/user.js b/src/socket.io/user.js index e3f9e7d95d..a2e4413c9a 100644 --- a/src/socket.io/user.js +++ b/src/socket.io/user.js @@ -257,6 +257,7 @@ SocketUser.getUnreadCounts = function (socket, data, callback) { async.parallel({ unreadTopicCount: async.apply(topics.getTotalUnread, socket.uid), unreadNewTopicCount: async.apply(topics.getTotalUnread, socket.uid, 'new'), + unreadWatchedTopicCount: async.apply(topics.getTotalUnread, socket.uid, 'watched'), unreadChatCount: async.apply(messaging.getUnreadCount, socket.uid), unreadNotificationCount: async.apply(user.notifications.getUnreadCount, socket.uid), }, callback); diff --git a/src/topics/follow.js b/src/topics/follow.js index 5b5f368f6a..494872c794 100644 --- a/src/topics/follow.js +++ b/src/topics/follow.js @@ -162,27 +162,31 @@ module.exports = function (Topics) { }; Topics.filterWatchedTids = function (tids, uid, callback) { - db.sortedSetScores('uid:' + uid + ':followed_tids', tids, function (err, scores) { - if (err) { - return callback(err); - } - tids = tids.filter(function (tid, index) { - return tid && !!scores[index]; - }); - callback(null, tids); - }); + async.waterfall([ + function (next) { + db.sortedSetScores('uid:' + uid + ':followed_tids', tids, next); + }, + function (scores, next) { + tids = tids.filter(function (tid, index) { + return tid && !!scores[index]; + }); + next(null, tids); + }, + ], callback); }; Topics.filterNotIgnoredTids = function (tids, uid, callback) { - db.sortedSetScores('uid:' + uid + ':ignored_tids', tids, function (err, scores) { - if (err) { - return callback(err); - } - tids = tids.filter(function (tid, index) { - return tid && !scores[index]; - }); - callback(null, tids); - }); + async.waterfall([ + function (next) { + db.sortedSetScores('uid:' + uid + ':ignored_tids', tids, next); + }, + function (scores, next) { + tids = tids.filter(function (tid, index) { + return tid && !scores[index]; + }); + next(null, tids); + }, + ], callback); }; Topics.notifyFollowers = function (postData, exceptUid, callback) { diff --git a/src/topics/unread.js b/src/topics/unread.js index 5f07171398..ef2f35f22c 100644 --- a/src/topics/unread.js +++ b/src/topics/unread.js @@ -22,7 +22,6 @@ module.exports = function (Topics) { }); }; - Topics.getUnreadTopics = function (params, callback) { var unreadTopics = { showSelect: true, @@ -71,18 +70,12 @@ module.exports = function (Topics) { if (uid === 0) { return callback(null, []); } + var cutoff = params.cutoff || Topics.unreadCutoff(); - var ignoredCids; async.waterfall([ function (next) { async.parallel({ - ignoredCids: function (next) { - if (params.filter === 'watched') { - return next(null, []); - } - user.getIgnoredCategories(uid, next); - }, ignoredTids: function (next) { user.getIgnoredTids(uid, 0, -1, next); }, @@ -102,8 +95,6 @@ module.exports = function (Topics) { return callback(null, []); } - ignoredCids = results.ignoredCids; - var userRead = {}; results.userScores.forEach(function (userItem) { userRead[userItem.value] = userItem.score; @@ -139,14 +130,14 @@ module.exports = function (Topics) { function (tids, next) { tids = tids.slice(0, 200); - filterTopics(uid, tids, params.cid, ignoredCids, params.filter, next); + filterTopics(uid, tids, params.cid, params.filter, next); }, ], callback); }; - function filterTopics(uid, tids, cid, ignoredCids, filter, callback) { - if (!Array.isArray(ignoredCids) || !tids.length) { + function filterTopics(uid, tids, cid, filter, callback) { + if (!tids.length) { return callback(null, tids); } @@ -165,13 +156,19 @@ module.exports = function (Topics) { } db.sortedSetScores('uid:' + uid + ':followed_tids', tids, next); }, + ignoredCids: function (next) { + if (filter === 'watched') { + return next(null, []); + } + user.getIgnoredCategories(uid, next); + }, }, next); }, function (results, next) { var topics = results.topics; tids = topics.filter(function (topic, index) { return topic && topic.cid && - (!!results.isTopicsFollowed[index] || ignoredCids.indexOf(topic.cid.toString()) === -1) && + (!!results.isTopicsFollowed[index] || results.ignoredCids.indexOf(topic.cid.toString()) === -1) && (!cid || parseInt(cid, 10) === parseInt(topic.cid, 10)); }).map(function (topic) { return topic.tid; @@ -185,16 +182,22 @@ module.exports = function (Topics) { callback = callback || function () {}; if (!uid || parseInt(uid, 10) === 0) { - return callback(); + return setImmediate(callback); } - Topics.getTotalUnread(uid, function (err, count) { - if (err) { - return callback(err); - } - require('../socket.io').in('uid_' + uid).emit('event:unread.updateCount', count); - callback(); - }); + async.waterfall([ + function (next) { + async.parallel({ + unreadTopicCount: async.apply(Topics.getTotalUnread, uid), + unreadNewTopicCount: async.apply(Topics.getTotalUnread, uid, 'new'), + unreadWatchedTopicCount: async.apply(Topics.getTotalUnread, uid, 'watched'), + }, next); + }, + function (results, next) { + require('../socket.io').in('uid_' + uid).emit('event:unread.updateCount', results); + setImmediate(next); + }, + ], callback); }; Topics.markAsUnreadForAll = function (tid, callback) { @@ -360,14 +363,16 @@ module.exports = function (Topics) { }; Topics.filterNewTids = function (tids, uid, callback) { - db.sortedSetScores('uid:' + uid + ':tids_read', tids, function (err, scores) { - if (err) { - return callback(err); - } - tids = tids.filter(function (tid, index) { - return tid && !scores[index]; - }); - callback(null, tids); - }); + async.waterfall([ + function (next) { + db.sortedSetScores('uid:' + uid + ':tids_read', tids, next); + }, + function (scores, next) { + tids = tids.filter(function (tid, index) { + return tid && !scores[index]; + }); + next(null, tids); + }, + ], callback); }; }; From d6703753da24c334df13d7fc9e08274db56a8a60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 21 Apr 2017 21:41:37 -0400 Subject: [PATCH 222/316] up request dep --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6f8ae9706e..0c546c71da 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "promise-polyfill": "^6.0.2", "prompt": "^1.0.0", "redis": "~2.6.2", - "request": "^2.44.0", + "request": "2.81.0", "rimraf": "~2.5.0", "rss": "^1.0.0", "sanitize-html": "^1.13.0", From 3d39a577f4391957d89d2d54e29b98b337129a56 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 21 Apr 2017 23:55:57 -0400 Subject: [PATCH 223/316] style change --- src/database/redis.js | 293 +++++++++++++++++++++--------------------- 1 file changed, 144 insertions(+), 149 deletions(-) diff --git a/src/database/redis.js b/src/database/redis.js index 7fb61565e3..acc2541af6 100644 --- a/src/database/redis.js +++ b/src/database/redis.js @@ -1,166 +1,161 @@ 'use strict'; -(function (module) { - var winston = require('winston'); - var nconf = require('nconf'); - var semver = require('semver'); - var session = require('express-session'); - var redis; - var redisClient; - - module.questions = [ - { - name: 'redis:host', - description: 'Host IP or address of your Redis instance', - default: nconf.get('redis:host') || '127.0.0.1', - }, - { - name: 'redis:port', - description: 'Host port of your Redis instance', - default: nconf.get('redis:port') || 6379, - }, - { - name: 'redis:password', - description: 'Password of your Redis database', - hidden: true, - default: nconf.get('redis:password') || '', - before: function (value) { value = value || nconf.get('redis:password') || ''; return value; }, - }, - { - name: 'redis:database', - description: 'Which database to use (0..n)', - default: nconf.get('redis:database') || 0, - }, - ]; - - module.init = function (callback) { - try { - redis = require('redis'); - } catch (err) { - winston.error('Unable to initialize Redis! Is Redis installed? Error :' + err.message); - process.exit(); - } - - redisClient = module.connect(); - - module.client = redisClient; - - require('./redis/main')(redisClient, module); - require('./redis/hash')(redisClient, module); - require('./redis/sets')(redisClient, module); - require('./redis/sorted')(redisClient, module); - require('./redis/list')(redisClient, module); - - if (typeof callback === 'function') { - callback(); - } - }; - - module.initSessionStore = function (callback) { - var meta = require('../meta'); - var sessionStore = require('connect-redis')(session); - - module.sessionStore = new sessionStore({ - client: module.client, - ttl: meta.getSessionTTLSeconds(), +var _ = require('underscore'); +var winston = require('winston'); +var nconf = require('nconf'); +var semver = require('semver'); +var session = require('express-session'); +var redis = require('redis'); +var redisClient; + +var redisModule = module.exports; + +redisModule.questions = [ + { + name: 'redis:host', + description: 'Host IP or address of your Redis instance', + default: nconf.get('redis:host') || '127.0.0.1', + }, + { + name: 'redis:port', + description: 'Host port of your Redis instance', + default: nconf.get('redis:port') || 6379, + }, + { + name: 'redis:password', + description: 'Password of your Redis database', + hidden: true, + default: nconf.get('redis:password') || '', + before: function (value) { value = value || nconf.get('redis:password') || ''; return value; }, + }, + { + name: 'redis:database', + description: 'Which database to use (0..n)', + default: nconf.get('redis:database') || 0, + }, +]; + +redisModule.init = function (callback) { + + redisClient = redisModule.connect(); + + redisModule.client = redisClient; + + require('./redis/main')(redisClient, redisModule); + require('./redis/hash')(redisClient, redisModule); + require('./redis/sets')(redisClient, redisModule); + require('./redis/sorted')(redisClient, redisModule); + require('./redis/list')(redisClient, redisModule); + + if (typeof callback === 'function') { + callback(); + } +}; + +redisModule.initSessionStore = function (callback) { + var meta = require('../meta'); + var sessionStore = require('connect-redis')(session); + + redisModule.sessionStore = new sessionStore({ + client: redisModule.client, + ttl: meta.getSessionTTLSeconds(), + }); + + if (typeof callback === 'function') { + callback(); + } +}; + +redisModule.connect = function (options) { + var redis_socket_or_host = nconf.get('redis:host'); + var cxn; + + if (!redis) { + redis = require('redis'); + } + + options = options || {}; + + if (nconf.get('redis:password')) { + options.auth_pass = nconf.get('redis:password'); + } + + if (redis_socket_or_host && redis_socket_or_host.indexOf('/') >= 0) { + /* If redis.host contains a path name character, use the unix dom sock connection. ie, /tmp/redis.sock */ + cxn = redis.createClient(nconf.get('redis:host'), options); + } else { + /* Else, connect over tcp/ip */ + cxn = redis.createClient(nconf.get('redis:port'), nconf.get('redis:host'), options); + } + + cxn.on('error', function (err) { + winston.error(err.stack); + process.exit(1); + }); + + if (nconf.get('redis:password')) { + cxn.auth(nconf.get('redis:password')); + } + + var dbIdx = parseInt(nconf.get('redis:database'), 10); + if (dbIdx) { + cxn.select(dbIdx, function (error) { + if (error) { + winston.error('NodeBB could not connect to your Redis database. Redis returned the following error: ' + error.message); + process.exit(); + } }); + } - if (typeof callback === 'function') { - callback(); - } - }; - - module.connect = function (options) { - var redis_socket_or_host = nconf.get('redis:host'); - var cxn; + return cxn; +}; - if (!redis) { - redis = require('redis'); - } - - options = options || {}; - if (nconf.get('redis:password')) { - options.auth_pass = nconf.get('redis:password'); - } +redisModule.createIndices = function (callback) { + setImmediate(callback); +}; - if (redis_socket_or_host && redis_socket_or_host.indexOf('/') >= 0) { - /* If redis.host contains a path name character, use the unix dom sock connection. ie, /tmp/redis.sock */ - cxn = redis.createClient(nconf.get('redis:host'), options); - } else { - /* Else, connect over tcp/ip */ - cxn = redis.createClient(nconf.get('redis:port'), nconf.get('redis:host'), options); +redisModule.checkCompatibility = function (callback) { + redisModule.info(redisModule.client, function (err, info) { + if (err) { + return callback(err); } - cxn.on('error', function (err) { - winston.error(err.stack); - process.exit(1); - }); - - if (nconf.get('redis:password')) { - cxn.auth(nconf.get('redis:password')); + if (semver.lt(info.redis_version, '2.8.9')) { + return callback(new Error('Your Redis version is not new enough to support NodeBB, please upgrade Redis to v2.8.9 or higher.')); } - var dbIdx = parseInt(nconf.get('redis:database'), 10); - if (dbIdx) { - cxn.select(dbIdx, function (error) { - if (error) { - winston.error('NodeBB could not connect to your Redis database. Redis returned the following error: ' + error.message); - process.exit(); - } - }); + callback(); + }); +}; + +redisModule.close = function () { + redisClient.quit(); +}; + +redisModule.info = function (cxn, callback) { + if (!cxn) { + return callback(); + } + cxn.info(function (err, data) { + if (err) { + return callback(err); } - return cxn; - }; - - module.createIndices = function (callback) { - setImmediate(callback); - }; - - module.checkCompatibility = function (callback) { - module.info(module.client, function (err, info) { - if (err) { - return callback(err); + var lines = data.toString().split('\r\n').sort(); + var redisData = {}; + lines.forEach(function (line) { + var parts = line.split(':'); + if (parts[1]) { + redisData[parts[0]] = parts[1]; } - - if (semver.lt(info.redis_version, '2.8.9')) { - return callback(new Error('Your Redis version is not new enough to support NodeBB, please upgrade Redis to v2.8.9 or higher.')); - } - - callback(); }); - }; - module.close = function () { - redisClient.quit(); - }; - - module.info = function (cxn, callback) { - if (!cxn) { - return callback(); - } - cxn.info(function (err, data) { - if (err) { - return callback(err); - } - - var lines = data.toString().split('\r\n').sort(); - var redisData = {}; - lines.forEach(function (line) { - var parts = line.split(':'); - if (parts[1]) { - redisData[parts[0]] = parts[1]; - } - }); - - redisData.raw = JSON.stringify(redisData, null, 4); - redisData.redis = true; - - callback(null, redisData); - }); - }; + redisData.raw = JSON.stringify(redisData, null, 4); + redisData.redis = true; - module.helpers = module.helpers || {}; - module.helpers.redis = require('./redis/helpers'); -}(exports)); + callback(null, redisData); + }); +}; +redisModule.helpers = redisModule.helpers || {}; +redisModule.helpers.redis = require('./redis/helpers'); From 2029de4cc5ce2e4f45ae6c18e78ade6f48ee7805 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 21 Apr 2017 23:56:24 -0400 Subject: [PATCH 224/316] closes #5449 --- src/database/redis.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/database/redis.js b/src/database/redis.js index acc2541af6..a19c207993 100644 --- a/src/database/redis.js +++ b/src/database/redis.js @@ -80,6 +80,8 @@ redisModule.connect = function (options) { options.auth_pass = nconf.get('redis:password'); } + options = _.deepExtend(options, nconf.get('redis:options') || {}); + if (redis_socket_or_host && redis_socket_or_host.indexOf('/') >= 0) { /* If redis.host contains a path name character, use the unix dom sock connection. ie, /tmp/redis.sock */ cxn = redis.createClient(nconf.get('redis:host'), options); From c7929ec7d83383c99a4847a156b0e1bfab053c33 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Fri, 21 Apr 2017 22:10:25 -0600 Subject: [PATCH 225/316] Add option for disabling language autodetection --- install/data/defaults.json | 3 ++- public/language/en-GB/admin/general/languages.json | 3 ++- src/controllers/admin/languages.js | 1 + src/views/admin/general/languages.tpl | 11 +++++++++++ src/webserver.js | 2 +- 5 files changed, 17 insertions(+), 3 deletions(-) diff --git a/install/data/defaults.json b/install/data/defaults.json index c471db6b89..86e63882cd 100644 --- a/install/data/defaults.json +++ b/install/data/defaults.json @@ -35,5 +35,6 @@ "allowPrivateGroups": 1, "unreadCutoff": 2, "bookmarkThreshold": 5, - "topicsPerList": 20 + "topicsPerList": 20, + "autoDetectLang": 1 } diff --git a/public/language/en-GB/admin/general/languages.json b/public/language/en-GB/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/en-GB/admin/general/languages.json +++ b/public/language/en-GB/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
    Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/src/controllers/admin/languages.js b/src/controllers/admin/languages.js index 0ac4e98e99..e2d848ddae 100644 --- a/src/controllers/admin/languages.js +++ b/src/controllers/admin/languages.js @@ -18,6 +18,7 @@ languagesController.get = function (req, res, next) { res.render('admin/general/languages', { languages: languages, + autoDetectLang: parseInt(meta.config.autoDetectLang, 10) === 1, }); }); }; diff --git a/src/views/admin/general/languages.tpl b/src/views/admin/general/languages.tpl index 310d1a366d..747c5d43af 100644 --- a/src/views/admin/general/languages.tpl +++ b/src/views/admin/general/languages.tpl @@ -16,6 +16,17 @@
    + +
    +
    +
    + +
    +
    +
    diff --git a/src/webserver.js b/src/webserver.js index 3911846b67..412378883c 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -206,7 +206,7 @@ function setupAutoLocale(app, callback) { }); app.use(function (req, res, next) { - if (parseInt(req.uid, 10) > 0) { + if (parseInt(req.uid, 10) > 0 || parseInt(meta.config.autoDetectLang, 10) !== 1) { return next(); } From a2784738a403fb9573280040b4ae0c90b5534baf Mon Sep 17 00:00:00 2001 From: barisusakli Date: Sat, 22 Apr 2017 00:15:12 -0400 Subject: [PATCH 226/316] fix lint --- src/database/redis.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/database/redis.js b/src/database/redis.js index a19c207993..3647f147a9 100644 --- a/src/database/redis.js +++ b/src/database/redis.js @@ -36,7 +36,6 @@ redisModule.questions = [ ]; redisModule.init = function (callback) { - redisClient = redisModule.connect(); redisModule.client = redisClient; From b7b65c7d63d08bdf7a785806c6e92100271f2d57 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Sat, 22 Apr 2017 00:19:23 -0400 Subject: [PATCH 227/316] change style --- src/database/mongo.js | 433 +++++++++++++++++++++--------------------- 1 file changed, 217 insertions(+), 216 deletions(-) diff --git a/src/database/mongo.js b/src/database/mongo.js index be13a185e8..aadd5dbfe7 100644 --- a/src/database/mongo.js +++ b/src/database/mongo.js @@ -1,239 +1,240 @@ 'use strict'; -(function (module) { - var winston = require('winston'); - var async = require('async'); - var nconf = require('nconf'); - var session = require('express-session'); - var _ = require('underscore'); - var semver = require('semver'); - var db; - - _.mixin(require('underscore.deep')); - - module.questions = [ - { - name: 'mongo:host', - description: 'Host IP or address of your MongoDB instance', - default: nconf.get('mongo:host') || '127.0.0.1', - }, - { - name: 'mongo:port', - description: 'Host port of your MongoDB instance', - default: nconf.get('mongo:port') || 27017, - }, - { - name: 'mongo:username', - description: 'MongoDB username', - default: nconf.get('mongo:username') || '', - }, - { - name: 'mongo:password', - description: 'Password of your MongoDB database', - hidden: true, - default: nconf.get('mongo:password') || '', - before: function (value) { value = value || nconf.get('mongo:password') || ''; return value; }, - }, - { - name: 'mongo:database', - description: 'MongoDB database name', - default: nconf.get('mongo:database') || 'nodebb', - }, - ]; - - module.helpers = module.helpers || {}; - module.helpers.mongo = require('./mongo/helpers'); - - module.init = function (callback) { - callback = callback || function () { }; - - var mongoClient = require('mongodb').MongoClient; - - var usernamePassword = ''; - if (nconf.get('mongo:username') && nconf.get('mongo:password')) { - usernamePassword = nconf.get('mongo:username') + ':' + encodeURIComponent(nconf.get('mongo:password')) + '@'; - } - // Sensible defaults for Mongo, if not set - if (!nconf.get('mongo:host')) { - nconf.set('mongo:host', '127.0.0.1'); - } - if (!nconf.get('mongo:port')) { - nconf.set('mongo:port', 27017); - } - if (!nconf.get('mongo:database')) { - nconf.set('mongo:database', 'nodebb'); - } +var winston = require('winston'); +var async = require('async'); +var nconf = require('nconf'); +var session = require('express-session'); +var _ = require('underscore'); +var semver = require('semver'); +var db; + +_.mixin(require('underscore.deep')); + +var mongoModule = module.exports; + +mongoModule.questions = [ + { + name: 'mongo:host', + description: 'Host IP or address of your MongoDB instance', + default: nconf.get('mongo:host') || '127.0.0.1', + }, + { + name: 'mongo:port', + description: 'Host port of your MongoDB instance', + default: nconf.get('mongo:port') || 27017, + }, + { + name: 'mongo:username', + description: 'MongoDB username', + default: nconf.get('mongo:username') || '', + }, + { + name: 'mongo:password', + description: 'Password of your MongoDB database', + hidden: true, + default: nconf.get('mongo:password') || '', + before: function (value) { value = value || nconf.get('mongo:password') || ''; return value; }, + }, + { + name: 'mongo:database', + description: 'MongoDB database name', + default: nconf.get('mongo:database') || 'nodebb', + }, +]; + +mongoModule.helpers = mongoModule.helpers || {}; +mongoModule.helpers.mongo = require('./mongo/helpers'); + +mongoModule.init = function (callback) { + callback = callback || function () { }; + + var mongoClient = require('mongodb').MongoClient; + + var usernamePassword = ''; + if (nconf.get('mongo:username') && nconf.get('mongo:password')) { + usernamePassword = nconf.get('mongo:username') + ':' + encodeURIComponent(nconf.get('mongo:password')) + '@'; + } + + // Sensible defaults for Mongo, if not set + if (!nconf.get('mongo:host')) { + nconf.set('mongo:host', '127.0.0.1'); + } + if (!nconf.get('mongo:port')) { + nconf.set('mongo:port', 27017); + } + if (!nconf.get('mongo:database')) { + nconf.set('mongo:database', 'nodebb'); + } + + var hosts = nconf.get('mongo:host').split(','); + var ports = nconf.get('mongo:port').toString().split(','); + var servers = []; + + for (var i = 0; i < hosts.length; i += 1) { + servers.push(hosts[i] + ':' + ports[i]); + } + + var connString = 'mongodb://' + usernamePassword + servers.join() + '/' + nconf.get('mongo:database'); + + var connOptions = { + poolSize: 10, + reconnectTries: 3600, + reconnectInterval: 1000, + autoReconnect: true, + }; - var hosts = nconf.get('mongo:host').split(','); - var ports = nconf.get('mongo:port').toString().split(','); - var servers = []; + connOptions = _.deepExtend(connOptions, nconf.get('mongo:options') || {}); - for (var i = 0; i < hosts.length; i += 1) { - servers.push(hosts[i] + ':' + ports[i]); + mongoClient.connect(connString, connOptions, function (err, _db) { + if (err) { + winston.error('NodeBB could not connect to your Mongo database. Mongo returned the following error: ' + err.message); + return callback(err); } - var connString = 'mongodb://' + usernamePassword + servers.join() + '/' + nconf.get('mongo:database'); - - var connOptions = { - poolSize: 10, - reconnectTries: 3600, - reconnectInterval: 1000, - autoReconnect: true, - }; - - connOptions = _.deepExtend(connOptions, nconf.get('mongo:options') || {}); - - mongoClient.connect(connString, connOptions, function (err, _db) { - if (err) { - winston.error('NodeBB could not connect to your Mongo database. Mongo returned the following error: ' + err.message); - return callback(err); - } - - db = _db; - - module.client = db; - - require('./mongo/main')(db, module); - require('./mongo/hash')(db, module); - require('./mongo/sets')(db, module); - require('./mongo/sorted')(db, module); - require('./mongo/list')(db, module); - - if (nconf.get('mongo:password') && nconf.get('mongo:username')) { - db.authenticate(nconf.get('mongo:username'), nconf.get('mongo:password'), function (err) { - callback(err); - }); - } else { - winston.warn('You have no mongo password setup!'); - callback(); - } - }); - }; - - module.initSessionStore = function (callback) { - var meta = require('../meta'); - var sessionStore; + db = _db; - var ttl = meta.getSessionTTLSeconds(); + mongoModule.client = db; - if (nconf.get('redis')) { - sessionStore = require('connect-redis')(session); - var rdb = require('./redis'); - rdb.client = rdb.connect(); + require('./mongo/main')(db, mongoModule); + require('./mongo/hash')(db, mongoModule); + require('./mongo/sets')(db, mongoModule); + require('./mongo/sorted')(db, mongoModule); + require('./mongo/list')(db, mongoModule); - module.sessionStore = new sessionStore({ - client: rdb.client, - ttl: ttl, - }); - } else if (nconf.get('mongo')) { - sessionStore = require('connect-mongo')(session); - module.sessionStore = new sessionStore({ - db: db, - ttl: ttl, + if (nconf.get('mongo:password') && nconf.get('mongo:username')) { + db.authenticate(nconf.get('mongo:username'), nconf.get('mongo:password'), function (err) { + callback(err); }); + } else { + winston.warn('You have no mongo password setup!'); + callback(); } + }); +}; - callback(); - }; +mongoModule.initSessionStore = function (callback) { + var meta = require('../meta'); + var sessionStore; - module.createIndices = function (callback) { - function createIndex(collection, index, options, callback) { - module.client.collection(collection).createIndex(index, options, callback); - } + var ttl = meta.getSessionTTLSeconds(); - if (!module.client) { - winston.warn('[database/createIndices] database not initialized'); - return callback(); - } + if (nconf.get('redis')) { + sessionStore = require('connect-redis')(session); + var rdb = require('./redis'); + rdb.client = rdb.connect(); - winston.info('[database] Checking database indices.'); - async.series([ - async.apply(createIndex, 'objects', { _key: 1, score: -1 }, { background: true }), - async.apply(createIndex, 'objects', { _key: 1, value: -1 }, { background: true, unique: true, sparse: true }), - async.apply(createIndex, 'objects', { expireAt: 1 }, { expireAfterSeconds: 0, background: true }), - ], function (err) { - if (err) { - winston.error('Error creating index ' + err.message); - return callback(err); - } - winston.info('[database] Checking database indices done!'); - callback(); + mongoModule.sessionStore = new sessionStore({ + client: rdb.client, + ttl: ttl, }); - }; - - module.checkCompatibility = function (callback) { - var mongoPkg = require('mongodb/package.json'); - - if (semver.lt(mongoPkg.version, '2.0.0')) { - return callback(new Error('The `mongodb` package is out-of-date, please run `./nodebb setup` again.')); + } else if (nconf.get('mongo')) { + sessionStore = require('connect-mongo')(session); + mongoModule.sessionStore = new sessionStore({ + db: db, + ttl: ttl, + }); + } + + callback(); +}; + +mongoModule.createIndices = function (callback) { + function createIndex(collection, index, options, callback) { + mongoModule.client.collection(collection).createIndex(index, options, callback); + } + + if (!mongoModule.client) { + winston.warn('[database/createIndices] database not initialized'); + return callback(); + } + + winston.info('[database] Checking database indices.'); + async.series([ + async.apply(createIndex, 'objects', { _key: 1, score: -1 }, { background: true }), + async.apply(createIndex, 'objects', { _key: 1, value: -1 }, { background: true, unique: true, sparse: true }), + async.apply(createIndex, 'objects', { expireAt: 1 }, { expireAfterSeconds: 0, background: true }), + ], function (err) { + if (err) { + winston.error('Error creating index ' + err.message); + return callback(err); } - + winston.info('[database] Checking database indices done!'); callback(); - }; - - module.info = function (db, callback) { - if (!db) { - return callback(); - } - async.parallel({ - serverStatus: function (next) { - db.command({ serverStatus: 1 }, next); - }, - stats: function (next) { - db.command({ dbStats: 1 }, next); - }, - listCollections: function (next) { - db.listCollections().toArray(function (err, items) { - if (err) { - return next(err); - } - async.map(items, function (collection, next) { - db.collection(collection.name).stats(next); - }, next); - }); - }, - }, function (err, results) { - if (err) { - return callback(err); - } - var stats = results.stats; - var scale = 1024 * 1024; - - results.listCollections = results.listCollections.map(function (collectionInfo) { - return { - name: collectionInfo.ns, - count: collectionInfo.count, - size: collectionInfo.size, - avgObjSize: collectionInfo.avgObjSize, - storageSize: collectionInfo.storageSize, - totalIndexSize: collectionInfo.totalIndexSize, - indexSizes: collectionInfo.indexSizes, - }; + }); +}; + +mongoModule.checkCompatibility = function (callback) { + var mongoPkg = require('mongodb/package.json'); + + if (semver.lt(mongoPkg.version, '2.0.0')) { + return callback(new Error('The `mongodb` package is out-of-date, please run `./nodebb setup` again.')); + } + + callback(); +}; + +mongoModule.info = function (db, callback) { + if (!db) { + return callback(); + } + async.parallel({ + serverStatus: function (next) { + db.command({ serverStatus: 1 }, next); + }, + stats: function (next) { + db.command({ dbStats: 1 }, next); + }, + listCollections: function (next) { + db.listCollections().toArray(function (err, items) { + if (err) { + return next(err); + } + async.map(items, function (collection, next) { + db.collection(collection.name).stats(next); + }, next); }); - - stats.mem = results.serverStatus.mem; - stats.collectionData = results.listCollections; - stats.network = results.serverStatus.network; - stats.raw = JSON.stringify(stats, null, 4); - - stats.avgObjSize = stats.avgObjSize.toFixed(2); - stats.dataSize = (stats.dataSize / scale).toFixed(2); - stats.storageSize = (stats.storageSize / scale).toFixed(2); - stats.fileSize = stats.fileSize ? (stats.fileSize / scale).toFixed(2) : 0; - stats.indexSize = (stats.indexSize / scale).toFixed(2); - stats.storageEngine = results.serverStatus.storageEngine ? results.serverStatus.storageEngine.name : 'mmapv1'; - stats.host = results.serverStatus.host; - stats.version = results.serverStatus.version; - stats.uptime = results.serverStatus.uptime; - stats.mongo = true; - - callback(null, stats); + }, + }, function (err, results) { + if (err) { + return callback(err); + } + var stats = results.stats; + var scale = 1024 * 1024; + + results.listCollections = results.listCollections.map(function (collectionInfo) { + return { + name: collectionInfo.ns, + count: collectionInfo.count, + size: collectionInfo.size, + avgObjSize: collectionInfo.avgObjSize, + storageSize: collectionInfo.storageSize, + totalIndexSize: collectionInfo.totalIndexSize, + indexSizes: collectionInfo.indexSizes, + }; }); - }; - module.close = function () { - db.close(); - }; -}(exports)); + stats.mem = results.serverStatus.mem; + stats.collectionData = results.listCollections; + stats.network = results.serverStatus.network; + stats.raw = JSON.stringify(stats, null, 4); + + stats.avgObjSize = stats.avgObjSize.toFixed(2); + stats.dataSize = (stats.dataSize / scale).toFixed(2); + stats.storageSize = (stats.storageSize / scale).toFixed(2); + stats.fileSize = stats.fileSize ? (stats.fileSize / scale).toFixed(2) : 0; + stats.indexSize = (stats.indexSize / scale).toFixed(2); + stats.storageEngine = results.serverStatus.storageEngine ? results.serverStatus.storageEngine.name : 'mmapv1'; + stats.host = results.serverStatus.host; + stats.version = results.serverStatus.version; + stats.uptime = results.serverStatus.uptime; + stats.mongo = true; + + callback(null, stats); + }); +}; + +mongoModule.close = function () { + db.close(); +}; From ba51a6c74610d7614a323e442ee1ed62c98c5ff9 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Sat, 22 Apr 2017 00:33:49 -0400 Subject: [PATCH 228/316] fix underscore --- src/database/redis.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/database/redis.js b/src/database/redis.js index 3647f147a9..0127a61600 100644 --- a/src/database/redis.js +++ b/src/database/redis.js @@ -8,6 +8,8 @@ var session = require('express-session'); var redis = require('redis'); var redisClient; +_.mixin(require('underscore.deep')); + var redisModule = module.exports; redisModule.questions = [ From 6432c02ab80f0832539c02cbc1843a6d755a0a01 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Fri, 21 Apr 2017 23:54:59 -0600 Subject: [PATCH 229/316] Add tests --- test/locale-detect.js | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 test/locale-detect.js diff --git a/test/locale-detect.js b/test/locale-detect.js new file mode 100644 index 0000000000..84ea5af59c --- /dev/null +++ b/test/locale-detect.js @@ -0,0 +1,41 @@ +'use strict'; + +var assert = require('assert'); +var nconf = require('nconf'); +var request = require('request'); + +var meta = require('../src/meta'); + +describe('Language detection', function () { + it('should detect the language for a guest', function (done) { + request(nconf.get('url') + '/api/config', { + headers: { + 'Accept-Language': 'de-DE,de;q=0.5', + }, + }, function (err, res, body) { + assert.ifError(err); + assert.ok(body); + + assert.strictEqual(JSON.parse(body).userLang, 'de'); + done(); + }); + }); + + it('should do nothing when disabled', function (done) { + meta.configs.set('autoDetectLang', 0, function (err) { + assert.ifError(err); + + request(nconf.get('url') + '/api/config', { + headers: { + 'Accept-Language': 'de-DE,de;q=0.5', + }, + }, function (err, res, body) { + assert.ifError(err); + assert.ok(body); + + assert.strictEqual(JSON.parse(body).userLang, 'en-GB'); + done(); + }); + }); + }); +}); From 64bf542d1705eb3cb78e6a9a573ab8eb2466d1a0 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Fri, 21 Apr 2017 23:55:58 -0600 Subject: [PATCH 230/316] Fix HTML `lang` attribute using the `defaultLang` Themes need `lang="{function.localeToHTML, userLang, defaultLang}"` in their header.tpl file --- public/src/modules/helpers.js | 3 ++- src/middleware/header.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/public/src/modules/helpers.js b/public/src/modules/helpers.js index 3f02a86758..e1cf620607 100644 --- a/public/src/modules/helpers.js +++ b/public/src/modules/helpers.js @@ -176,7 +176,8 @@ }).join(''); }; - helpers.localeToHTML = function (locale) { + helpers.localeToHTML = function (locale, fallback) { + locale = locale || fallback || 'en-GB'; return locale.replace('_', '-'); }; diff --git a/src/middleware/header.js b/src/middleware/header.js index 70c0755def..0eb9cc9a1f 100644 --- a/src/middleware/header.js +++ b/src/middleware/header.js @@ -133,6 +133,7 @@ module.exports = function (middleware) { templateValues.customJS = templateValues.useCustomJS ? meta.config.customJS : ''; templateValues.maintenanceHeader = parseInt(meta.config.maintenanceMode, 10) === 1 && !results.isAdmin; templateValues.defaultLang = meta.config.defaultLang || 'en-GB'; + templateValues.userLang = res.locals.config.userLang; templateValues.privateUserInfo = parseInt(meta.config.privateUserInfo, 10) === 1; templateValues.privateTagListing = parseInt(meta.config.privateTagListing, 10) === 1; From 94a9a6ad39f5e8602505029995fd14677d0f7252 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Sat, 22 Apr 2017 09:22:41 +0000 Subject: [PATCH 231/316] Latest translations and fallbacks --- .../language/el/admin/advanced/database.json | 4 +- .../el/admin/settings/notifications.json | 4 +- public/language/el/global.json | 10 +- public/language/el/groups.json | 22 ++--- public/language/el/pages.json | 14 +-- public/language/el/unread.json | 6 +- .../language/es/admin/advanced/database.json | 22 ++--- .../language/es/admin/appearance/skins.json | 14 +-- .../language/es/admin/appearance/themes.json | 18 ++-- .../language/es/admin/development/info.json | 6 +- public/language/es/admin/extend/plugins.json | 62 ++++++------ .../language/es/admin/general/dashboard.json | 96 +++++++++---------- .../language/es/admin/general/homepage.json | 8 +- public/language/it/global.json | 4 +- public/language/it/user.json | 12 +-- public/language/ja/admin/menu.json | 2 +- .../zh-CN/admin/settings/general.json | 2 +- 17 files changed, 153 insertions(+), 153 deletions(-) diff --git a/public/language/el/admin/advanced/database.json b/public/language/el/admin/advanced/database.json index f7db6220ee..a900083d84 100644 --- a/public/language/el/admin/advanced/database.json +++ b/public/language/el/admin/advanced/database.json @@ -5,7 +5,7 @@ "uptime-days": "Uptime in Days", "mongo": "Mongo", - "mongo.version": "MongoDB Version", + "mongo.version": "Έκδοση MongoDB", "mongo.storage-engine": "Storage Engine", "mongo.collections": "Collections", "mongo.objects": "Objects", @@ -20,7 +20,7 @@ "mongo.raw-info": "MongoDB Raw Info", "redis": "Redis", - "redis.version": "Redis Version", + "redis.version": "Έκδοση Redis", "redis.connected-clients": "Connected Clients", "redis.connected-slaves": "Connected Slaves", "redis.blocked-clients": "Blocked Clients", diff --git a/public/language/el/admin/settings/notifications.json b/public/language/el/admin/settings/notifications.json index 4eff7f341a..10009c6e08 100644 --- a/public/language/el/admin/settings/notifications.json +++ b/public/language/el/admin/settings/notifications.json @@ -1,5 +1,5 @@ { - "notifications": "Notifications", - "welcome-notification": "Welcome Notification", + "notifications": "Ειδοποιήσεις", + "welcome-notification": "Ειδοποίηση καλωσορίσματος", "welcome-notification-link": "Welcome Notification Link" } \ No newline at end of file diff --git a/public/language/el/global.json b/public/language/el/global.json index 79965ed553..c2177d908a 100644 --- a/public/language/el/global.json +++ b/public/language/el/global.json @@ -19,7 +19,7 @@ "welcome_back": "Καλωσόρισες Πάλι", "you_have_successfully_logged_in": "Συνδέθηκες με επιτυχία", "save_changes": "Αποθήκευση Αλλαγών", - "save": "Save", + "save": "Αποθήκευση", "close": "Κλείσιμο", "pagination": "Σελιδοποίηση", "pagination.out_of": "%1 από %2", @@ -31,7 +31,7 @@ "header.tags": "Ετικέτες", "header.popular": "Δημοφιλή", "header.users": "Χρήστες", - "header.groups": "Groups", + "header.groups": "Ομάδες", "header.chats": "Συνομιλίες", "header.notifications": "Ειδοποιήσεις", "header.search": "Αναζήτηση", @@ -77,7 +77,7 @@ "recentips": "Πρόσφατη IP Σύνδεσης", "moderator_tools": "Moderator Tools", "away": "Απών/ούσα", - "dnd": "Do not disturb", + "dnd": "Μην ενοχλείτε", "invisible": "Αόρατος/η", "offline": "Εκτός Σύνδεσης", "email": "Email", @@ -89,10 +89,10 @@ "privacy": "Privacy", "follow": "Follow", "unfollow": "Unfollow", - "delete_all": "Delete All", + "delete_all": "Διαγραφή Όλων", "map": "Map", "sessions": "Login Sessions", - "ip_address": "IP Address", + "ip_address": "Διεύθυνση IP", "enter_page_number": "Enter page number", "upload_file": "Upload file", "upload": "Upload", diff --git a/public/language/el/groups.json b/public/language/el/groups.json index b5b19b39e9..7c19a8733e 100644 --- a/public/language/el/groups.json +++ b/public/language/el/groups.json @@ -1,13 +1,13 @@ { - "groups": "Groups", + "groups": "Ομάδες", "view_group": "Προβολή Ομάδας", - "owner": "Group Owner", - "new_group": "Create New Group", + "owner": "Κάτοχος Ομάδας", + "new_group": "Δημιουργία Νέας Ομάδας", "no_groups_found": "There are no groups to see", - "pending.accept": "Accept", - "pending.reject": "Reject", - "pending.accept_all": "Accept All", - "pending.reject_all": "Reject All", + "pending.accept": "Αποδοχή", + "pending.reject": "Απόρριψη", + "pending.accept_all": "Αποδοχή Όλων", + "pending.reject_all": "Απόρριψη Όλων", "pending.none": "There are no pending members at this time", "invited.none": "There are no invited members at this time", "invited.uninvite": "Rescind Invitation", @@ -15,7 +15,7 @@ "invited.notification_title": "You have been invited to join %1", "request.notification_title": "Group Membership Request from %1", "request.notification_text": "%1 has requested to become a member of %2", - "cover-save": "Save", + "cover-save": "Αποθήκευση", "cover-saving": "Saving", "details.title": "Λεπτομέρειες Ομάδας", "details.members": "Λίστα Μελών", @@ -31,8 +31,8 @@ "details.owner_options": "Group Administration", "details.group_name": "Group Name", "details.member_count": "Member Count", - "details.creation_date": "Creation Date", - "details.description": "Description", + "details.creation_date": "Ημερομηνία Δημιουργίας", + "details.description": "Περιγραφή", "details.badge_preview": "Badge Preview", "details.change_icon": "Change Icon", "details.change_colour": "Change Colour", @@ -41,7 +41,7 @@ "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", - "details.delete_group": "Delete Group", + "details.delete_group": "Διαγραφή Ομάδας", "details.private_system_help": "Private groups is disabled at system level, this option does not do anything", "event.updated": "Group details have been updated", "event.deleted": "The group \"%1\" has been deleted", diff --git a/public/language/el/pages.json b/public/language/el/pages.json index 243f8511da..242d4309e6 100644 --- a/public/language/el/pages.json +++ b/public/language/el/pages.json @@ -8,15 +8,15 @@ "recent": "Πρόσφατα Θέματα", "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", - "users/online": "Online Users", - "users/latest": "Latest Users", + "users/online": "Συνδεδεμένοι Χρήστες", + "users/latest": "Πρόσφατοι Χρήστες", "users/sort-posts": "Users with the most posts", "users/sort-reputation": "Users with the most reputation", - "users/banned": "Banned Users", + "users/banned": "Αποκλεισμένοι Χρήστες", "users/most-flags": "Most flagged users", - "users/search": "User Search", + "users/search": "Αναζήτηση Χρήστη", "notifications": "Ειδοποιήσεις", - "tags": "Tags", + "tags": "Ετικέτες", "tag": "Topics tagged under \"%1\"", "register": "Register an account", "registration-complete": "Registration complete", @@ -33,14 +33,14 @@ "account/edit/password": "Editing password of \"%1\"", "account/edit/username": "Editing username of \"%1\"", "account/edit/email": "Editing email of \"%1\"", - "account/info": "Account Info", + "account/info": "Πληροφορίες Λογαρισμού", "account/following": "People %1 follows", "account/followers": "People who follow %1", "account/posts": "Posts made by %1", "account/topics": "Topics created by %1", "account/groups": "%1's Groups", "account/bookmarks": "%1's Bookmarked Posts", - "account/settings": "User Settings", + "account/settings": "Επιλογές Χρήστη", "account/watched": "Topics watched by %1", "account/upvoted": "Posts upvoted by %1", "account/downvoted": "Posts downvoted by %1", diff --git a/public/language/el/unread.json b/public/language/el/unread.json index 803596873f..9d5af4e5a2 100644 --- a/public/language/el/unread.json +++ b/public/language/el/unread.json @@ -5,9 +5,9 @@ "mark_as_read": "Σημείωση ώς Αναγνωσμένα", "selected": "Επιλεγμένα", "all": "Όλα", - "all_categories": "All categories", + "all_categories": "Όλες οι κατηγορίες", "topics_marked_as_read.success": "Τα θέματα σημειώθηκαν ως αναγνωσμένα!", - "all-topics": "All Topics", - "new-topics": "New Topics", + "all-topics": "Όλα τα θέματα", + "new-topics": "Νέα Θέματα", "watched-topics": "Watched Topics" } \ No newline at end of file diff --git a/public/language/es/admin/advanced/database.json b/public/language/es/admin/advanced/database.json index f7db6220ee..91abeec537 100644 --- a/public/language/es/admin/advanced/database.json +++ b/public/language/es/admin/advanced/database.json @@ -7,28 +7,28 @@ "mongo": "Mongo", "mongo.version": "MongoDB Version", "mongo.storage-engine": "Storage Engine", - "mongo.collections": "Collections", - "mongo.objects": "Objects", - "mongo.avg-object-size": "Avg. Object Size", - "mongo.data-size": "Data Size", + "mongo.collections": "Colecciones", + "mongo.objects": "Objetos", + "mongo.avg-object-size": "Tamaño promedio por Objeto", + "mongo.data-size": "Tamaño de los Datos", "mongo.storage-size": "Storage Size", "mongo.index-size": "Index Size", "mongo.file-size": "File Size", "mongo.resident-memory": "Resident Memory", - "mongo.virtual-memory": "Virtual Memory", + "mongo.virtual-memory": "Memoria Virtual", "mongo.mapped-memory": "Mapped Memory", "mongo.raw-info": "MongoDB Raw Info", "redis": "Redis", "redis.version": "Redis Version", "redis.connected-clients": "Connected Clients", - "redis.connected-slaves": "Connected Slaves", - "redis.blocked-clients": "Blocked Clients", - "redis.used-memory": "Used Memory", + "redis.connected-slaves": "Esclavos Conectados", + "redis.blocked-clients": "Clientes Bloqueados", + "redis.used-memory": "Memoria Utilizada", "redis.memory-frag-ratio": "Memory Fragmentation Ratio", - "redis.total-connections-recieved": "Total Connections Received", - "redis.total-commands-processed": "Total Commands Processed", - "redis.iops": "Instantaneous Ops. Per Second", + "redis.total-connections-recieved": "Total de Conexiones Recividas ", + "redis.total-commands-processed": "Total de Comandos Procesados", + "redis.iops": "Operaciones Instantáneas por Segundo", "redis.keyspace-hits": "Keyspace Hits", "redis.keyspace-misses": "Keyspace Misses", "redis.raw-info": "Redis Raw Info" diff --git a/public/language/es/admin/appearance/skins.json b/public/language/es/admin/appearance/skins.json index 4db6fbdd8a..3f1a331fc2 100644 --- a/public/language/es/admin/appearance/skins.json +++ b/public/language/es/admin/appearance/skins.json @@ -1,9 +1,9 @@ { - "loading": "Loading Skins...", - "homepage": "Homepage", - "select-skin": "Select Skin", - "current-skin": "Current Skin", - "skin-updated": "Skin Updated", - "applied-success": "%1 skin was succesfully applied", - "revert-success": "Skin reverted to base colours" + "loading": "Cargando Temas...", + "homepage": "Pagina Principal", + "select-skin": "Selecciona el Tema", + "current-skin": "Tema Actual", + "skin-updated": "Tema Actualizado", + "applied-success": "El tema %1 se aplicó correctamente", + "revert-success": "El tema revierte los colores base" } \ No newline at end of file diff --git a/public/language/es/admin/appearance/themes.json b/public/language/es/admin/appearance/themes.json index 3148a01337..ab93165d08 100644 --- a/public/language/es/admin/appearance/themes.json +++ b/public/language/es/admin/appearance/themes.json @@ -1,11 +1,11 @@ { - "checking-for-installed": "Checking for installed themes...", - "homepage": "Homepage", - "select-theme": "Select Theme", - "current-theme": "Current Theme", - "no-themes": "No installed themes found", - "revert-confirm": "Are you sure you wish to restore the default NodeBB theme?", - "theme-changed": "Theme Changed", - "revert-success": "You have successfully reverted your NodeBB back to it's default theme.", - "restart-to-activate": "Please restart your NodeBB to fully activate this theme" + "checking-for-installed": "Buscando los temas instalados...", + "homepage": "Pagina Principal", + "select-theme": "Tema Seleccionado", + "current-theme": "Tema Actual ", + "no-themes": "No se encontraron temas instalados", + "revert-confirm": "¿Estas seguro/a que quieres restaurar el tema de fabrica de NodeBB?", + "theme-changed": "Se Cambió el Tema", + "revert-success": "Has revertido con exito el tema de fabrica de NodeBB.", + "restart-to-activate": "Por favor reinicia NodeBB para activar por completo este tema." } \ No newline at end of file diff --git a/public/language/es/admin/development/info.json b/public/language/es/admin/development/info.json index b2768ca212..a068347c43 100644 --- a/public/language/es/admin/development/info.json +++ b/public/language/es/admin/development/info.json @@ -1,11 +1,11 @@ { - "you-are-on": "Info - You are on %1:%2", + "you-are-on": "Info - Tu estas en %1:%2", "host": "host", "pid": "pid", "nodejs": "nodejs", - "online": "online", + "online": "en-linea", "git": "git", - "load": "load", + "load": "cargar", "uptime": "uptime", "registered": "Registered", diff --git a/public/language/es/admin/extend/plugins.json b/public/language/es/admin/extend/plugins.json index 4bb8cff1c1..dba3e65d87 100644 --- a/public/language/es/admin/extend/plugins.json +++ b/public/language/es/admin/extend/plugins.json @@ -1,41 +1,41 @@ { "installed": "Instalado", - "active": "Active", - "inactive": "Inactive", - "out-of-date": "Out of Date", - "none-found": "No plugins found.", - "none-active": "No Active Plugins", - "find-plugins": "Find Plugins", + "active": "Activo", + "inactive": "Inactivo ", + "out-of-date": "Desactualizado", + "none-found": "No se encontraron plugins.", + "none-active": "No hay Plug-ins activos", + "find-plugins": "Buscar Plug-in", - "plugin-search": "Plugin Search", - "plugin-search-placeholder": "Search for plugin...", - "reorder-plugins": "Re-order Plugins", - "order-active": "Order Active Plugins", - "dev-interested": "Interested in writing plugins for NodeBB?", + "plugin-search": "Plug-in de Búsqueda", + "plugin-search-placeholder": "Búscando Plug-in", + "reorder-plugins": "Re-ordenar Plug-ins", + "order-active": "Ordenar Plug-ins Activos", + "dev-interested": "¿Estas interesado en escribir plug-ins para NodeBB?", "docs-info": "Full documentation regarding plugin authoring can be found in the NodeBB Docs Portal.", - "order.description": "Certain plugins work ideally when they are initialised before/after other plugins.", - "order.explanation": "Plugins load in the order specified here, from top to bottom", + "order.description": "Algunos plug-in funcionan idealmente cuando son inicializados antes o despues de otros.", + "order.explanation": "Los plug-in son cargados en el orden especificado, de arriba a abajo.", - "plugin-item.themes": "Themes", - "plugin-item.deactivate": "Deactivate", - "plugin-item.activate": "Activate", - "plugin-item.install": "Install", - "plugin-item.uninstall": "Uninstall", - "plugin-item.settings": "Settings", - "plugin-item.installed": "Installed", - "plugin-item.latest": "Latest", - "plugin-item.upgrade": "Upgrade", - "plugin-item.more-info": "For more information:", - "plugin-item.unknown": "Unknown", - "plugin-item.unknown-explanation": "The state of this plugin could not be determined, possibly due to a misconfiguration error.", + "plugin-item.themes": "Temas", + "plugin-item.deactivate": "Desactivado", + "plugin-item.activate": "Activado", + "plugin-item.install": "Instalar", + "plugin-item.uninstall": "Desinstalar", + "plugin-item.settings": "Configuraciones", + "plugin-item.installed": "Instalados", + "plugin-item.latest": "Ultimos", + "plugin-item.upgrade": "Actualizado", + "plugin-item.more-info": "Para mas información:", + "plugin-item.unknown": "Desconocido", + "plugin-item.unknown-explanation": "El estado de este plug-in no puede determinsarse, posiblemente es debido a un error de configuración.", - "alert.enabled": "Plugin Enabled", - "alert.disabled": "Plugin Disabled", - "alert.upgraded": "Plugin Upgraded", - "alert.installed": "Plugin Installed", - "alert.uninstalled": "Plugin Uninstalled", - "alert.activate-success": "Please restart your NodeBB to fully activate this plugin", + "alert.enabled": "El plug-in esta Activo", + "alert.disabled": "Plug-in Des-habilitado", + "alert.upgraded": "Plug-in Actualizado", + "alert.installed": "Plug-in Instalado", + "alert.uninstalled": "Plug-in Desinstalado", + "alert.activate-success": "Por favor reinicia NodeBB para activar el plug-in por completo", "alert.deactivate-success": "Plugin successfully deactivated", "alert.upgrade-success": "Please reload your NodeBB to fully upgrade this plugin", "alert.install-success": "Plugin successfully installed, please activate the plugin.", diff --git a/public/language/es/admin/general/dashboard.json b/public/language/es/admin/general/dashboard.json index 02046bd17a..c68d48da21 100644 --- a/public/language/es/admin/general/dashboard.json +++ b/public/language/es/admin/general/dashboard.json @@ -1,64 +1,64 @@ { - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "users": "Users", - "posts": "Posts", - "topics": "Topics", - "page-views-last-month": "Page views Last Month", - "page-views-this-month": "Page views This Month", - "page-views-last-day": "Page views in last 24 hours", + "forum-traffic": "Trafico del Foro", + "page-views": "Vistas de la Pagina", + "unique-visitors": "Visitantes Unicos", + "users": "Usuario", + "posts": "Publicación", + "topics": "Temas", + "page-views-last-month": "Vistas de la Pagina del Mes Pasado", + "page-views-this-month": "Vistas de la Pagina de este Mes.", + "page-views-last-day": "Vistas de la Pagina en las ultimas 24 horas", - "stats.day": "Day", - "stats.week": "Week", - "stats.month": "Month", + "stats.day": "Día", + "stats.week": "Semana", + "stats.month": "Mes", "stats.all": "All Time", - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", + "updates": "Actualizaciones", + "running-version": "Estas ejecutando NodeBB v%1.", + "keep-updated": "Asegúrate que tu NodeBB este al día en los últimos parches de seguridad y actualizaciones.", "up-to-date": "

    You are up-to-date

    ", - "upgrade-available": "

    A new version (v%1) has been released. Consider upgrading your NodeBB.

    ", - "prerelease-upgrade-available": "

    This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

    ", - "prerelease-warning": "

    This is a pre-release version of NodeBB. Unintended bugs may occur.

    ", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", + "upgrade-available": "

    La nueva versión (v%1) ha sido lanzada. Considera actualizar tu NodeBB.

    ", + "prerelease-upgrade-available": "

    Esta es una versión antigua pre-lanzamiento de NodeBB. La nueva versión (v%1) ha sido lanzada. Considera actualizar tu NodeBB.

    ", + "prerelease-warning": "

    Esta es una versión depre-lanzamiento de NodeBB. Algunas fallas pueden ocurrir.

    ", + "running-in-development": "Forum esta siendo ejecutado en modo de desarrollador. El foro puede estar abierto a vulnerabilidades potenciales; por favor contacta tu administrador del sistema.", - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", + "notices": "Noticias", + "restart-not-required": "No se require reiniciar.", + "restart-required": "Se requiere reiniciar", + "search-plugin-installed": "El plug-in de búsqueda esta instalado.", + "search-plugin-not-installed": "El plug-in de busqueda no esta instalado", + "search-plugin-tooltip": "Instala el plug-in de búsqueda desde la pagina de plugins para activar esta funcionalidad.", - "control-panel": "System Control", - "reload": "Reload", - "restart": "Restart", - "restart-warning": "Reloading or Restarting your NodeBB will drop all existing connections for a few seconds.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", + "control-panel": "Control del Systema", + "reload": "Recargar", + "restart": "Reiniciar", + "restart-warning": "Recargar o Reiniciar tu NodeBB va a tumbar todas las conexiones existentes por algunos segundos.", + "maintenance-mode": "Modo de Mantenimiento", + "maintenance-mode-title": "Haz clic aquí para activar el modo de mantenimiento de NodeBB", + "realtime-chart-updates": "Actualizar el Grafo en Tiempo Real", - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", + "active-users": "Usuarios Activos", + "active-users.users": "Usuarios", + "active-users.guests": "Invitados", "active-users.total": "Total", - "active-users.connections": "Connections", + "active-users.connections": "Conexiones", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", + "anonymous-registered-users": "Usuarios Anónimos vs Registrados", + "anonymous": "Anónimos", + "registered": "Registrados", - "user-presence": "User Presence", - "on-categories": "On categories list", + "user-presence": "Presencia del Usuario", + "on-categories": "Listado en Categorias", "reading-posts": "Reading posts", "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", + "recent": "Recientes", + "unread": "Sin Leer", - "high-presence-topics": "High Presence Topics", + "high-presence-topics": "Temas con Alta Presencia", - "graphs.page-views": "Page Views", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users" + "graphs.page-views": "Vista de la Pagina", + "graphs.unique-visitors": "Visitantes Unicos", + "graphs.registered-users": "Usuarios Registrados", + "graphs.anonymous-users": "Usuarios Anónimos" } diff --git a/public/language/es/admin/general/homepage.json b/public/language/es/admin/general/homepage.json index 4866b8baf6..4b9ecd81c8 100644 --- a/public/language/es/admin/general/homepage.json +++ b/public/language/es/admin/general/homepage.json @@ -1,7 +1,7 @@ { - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", + "home-page": "Página Principal", + "description": "Escoge que pagina se muestra cuando los usuarios navegan en la raíz del foro.", + "home-page-route": "Ruta de la Pagina Principal", "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages" + "allow-user-home-pages": "Permitir Pagina de Perfil del Usuario" } \ No newline at end of file diff --git a/public/language/it/global.json b/public/language/it/global.json index 7b7fbae4c8..b0599c51d0 100644 --- a/public/language/it/global.json +++ b/public/language/it/global.json @@ -75,7 +75,7 @@ "norecenttopics": "Nessuna Discussione Recente", "recentposts": "Post Recenti", "recentips": "Indirizzi IP Recentemente Loggati", - "moderator_tools": "Moderator Tools", + "moderator_tools": "Strumenti di amministrazione", "away": "Non disponibile", "dnd": "Non disturbare", "invisible": "Invisibile", @@ -98,7 +98,7 @@ "upload": "Carica", "allowed-file-types": "Le estensioni permesse dei file sono %1", "unsaved-changes": "Hai delle modifiche non salvate. Sei sicuro che vuoi lasciare la pagina?", - "reconnecting-message": "Sembra che la tua connessione a %1 sia stata persa, per favore attenti mentre proviamo a riconnetterti.", + "reconnecting-message": "Sembra che la tua connessione a %1 sia stata persa, per favore attendi mentre proviamo a riconnetterti.", "play": "Play", "cookies.message": "Questo sito utilizza i cookie per garantirti la miglior esperienza di navigazione possibile", "cookies.accept": "Ho capito!", diff --git a/public/language/it/user.json b/public/language/it/user.json index 9d5a88d4e0..d994858692 100644 --- a/public/language/it/user.json +++ b/public/language/it/user.json @@ -33,7 +33,7 @@ "chat": "Chat", "chat_with": "Continua la chat con %1", "new_chat_with": "Inizia una nuova chat con %1", - "flag-profile": "Flag Profile", + "flag-profile": "Flag Profilo", "follow": "Segui", "unfollow": "Smetti di seguire", "more": "Altro", @@ -65,8 +65,8 @@ "remove_uploaded_picture": "Elimina foto caricata", "upload_cover_picture": "Carica immagine di copertina", "remove_cover_picture_confirm": "Sei sicuro di voler eliminare l'immagine di copertina?", - "crop_picture": "Crop picture", - "upload_cropped_picture": "Crop and upload", + "crop_picture": "Ritaglia immagine", + "upload_cropped_picture": "Ritaglia e carica", "settings": "Impostazioni", "show_email": "Mostra la mia Email", "show_fullname": "Mostra il mio nome completo", @@ -129,7 +129,7 @@ "info.banned-no-reason": "Non è stata data nessuna motivazione.", "info.username-history": "Storico del nome utente", "info.email-history": "Storico dell'Email", - "info.moderation-note": "Moderation Note", - "info.moderation-note.success": "Moderation note saved", - "info.moderation-note.add": "Add note" + "info.moderation-note": "Nota di moderazione", + "info.moderation-note.success": "Nota di moderazione salvata", + "info.moderation-note.add": "Aggiungi nota" } \ No newline at end of file diff --git a/public/language/ja/admin/menu.json b/public/language/ja/admin/menu.json index 0f1be1b2a9..7408a1aa5d 100644 --- a/public/language/ja/admin/menu.json +++ b/public/language/ja/admin/menu.json @@ -7,7 +7,7 @@ "general/sounds": "サウンド", "general/social": "ソーシャル", - "section-manage": "メッセージ", + "section-manage": "管理", "manage/categories": "カテゴリ", "manage/tags": "タグ", "manage/users": "ユーザー", diff --git a/public/language/zh-CN/admin/settings/general.json b/public/language/zh-CN/admin/settings/general.json index cbb324c9e7..c003b93469 100644 --- a/public/language/zh-CN/admin/settings/general.json +++ b/public/language/zh-CN/admin/settings/general.json @@ -6,7 +6,7 @@ "browser-title": "浏览器标题", "browser-title-help": "如果没有指定浏览器标题,将会使用站点标题", "title-layout": "标题布局", - "title-layout-help": "定义浏览器标题的布局,即{页面标题} | {浏览器标题}", + "title-layout-help": "定义浏览器标题的布局,即{pageTitle} | {browserTitle}", "description.placeholder": "关于您的社区的简短说明", "description": "站点描述", "keywords": "站点关键字", From 55163527589ff13831f89a32e1d09d10dea255e0 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Sat, 22 Apr 2017 13:43:58 -0400 Subject: [PATCH 232/316] closes #5612 --- src/upgrade.js | 13 ++++++------- src/upgrades/1.5.0/moderation_history_refactor.js | 1 + 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/upgrade.js b/src/upgrade.js index c149dc4bbd..c395dc8884 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -5,6 +5,7 @@ var async = require('async'); var path = require('path'); var semver = require('semver'); +var readline = require('readline'); var db = require('./database'); var file = require('../src/file'); @@ -131,7 +132,7 @@ Upgrade.process = function (files, skipCount, callback) { date: date, }; - process.stdout.write(' → '.white + String('[' + [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()].join('/') + '] ').gray + String(scriptExport.name).reset + '... '); + process.stdout.write(' → '.white + String('[' + [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()].join('/') + '] ').gray + String(scriptExport.name).reset + '...\n '); // For backwards compatibility, cross-reference with schemaDate (if found). If a script's date is older, skip it if ((!results.schemaDate && !results.schemaLogCount) || (scriptExport.timestamp <= results.schemaDate && semver.lt(version, '1.5.0'))) { @@ -150,8 +151,8 @@ Upgrade.process = function (files, skipCount, callback) { } if (progress.total > 0) { - process.stdout.clearLine(); - process.stdout.cursorTo(0); + readline.clearLine(process.stdout, 0); + readline.cursorTo(process.stdout, 0); process.stdout.write(' → '.white + String('[' + [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()].join('/') + '] ').gray + String(scriptExport.name).reset + '... '); } @@ -180,11 +181,9 @@ Upgrade.incrementProgress = function () { filled = Math.floor((this.current / this.total) * 15); unfilled = 15 - filled; } - process.stdout.clearLine(); - process.stdout.cursorTo(0); - process.stdout.write(' → '.white + String('[' + [this.date.getUTCFullYear(), this.date.getUTCMonth() + 1, this.date.getUTCDate()].join('/') + '] ').gray + String(this.script.name).reset + '... '); - process.stdout.write('[' + (filled ? new Array(filled).join('#') : '') + new Array(unfilled).join(' ') + '] (' + this.current + '/' + (this.total || '??') + ') ' + percentage); + readline.cursorTo(process.stdout, 0); + process.stdout.write(' [' + (filled ? new Array(filled).join('#') : '') + new Array(unfilled).join(' ') + '] (' + this.current + '/' + (this.total || '??') + ') ' + percentage + ' '); }; module.exports = Upgrade; diff --git a/src/upgrades/1.5.0/moderation_history_refactor.js b/src/upgrades/1.5.0/moderation_history_refactor.js index f98e1c62db..50bb10a7e8 100644 --- a/src/upgrades/1.5.0/moderation_history_refactor.js +++ b/src/upgrades/1.5.0/moderation_history_refactor.js @@ -17,6 +17,7 @@ module.exports = { async.each(ids, function (uid, next) { db.getObjectField('user:' + uid, 'moderationNote', function (err, moderationNote) { if (err || !moderationNote) { + progress.incr(); return next(err); } var note = { From 08049c071286dff4176ca0277d6d2c119ff8c1d7 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Sat, 22 Apr 2017 13:49:10 -0400 Subject: [PATCH 233/316] remove empty block --- src/batch.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/batch.js b/src/batch.js index f15d06a453..9a0271f29b 100644 --- a/src/batch.js +++ b/src/batch.js @@ -24,9 +24,7 @@ exports.processSortedSet = function (setKey, process, options, callback) { // Progress bar handling (upgrade scripts) if (options.progress) { db.sortedSetCard(setKey, function (err, total) { - if (err) { - // Unable to get total, do nothing. - } else { + if (!err) { options.progress.total = total; } }); From fa13e9acc0bf6412dd506b5bb1e0991a1bf415b2 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Sat, 22 Apr 2017 14:38:43 -0400 Subject: [PATCH 234/316] closes #5621 --- src/controllers/authentication.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js index 396aafd130..76f2da243d 100644 --- a/src/controllers/authentication.js +++ b/src/controllers/authentication.js @@ -344,10 +344,15 @@ authenticationController.localLogin = function (req, username, password, next) { var uid; var userData = {}; + if (!password || !utils.isPasswordValid(password)) { + return next(new Error('[[error:invalid-password]]')); + } + + if (password.length > 4096) { + return next(new Error('[[error:password-too-long]]')); + } + async.waterfall([ - function (next) { - user.isPasswordValid(password, next); - }, function (next) { user.getUidByUserslug(userslug, next); }, From b91b41dfa30b6051356f0ff135cf21702ca27cb0 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Sat, 22 Apr 2017 17:08:54 -0400 Subject: [PATCH 235/316] show database values in gbs --- .../language/en-GB/admin/advanced/database.json | 1 + src/database/mongo.js | 6 +++++- src/database/redis.js | 2 +- src/views/admin/advanced/database.tpl | 16 ++++++++-------- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/public/language/en-GB/admin/advanced/database.json b/public/language/en-GB/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/en-GB/admin/advanced/database.json +++ b/public/language/en-GB/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/src/database/mongo.js b/src/database/mongo.js index aadd5dbfe7..273ec18001 100644 --- a/src/database/mongo.js +++ b/src/database/mongo.js @@ -201,7 +201,7 @@ mongoModule.info = function (db, callback) { return callback(err); } var stats = results.stats; - var scale = 1024 * 1024; + var scale = 1024 * 1024 * 1024; results.listCollections = results.listCollections.map(function (collectionInfo) { return { @@ -216,6 +216,10 @@ mongoModule.info = function (db, callback) { }); stats.mem = results.serverStatus.mem; + stats.mem = results.serverStatus.mem; + stats.mem.resident = (stats.mem.resident / 1024).toFixed(2); + stats.mem.virtual = (stats.mem.virtual / 1024).toFixed(2); + stats.mem.mapped = (stats.mem.mapped / 1024).toFixed(2); stats.collectionData = results.listCollections; stats.network = results.serverStatus.network; stats.raw = JSON.stringify(stats, null, 4); diff --git a/src/database/redis.js b/src/database/redis.js index 0127a61600..9ebc154705 100644 --- a/src/database/redis.js +++ b/src/database/redis.js @@ -152,7 +152,7 @@ redisModule.info = function (cxn, callback) { redisData[parts[0]] = parts[1]; } }); - + redisData.used_memory_human = (redisData.used_memory / (1024 * 1024 * 1024)).toFixed(2); redisData.raw = JSON.stringify(redisData, null, 4); redisData.redis = true; diff --git a/src/views/admin/advanced/database.tpl b/src/views/admin/advanced/database.tpl index c9003e52f9..9519ce9141 100644 --- a/src/views/admin/advanced/database.tpl +++ b/src/views/admin/advanced/database.tpl @@ -13,16 +13,16 @@ [[admin/advanced/database:mongo.objects]] {mongo.objects}
    [[admin/advanced/database:mongo.avg-object-size]] [[admin/advanced/database:x-b, {mongo.avgObjSize}]]

    - [[admin/advanced/database:mongo.data-size]] [[admin/advanced/database:x-mb, {mongo.dataSize}]]
    - [[admin/advanced/database:mongo.storage-size]] [[admin/advanced/database:x-mb, {mongo.storageSize}]]
    - [[admin/advanced/database:mongo.index-size]] [[admin/advanced/database:x-mb, {mongo.indexSize}]]
    + [[admin/advanced/database:mongo.data-size]] [[admin/advanced/database:x-gb, {mongo.dataSize}]]
    + [[admin/advanced/database:mongo.storage-size]] [[admin/advanced/database:x-gb, {mongo.storageSize}]]
    + [[admin/advanced/database:mongo.index-size]] [[admin/advanced/database:x-gb, {mongo.indexSize}]]
    - [[admin/advanced/database:mongo.file-size]] [[admin/advanced/database:x-mb, {mongo.fileSize}]]
    + [[admin/advanced/database:mongo.file-size]] [[admin/advanced/database:x-gb, {mongo.fileSize}]]

    - [[admin/advanced/database:mongo.resident-memory]] [[admin/advanced/database:x-mb, {mongo.mem.resident}]]
    - [[admin/advanced/database:mongo.virtual-memory]] [[admin/advanced/database:x-mb, {mongo.mem.virtual}]]
    - [[admin/advanced/database:mongo.mapped-memory]] [[admin/advanced/database:x-mb, {mongo.mem.mapped}]]
    + [[admin/advanced/database:mongo.resident-memory]] [[admin/advanced/database:x-gb, {mongo.mem.resident}]]
    + [[admin/advanced/database:mongo.virtual-memory]] [[admin/advanced/database:x-gb, {mongo.mem.virtual}]]
    + [[admin/advanced/database:mongo.mapped-memory]] [[admin/advanced/database:x-gb, {mongo.mem.mapped}]]
    @@ -43,7 +43,7 @@ [[admin/advanced/database:redis.blocked-clients]] {redis.blocked_clients}

    - [[admin/advanced/database:redis.used-memory]] {redis.used_memory_human}
    + [[admin/advanced/database:redis.used-memory]] [[admin/advanced/database:x-gb, {redis.used_memory_human}]]
    [[admin/advanced/database:redis.memory-frag-ratio]] {redis.mem_fragmentation_ratio}

    [[admin/advanced/database:redis.total-connections-recieved]] {redis.total_connections_received}
    From 33e8ecc1f46094da220fcddd3da8d5e0b8810280 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Sat, 22 Apr 2017 19:07:57 -0400 Subject: [PATCH 236/316] closes #5404 --- install/data/defaults.json | 2 +- src/controllers/accounts/edit.js | 2 +- src/controllers/categories.js | 11 -------- src/controllers/tags.js | 6 ----- src/controllers/topics.js | 5 ---- src/meta/tags.js | 39 ++++++++++++++++++++-------- src/middleware/header.js | 2 +- src/user/picture.js | 2 +- src/views/admin/settings/uploads.tpl | 2 +- 9 files changed, 33 insertions(+), 38 deletions(-) diff --git a/install/data/defaults.json b/install/data/defaults.json index 25fe659b27..98de790158 100644 --- a/install/data/defaults.json +++ b/install/data/defaults.json @@ -29,7 +29,7 @@ "maximumAboutMeLength": 1000, "maximumProfileImageSize": 256, "maximumCoverImageSize": 2048, - "profileImageDimension": 128, + "profileImageDimension": 200, "requireEmailConfirmation": 0, "allowProfileImageUploads": 1, "teaserPost": "last-reply", diff --git a/src/controllers/accounts/edit.js b/src/controllers/accounts/edit.js index d659b0153f..eca16192c9 100644 --- a/src/controllers/accounts/edit.js +++ b/src/controllers/accounts/edit.js @@ -26,7 +26,7 @@ editController.get = function (req, res, callback) { userData.maximumProfileImageSize = parseInt(meta.config.maximumProfileImageSize, 10); userData.allowProfileImageUploads = parseInt(meta.config.allowProfileImageUploads, 10) === 1; userData.allowAccountDelete = parseInt(meta.config.allowAccountDelete, 10) === 1; - userData.profileImageDimension = parseInt(meta.config.profileImageDimension, 10) || 128; + userData.profileImageDimension = parseInt(meta.config.profileImageDimension, 10) || 200; userData.groups = userData.groups.filter(function (group) { return group && group.userTitleEnabled && !groups.isPrivilegeGroup(group.name) && group.name !== 'registered-users'; diff --git a/src/controllers/categories.js b/src/controllers/categories.js index e02f107b4a..9a17a47fc2 100644 --- a/src/controllers/categories.js +++ b/src/controllers/categories.js @@ -21,17 +21,6 @@ categoriesController.list = function (req, res, next) { content: 'website', }]; - var ogImage = meta.config['og:image'] || meta.config['brand:logo'] || ''; - if (ogImage) { - if (!ogImage.startsWith('http')) { - ogImage = nconf.get('url') + ogImage; - } - res.locals.metaTags.push({ - property: 'og:image', - content: ogImage, - }); - } - var categoryData; async.waterfall([ function (next) { diff --git a/src/controllers/tags.js b/src/controllers/tags.js index cffff0e44c..64e1640706 100644 --- a/src/controllers/tags.js +++ b/src/controllers/tags.js @@ -1,8 +1,6 @@ 'use strict'; - var async = require('async'); -var nconf = require('nconf'); var validator = require('validator'); var user = require('../user'); @@ -63,10 +61,6 @@ tagsController.getTag = function (req, res, next) { property: 'og:title', content: tag, }, - { - property: 'og:url', - content: nconf.get('url') + '/tags/' + tag, - }, ]; templateData.topics = topics; diff --git a/src/controllers/topics.js b/src/controllers/topics.js index 4fe1bf96d5..ddd6fd889a 100644 --- a/src/controllers/topics.js +++ b/src/controllers/topics.js @@ -206,11 +206,6 @@ topicsController.get = function (req, res, callback) { property: 'og:type', content: 'article', }, - { - property: 'og:url', - content: nconf.get('url') + '/topic/' + topicData.slug + (req.params.post_index ? ('/' + req.params.post_index) : ''), - noEscape: true, - }, { property: 'og:image', content: ogImageUrl, diff --git a/src/meta/tags.js b/src/meta/tags.js index 5b1097d427..ac0b395a23 100644 --- a/src/meta/tags.js +++ b/src/meta/tags.js @@ -9,7 +9,7 @@ var plugins = require('../plugins'); module.exports = function (Meta) { Meta.tags = {}; - Meta.tags.parse = function (meta, link, callback) { + Meta.tags.parse = function (req, meta, link, callback) { async.parallel({ tags: function (next) { var defaultTags = [{ @@ -120,7 +120,23 @@ module.exports = function (Meta) { return tag; }); - addDescription(meta); + addIfNotExists(meta, 'property', 'og:title', Meta.config.title || 'NodeBB'); + + var ogUrl = nconf.get('url') + req.path; + addIfNotExists(meta, 'property', 'og:url', ogUrl); + + addIfNotExists(meta, 'name', 'description', Meta.config.description); + addIfNotExists(meta, 'property', 'og:description', Meta.config.description); + + var ogImage = Meta.config['og:image'] || Meta.config['brand:logo'] || ''; + if (ogImage && !ogImage.startsWith('http')) { + ogImage = nconf.get('url') + ogImage; + } + addIfNotExists(meta, 'property', 'og:image', ogImage); + if (ogImage) { + addIfNotExists(meta, 'property', 'og:image:width', 200); + addIfNotExists(meta, 'property', 'og:image:height', 200); + } link = results.links.concat(link || []); @@ -131,19 +147,20 @@ module.exports = function (Meta) { }); }; - function addDescription(meta) { - var hasDescription = false; + function addIfNotExists(meta, keyName, tagName, value) { + var exists = false; meta.forEach(function (tag) { - if (tag.name === 'description') { - hasDescription = true; + if (tag[keyName] === tagName) { + exists = true; } }); - if (!hasDescription && Meta.config.description) { - meta.push({ - name: 'description', - content: validator.escape(String(Meta.config.description)), - }); + if (!exists && value) { + var data = { + content: validator.escape(String(value)), + }; + data[keyName] = tagName; + meta.push(data); } } }; diff --git a/src/middleware/header.js b/src/middleware/header.js index 70c0755def..af1a2e0285 100644 --- a/src/middleware/header.js +++ b/src/middleware/header.js @@ -97,7 +97,7 @@ module.exports = function (middleware) { db.get('uid:' + req.uid + ':confirm:email:sent', next); }, navigation: async.apply(navigation.get), - tags: async.apply(meta.tags.parse, res.locals.metaTags, res.locals.linkTags), + tags: async.apply(meta.tags.parse, req, res.locals.metaTags, res.locals.linkTags), banned: async.apply(user.isBanned, req.uid), banReason: async.apply(user.getBannedReason, req.uid), }, next); diff --git a/src/user/picture.js b/src/user/picture.js index 2b4f45d344..dfe0aa72bb 100644 --- a/src/user/picture.js +++ b/src/user/picture.js @@ -154,7 +154,7 @@ module.exports = function (User) { function (path, next) { picture.path = path; - var imageDimension = parseInt(meta.config.profileImageDimension, 10) || 128; + var imageDimension = parseInt(meta.config.profileImageDimension, 10) || 200; image.resizeImage({ path: picture.path, extension: extension, diff --git a/src/views/admin/settings/uploads.tpl b/src/views/admin/settings/uploads.tpl index feff2dbff3..32e432adfc 100644 --- a/src/views/admin/settings/uploads.tpl +++ b/src/views/admin/settings/uploads.tpl @@ -91,7 +91,7 @@
    - +

    [[admin/settings/uploads:profile-image-dimension-help]]

    From 4cf87264cf544ec6f24e58ae887b7a7160fbca0e Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Sun, 23 Apr 2017 09:22:16 +0000 Subject: [PATCH 237/316] Latest translations and fallbacks --- public/language/el/global.json | 14 ++++++------ public/language/el/login.json | 4 ++-- public/language/el/recent.json | 4 ++-- public/language/el/search.json | 22 +++++++++---------- public/language/el/uploads.json | 8 +++---- public/language/el/users.json | 18 +++++++-------- .../language/zh-CN/admin/extend/plugins.json | 2 +- 7 files changed, 36 insertions(+), 36 deletions(-) diff --git a/public/language/el/global.json b/public/language/el/global.json index c2177d908a..3a98632abf 100644 --- a/public/language/el/global.json +++ b/public/language/el/global.json @@ -3,11 +3,11 @@ "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": "Perhaps you should try logging in?", "404.title": "Δεν βρέθηκε", "404.message": "You seem to have stumbled upon a page that does not exist. Return to the home page.", - "500.title": "Internal Error.", + "500.title": "Εσωτερικό Σφάλμα.", "500.message": "Ουπς! Φαίνεται πως κάτι πήγε στραβά!", "400.title": "Bad Request.", "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.", @@ -25,7 +25,7 @@ "pagination.out_of": "%1 από %2", "pagination.enter_index": "Εισαγωγή Σελίδας", "header.admin": "Διαχειριστής", - "header.categories": "Categories", + "header.categories": "Κατηγορίες", "header.recent": "Πρόσφατα", "header.unread": "Μη αναγνωσμένα", "header.tags": "Ετικέτες", @@ -60,7 +60,7 @@ "views": "Εμφανίσεις", "reputation": "Φήμη", "read_more": "διάβασε περισσότερα", - "more": "More", + "more": "Περισσότερα", "posted_ago_by_guest": "δημοσιεύτηκε πριν από %1 από Επισκέπτη", "posted_ago_by": "δημοσιεύτηκε πριν από %1 από τον/την %2", "posted_ago": "δημοσιεύτηκε πρίν από %1", @@ -94,14 +94,14 @@ "sessions": "Login Sessions", "ip_address": "Διεύθυνση IP", "enter_page_number": "Enter page number", - "upload_file": "Upload file", - "upload": "Upload", + "upload_file": "Ανέβασμα αρχείου", + "upload": "Ανέβασμα", "allowed-file-types": "Allowed file types are %1", "unsaved-changes": "You have unsaved changes. Are you sure you wish to navigate away?", "reconnecting-message": "Looks like your connection to %1 was lost, please wait while we try to reconnect.", "play": "Play", "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", "cookies.accept": "Got it!", - "cookies.learn_more": "Learn More", + "cookies.learn_more": "Μάθε Περισσότερα", "edited": "Edited" } \ No newline at end of file diff --git a/public/language/el/login.json b/public/language/el/login.json index 126c06b283..11cc226b06 100644 --- a/public/language/el/login.json +++ b/public/language/el/login.json @@ -1,6 +1,6 @@ { - "username-email": "Username / Email", - "username": "Username", + "username-email": "Όνομα χρήστη / Email", + "username": "Όνομα Χρήστη", "email": "Email", "remember_me": "Απομνημόνευση;", "forgot_password": "Ξέχασες τον κωδικό σου;", diff --git a/public/language/el/recent.json b/public/language/el/recent.json index 8424eb4616..85f948e9c8 100644 --- a/public/language/el/recent.json +++ b/public/language/el/recent.json @@ -6,8 +6,8 @@ "year": "Έτος", "alltime": "Όλο το Ιστορικό", "no_recent_topics": "Δεν υπάρχουν πρόσφατα θέματα.", - "no_popular_topics": "There are no popular topics.", - "there-is-a-new-topic": "There is a new topic.", + "no_popular_topics": "Δεν υπάρχουν δημοφιλή θέματα.", + "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.", diff --git a/public/language/el/search.json b/public/language/el/search.json index 61aa1e3128..fb3094590e 100644 --- a/public/language/el/search.json +++ b/public/language/el/search.json @@ -9,21 +9,21 @@ "in-categories": "In Categories", "search-child-categories": "Search child categories", "has-tags": "Has tags", - "reply-count": "Reply Count", - "at-least": "At least", + "reply-count": "Αριθμός Απαντήσεων", + "at-least": "Τουλάχιστον", "at-most": "At most", "relevance": "Relevance", "post-time": "Post time", - "newer-than": "Newer than", - "older-than": "Older than", + "newer-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", + "yesterday": "Χθες", + "one-week": "Μία εβδομάδα", + "two-weeks": "Δύο εβδομάδες", + "one-month": "Ένας μήνας", + "three-months": "Τρεις μήνες", "six-months": "Six months", - "one-year": "One year", + "one-year": "Ένας χρόνος", "sort-by": "Sort by", "last-reply-time": "Last reply time", "topic-title": "Topic title", @@ -31,7 +31,7 @@ "number-of-views": "Number of views", "topic-start-date": "Topic start date", "username": "Username", - "category": "Category", + "category": "Κατηγορία", "descending": "In descending order", "ascending": "In ascending order", "save-preferences": "Save preferences", diff --git a/public/language/el/uploads.json b/public/language/el/uploads.json index 1622cb5693..08a9da99dd 100644 --- a/public/language/el/uploads.json +++ b/public/language/el/uploads.json @@ -1,6 +1,6 @@ { - "uploading-file": "Uploading the file...", - "select-file-to-upload": "Select a file to upload!", - "upload-success": "File uploaded successfully!", - "maximum-file-size": "Maximum %1 kb" + "uploading-file": "Το αρχείο ανεβαίνει...", + "select-file-to-upload": "Επίλεξε αρχείο για ανέβασμα!", + "upload-success": "Το αρχείο ανέβηκε επιτυχώς!", + "maximum-file-size": "Μέγιστο %1 kb" } \ No newline at end of file diff --git a/public/language/el/users.json b/public/language/el/users.json index be5f770f99..1e97404f00 100644 --- a/public/language/el/users.json +++ b/public/language/el/users.json @@ -8,14 +8,14 @@ "load_more": "Φόρτωση περισσότερων", "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", "filter-by": "Filter By", - "online-only": "Online only", - "invite": "Invite", + "online-only": "Μόνο Συνδεδεμένοι", + "invite": "Πρόσκληση", "invitation-email-sent": "An invitation email has been sent to %1", - "user_list": "User List", - "recent_topics": "Recent Topics", - "popular_topics": "Popular Topics", - "unread_topics": "Unread Topics", - "categories": "Categories", - "tags": "Tags", - "no-users-found": "No users found!" + "user_list": "Λίστα Χρηστών", + "recent_topics": "Πρόσφατα Θέματα", + "popular_topics": "Δημοφιλή Θέματα", + "unread_topics": "Μη αναγνωσμένα Θέματα", + "categories": "Κατηγορίες", + "tags": "Ετικέτες", + "no-users-found": "Δε βρέθηκαν χρήστες!" } \ No newline at end of file diff --git a/public/language/zh-CN/admin/extend/plugins.json b/public/language/zh-CN/admin/extend/plugins.json index ab2173dda1..3ca42b1c2e 100644 --- a/public/language/zh-CN/admin/extend/plugins.json +++ b/public/language/zh-CN/admin/extend/plugins.json @@ -1,7 +1,7 @@ { "installed": "已安装", "active": "已启用", - "inactive": "未生效", + "inactive": "未启用", "out-of-date": "已过期", "none-found": "无插件。", "none-active": "无生效插件", From 04f884db6507df3b0a39058b92d1abaa3d1f267a Mon Sep 17 00:00:00 2001 From: barisusakli Date: Sun, 23 Apr 2017 12:47:48 -0400 Subject: [PATCH 238/316] fix crash in languages if lang doesn't exist @pitaj --- src/languages.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages.js b/src/languages.js index 520ae8bba1..4414562e11 100644 --- a/src/languages.js +++ b/src/languages.js @@ -89,7 +89,7 @@ Languages.list = function (callback) { // filter out invalid ones languages = languages.filter(function (lang) { - return lang.code && lang.name && lang.dir; + return lang && lang.code && lang.name && lang.dir; }); listCache = languages; From 81e23a8a9ae1a951f6e48918b3fc5829366eaa95 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Sun, 23 Apr 2017 12:48:35 -0600 Subject: [PATCH 239/316] Fix #5622 --- public/src/modules/storage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/modules/storage.js b/public/src/modules/storage.js index 5cb6051586..bccaabcf6b 100644 --- a/public/src/modules/storage.js +++ b/public/src/modules/storage.js @@ -48,7 +48,7 @@ define('storage', function () { } var storage; - var item = Date.now(); + var item = Date.now().toString(); try { storage = window.localStorage; From 066e54474ec915b4649d3235372bd2a7ef0eda74 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Mon, 24 Apr 2017 09:22:48 +0000 Subject: [PATCH 240/316] Latest translations and fallbacks --- public/language/ja/admin/extend/plugins.json | 2 +- .../language/ja/admin/general/dashboard.json | 6 +++--- public/language/ru/admin/advanced/cache.json | 10 +++++----- .../language/ru/admin/advanced/database.json | 14 ++++++------- public/language/ru/admin/advanced/errors.json | 20 +++++++++---------- .../ru/admin/appearance/customise.json | 2 +- public/language/ru/groups.json | 2 +- public/language/ru/login.json | 2 +- public/language/ru/search.json | 2 +- 9 files changed, 30 insertions(+), 30 deletions(-) diff --git a/public/language/ja/admin/extend/plugins.json b/public/language/ja/admin/extend/plugins.json index 4756c93c85..35159ce1df 100644 --- a/public/language/ja/admin/extend/plugins.json +++ b/public/language/ja/admin/extend/plugins.json @@ -5,7 +5,7 @@ "out-of-date": "期限切れ", "none-found": "プラグインが見つかりませんでした", "none-active": "アクティブなプラグインが見つかりませんでした", - "find-plugins": "プラグインが見つかりました", + "find-plugins": "プラグイン一覧", "plugin-search": "プラグインの検索", "plugin-search-placeholder": "プラグインを検索します...", diff --git a/public/language/ja/admin/general/dashboard.json b/public/language/ja/admin/general/dashboard.json index 86d2db6e52..7dcb9e74ec 100644 --- a/public/language/ja/admin/general/dashboard.json +++ b/public/language/ja/admin/general/dashboard.json @@ -15,13 +15,13 @@ "stats.all": "全て", "updates": "更新", - "running-version": "NodeBB v %1 を実行しています。", + "running-version": "NodeBB v %1 を実行しています。", "keep-updated": "常に最新のセキュリティパッチとバグ修正のためにNodeBBが最新であることを確認してください。", "up-to-date": "

    あなたは最新の状態です。 ", "upgrade-available": "

    新しいバージョン (v%1) がリリースされました。NodeBBのアップグレードを検討してください。

    ", - "prerelease-upgrade-available": "

    これはNodeBBの旧リリースのバージョンです。新しいバージョン(v%1)がリリースされました。 NodeBBのアップグレードを検討してください。", + "prerelease-upgrade-available": "

    これはNodeBBの旧リリースのバージョンです。新しいバージョン(v%1)がリリースされました。 NodeBBのアップグレードを検討してください。", "prerelease-warning": "

    これはNodeBBのプレリリース版です。意図しないバグが発生することがあります。

    ", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", + "running-in-development": "フォーラムが開発モードで動作しています。フォーラムの動作が脆弱かもしれませんので、管理者に問い合わせてください。", "notices": "通知", "restart-not-required": "再起動は必要ありません", diff --git a/public/language/ru/admin/advanced/cache.json b/public/language/ru/admin/advanced/cache.json index c0487f399b..70e88bd261 100644 --- a/public/language/ru/admin/advanced/cache.json +++ b/public/language/ru/admin/advanced/cache.json @@ -1,10 +1,10 @@ { - "post-cache": "Кэш записи", - "posts-in-cache": "Записей в кэше", - "average-post-size": "Средний размер записи", - "length-to-max": "Длина / Максимальная", + "post-cache": "Кэш сообщений", + "posts-in-cache": "Закешировано сообщений", + "average-post-size": "Средний размер сообщения", + "length-to-max": "Размер / Максимум", "percent-full": "%1% Full", - "post-cache-size": "Размер записи в кэше", + "post-cache-size": "Размер кэша сообщений", "items-in-cache": "Items in Cache", "control-panel": "Панель управления", "update-settings": "Обновить настройки кэша" diff --git a/public/language/ru/admin/advanced/database.json b/public/language/ru/admin/advanced/database.json index f7db6220ee..134287bc14 100644 --- a/public/language/ru/admin/advanced/database.json +++ b/public/language/ru/admin/advanced/database.json @@ -5,22 +5,22 @@ "uptime-days": "Uptime in Days", "mongo": "Mongo", - "mongo.version": "MongoDB Version", + "mongo.version": "Версия MongoDB", "mongo.storage-engine": "Storage Engine", - "mongo.collections": "Collections", - "mongo.objects": "Objects", - "mongo.avg-object-size": "Avg. Object Size", + "mongo.collections": "Коллекции", + "mongo.objects": "Документы", + "mongo.avg-object-size": "Средний размер документа", "mongo.data-size": "Data Size", "mongo.storage-size": "Storage Size", "mongo.index-size": "Index Size", - "mongo.file-size": "File Size", + "mongo.file-size": "Размер файла", "mongo.resident-memory": "Resident Memory", "mongo.virtual-memory": "Virtual Memory", "mongo.mapped-memory": "Mapped Memory", - "mongo.raw-info": "MongoDB Raw Info", + "mongo.raw-info": "Сырые данные о MongoDB", "redis": "Redis", - "redis.version": "Redis Version", + "redis.version": "Версия Redis", "redis.connected-clients": "Connected Clients", "redis.connected-slaves": "Connected Slaves", "redis.blocked-clients": "Blocked Clients", diff --git a/public/language/ru/admin/advanced/errors.json b/public/language/ru/admin/advanced/errors.json index 546f0f1508..b3185f3cdb 100644 --- a/public/language/ru/admin/advanced/errors.json +++ b/public/language/ru/admin/advanced/errors.json @@ -1,14 +1,14 @@ { - "figure-x": "Figure %1", - "error-events-per-day": "%1 events per day", - "error.404": "404 Not Found", - "error.503": "503 Service Unavailable", - "manage-error-log": "Manage Error Log", + "figure-x": "Рисунок %1", + "error-events-per-day": "событий %1 в день", + "error.404": "404 Не найдено", + "error.503": "503 Сервис недоступен", + "manage-error-log": "Управление журналами ошибок", "export-error-log": "Export Error Log (CSV)", "clear-error-log": "Clear Error Log", - "route": "Route", - "count": "Count", - "no-routes-not-found": "Hooray! No 404 errors!", - "clear404-confirm": "Are you sure you wish to clear the 404 error logs?", - "clear404-success": "\"404 Not Found\" errors cleared" + "route": "Путь", + "count": "Кол-во", + "no-routes-not-found": "Ура! Ошибок 404 нет!", + "clear404-confirm": "Вы уверены, что хотите очистить журнал ошибок 404?", + "clear404-success": "Журнал ошибок 404 очищен" } \ No newline at end of file diff --git a/public/language/ru/admin/appearance/customise.json b/public/language/ru/admin/appearance/customise.json index 767d443e29..226ce535ee 100644 --- a/public/language/ru/admin/appearance/customise.json +++ b/public/language/ru/admin/appearance/customise.json @@ -1,5 +1,5 @@ { - "custom-css": "Custom CSS", + "custom-css": "Свой CSS", "custom-css.description": "Enter your own CSS declarations here, which will be applied after all other styles.", "custom-css.enable": "Enable Custom CSS", diff --git a/public/language/ru/groups.json b/public/language/ru/groups.json index 9f0852bcd3..7f98491798 100644 --- a/public/language/ru/groups.json +++ b/public/language/ru/groups.json @@ -27,7 +27,7 @@ "details.disableJoinRequests": "Отключить запросы на приглашение", "details.grant": "Выдать/забрать привилегии администратора", "details.kick": "Исключить", - "details.kick_confirm": "Are you sure you want to remove this member from the group?", + "details.kick_confirm": "Вы уверены, что хотите удалить этого участника из группы?", "details.owner_options": "Настройки группы", "details.group_name": "Имя группы", "details.member_count": "Количество участников", diff --git a/public/language/ru/login.json b/public/language/ru/login.json index 4bf5fc392d..a0ab9e619b 100644 --- a/public/language/ru/login.json +++ b/public/language/ru/login.json @@ -7,6 +7,6 @@ "alternative_logins": "Войти через", "failed_login_attempt": "Неправильно указано имя пользователя или электронная почта", "login_successful": "Вы успешно вошли!", - "dont_have_account": "Нет акканута?", + "dont_have_account": "Нет аккаунта?", "logged-out-due-to-inactivity": "Вы вышли из панели управления администратора из-за бездействия" } \ No newline at end of file diff --git a/public/language/ru/search.json b/public/language/ru/search.json index dcc85a00ef..9ff5643500 100644 --- a/public/language/ru/search.json +++ b/public/language/ru/search.json @@ -12,7 +12,7 @@ "reply-count": "Количество ответов", "at-least": "Минимум", "at-most": "Максимум", - "relevance": "Relevance", + "relevance": "Релевантность", "post-time": "Время публикации", "newer-than": "Ранее чем", "older-than": "Позже чем", From 1ac14a466e66539f101775963351d3abb63a3184 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Mon, 24 Apr 2017 11:22:38 -0600 Subject: [PATCH 241/316] Reject non-audio upload requests to the sounds route --- src/controllers/admin/uploads.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/controllers/admin/uploads.js b/src/controllers/admin/uploads.js index 02bd065c0b..4280a2793b 100644 --- a/src/controllers/admin/uploads.js +++ b/src/controllers/admin/uploads.js @@ -5,6 +5,7 @@ var path = require('path'); var async = require('async'); var nconf = require('nconf'); var winston = require('winston'); +var mime = require('mime'); var meta = require('../../meta'); var file = require('../../file'); @@ -102,6 +103,11 @@ uploadsController.uploadLogo = function (req, res, next) { uploadsController.uploadSound = function (req, res, next) { var uploadedFile = req.files.files[0]; + var mimeType = mime.lookup(uploadedFile.name); + if (!/^audio\//.test(mimeType)) { + return next(Error('[[error:invalid-data]]')); + } + file.saveFileToLocal(uploadedFile.name, 'sounds', uploadedFile.path, function (err) { if (err) { return next(err); From 4900f21dd5323883ad8a90bb335e200d7aab8bbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 24 Apr 2017 13:30:01 -0400 Subject: [PATCH 242/316] fix crash in header --- src/middleware/header.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/middleware/header.js b/src/middleware/header.js index 0eb9cc9a1f..f58d4811c2 100644 --- a/src/middleware/header.js +++ b/src/middleware/header.js @@ -41,6 +41,7 @@ module.exports = function (middleware) { middleware.renderHeader = function (req, res, data, callback) { var registrationType = meta.config.registrationType || 'normal'; + res.locals.config = res.locals.config || {}; var templateValues = { title: meta.config.title || '', description: meta.config.description || '', From c45c4a5fdbb36e3c48655838c0004bfe6b703f73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 24 Apr 2017 13:31:38 -0400 Subject: [PATCH 243/316] on login display invalid-login-credentials --- public/language/en-GB/error.json | 1 + src/controllers/authentication.js | 21 +++++++-------------- src/user/auth.js | 3 +++ 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/public/language/en-GB/error.json b/public/language/en-GB/error.json index b5b90c04e5..e19075cd64 100644 --- a/public/language/en-GB/error.json +++ b/public/language/en-GB/error.json @@ -15,6 +15,7 @@ "invalid-title": "Invalid title", "invalid-user-data": "Invalid User Data", "invalid-password": "Invalid Password", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Please specify both a username and password", "invalid-search-term": "Invalid search term", "csrf-invalid": "We were unable to log you in, likely due to an expired session. Please try again", diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js index 76f2da243d..c06f1ee400 100644 --- a/src/controllers/authentication.js +++ b/src/controllers/authentication.js @@ -16,7 +16,7 @@ var Password = require('../password'); var sockets = require('../socket.io'); -var authenticationController = {}; +var authenticationController = module.exports; authenticationController.register = function (req, res) { var registrationType = meta.config.registrationType || 'normal'; @@ -357,13 +357,8 @@ authenticationController.localLogin = function (req, username, password, next) { user.getUidByUserslug(userslug, next); }, function (_uid, next) { - if (!_uid) { - return next(new Error('[[error:no-user]]')); - } uid = _uid; - user.auth.logAttempt(uid, req.ip, next); - }, - function (next) { + async.parallel({ userData: function (next) { db.getObjectFields('user:' + uid, ['password', 'passwordExpiry'], next); @@ -384,9 +379,7 @@ authenticationController.localLogin = function (req, username, password, next) { if (!result.isAdmin && parseInt(meta.config.allowLocalLogin, 10) === 0) { return next(new Error('[[error:local-login-disabled]]')); } - if (!userData || !userData.password) { - return next(new Error('[[error:invalid-user-data]]')); - } + if (result.banned) { // Retrieve ban reason and show error return user.getLatestBanInfo(uid, function (err, banInfo) { @@ -404,11 +397,14 @@ authenticationController.localLogin = function (req, username, password, next) { }); } + user.auth.logAttempt(uid, req.ip, next); + }, + function (next) { Password.compare(password, userData.password, next); }, function (passwordMatch, next) { if (!passwordMatch) { - return next(new Error('[[error:invalid-password]]')); + return next(new Error('[[error:invalid-login-credentials]]')); } user.auth.clearLoginAttempts(uid); next(null, userData, '[[success:authentication-successful]]'); @@ -441,6 +437,3 @@ authenticationController.logout = function (req, res, next) { res.status(200).send(''); } }; - - -module.exports = authenticationController; diff --git a/src/user/auth.js b/src/user/auth.js index 29a79f39c4..8a4582c155 100644 --- a/src/user/auth.js +++ b/src/user/auth.js @@ -11,6 +11,9 @@ module.exports = function (User) { User.auth = {}; User.auth.logAttempt = function (uid, ip, callback) { + if (!parseInt(uid, 10)) { + return setImmediate(callback); + } async.waterfall([ function (next) { db.exists('lockout:' + uid, next); From 65e31493a9c5c921431d18d2487a2994c7f7285c Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Mon, 24 Apr 2017 11:40:39 -0600 Subject: [PATCH 244/316] Escape ACP search values --- public/src/admin/modules/search.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/src/admin/modules/search.js b/public/src/admin/modules/search.js index 7694b21b3a..0df7b7b385 100644 --- a/public/src/admin/modules/search.js +++ b/public/src/admin/modules/search.js @@ -73,7 +73,7 @@ define('admin/modules/search', ['mousetrap'], function (mousetrap) { if (!selected.length) { selected = menu.find('li.result > a').first().attr('href'); } - var href = selected || config.relative_path + '/search?in=titlesposts&term=' + input.val(); + var href = selected || config.relative_path + '/search?in=titlesposts&term=' + escape(input.val()); ajaxify.go(href.replace(/^\//, '')); @@ -140,9 +140,9 @@ define('admin/modules/search', ['mousetrap'], function (mousetrap) { menu.find('.search-forum') .not('.divider') .find('a') - .attr('href', config.relative_path + '/search?in=titlesposts&term=' + value) + .attr('href', config.relative_path + '/search?in=titlesposts&term=' + escape(value)) .find('strong') - .html(value); + .text(value); } else { menu.removeClass('state-no-results state-yes-results'); } From 9d229ae960c96b434ba8311b7cad12261d815851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 24 Apr 2017 18:52:59 -0400 Subject: [PATCH 245/316] fix path to log file if base_dir is undefined --- src/meta/logs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meta/logs.js b/src/meta/logs.js index e85c0a5e11..f6e0a84909 100644 --- a/src/meta/logs.js +++ b/src/meta/logs.js @@ -7,7 +7,7 @@ var winston = require('winston'); module.exports = function (Meta) { Meta.logs = { - path: path.join(nconf.get('base_dir'), 'logs', 'output.log'), + path: path.join(nconf.get('base_dir') || (__dirname + '../../'), 'logs', 'output.log'), }; Meta.logs.get = function (callback) { From 085d8c2f92e51a723d6e4d9674c4e9601c22371f Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 25 Apr 2017 16:48:54 -0400 Subject: [PATCH 246/316] fix lint --- src/meta/logs.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/meta/logs.js b/src/meta/logs.js index f6e0a84909..4df96261cc 100644 --- a/src/meta/logs.js +++ b/src/meta/logs.js @@ -1,13 +1,12 @@ 'use strict'; var path = require('path'); -var nconf = require('nconf'); var fs = require('fs'); var winston = require('winston'); module.exports = function (Meta) { Meta.logs = { - path: path.join(nconf.get('base_dir') || (__dirname + '../../'), 'logs', 'output.log'), + path: path.join(__dirname, '..', '..', 'logs', 'output.log'), }; Meta.logs.get = function (callback) { From 870bb87b0858481cacf8c6a1d4bbd367917eaf58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 26 Apr 2017 13:17:03 -0400 Subject: [PATCH 247/316] always pass strings to bcrypt compare --- src/bcrypt.js | 2 +- src/password.js | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bcrypt.js b/src/bcrypt.js index 40a493e75d..8cce80372e 100644 --- a/src/bcrypt.js +++ b/src/bcrypt.js @@ -9,7 +9,7 @@ process.on('message', function (msg) { if (msg.type === 'hash') { hashPassword(msg.password, msg.rounds); } else if (msg.type === 'compare') { - bcrypt.compare(msg.password, msg.hash, done); + bcrypt.compare(String(msg.password || ''), String(msg.hash || ''), done); } }); diff --git a/src/password.js b/src/password.js index 816e357d12..5405941fff 100644 --- a/src/password.js +++ b/src/password.js @@ -9,6 +9,9 @@ }; module.compare = function (password, hash, callback) { + if (!hash || !password) { + return setImmediate(callback, null, false); + } forkChild({ type: 'compare', password: password, hash: hash }, callback); }; From 7a6b685aa0d96f5ddec4988f0bccdc71698e3fa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 26 Apr 2017 14:19:48 -0400 Subject: [PATCH 248/316] if the main and last post is purged, purge the topic as well --- src/socket.io/posts/tools.js | 33 ++++++++++++++++++++------------- test/posts.js | 6 +++--- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/socket.io/posts/tools.js b/src/socket.io/posts/tools.js index c1ad05b119..c075a96a8e 100644 --- a/src/socket.io/posts/tools.js +++ b/src/socket.io/posts/tools.js @@ -138,39 +138,46 @@ module.exports = function (SocketPosts) { return callback(new Error('[[error:invalid-data]]')); } var postData; + var topicData; + var isMainAndLast = false; async.waterfall([ function (next) { isMainAndLastPost(data.pid, next); }, function (results, next) { if (results.isMain && !results.isLast) { - return callback(new Error('[[error:cant-purge-main-post]]')); + return next(new Error('[[error:cant-purge-main-post]]')); } - if (results.isMain && results.isLast) { - return deleteTopicOf(data.pid, socket, next); - } - setImmediate(next); - }, - function (next) { - posts.getPostField(data.pid, 'toPid', next); + isMainAndLast = results.isMain && results.isLast; + + posts.getPostFields(data.pid, ['toPid', 'tid'], next); }, - function (toPid, next) { - postData = { pid: data.pid, toPid: toPid }; + function (_postData, next) { + postData = _postData; + postData.pid = data.pid; posts.tools.purge(socket.uid, data.pid, next); }, function (next) { websockets.in('topic_' + data.tid).emit('event:post_purged', postData); - topics.getTopicField(data.tid, 'title', next); + topics.getTopicFields(data.tid, ['title', 'cid'], next); }, - function (title, next) { + function (_topicData, next) { + topicData = _topicData; events.log({ type: 'post-purge', uid: socket.uid, pid: data.pid, ip: socket.ip, - title: String(title), + title: String(topicData.title), }, next); }, + function (next) { + if (isMainAndLast) { + socketTopics.doTopicAction('purge', 'event:topic_purged', socket, { tids: [postData.tid], cid: topicData.cid }, next); + } else { + setImmediate(next); + } + }, ], callback); }; diff --git a/test/posts.js b/test/posts.js index 49a4783872..50441a58e1 100644 --- a/test/posts.js +++ b/test/posts.js @@ -274,16 +274,16 @@ describe('Post\'s', function () { }); }); - it('should purge posts and delete topic', function (done) { + it('should purge posts and purge topic', function (done) { createTopicWithReply(function (topicPostData, replyData) { socketPosts.purgePosts({ uid: voterUid }, { pids: [replyData.pid, topicPostData.postData.pid], tid: topicPostData.topicData.tid }, function (err) { assert.ifError(err); posts.exists('post:' + replyData.pid, function (err, exists) { assert.ifError(err); assert.equal(exists, false); - topics.getTopicField(topicPostData.topicData.tid, 'deleted', function (err, deleted) { + topics.exists(topicPostData.topicData.tid, function (err, exists) { assert.ifError(err); - assert.equal(parseInt(deleted, 10), 1); + assert(!exists); done(); }); }); From 2a0cb3b9eed9da1fbc0fe0215f9bc8928a856ba2 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Wed, 26 Apr 2017 14:57:49 -0600 Subject: [PATCH 249/316] Fix profile covers not working with subfolders Remove relative_path from the paths saved in the database so they're more portable --- src/controllers/accounts/helpers.js | 3 +- src/controllers/accounts/profile.js | 3 -- .../remove_relative_uploaded_profile_cover.js | 38 +++++++++++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 src/upgrades/1.5.0/remove_relative_uploaded_profile_cover.js diff --git a/src/controllers/accounts/helpers.js b/src/controllers/accounts/helpers.js index 339b3a2c2f..3c73d31800 100644 --- a/src/controllers/accounts/helpers.js +++ b/src/controllers/accounts/helpers.js @@ -4,6 +4,7 @@ var async = require('async'); var validator = require('validator'); var winston = require('winston'); +var nconf = require('nconf'); var user = require('../../user'); var groups = require('../../groups'); @@ -144,7 +145,7 @@ helpers.getUserDataByUserSlug = function (userslug, callerUID, callback) { userData.birthday = validator.escape(String(userData.birthday || '')); userData.moderationNote = validator.escape(String(userData.moderationNote || '')); - userData['cover:url'] = userData['cover:url'] || require('../../coverPhoto').getDefaultProfileCover(userData.uid); + userData['cover:url'] = (nconf.get('relative_path') + userData['cover:url']) || require('../../coverPhoto').getDefaultProfileCover(userData.uid); userData['cover:position'] = validator.escape(String(userData['cover:position'] || '50% 50%')); userData['username:disableEdit'] = !userData.isAdmin && parseInt(meta.config['username:disableEdit'], 10) === 1; userData['email:disableEdit'] = !userData.isAdmin && parseInt(meta.config['email:disableEdit'], 10) === 1; diff --git a/src/controllers/accounts/profile.js b/src/controllers/accounts/profile.js index d22e3256bf..450ee92f60 100644 --- a/src/controllers/accounts/profile.js +++ b/src/controllers/accounts/profile.js @@ -82,9 +82,6 @@ profileController.get = function (req, res, callback) { var pageCount = Math.ceil(userData.postcount / itemsPerPage); userData.pagination = pagination.create(page, pageCount, req.query); - userData['cover:url'] = userData['cover:url'] || require('../../coverPhoto').getDefaultProfileCover(userData.uid); - userData['cover:position'] = userData['cover:position'] || '50% 50%'; - if (!parseInt(userData.profileviews, 10)) { userData.profileviews = 1; } diff --git a/src/upgrades/1.5.0/remove_relative_uploaded_profile_cover.js b/src/upgrades/1.5.0/remove_relative_uploaded_profile_cover.js new file mode 100644 index 0000000000..54a5e80ff2 --- /dev/null +++ b/src/upgrades/1.5.0/remove_relative_uploaded_profile_cover.js @@ -0,0 +1,38 @@ +/* jslint node: true */ + +'use strict'; + +var db = require('../../database'); +var batch = require('../../batch'); + +var async = require('async'); + +module.exports = { + name: 'Remove relative_path from uploaded profile cover urls', + timestamp: Date.UTC(2017, 3, 26), + method: function (callback) { + var progress = this.progress; + + batch.processSortedSet('users:joindate', function (ids, done) { + async.each(ids, function (uid, cb) { + async.waterfall([ + function (next) { + db.getObjectField('user:' + uid, 'cover:url', next); + }, + function (url, next) { + progress.incr(); + + if (!url) { + return next(); + } + + var newUrl = url.replace(/^.*?\/uploads\//, '/assets/uploads/'); + db.setObjectField('user:' + uid, 'cover:url', newUrl, next); + }, + ], cb); + }, done); + }, { + progress: this.progress, + }, callback); + }, +}; From 8a87311d5c35c259e0d4f6b5889d78675dec32d1 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Wed, 26 Apr 2017 15:08:12 -0600 Subject: [PATCH 250/316] Remove annoying jslint comment Use an rc file instead --- src/upgrade.js | 2 -- src/upgrades/1.0.0/chat_room_hashes.js | 2 -- src/upgrades/1.0.0/chat_upgrade.js | 2 -- src/upgrades/1.0.0/global_moderators.js | 2 -- src/upgrades/1.0.0/social_post_sharing.js | 2 -- src/upgrades/1.0.0/theme_to_active_plugins.js | 2 -- src/upgrades/1.0.0/user_best_posts.js | 2 -- src/upgrades/1.0.0/users_notvalidated.js | 2 -- src/upgrades/1.1.0/assign_topic_read_privilege.js | 2 -- src/upgrades/1.1.0/dismiss_flags_from_deleted_topics.js | 2 -- src/upgrades/1.1.0/group_title_update.js | 2 -- src/upgrades/1.1.0/separate_upvote_downvote.js | 2 -- src/upgrades/1.1.0/user_post_count_per_tid.js | 2 -- src/upgrades/1.1.1/remove_negative_best_posts.js | 2 -- src/upgrades/1.1.1/upload_privileges.js | 2 -- src/upgrades/1.2.0/category_recent_tids.js | 2 -- src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js | 2 -- src/upgrades/1.3.0/favourites_to_bookmarks.js | 2 -- src/upgrades/1.3.0/sorted_sets_for_post_replies.js | 2 -- src/upgrades/1.4.0/global_and_user_language_keys.js | 2 -- src/upgrades/1.4.0/sorted_set_for_pinned_topics.js | 2 -- src/upgrades/1.4.4/config_urls_update.js | 2 -- src/upgrades/1.4.4/sound_settings.js | 2 -- src/upgrades/1.4.6/delete_sessions.js | 2 -- src/upgrades/1.5.0/allowed_file_extensions.js | 2 -- src/upgrades/1.5.0/flags_refactor.js | 2 -- src/upgrades/1.5.0/moderation_history_refactor.js | 2 -- src/upgrades/1.5.0/post_votes_zset.js | 2 -- src/upgrades/1.5.0/remove_relative_uploaded_profile_cover.js | 2 -- src/upgrades/TEMPLATE | 2 -- 30 files changed, 60 deletions(-) diff --git a/src/upgrade.js b/src/upgrade.js index c395dc8884..22c7867eeb 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var async = require('async'); diff --git a/src/upgrades/1.0.0/chat_room_hashes.js b/src/upgrades/1.0.0/chat_room_hashes.js index 054b6c8d7e..ae52be31f7 100644 --- a/src/upgrades/1.0.0/chat_room_hashes.js +++ b/src/upgrades/1.0.0/chat_room_hashes.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.0.0/chat_upgrade.js b/src/upgrades/1.0.0/chat_upgrade.js index ae93ca95a5..d5a971d4b4 100644 --- a/src/upgrades/1.0.0/chat_upgrade.js +++ b/src/upgrades/1.0.0/chat_upgrade.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.0.0/global_moderators.js b/src/upgrades/1.0.0/global_moderators.js index 3ba5f49955..7639da9453 100644 --- a/src/upgrades/1.0.0/global_moderators.js +++ b/src/upgrades/1.0.0/global_moderators.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var async = require('async'); diff --git a/src/upgrades/1.0.0/social_post_sharing.js b/src/upgrades/1.0.0/social_post_sharing.js index 03d303fe07..3477da86bb 100644 --- a/src/upgrades/1.0.0/social_post_sharing.js +++ b/src/upgrades/1.0.0/social_post_sharing.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.0.0/theme_to_active_plugins.js b/src/upgrades/1.0.0/theme_to_active_plugins.js index 3a3bdd25eb..07b95f45aa 100644 --- a/src/upgrades/1.0.0/theme_to_active_plugins.js +++ b/src/upgrades/1.0.0/theme_to_active_plugins.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.0.0/user_best_posts.js b/src/upgrades/1.0.0/user_best_posts.js index 560ef7b29c..986e88efa7 100644 --- a/src/upgrades/1.0.0/user_best_posts.js +++ b/src/upgrades/1.0.0/user_best_posts.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.0.0/users_notvalidated.js b/src/upgrades/1.0.0/users_notvalidated.js index 5b5e60270b..a69deb90df 100644 --- a/src/upgrades/1.0.0/users_notvalidated.js +++ b/src/upgrades/1.0.0/users_notvalidated.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.1.0/assign_topic_read_privilege.js b/src/upgrades/1.1.0/assign_topic_read_privilege.js index 9f23cd7384..93d06485e6 100644 --- a/src/upgrades/1.1.0/assign_topic_read_privilege.js +++ b/src/upgrades/1.1.0/assign_topic_read_privilege.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.1.0/dismiss_flags_from_deleted_topics.js b/src/upgrades/1.1.0/dismiss_flags_from_deleted_topics.js index 85d14f6bbc..6cbf6b7254 100644 --- a/src/upgrades/1.1.0/dismiss_flags_from_deleted_topics.js +++ b/src/upgrades/1.1.0/dismiss_flags_from_deleted_topics.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.1.0/group_title_update.js b/src/upgrades/1.1.0/group_title_update.js index 1c6065a3d4..7b8847f276 100644 --- a/src/upgrades/1.1.0/group_title_update.js +++ b/src/upgrades/1.1.0/group_title_update.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.1.0/separate_upvote_downvote.js b/src/upgrades/1.1.0/separate_upvote_downvote.js index 3f78667dc5..dbfb5aee09 100644 --- a/src/upgrades/1.1.0/separate_upvote_downvote.js +++ b/src/upgrades/1.1.0/separate_upvote_downvote.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.1.0/user_post_count_per_tid.js b/src/upgrades/1.1.0/user_post_count_per_tid.js index 7b27606c99..f275e7a9a4 100644 --- a/src/upgrades/1.1.0/user_post_count_per_tid.js +++ b/src/upgrades/1.1.0/user_post_count_per_tid.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.1.1/remove_negative_best_posts.js b/src/upgrades/1.1.1/remove_negative_best_posts.js index 22b6bb9ad1..cadf1a397f 100644 --- a/src/upgrades/1.1.1/remove_negative_best_posts.js +++ b/src/upgrades/1.1.1/remove_negative_best_posts.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.1.1/upload_privileges.js b/src/upgrades/1.1.1/upload_privileges.js index d84516efb7..217e6e19fe 100644 --- a/src/upgrades/1.1.1/upload_privileges.js +++ b/src/upgrades/1.1.1/upload_privileges.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.2.0/category_recent_tids.js b/src/upgrades/1.2.0/category_recent_tids.js index 9256cde5f5..3d8d1cdf9c 100644 --- a/src/upgrades/1.2.0/category_recent_tids.js +++ b/src/upgrades/1.2.0/category_recent_tids.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js b/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js index acc87b7b19..40f8eda0ce 100644 --- a/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js +++ b/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.3.0/favourites_to_bookmarks.js b/src/upgrades/1.3.0/favourites_to_bookmarks.js index 065f02c94a..b1037c860f 100644 --- a/src/upgrades/1.3.0/favourites_to_bookmarks.js +++ b/src/upgrades/1.3.0/favourites_to_bookmarks.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.3.0/sorted_sets_for_post_replies.js b/src/upgrades/1.3.0/sorted_sets_for_post_replies.js index 1cce287829..e5a5506591 100644 --- a/src/upgrades/1.3.0/sorted_sets_for_post_replies.js +++ b/src/upgrades/1.3.0/sorted_sets_for_post_replies.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.4.0/global_and_user_language_keys.js b/src/upgrades/1.4.0/global_and_user_language_keys.js index 20548e6820..3428ad5678 100644 --- a/src/upgrades/1.4.0/global_and_user_language_keys.js +++ b/src/upgrades/1.4.0/global_and_user_language_keys.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.4.0/sorted_set_for_pinned_topics.js b/src/upgrades/1.4.0/sorted_set_for_pinned_topics.js index a82ef80884..c461c8df05 100644 --- a/src/upgrades/1.4.0/sorted_set_for_pinned_topics.js +++ b/src/upgrades/1.4.0/sorted_set_for_pinned_topics.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.4.4/config_urls_update.js b/src/upgrades/1.4.4/config_urls_update.js index 9f4bba93ac..02689ceecb 100644 --- a/src/upgrades/1.4.4/config_urls_update.js +++ b/src/upgrades/1.4.4/config_urls_update.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.4.4/sound_settings.js b/src/upgrades/1.4.4/sound_settings.js index 73b15e881a..57768fcc65 100644 --- a/src/upgrades/1.4.4/sound_settings.js +++ b/src/upgrades/1.4.4/sound_settings.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.4.6/delete_sessions.js b/src/upgrades/1.4.6/delete_sessions.js index 3e143ffed2..571d4e125c 100644 --- a/src/upgrades/1.4.6/delete_sessions.js +++ b/src/upgrades/1.4.6/delete_sessions.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.5.0/allowed_file_extensions.js b/src/upgrades/1.5.0/allowed_file_extensions.js index 158c04ab1d..29e348f16f 100644 --- a/src/upgrades/1.5.0/allowed_file_extensions.js +++ b/src/upgrades/1.5.0/allowed_file_extensions.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.5.0/flags_refactor.js b/src/upgrades/1.5.0/flags_refactor.js index 08fc1831d8..c8be689f18 100644 --- a/src/upgrades/1.5.0/flags_refactor.js +++ b/src/upgrades/1.5.0/flags_refactor.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.5.0/moderation_history_refactor.js b/src/upgrades/1.5.0/moderation_history_refactor.js index 50bb10a7e8..59e10367d8 100644 --- a/src/upgrades/1.5.0/moderation_history_refactor.js +++ b/src/upgrades/1.5.0/moderation_history_refactor.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.5.0/post_votes_zset.js b/src/upgrades/1.5.0/post_votes_zset.js index 7feabe8169..4b5795a4b8 100644 --- a/src/upgrades/1.5.0/post_votes_zset.js +++ b/src/upgrades/1.5.0/post_votes_zset.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.5.0/remove_relative_uploaded_profile_cover.js b/src/upgrades/1.5.0/remove_relative_uploaded_profile_cover.js index 54a5e80ff2..fc8eb542f6 100644 --- a/src/upgrades/1.5.0/remove_relative_uploaded_profile_cover.js +++ b/src/upgrades/1.5.0/remove_relative_uploaded_profile_cover.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/TEMPLATE b/src/upgrades/TEMPLATE index 83cd01586d..9618bc4f9e 100644 --- a/src/upgrades/TEMPLATE +++ b/src/upgrades/TEMPLATE @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); From 6095b7e6dbef589a25baf2f5f2c8da941935cb4b Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 27 Apr 2017 02:44:02 -0400 Subject: [PATCH 251/316] change where progress.incr is called for flags_refactor upgrade script --- src/upgrades/1.5.0/flags_refactor.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/upgrades/1.5.0/flags_refactor.js b/src/upgrades/1.5.0/flags_refactor.js index 08fc1831d8..a175cd2854 100644 --- a/src/upgrades/1.5.0/flags_refactor.js +++ b/src/upgrades/1.5.0/flags_refactor.js @@ -26,6 +26,8 @@ module.exports = { }); async.each(posts, function (post, next) { + progress.incr(); + async.parallel({ uids: async.apply(db.getSortedSetRangeWithScores, 'pid:' + post.pid + ':flag:uids', 0, -1), reasons: async.apply(db.getSortedSetRange, 'pid:' + post.pid + ':flag:uid:reason', 0, -1), @@ -81,8 +83,6 @@ module.exports = { } else { next(err); } - - progress.incr(); }); }); }, next); From db4fdbaebe4d248f44333e77f7a73228d23bd871 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 27 Apr 2017 02:47:13 -0400 Subject: [PATCH 252/316] removing jslint directive at top of upgrade scripts @pitaj --- src/upgrades/1.0.0/chat_room_hashes.js | 2 -- src/upgrades/1.0.0/chat_upgrade.js | 2 -- src/upgrades/1.0.0/global_moderators.js | 2 -- src/upgrades/1.0.0/social_post_sharing.js | 2 -- src/upgrades/1.0.0/theme_to_active_plugins.js | 2 -- src/upgrades/1.0.0/user_best_posts.js | 2 -- src/upgrades/1.0.0/users_notvalidated.js | 2 -- src/upgrades/1.1.0/assign_topic_read_privilege.js | 2 -- src/upgrades/1.1.0/dismiss_flags_from_deleted_topics.js | 2 -- src/upgrades/1.1.0/group_title_update.js | 2 -- src/upgrades/1.1.0/separate_upvote_downvote.js | 2 -- src/upgrades/1.1.0/user_post_count_per_tid.js | 2 -- src/upgrades/1.1.1/remove_negative_best_posts.js | 2 -- src/upgrades/1.1.1/upload_privileges.js | 2 -- src/upgrades/1.2.0/category_recent_tids.js | 2 -- src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js | 2 -- src/upgrades/1.3.0/favourites_to_bookmarks.js | 2 -- src/upgrades/1.3.0/sorted_sets_for_post_replies.js | 2 -- src/upgrades/1.4.0/global_and_user_language_keys.js | 2 -- src/upgrades/1.4.0/sorted_set_for_pinned_topics.js | 2 -- src/upgrades/1.4.4/config_urls_update.js | 2 -- src/upgrades/1.4.4/sound_settings.js | 2 -- src/upgrades/1.4.6/delete_sessions.js | 2 -- src/upgrades/1.5.0/allowed_file_extensions.js | 2 -- src/upgrades/1.5.0/flags_refactor.js | 2 -- src/upgrades/1.5.0/moderation_history_refactor.js | 2 -- src/upgrades/1.5.0/post_votes_zset.js | 2 -- src/upgrades/TEMPLATE | 2 -- 28 files changed, 56 deletions(-) diff --git a/src/upgrades/1.0.0/chat_room_hashes.js b/src/upgrades/1.0.0/chat_room_hashes.js index 054b6c8d7e..ae52be31f7 100644 --- a/src/upgrades/1.0.0/chat_room_hashes.js +++ b/src/upgrades/1.0.0/chat_room_hashes.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.0.0/chat_upgrade.js b/src/upgrades/1.0.0/chat_upgrade.js index ae93ca95a5..d5a971d4b4 100644 --- a/src/upgrades/1.0.0/chat_upgrade.js +++ b/src/upgrades/1.0.0/chat_upgrade.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.0.0/global_moderators.js b/src/upgrades/1.0.0/global_moderators.js index 3ba5f49955..7639da9453 100644 --- a/src/upgrades/1.0.0/global_moderators.js +++ b/src/upgrades/1.0.0/global_moderators.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var async = require('async'); diff --git a/src/upgrades/1.0.0/social_post_sharing.js b/src/upgrades/1.0.0/social_post_sharing.js index 03d303fe07..3477da86bb 100644 --- a/src/upgrades/1.0.0/social_post_sharing.js +++ b/src/upgrades/1.0.0/social_post_sharing.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.0.0/theme_to_active_plugins.js b/src/upgrades/1.0.0/theme_to_active_plugins.js index 3a3bdd25eb..07b95f45aa 100644 --- a/src/upgrades/1.0.0/theme_to_active_plugins.js +++ b/src/upgrades/1.0.0/theme_to_active_plugins.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.0.0/user_best_posts.js b/src/upgrades/1.0.0/user_best_posts.js index 560ef7b29c..986e88efa7 100644 --- a/src/upgrades/1.0.0/user_best_posts.js +++ b/src/upgrades/1.0.0/user_best_posts.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.0.0/users_notvalidated.js b/src/upgrades/1.0.0/users_notvalidated.js index 5b5e60270b..a69deb90df 100644 --- a/src/upgrades/1.0.0/users_notvalidated.js +++ b/src/upgrades/1.0.0/users_notvalidated.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.1.0/assign_topic_read_privilege.js b/src/upgrades/1.1.0/assign_topic_read_privilege.js index 9f23cd7384..93d06485e6 100644 --- a/src/upgrades/1.1.0/assign_topic_read_privilege.js +++ b/src/upgrades/1.1.0/assign_topic_read_privilege.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.1.0/dismiss_flags_from_deleted_topics.js b/src/upgrades/1.1.0/dismiss_flags_from_deleted_topics.js index 85d14f6bbc..6cbf6b7254 100644 --- a/src/upgrades/1.1.0/dismiss_flags_from_deleted_topics.js +++ b/src/upgrades/1.1.0/dismiss_flags_from_deleted_topics.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.1.0/group_title_update.js b/src/upgrades/1.1.0/group_title_update.js index 1c6065a3d4..7b8847f276 100644 --- a/src/upgrades/1.1.0/group_title_update.js +++ b/src/upgrades/1.1.0/group_title_update.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.1.0/separate_upvote_downvote.js b/src/upgrades/1.1.0/separate_upvote_downvote.js index 3f78667dc5..dbfb5aee09 100644 --- a/src/upgrades/1.1.0/separate_upvote_downvote.js +++ b/src/upgrades/1.1.0/separate_upvote_downvote.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.1.0/user_post_count_per_tid.js b/src/upgrades/1.1.0/user_post_count_per_tid.js index 7b27606c99..f275e7a9a4 100644 --- a/src/upgrades/1.1.0/user_post_count_per_tid.js +++ b/src/upgrades/1.1.0/user_post_count_per_tid.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.1.1/remove_negative_best_posts.js b/src/upgrades/1.1.1/remove_negative_best_posts.js index 22b6bb9ad1..cadf1a397f 100644 --- a/src/upgrades/1.1.1/remove_negative_best_posts.js +++ b/src/upgrades/1.1.1/remove_negative_best_posts.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.1.1/upload_privileges.js b/src/upgrades/1.1.1/upload_privileges.js index d84516efb7..217e6e19fe 100644 --- a/src/upgrades/1.1.1/upload_privileges.js +++ b/src/upgrades/1.1.1/upload_privileges.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.2.0/category_recent_tids.js b/src/upgrades/1.2.0/category_recent_tids.js index 9256cde5f5..3d8d1cdf9c 100644 --- a/src/upgrades/1.2.0/category_recent_tids.js +++ b/src/upgrades/1.2.0/category_recent_tids.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js b/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js index acc87b7b19..40f8eda0ce 100644 --- a/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js +++ b/src/upgrades/1.2.0/edit_delete_deletetopic_privileges.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.3.0/favourites_to_bookmarks.js b/src/upgrades/1.3.0/favourites_to_bookmarks.js index 065f02c94a..b1037c860f 100644 --- a/src/upgrades/1.3.0/favourites_to_bookmarks.js +++ b/src/upgrades/1.3.0/favourites_to_bookmarks.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.3.0/sorted_sets_for_post_replies.js b/src/upgrades/1.3.0/sorted_sets_for_post_replies.js index 1cce287829..e5a5506591 100644 --- a/src/upgrades/1.3.0/sorted_sets_for_post_replies.js +++ b/src/upgrades/1.3.0/sorted_sets_for_post_replies.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.4.0/global_and_user_language_keys.js b/src/upgrades/1.4.0/global_and_user_language_keys.js index 20548e6820..3428ad5678 100644 --- a/src/upgrades/1.4.0/global_and_user_language_keys.js +++ b/src/upgrades/1.4.0/global_and_user_language_keys.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.4.0/sorted_set_for_pinned_topics.js b/src/upgrades/1.4.0/sorted_set_for_pinned_topics.js index a82ef80884..c461c8df05 100644 --- a/src/upgrades/1.4.0/sorted_set_for_pinned_topics.js +++ b/src/upgrades/1.4.0/sorted_set_for_pinned_topics.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.4.4/config_urls_update.js b/src/upgrades/1.4.4/config_urls_update.js index 9f4bba93ac..02689ceecb 100644 --- a/src/upgrades/1.4.4/config_urls_update.js +++ b/src/upgrades/1.4.4/config_urls_update.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.4.4/sound_settings.js b/src/upgrades/1.4.4/sound_settings.js index 73b15e881a..57768fcc65 100644 --- a/src/upgrades/1.4.4/sound_settings.js +++ b/src/upgrades/1.4.4/sound_settings.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.4.6/delete_sessions.js b/src/upgrades/1.4.6/delete_sessions.js index 3e143ffed2..571d4e125c 100644 --- a/src/upgrades/1.4.6/delete_sessions.js +++ b/src/upgrades/1.4.6/delete_sessions.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.5.0/allowed_file_extensions.js b/src/upgrades/1.5.0/allowed_file_extensions.js index 158c04ab1d..29e348f16f 100644 --- a/src/upgrades/1.5.0/allowed_file_extensions.js +++ b/src/upgrades/1.5.0/allowed_file_extensions.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.5.0/flags_refactor.js b/src/upgrades/1.5.0/flags_refactor.js index a175cd2854..f80714ee50 100644 --- a/src/upgrades/1.5.0/flags_refactor.js +++ b/src/upgrades/1.5.0/flags_refactor.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.5.0/moderation_history_refactor.js b/src/upgrades/1.5.0/moderation_history_refactor.js index 50bb10a7e8..59e10367d8 100644 --- a/src/upgrades/1.5.0/moderation_history_refactor.js +++ b/src/upgrades/1.5.0/moderation_history_refactor.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/1.5.0/post_votes_zset.js b/src/upgrades/1.5.0/post_votes_zset.js index 7feabe8169..4b5795a4b8 100644 --- a/src/upgrades/1.5.0/post_votes_zset.js +++ b/src/upgrades/1.5.0/post_votes_zset.js @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); diff --git a/src/upgrades/TEMPLATE b/src/upgrades/TEMPLATE index 83cd01586d..9618bc4f9e 100644 --- a/src/upgrades/TEMPLATE +++ b/src/upgrades/TEMPLATE @@ -1,5 +1,3 @@ -/* jslint node: true */ - 'use strict'; var db = require('../../database'); From 0b3656f34cf150f5ebc3a8e31efe9b56bfe2f8fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 27 Apr 2017 16:40:01 -0400 Subject: [PATCH 253/316] fix timeouts on category.purge when there are 20k groups --- src/categories/delete.js | 2 +- src/groups/delete.js | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/categories/delete.js b/src/categories/delete.js index 4aad25846f..d5da434c26 100644 --- a/src/categories/delete.js +++ b/src/categories/delete.js @@ -58,7 +58,7 @@ module.exports = function (Categories) { ], next); }, function (next) { - async.each(privileges.privilegeList, function (privilege, next) { + async.eachSeries(privileges.privilegeList, function (privilege, next) { groups.destroy('cid:' + cid + ':privileges:' + privilege, next); }, next); }, diff --git a/src/groups/delete.js b/src/groups/delete.js index e05eff6370..1a9f91ce0a 100644 --- a/src/groups/delete.js +++ b/src/groups/delete.js @@ -4,6 +4,7 @@ var async = require('async'); var plugins = require('../plugins'); var utils = require('../utils'); var db = require('./../database'); +var batch = require('../batch'); module.exports = function (Groups) { Groups.destroy = function (groupName, callback) { @@ -30,14 +31,14 @@ module.exports = function (Groups) { async.apply(db.delete, 'group:' + groupName + ':owners'), async.apply(db.deleteObjectField, 'groupslug:groupname', utils.slugify(groupName)), function (next) { - db.getSortedSetRange('groups:createtime', 0, -1, function (err, groups) { - if (err) { - return next(err); - } - async.each(groups, function (group, next) { - db.sortedSetRemove('group:' + group + ':members', groupName, next); - }, next); - }); + batch.processSortedSet('groups:createtime', function (groupNames, next) { + var keys = groupNames.map(function (group) { + return 'group:' + group + ':members'; + }); + db.sortedSetsRemove(keys, groupName, next); + }, { + batch: 500, + }, next); }, ], function (err) { if (err) { From af6cde553418cc25f1b974bec5987e899d89747c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 27 Apr 2017 18:58:21 -0400 Subject: [PATCH 254/316] show how many nodes responded --- .../en-GB/admin/development/info.json | 1 + src/controllers/admin/info.js | 19 ++++++++++++++----- src/views/admin/development/info.tpl | 2 ++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/public/language/en-GB/admin/development/info.json b/public/language/en-GB/admin/development/info.json index b2768ca212..24bf179655 100644 --- a/public/language/en-GB/admin/development/info.json +++ b/public/language/en-GB/admin/development/info.json @@ -1,5 +1,6 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", diff --git a/src/controllers/admin/info.js b/src/controllers/admin/info.js index 88fef98b4b..94c2b2c0d4 100644 --- a/src/controllers/admin/info.js +++ b/src/controllers/admin/info.js @@ -16,22 +16,30 @@ var info = {}; infoController.get = function (req, res) { info = {}; pubsub.publish('sync:node:info:start'); + var timeoutMS = 1000; setTimeout(function () { var data = []; Object.keys(info).forEach(function (key) { data.push(info[key]); }); data.sort(function (a, b) { - if (a.os.hostname < b.os.hostname) { + if (a.id < b.id) { return -1; } - if (a.os.hostname > b.os.hostname) { + if (a.id > b.id) { return 1; } return 0; }); - res.render('admin/development/info', { info: data, infoJSON: JSON.stringify(data, null, 4), host: os.hostname(), port: nconf.get('port') }); - }, 500); + res.render('admin/development/info', { + info: data, + infoJSON: JSON.stringify(data, null, 4), + host: os.hostname(), + port: nconf.get('port'), + nodeCount: data.length, + timeout: timeoutMS + }); + }, timeoutMS); }; pubsub.on('sync:node:info:start', function () { @@ -39,7 +47,8 @@ pubsub.on('sync:node:info:start', function () { if (err) { return winston.error(err); } - pubsub.publish('sync:node:info:end', { data: data, id: os.hostname() + ':' + nconf.get('port') }); + data.id = os.hostname() + ':' + nconf.get('port'); + pubsub.publish('sync:node:info:end', { data: data, id: data.id }); }); }); diff --git a/src/views/admin/development/info.tpl b/src/views/admin/development/info.tpl index 50963d4d8c..4a6d9b90b3 100644 --- a/src/views/admin/development/info.tpl +++ b/src/views/admin/development/info.tpl @@ -5,6 +5,8 @@
    + [[admin/development/info:nodes-responded, {nodeCount}, {timeOut}]] + From 13f5d29cb9428e252b7c39ecf4352b633ac7e146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 27 Apr 2017 19:16:32 -0400 Subject: [PATCH 255/316] eslint --- src/controllers/admin/info.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/controllers/admin/info.js b/src/controllers/admin/info.js index 94c2b2c0d4..734d151a3d 100644 --- a/src/controllers/admin/info.js +++ b/src/controllers/admin/info.js @@ -31,13 +31,13 @@ infoController.get = function (req, res) { } return 0; }); - res.render('admin/development/info', { - info: data, - infoJSON: JSON.stringify(data, null, 4), - host: os.hostname(), + res.render('admin/development/info', { + info: data, + infoJSON: JSON.stringify(data, null, 4), + host: os.hostname(), port: nconf.get('port'), nodeCount: data.length, - timeout: timeoutMS + timeout: timeoutMS, }); }, timeoutMS); }; From 82863a28f0aa701ec7e34bf6bdf1137eea4a6140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 28 Apr 2017 02:08:14 -0400 Subject: [PATCH 256/316] add the loaded topics after the last topic on the list --- public/src/client/recent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/client/recent.js b/public/src/client/recent.js index b8f9380198..71b91beced 100644 --- a/public/src/client/recent.js +++ b/public/src/client/recent.js @@ -158,7 +158,7 @@ define('forum/recent', ['forum/infinitescroll', 'components'], function (infinit app.parseAndTranslate(templateName, 'topics', { topics: topics, showSelect: showSelect }, function (html) { $('#category-no-topics').remove(); - $('[component="category"]').append(html); + html.insertAfter($('[component="category/topic"]').last()); html.find('.timeago').timeago(); app.createUserTooltips(); utils.makeNumbersHumanReadable(html.find('.human-readable-number')); From 59d081507345566fd43aed9029955f0e6fa56215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 28 Apr 2017 18:48:32 -0400 Subject: [PATCH 257/316] closes #5637 --- src/controllers/accounts/helpers.js | 10 ++++--- src/privileges/users.js | 46 +++++++++++++++++++++++++++++ src/socket.io/user/ban.js | 9 +++--- 3 files changed, 57 insertions(+), 8 deletions(-) diff --git a/src/controllers/accounts/helpers.js b/src/controllers/accounts/helpers.js index 339b3a2c2f..619c22cfcc 100644 --- a/src/controllers/accounts/helpers.js +++ b/src/controllers/accounts/helpers.js @@ -10,8 +10,9 @@ var groups = require('../../groups'); var plugins = require('../../plugins'); var meta = require('../../meta'); var utils = require('../../utils'); +var privileges = require('../../privileges'); -var helpers = {}; +var helpers = module.exports; helpers.getUserDataByUserSlug = function (userslug, callerUID, callback) { async.waterfall([ @@ -60,6 +61,9 @@ helpers.getUserDataByUserSlug = function (userslug, callerUID, callback) { sso: function (next) { plugins.fireHook('filter:auth.list', { uid: uid, associations: [] }, next); }, + canBanUser: function (next) { + privileges.users.canBanUser(callerUID, uid, next); + }, }, next); }, function (results, next) { @@ -109,7 +113,7 @@ helpers.getUserDataByUserSlug = function (userslug, callerUID, callback) { userData.isAdminOrGlobalModeratorOrModerator = isAdmin || isGlobalModerator || isModerator; userData.isSelfOrAdminOrGlobalModerator = isSelf || isAdmin || isGlobalModerator; userData.canEdit = isAdmin || (isGlobalModerator && !results.isTargetAdmin); - userData.canBan = isAdmin || (isGlobalModerator && !results.isTargetAdmin); + userData.canBan = results.canBanUser; userData.canChangePassword = isAdmin || (isSelf && parseInt(meta.config['password:disableEdit'], 10) !== 1); userData.isSelf = isSelf; userData.isFollowing = results.isFollowing; @@ -186,5 +190,3 @@ function filterLinks(links, states) { return permit; }); } - -module.exports = helpers; diff --git a/src/privileges/users.js b/src/privileges/users.js index ed72efb147..26557a1798 100644 --- a/src/privileges/users.js +++ b/src/privileges/users.js @@ -3,6 +3,7 @@ var async = require('async'); +var user = require('../user'); var groups = require('../groups'); var plugins = require('../plugins'); @@ -157,4 +158,49 @@ module.exports = function (privileges) { callback(null, canEdit); }); }; + + privileges.users.canBanUser = function (callerUid, uid, callback) { + async.waterfall([ + function (next) { + async.parallel({ + isAdmin: function (next) { + privileges.users.isAdministrator(callerUid, next); + }, + isGlobalMod: function (next) { + privileges.users.isGlobalModerator(callerUid, next); + }, + isTargetAdmin: function (next) { + privileges.users.isAdministrator(uid, next); + }, + }, next); + }, + function (results, next) { + results.canBan = !results.isTargetAdmin && (results.isAdmin || results.isGlobalMod); + results.callerUid = callerUid; + results.uid = uid; + plugins.fireHook('filter:user.canBanUser', results, next); + }, + function (data, next) { + next(null, data.canBan); + }, + ], callback); + }; + + privileges.users.hasBanPrivilege = function (uid, callback) { + async.waterfall([ + function (next) { + user.isAdminOrGlobalMod(uid, next); + }, + function (isAdminOrGlobalMod, next) { + plugins.fireHook('filter:user.hasBanPrivilege', { + uid: uid, + isAdminOrGlobalMod: isAdminOrGlobalMod, + canBan: isAdminOrGlobalMod, + }, next); + }, + function (data, next) { + next(null, data.canBan); + }, + ], callback); + }; }; diff --git a/src/socket.io/user/ban.js b/src/socket.io/user/ban.js index 54ce94fd24..084307c017 100644 --- a/src/socket.io/user/ban.js +++ b/src/socket.io/user/ban.js @@ -1,10 +1,11 @@ 'use strict'; var async = require('async'); + var user = require('../../user'); var websockets = require('../index'); var events = require('../../events'); - +var privileges = require('../../privileges'); var plugins = require('../../plugins'); module.exports = function (SocketUser) { @@ -72,10 +73,10 @@ module.exports = function (SocketUser) { async.waterfall([ function (next) { - user.isAdminOrGlobalMod(uid, next); + privileges.users.hasBanPrivilege(uid, next); }, - function (isAdminOrGlobalMod, next) { - if (!isAdminOrGlobalMod) { + function (hasBanPrivilege, next) { + if (!hasBanPrivilege) { return next(new Error('[[error:no-privileges]]')); } async.each(uids, method, next); From f3c4d2ebe3876ba3cd3f83a3db1286cedad176f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 28 Apr 2017 19:35:48 -0400 Subject: [PATCH 258/316] closes #5636 --- src/controllers/index.js | 4 ---- src/routes/index.js | 3 --- src/webserver.js | 7 +++++++ 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/controllers/index.js b/src/controllers/index.js index 6cebf29d8f..0f15d2f4ab 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -370,7 +370,3 @@ Controllers.termsOfUse = function (req, res, next) { termsOfUse: meta.config.termsOfUse, }); }; - -Controllers.ping = function (req, res) { - res.status(200).send(req.path === '/sping' ? 'healthy' : '200'); -}; diff --git a/src/routes/index.js b/src/routes/index.js index 969577aabf..3dc6047b22 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -34,9 +34,6 @@ function mainRoutes(app, middleware, controllers) { setupPageRoute(app, '/search', middleware, [], controllers.search.search); setupPageRoute(app, '/reset/:code?', middleware, [], controllers.reset); setupPageRoute(app, '/tos', middleware, [], controllers.termsOfUse); - - app.get('/ping', controllers.ping); - app.get('/sping', controllers.ping); } function modRoutes(app, middleware, controllers) { diff --git a/src/webserver.js b/src/webserver.js index 7f10ac707a..f3ab28f630 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -130,6 +130,9 @@ function setupExpressApp(app) { app.use(compression()); + app.get('/ping', ping); + app.get('/sping', ping); + setupFavicon(app); app.use(relativePath + '/apple-touch-icon', middleware.routeTouchIcon); @@ -157,6 +160,10 @@ function setupExpressApp(app) { toobusy.interval(parseInt(meta.config.eventLoopInterval, 10) || 500); } +function ping(req, res) { + res.status(200).send(req.path === '/sping' ? 'healthy' : '200'); +} + function setupFavicon(app) { var faviconPath = meta.config['brand:favicon'] || 'favicon.ico'; faviconPath = path.join(nconf.get('base_dir'), 'public', faviconPath.replace(/assets\/uploads/, 'uploads')); From 2f3aceb7d3eaca22142e84e14e67c8eea1528d51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 28 Apr 2017 23:09:36 -0400 Subject: [PATCH 259/316] dont do anything if config is off --- public/src/client/topic/images.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/src/client/topic/images.js b/public/src/client/topic/images.js index 52f0671a78..65ad386371 100644 --- a/public/src/client/topic/images.js +++ b/public/src/client/topic/images.js @@ -28,6 +28,10 @@ define('forum/topic/images', [ clearTimeout(Images._imageLoaderTimeout); } + if (!config.delayImageLoading) { + return; + } + Images._imageLoaderTimeout = setTimeout(function () { /* If threshold is defined, images loaded above this threshold will modify From cd2aac9d285f87f1293f70c4f72cad8a9594f972 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Sat, 29 Apr 2017 03:19:31 -0400 Subject: [PATCH 260/316] limiting amount of tid's being loaded via sitemap temporary stopgap for load issues, fixing this properly next @barisusakli --- src/sitemap.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sitemap.js b/src/sitemap.js index d828f9d784..8cd91fec6b 100644 --- a/src/sitemap.js +++ b/src/sitemap.js @@ -27,7 +27,7 @@ sitemap.render = function (callback) { var numPages; async.waterfall([ - async.apply(db.getSortedSetRange, 'topics:recent', 0, -1), + async.apply(db.getSortedSetRange, 'topics:recent', 0, 1000), function (tids, next) { privileges.topics.filterTids('read', tids, 0, next); }, From 5b4dcfafe23c0a9ac495066091d1e5ed502a8259 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Sat, 29 Apr 2017 03:44:55 -0400 Subject: [PATCH 261/316] Limiting group members here Refactor incoming. @barisusakli --- src/groups/posts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/groups/posts.js b/src/groups/posts.js index 6f4d65520d..2fc40cac37 100644 --- a/src/groups/posts.js +++ b/src/groups/posts.js @@ -10,7 +10,7 @@ module.exports = function (Groups) { Groups.getLatestMemberPosts = function (groupName, max, uid, callback) { async.waterfall([ function (next) { - Groups.getMembers(groupName, 0, -1, next); + Groups.getMembers(groupName, 0, 100, next); }, function (uids, next) { if (!Array.isArray(uids) || !uids.length) { From da24e342f2af7968ad5bf84d1b662cdbe6581bf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Sun, 30 Apr 2017 15:11:51 -0400 Subject: [PATCH 262/316] limit sessions displayed to 20 --- src/user/auth.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/user/auth.js b/src/user/auth.js index 29a79f39c4..b288f857f5 100644 --- a/src/user/auth.js +++ b/src/user/auth.js @@ -65,7 +65,7 @@ module.exports = function (User) { } async.waterfall([ - async.apply(db.getSortedSetRevRange, 'uid:' + uid + ':sessions', 0, -1), + async.apply(db.getSortedSetRevRange, 'uid:' + uid + ':sessions', 0, 19), function (sids, next) { _sids = sids; async.map(sids, db.sessionStore.get.bind(db.sessionStore), next); From 1d4923a606af2eedc09b9ee86ff4b111f53cdd81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Sun, 30 Apr 2017 15:52:19 -0400 Subject: [PATCH 263/316] limit # of results returned --- src/meta/errors.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meta/errors.js b/src/meta/errors.js index fb169764ba..abacfa297c 100644 --- a/src/meta/errors.js +++ b/src/meta/errors.js @@ -19,7 +19,7 @@ module.exports = function (Meta) { Meta.errors.get = function (escape, callback) { async.waterfall([ function (next) { - db.getSortedSetRevRangeWithScores('errors:404', 0, -1, next); + db.getSortedSetRevRangeWithScores('errors:404', 0, 199, next); }, function (data, next) { data = data.map(function (nfObject) { From 029e2ea8da3cdf030fcbfa2cf98f047667978ab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Sun, 30 Apr 2017 16:32:14 -0400 Subject: [PATCH 264/316] 404 logging change dont write to db on every 404 dont log 404 if route is empty --- src/meta/errors.js | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/meta/errors.js b/src/meta/errors.js index abacfa297c..f269490bbf 100644 --- a/src/meta/errors.js +++ b/src/meta/errors.js @@ -1,7 +1,9 @@ 'use strict'; var async = require('async'); +var winston = require('winston'); var validator = require('validator'); +var cronJob = require('cron').CronJob; var db = require('../database'); var analytics = require('../analytics'); @@ -9,11 +11,39 @@ var analytics = require('../analytics'); module.exports = function (Meta) { Meta.errors = {}; + var counters = {}; + + new cronJob('0 * * * * *', function () { + Meta.errors.writeData(); + }, null, true); + + Meta.errors.writeData = function () { + var dbQueue = []; + if (Object.keys(counters).length > 0) { + for (var key in counters) { + if (counters.hasOwnProperty(key)) { + dbQueue.push(async.apply(db.sortedSetIncrBy, 'errors:404', counters[key], key)); + } + } + counters = {}; + async.series(dbQueue, function (err) { + if (err) { + winston.error(err); + } + }); + } + }; + Meta.errors.log404 = function (route, callback) { callback = callback || function () {}; + if (!route) { + return setImmediate(callback); + } route = route.replace(/\/$/, ''); // remove trailing slashes analytics.increment('errors:404'); - db.sortedSetIncrBy('errors:404', 1, route, callback); + counters[route] = counters[route] || 0; + counters[route] += 1; + setImmediate(callback); }; Meta.errors.get = function (escape, callback) { From c0807ab327dd28bd7cdb986bafe08caadf667859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 1 May 2017 15:37:58 -0400 Subject: [PATCH 265/316] closes #5639 --- src/socket.io/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/socket.io/index.js b/src/socket.io/index.js index 93c1697c98..00d3433a3d 100644 --- a/src/socket.io/index.js +++ b/src/socket.io/index.js @@ -219,7 +219,11 @@ function addRedisAdapter(io) { var redis = require('../database/redis'); var pub = redis.connect(); var sub = redis.connect(); - io.adapter(redisAdapter({ pubClient: pub, subClient: sub })); + io.adapter(redisAdapter({ + key: 'db:' + nconf.get('redis:database') + ':adapter_key', + pubClient: pub, + subClient: sub, + })); } else if (nconf.get('isCluster') === 'true') { winston.warn('[socket.io] Clustering detected, you are advised to configure Redis as a websocket store.'); } From 7a1a030865afdeb41af39446327e4ac5e49a0db1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 1 May 2017 20:40:04 -0400 Subject: [PATCH 266/316] closes #5640 --- src/socket.io/admin/categories.js | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/socket.io/admin/categories.js b/src/socket.io/admin/categories.js index 50c2061038..d4e8ef1414 100644 --- a/src/socket.io/admin/categories.js +++ b/src/socket.io/admin/categories.js @@ -7,7 +7,9 @@ var groups = require('../../groups'); var categories = require('../../categories'); var privileges = require('../../privileges'); var plugins = require('../../plugins'); -var Categories = {}; +var events = require('../../events'); + +var Categories = module.exports; Categories.create = function (socket, data, callback) { if (!data) { @@ -42,7 +44,26 @@ Categories.getNames = function (socket, data, callback) { }; Categories.purge = function (socket, cid, callback) { - categories.purge(cid, socket.uid, callback); + var name; + async.waterfall([ + function (next) { + categories.getCategoryField(cid, 'name', next); + }, + function (_name, next) { + name = _name; + categories.purge(cid, socket.uid, next); + }, + function (next) { + events.log({ + type: 'category-purge', + uid: socket.uid, + ip: socket.ip, + cid: cid, + name: name, + }); + setImmediate(next); + }, + ], callback); }; Categories.update = function (socket, data, callback) { @@ -102,5 +123,3 @@ Categories.copySettingsFrom = function (socket, data, callback) { Categories.copyPrivilegesFrom = function (socket, data, callback) { categories.copyPrivilegesFrom(data.fromCid, data.toCid, callback); }; - -module.exports = Categories; From 13a04123e0fe32d6f23f388c3da42556b0230271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 1 May 2017 20:58:34 -0400 Subject: [PATCH 267/316] closes #5641 --- src/controllers/uploads.js | 3 ++- src/file.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/controllers/uploads.js b/src/controllers/uploads.js index a9768755db..3ef09c89cc 100644 --- a/src/controllers/uploads.js +++ b/src/controllers/uploads.js @@ -209,7 +209,8 @@ uploadsController.uploadFile = function (uid, uploadedFile, callback) { } var allowed = file.allowedExtensions(); - var extension = path.extname(uploadedFile.name); + + var extension = path.extname(uploadedFile.name).toLowerCase(); if (!extension || extension === '.' || (allowed.length > 0 && allowed.indexOf(extension) === -1)) { return callback(new Error('[[error:invalid-file-type, ' + allowed.join(', ') + ']]')); } diff --git a/src/file.js b/src/file.js index 3c510e58d4..35b57d9113 100644 --- a/src/file.js +++ b/src/file.js @@ -81,7 +81,7 @@ file.allowedExtensions = function () { if (!extension.startsWith('.')) { extension = '.' + extension; } - return extension; + return extension.toLowerCase(); }); if (allowedExtensions.indexOf('.jpg') !== -1 && allowedExtensions.indexOf('.jpeg') === -1) { From 6fb90a17e061be577161a36667b89d523905ea63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 1 May 2017 21:38:03 -0400 Subject: [PATCH 268/316] closes #5613 --- public/src/app.js | 4 +++- public/src/client/topic/postTools.js | 2 +- public/src/client/topic/votes.js | 2 +- src/socket.io/posts/helpers.js | 6 +++++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/public/src/app.js b/public/src/app.js index 1c20346a7a..61e5deac50 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -191,11 +191,13 @@ app.cacheBuster = null; if (!socket) { return; } + var previousRoom = app.currentRoom; + app.currentRoom = ''; socket.emit('meta.rooms.leaveCurrent', function (err) { if (err) { + app.currentRoom = previousRoom; return app.alertError(err.message); } - app.currentRoom = ''; }); }; diff --git a/public/src/client/topic/postTools.js b/public/src/client/topic/postTools.js index 90de1cf810..8bbda4d79c 100644 --- a/public/src/client/topic/postTools.js +++ b/public/src/client/topic/postTools.js @@ -295,7 +295,7 @@ define('forum/topic/postTools', [ socket.emit(method, { pid: pid, - room_id: app.currentRoom, + room_id: 'topic_' + ajaxify.data.tid, }, function (err) { if (err) { app.alertError(err.message); diff --git a/public/src/client/topic/votes.js b/public/src/client/topic/votes.js index d152d9f4a0..99ca469fe3 100644 --- a/public/src/client/topic/votes.js +++ b/public/src/client/topic/votes.js @@ -67,7 +67,7 @@ define('forum/topic/votes', ['components', 'translator'], function (components, socket.emit(currentState ? 'posts.unvote' : method, { pid: post.attr('data-pid'), - room_id: app.currentRoom, + room_id: 'topic_' + ajaxify.data.tid, }, function (err) { if (err) { if (err.message === 'self-vote') { diff --git a/src/socket.io/posts/helpers.js b/src/socket.io/posts/helpers.js index c7b92488d5..a9bb9b451d 100644 --- a/src/socket.io/posts/helpers.js +++ b/src/socket.io/posts/helpers.js @@ -14,10 +14,14 @@ helpers.postCommand = function (socket, command, eventName, notification, data, return callback(new Error('[[error:not-logged-in]]')); } - if (!data || !data.pid || !data.room_id) { + if (!data || !data.pid) { return callback(new Error('[[error:invalid-data]]')); } + if (!data.room_id) { + return callback(new Error('[[error:invalid-room-id, ' + data.room_id + ' ]]')); + } + async.waterfall([ function (next) { async.parallel({ From 2fc53a1e30a0cd239de8e7ceddd04c0b85a2a94b Mon Sep 17 00:00:00 2001 From: psychobunny Date: Tue, 2 May 2017 16:47:16 -0400 Subject: [PATCH 269/316] closes #5625 note: `.avatar-lg` wasn't updated to match the `develop` branch (where `avatar-xl` was added) intentionally --- public/less/generics.less | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/public/less/generics.less b/public/less/generics.less index 5193627445..777bfcdbd9 100644 --- a/public/less/generics.less +++ b/public/less/generics.less @@ -111,6 +111,12 @@ height: 128px; .user-icon-style(128px, 7.5rem); } + + &.avatar-xl { + width: 128px; + height: 128px; + .user-icon-style(128px, 7.5rem); + } &.avatar-rounded { border-radius: 50%; From d7cbf93982a358d40ba084a242b26c3f1c48d3d2 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Wed, 3 May 2017 09:22:13 +0000 Subject: [PATCH 270/316] Latest translations and fallbacks --- public/language/de/admin/advanced/cache.json | 10 ++--- .../language/de/admin/advanced/database.json | 8 ++-- public/language/de/admin/advanced/errors.json | 2 +- .../de/admin/appearance/customise.json | 4 +- .../language/de/admin/appearance/skins.json | 12 +++--- .../language/de/admin/appearance/themes.json | 14 +++---- .../language/de/admin/development/info.json | 2 +- public/language/de/admin/extend/plugins.json | 20 ++++----- public/language/de/admin/extend/rewards.json | 2 +- public/language/de/admin/extend/widgets.json | 10 ++--- .../language/de/admin/general/dashboard.json | 10 ++--- .../language/de/admin/general/languages.json | 2 +- public/language/de/admin/general/social.json | 2 +- public/language/de/admin/manage/groups.json | 2 +- .../de/admin/manage/ip-blacklist.json | 2 +- .../de/admin/manage/registration.json | 8 ++-- public/language/de/admin/manage/tags.json | 6 +-- public/language/de/admin/manage/users.json | 42 +++++++++---------- .../language/de/admin/settings/advanced.json | 2 +- .../language/de/admin/settings/cookies.json | 2 +- public/language/de/admin/settings/email.json | 26 ++++++------ .../language/de/admin/settings/general.json | 30 ++++++------- public/language/de/admin/settings/group.json | 4 +- public/language/de/admin/settings/guest.json | 2 +- .../de/admin/settings/notifications.json | 2 +- .../de/admin/settings/pagination.json | 6 +-- .../language/de/admin/settings/sockets.json | 6 +-- .../de/admin/settings/web-crawler.json | 12 +++--- public/language/de/login.json | 2 +- 29 files changed, 126 insertions(+), 126 deletions(-) diff --git a/public/language/de/admin/advanced/cache.json b/public/language/de/admin/advanced/cache.json index 75a8f8a48c..ce20ed57ee 100644 --- a/public/language/de/admin/advanced/cache.json +++ b/public/language/de/admin/advanced/cache.json @@ -1,11 +1,11 @@ { - "post-cache": "Eintrag Zwischenspeicher", - "posts-in-cache": "Einträge im Zwischenspeicher", - "average-post-size": "Durchschnittliche Forum Eintrags Größe", + "post-cache": "Beitrags Cache", + "posts-in-cache": "Beiträge im Cache", + "average-post-size": "Durchschnittliche Beitragsgröße", "length-to-max": "Länge / Maximum", "percent-full": "%1% Voll", - "post-cache-size": "Eintrags Zwischenspeicher Größe", - "items-in-cache": "Objekte im Zwischenspeicher", + "post-cache-size": "Beitrags Cache Größe", + "items-in-cache": "Objekte im Cache", "control-panel": "Systemsteuerung", "update-settings": "Aktualisiere Zwischenspeicher Einstellungen" } \ No newline at end of file diff --git a/public/language/de/admin/advanced/database.json b/public/language/de/admin/advanced/database.json index eebefbb703..55cc4e0dc8 100644 --- a/public/language/de/admin/advanced/database.json +++ b/public/language/de/admin/advanced/database.json @@ -1,12 +1,12 @@ { "x-b": "%1 B", "x-mb": "%1 MB", - "uptime-seconds": "Laufzeit in Sekunden", - "uptime-days": "Laufzeit in Tagen", + "uptime-seconds": "Uptime in Sekunden", + "uptime-days": "Uptime in Tagen", "mongo": "Mongo", "mongo.version": "MongoDB Version", - "mongo.storage-engine": "Speicherengine", + "mongo.storage-engine": "Storage Engine", "mongo.collections": "Collections", "mongo.objects": "Objekte", "mongo.avg-object-size": "Durchschnittliche Objektgröße", @@ -26,7 +26,7 @@ "redis.blocked-clients": "Blockierte Clients", "redis.used-memory": "Speicherverbrauch", "redis.memory-frag-ratio": "Speicherfragmentierungsgrad", - "redis.total-connections-recieved": "Insgesamt Verbindungen empfangen", + "redis.total-connections-recieved": "Gesamte empfangen Verbindungen", "redis.total-commands-processed": "Insgesamt Kommandos ausgeführt", "redis.iops": "Durchschnittliche Anzahl von Ein-/Ausgaben pro Sekunde", "redis.keyspace-hits": "Schlüsselraum Treffer", diff --git a/public/language/de/admin/advanced/errors.json b/public/language/de/admin/advanced/errors.json index 42c26ee8af..326bfdd1cf 100644 --- a/public/language/de/admin/advanced/errors.json +++ b/public/language/de/admin/advanced/errors.json @@ -6,7 +6,7 @@ "manage-error-log": "Fehlerprotokoll verwalten", "export-error-log": "Exportiere das Fehlerprotokoll (CSV)", "clear-error-log": "Fehlerprotokoll leeren", - "route": "Zielroute", + "route": "Pfad", "count": "Anzahl", "no-routes-not-found": "Hurra! Keine 404 Fehler!", "clear404-confirm": "Bist du dir sicher, dass du das 404 Fehlerprotokoll löschen möchtest?", diff --git a/public/language/de/admin/appearance/customise.json b/public/language/de/admin/appearance/customise.json index 029a9de35b..426750609f 100644 --- a/public/language/de/admin/appearance/customise.json +++ b/public/language/de/admin/appearance/customise.json @@ -3,7 +3,7 @@ "custom-css.description": "Füge hier deine eigenen CSS-Eigenschaften ein, sie werden als letztes angewendet.", "custom-css.enable": "Benutzerdefiniertes CSS aktivieren", - "custom-header": "Benutzerdefinierter Kopfbereich", + "custom-header": "Benutzerdefinierter Header", "custom-header.description": "Füge hier dein benutzerdefiniertes HTML (z.B. Javascript, Meta Tags, usw.) ein, welches in den <head> Tag eingefügt werden soll.", - "custom-header.enable": "Benutzerdefinierten Kopfbereich aktivieren" + "custom-header.enable": "Benutzerdefinierten Header aktivieren" } \ No newline at end of file diff --git a/public/language/de/admin/appearance/skins.json b/public/language/de/admin/appearance/skins.json index 960c3196a7..849f5bc704 100644 --- a/public/language/de/admin/appearance/skins.json +++ b/public/language/de/admin/appearance/skins.json @@ -1,9 +1,9 @@ { - "loading": "Lade Aussehen...", + "loading": "Lade Skins...", "homepage": "Homepage", - "select-skin": "Aussehen auswählen", - "current-skin": "Aktuelles Aussehen", - "skin-updated": "Aussehen aktualisiert", - "applied-success": "Aussehen %1 wurde erfolgreich angewendet", - "revert-success": "Aussehen auf Basisfarben zurückgestellt." + "select-skin": "Skin auswählen", + "current-skin": "Aktueller Skin", + "skin-updated": "Skin aktualisiert", + "applied-success": "Skin %1 wurde erfolgreich angewendet", + "revert-success": "Skin auf Basisfarben zurückgestellt." } \ No newline at end of file diff --git a/public/language/de/admin/appearance/themes.json b/public/language/de/admin/appearance/themes.json index 2dede042b2..8eca7c6390 100644 --- a/public/language/de/admin/appearance/themes.json +++ b/public/language/de/admin/appearance/themes.json @@ -1,11 +1,11 @@ { - "checking-for-installed": "Prüfe auf installierte Designs...", + "checking-for-installed": "Prüfe auf installierte Themes...", "homepage": "Homepage", - "select-theme": "Wähle Design", - "current-theme": "Aktuelles Design", - "no-themes": "Keine installierten Designs gefunden.", - "revert-confirm": "Bist du dir sicher, dass du das standard NodeBB Design wieder herstellen willst?", - "theme-changed": "Design geändert", - "revert-success": "Du hast dein NodeBB erfolgreich wieder auf das Standarddesign zurückgesetzt.", + "select-theme": "Wähle Theme", + "current-theme": "Aktuelles Theme", + "no-themes": "Keine installierten Theme gefunden.", + "revert-confirm": "Bist du dir sicher, dass du das standard NodeBB Theme wieder herstellen willst?", + "theme-changed": "Theme geändert", + "revert-success": "Du hast dein NodeBB erfolgreich wieder auf das Standard-Theme zurückgesetzt.", "restart-to-activate": "Bitte starte dein NodeBB neu um das Design voll zu aktivieren." } \ No newline at end of file diff --git a/public/language/de/admin/development/info.json b/public/language/de/admin/development/info.json index 601de896a5..810511ae04 100644 --- a/public/language/de/admin/development/info.json +++ b/public/language/de/admin/development/info.json @@ -6,7 +6,7 @@ "online": "Online", "git": "git", "load": "Auslastung", - "uptime": "Online Zeit", + "uptime": "Uptime", "registered": "Registriert", "sockets": "Sockets", diff --git a/public/language/de/admin/extend/plugins.json b/public/language/de/admin/extend/plugins.json index 355b20b0cd..fd67906de6 100644 --- a/public/language/de/admin/extend/plugins.json +++ b/public/language/de/admin/extend/plugins.json @@ -14,10 +14,10 @@ "dev-interested": "Daran interessiert selbst Plugins für NodeBB zu schreiben?", "docs-info": "Die komplette Dokumentation zur erstellung von Plugins kann im NodeBB Dokumentations Portal gefunden werden.", - "order.description": "Bestimmte Plugins funktionieren ideal, wenn diese for/nach anderen Plugins initialisiert werden.", - "order.explanation": "Die Plugins werden in der hier spezifizierten Reihenfolge geladen, von Oben nach Unten", + "order.description": "Bestimmte Plugins funktionieren optimal, wenn diese vor/nach anderen Plugins initialisiert werden.", + "order.explanation": "Die Plugins werden in der hier spezifizierten Reihenfolge geladen, von oben nach unten", - "plugin-item.themes": "Designs", + "plugin-item.themes": "Themes", "plugin-item.deactivate": "Deaktivieren", "plugin-item.activate": "Aktivieren", "plugin-item.install": "Installieren", @@ -28,20 +28,20 @@ "plugin-item.upgrade": "Aktualisieren", "plugin-item.more-info": "Für weitere Informationen:", "plugin-item.unknown": "Unbekannt", - "plugin-item.unknown-explanation": "Der Status dieses Plugins konnte nicht bestimmt werden, möglicherweise aufgrund eines Fehlkonfigurationsfehlers.", + "plugin-item.unknown-explanation": "Der Status dieses Plugins konnte nicht bestimmt werden, möglicherweise aufgrund eines Konfigurationsfehlers.", "alert.enabled": "Plugin aktiviert", "alert.disabled": "Plugin deaktiviert", "alert.upgraded": "Plugin aktualisiert", "alert.installed": "Plugin installiert", "alert.uninstalled": "Plugin deinstalliert", - "alert.activate-success": "Bitte starten Sie ihr NodeBB neu, um dieses Plugin vollständig zu aktivieren", + "alert.activate-success": "Bitte starte NodeBB neu, um dieses Plugin vollständig zu aktivieren", "alert.deactivate-success": "Plugin erfolgreich deaktiviert", - "alert.upgrade-success": "Bitte laden Sie ihr NodeBB neu um dieses Plugin vollständig zu aktualisieren", - "alert.install-success": "Plugin erfolgreich installiert. Bitte aktivieren Sie das Plugin", + "alert.upgrade-success": "Bitte lade NodeBB neu, um dieses Plugin vollständig zu aktualisieren", + "alert.install-success": "Plugin erfolgreich installiert. Bitte aktiviere das Plugin", "alert.uninstall-success": "Das Plugin wurde erfolgreich deaktiviert und deinstalliert.", - "alert.suggest-error": "

    NodeBB konnte den Paket-Manager nicht erreichen, wollen Sie mit der Installation der neuesten Version fortfahren

    Der Server meldete (%1): %2
    ", - "alert.package-manager-unreachable": "

    NodeBB konnte den Paket-manager nicht erreichen, eine aktualisierung wird momentan nicht empfohlen.

    ", - "alert.incompatible": "

    Ihre NodeBB Version (v%1) ist nur für aktualisierungen bis v%2 dieses Plugins bestimmt. Bitte aktualisieren Sie NodeBB wenn Sie eine neuere Version dieses plugins installieren wollen.

    ", + "alert.suggest-error": "

    NodeBB konnte den Paket-Manager nicht erreichen. Willst Du mit der Installation der neuesten Version fortfahren

    Der Server meldete (%1): %2
    ", + "alert.package-manager-unreachable": "

    NodeBB konnte den Paket-Manager nicht erreichen, eine Aktualisierung wird momentan nicht empfohlen.

    ", + "alert.incompatible": "

    NodeBB Version (v%1) ist nur für Aktualisierungen bis v%2 dieses Plugins bestimmt. Bitte aktualisiere NodeBB, wenn eine neuere Version dieses Plugins installiert werden soll.

    ", "alert.possibly-incompatible": "

    Keine Kompatibilitätsinformationen gefunden

    Dieses Plugin legte keine spezifische NodeBB version fest, welche für die Installation benötigt wird. Volle Kompatibilität kann nicht gewährleistet werden, was dazu führen könnte, dass ihr NodeBB nicht mehr korrekt startet.

    Für den Fall, dass NodeBB nicht mehr ordnungsgemäß startet:

    $ ./nodebb reset plugin=\"%1\"

    Soll mit der installation der neuesten Version dieses Plugins fortgefahren werden?

    " } diff --git a/public/language/de/admin/extend/rewards.json b/public/language/de/admin/extend/rewards.json index 5d4e5cb3da..38c39fcf44 100644 --- a/public/language/de/admin/extend/rewards.json +++ b/public/language/de/admin/extend/rewards.json @@ -4,7 +4,7 @@ "condition-is": "Ist:", "condition-then": "Dann:", "max-claims": "Anzahl der male, die diese Belohnung beansprucht werden kann", - "zero-infinite": "Geben sie 0 für unendlich ein", + "zero-infinite": "Gib 0 für unendlich ein", "delete": "Entfernen", "enable": "Aktivieren", "disable": "Deaktivieren", diff --git a/public/language/de/admin/extend/widgets.json b/public/language/de/admin/extend/widgets.json index 73308e9c16..b633981898 100644 --- a/public/language/de/admin/extend/widgets.json +++ b/public/language/de/admin/extend/widgets.json @@ -1,14 +1,14 @@ { "available": "Verfügbare Widgets", - "explanation": "Wählen Sie ein Widget vom Dropdown-Menu aus und ziehen Sie es anschließend links in den Widget-Bereich einer Vorlage.", - "none-installed": "Keine Widgets gefunden! Aktivieren Sie das \"essential widgets\"-Plugin in den Plugin-Einstellungen.", + "explanation": "Widget vom Dropdown-Menu auswählen und anschließend links in den Widget-Bereich einer Vorlage ziehen.", + "none-installed": "Keine Widgets gefunden! Aktiviere das \"Essential Widgets\"-Plugin in den Plugin-Einstellungen.", "containers.available": "Verfügbare Container", - "containers.explanation": "Ziehen Sie sie auf ein beliebiges aktives Widget", + "containers.explanation": "Auf ein beliebiges aktives Widget ziehen", "containers.none": "Nichts", - "container.well": "Brunnen", + "container.well": "Well", "container.jumbotron": "Jumbotron", "container.panel": "Panel", - "container.panel-header": "Panel Kopfbereich", + "container.panel-header": "Panel Header", "container.panel-body": "Panel Körper", "container.alert": "Alarm", diff --git a/public/language/de/admin/general/dashboard.json b/public/language/de/admin/general/dashboard.json index c417fa64a6..29772ca3e6 100644 --- a/public/language/de/admin/general/dashboard.json +++ b/public/language/de/admin/general/dashboard.json @@ -17,18 +17,18 @@ "updates": "Updates", "running-version": "Es läuft NodeBB v%1.", "keep-updated": "Stelle sicher, dass dein NodeBB immer auf dem neuesten Stand für die neuesten Sicherheits-Patches und Bug-fixes ist.", - "up-to-date": "

    System ist aktuell

    ", - "upgrade-available": "

    Version (v%1) wurde veröffentlicht. Ziehen Sie einNodeBB Upgrade in Betracht.

    ", - "prerelease-upgrade-available": "

    Das ist eine veraltete pre-release Version von NodeBB. Version (v%1) wurde veröffentlicht. Ziehen Sie einNodeBB Upgrade in Betracht.

    ", + "up-to-date": "

    NodeBB Version ist aktuell

    ", + "upgrade-available": "

    Version (v%1) wurde veröffentlicht. Es wird ein NodeBB Upgrade empfohlen.

    ", + "prerelease-upgrade-available": "

    Das ist eine veraltete pre-release Version von NodeBB. Version (v%1) wurde veröffentlicht. Es wird ein NodeBB Upgrade empfohlen.

    ", "prerelease-warning": "

    Das ist eine pre-release Version von NodeBB. Es können ungewollte Fehler auftreten.

    ", - "running-in-development": "Das Forum wurde im Entwicklermodus gestartet. Das Forum könnte potenziellen Gefahren ausgeliefert sein. Bitte kontaktieren Sie Ihren Systemadministrator.", + "running-in-development": "Das Forum wurde im Entwicklermodus gestartet. Das Forum könnte potenziellen Gefahren ausgeliefert sein. Bitte kontaktiere den Systemadministrator.", "notices": "Hinweise", "restart-not-required": "Kein Neustart benötigt", "restart-required": "Neustart benötigt", "search-plugin-installed": "Such-Plugin installiert", "search-plugin-not-installed": "Kein Such-Plugin installiert", - "search-plugin-tooltip": "Installieren Sie ein Such-Plugin auf der Plugin seite um die Such-Funktionalität zu aktivieren", + "search-plugin-tooltip": "Installiere ein Such-Plugin auf der Plugin-Seite um die Such-Funktionalität zu aktivieren", "control-panel": "Systemsteuerung", "reload": "Reload", diff --git a/public/language/de/admin/general/languages.json b/public/language/de/admin/general/languages.json index ffea3c3f7d..af0bb78d1f 100644 --- a/public/language/de/admin/general/languages.json +++ b/public/language/de/admin/general/languages.json @@ -1,5 +1,5 @@ { "language-settings": "Spracheinstellungen", - "description": "Die Standardsprache legt die Spracheinstellungen für alle Benutzer fest, die das Forum besuchen.
    Einzelne Benutzer können die Standardsprache auf der Seite mit den Kontoeinstellungen überschreiben.", + "description": "Die Standardsprache legt die Spracheinstellungen für alle Benutzer fest, die das Forum besuchen.
    Einzelne Benutzer können die Standardsprache auf der Seite in ihren Kontoeinstellungen überschreiben.", "default-language": "Standardsprache" } \ No newline at end of file diff --git a/public/language/de/admin/general/social.json b/public/language/de/admin/general/social.json index 5f5d7b28e8..0614aca7da 100644 --- a/public/language/de/admin/general/social.json +++ b/public/language/de/admin/general/social.json @@ -1,5 +1,5 @@ { "post-sharing": "Beiträge teilen", - "info-plugins-additional": "Plugins können zusätzliche Netzwerke für das Teilen von Beiträgen hinzufügen.", + "info-plugins-additional": "Plugins können zusätzliche soziale Netzwerke für das Teilen von Beiträgen hinzufügen.", "save-success": "Erfolgreich gespeichert!" } \ No newline at end of file diff --git a/public/language/de/admin/manage/groups.json b/public/language/de/admin/manage/groups.json index 818d721601..e77558609b 100644 --- a/public/language/de/admin/manage/groups.json +++ b/public/language/de/admin/manage/groups.json @@ -9,7 +9,7 @@ "create-button": "Erstellen", "alerts.create-failure": "Oh Oh

    Ein Problem ist beim erstellen deiner Gruppe aufgetreten. Bitte versuche es später noch mal!

    ", - "alerts.confirm-delete": "Sind Sie sicher, dass Sie diese Gruppe löschen wollen?", + "alerts.confirm-delete": "Diese Gruppe wirklich löschen ?", "edit.name": "Name", "edit.description": "Beschreibung", diff --git a/public/language/de/admin/manage/ip-blacklist.json b/public/language/de/admin/manage/ip-blacklist.json index e98633eee8..a6d4f1cff9 100644 --- a/public/language/de/admin/manage/ip-blacklist.json +++ b/public/language/de/admin/manage/ip-blacklist.json @@ -5,7 +5,7 @@ "validate": "Blacklist validieren", "apply": "Blacklist anwenden", "hints": "Syntax Hinweise", - "hint-1": "Fügen Sie einzelne IP-Adresses pro Zeile ein. Sie können IP-Blöcke hinzufügen, so lange diese im CIDR Format (z.b. 192.168.100.0/22) eingegeben werden.", + "hint-1": "Pro Zeile kann eine IP-Adresse angegeben werden. Es können auch IP-Blöcke im CIDR Format (z.B. 192.168.100.0/22) hinzugefügt werden.", "hint-2": "Sie können Kommentare hinzufügen, indem Sie die Zeilen mit dem # Symbol beginnen.", "validate.x-valid": "%1 von %2 Regel(n) zulässig.", diff --git a/public/language/de/admin/manage/registration.json b/public/language/de/admin/manage/registration.json index f8e1f5f4ad..1971466302 100644 --- a/public/language/de/admin/manage/registration.json +++ b/public/language/de/admin/manage/registration.json @@ -1,9 +1,9 @@ { - "queue": "Schlange", - "description": "Es sind keine Benutzer in der Registrierungsschlange.
    Um diese Funktion zu aktivieren, gehen SIe zur Einstellungen → Benutzer → Benutzer registrierung und ändern sie Registrierungsart auf \"Admin Genehmigung\".", + "queue": "Warteschlange", + "description": "Es sind keine Benutzer in der Registrierungs-Warteschlange.
    Um diese Funktion zu aktivieren, gehe zu Einstellungen → Benutzer → Benutzer erstellen und ändern sie Registrierungsart auf \"Admin Genehmigung\".", "list.name": "Name", - "list.email": "Email", + "list.email": "E-Mail", "list.ip": "IP-Adresse", "list.time": "Zeit", "list.username-spam": "Häufigkeit: %1 Erscheint: %2 Sicherheit: %3", @@ -11,7 +11,7 @@ "list.ip-spam": "Häufigkeit: %1 Erscheint: %2", "invitations": "Einladungen", - "invitations.description": "Unterhalb ist eine komplette Liste der versandten Einladungen. Benutze Strg+F um die Liste per Email oder Nutzername zu durchsuchen.

    Der Nutzername wird für die Nutzer die ihre einladung angenommen haben Rechts von den Emails angezeigt.", + "invitations.description": "Unterhalb ist eine komplette Liste der versandten Einladungen. Benutze Strg+F um die Liste per Email oder Nutzername zu durchsuchen.

    Der Nutzername wird für die Nutzer die ihre Einladung angenommen haben rechts von den E-Mails angezeigt.", "invitations.inviter-username": "Nutzername des Einladenden", "invitations.invitee-email": "Email des eingeladenen", "invitations.invitee-username": "Nutzername des eingeladenen (Wenn registriert)", diff --git a/public/language/de/admin/manage/tags.json b/public/language/de/admin/manage/tags.json index d9230c2253..be8bdd9650 100644 --- a/public/language/de/admin/manage/tags.json +++ b/public/language/de/admin/manage/tags.json @@ -1,14 +1,14 @@ { - "none": "Ihr Form hat bisher noch keine Themen mit Tags.", + "none": "Das Forum hat bisher noch keine Themen mit Tags.", "bg-color": "Hintergrundfarbe", "text-color": "Textfarbe", "create-modify": "Tags erstellen & bearbeiten", - "description": "Wählen sie Tags aus indem sie klicken und/oder ziehen, drücken die Shift um mehrere auszuwählen", + "description": "Tags auswählen indem Du klickst und/oder ziehst, drücke die SHIFT-Taste, um mehrere auszuwählen", "create": "Tag erstellen", "modify": "Tag bearbeiten", "delete": "Ausgewählte Tags entfernen", "search": "Nach Tags suchen", - "settings": "Klicken Sie hier um die Tag-Einstellungsseite zu öffnen.", + "settings": "Klicke hier, um die Tag-Einstellungsseite zu öffnen.", "name": "Tagname", "alerts.editing-multiple": "Bearbeite mehrere Tags", diff --git a/public/language/de/admin/manage/users.json b/public/language/de/admin/manage/users.json index 277b0a200e..c1f24ae8fe 100644 --- a/public/language/de/admin/manage/users.json +++ b/public/language/de/admin/manage/users.json @@ -3,9 +3,9 @@ "edit": "Bearbeiten", "make-admin": "Zum Administrator befördern", "remove-admin": "Adminstatus entfernen", - "validate-email": "Email bestätigen", - "send-validation-email": "Bestätigungsemail senden", - "password-reset-email": "Passwortreset email senden", + "validate-email": "E-Mail bestätigen", + "send-validation-email": "Bestätigungs E-Mail senden", + "password-reset-email": "Passwort-Reset E-Mail senden", "ban": "Benutzer verbannen", "temp-ban": "Benutzer temporär verbannen", "unban": "Benutzer entbannen", @@ -23,16 +23,16 @@ "pills.top-posters": "Top Poster", "pills.top-rep": "Größtes Ansehen", "pills.inactive": "Inaktiv", - "pills.flagged": "Meist gemeldetster", + "pills.flagged": "Meist gemeldete", "pills.banned": "Gebannt", "pills.search": "Benutzer Suche", "search.username": "Nach Nutzernamen", - "search.username-placeholder": "Geben Sie einen Nutzernamen ein um danach zu suchen", - "search.email": "Nach Email", - "search.email-placeholder": "Geben Sie eine Email Adresse ein um danach zu suchen", + "search.username-placeholder": "Einen Nutzernamen eingeben, um danach zu suchen", + "search.email": "Nach E-Mail", + "search.email-placeholder": "Eine E-Mail Adresse eingeben, um danach zu suchen", "search.ip": "Nach IP-Adresse", - "search.ip-placeholder": "Geben Sie eine IP Adresse ein um danach zu suchen", + "search.ip-placeholder": "IP Adresse eingeben, um danach zu suchen", "search.not-found": "Benutzer nicht gefunden!", "inactive.3-months": "3 Monate", @@ -41,7 +41,7 @@ "users.uid": "UID", "users.username": "Nutzername", - "users.email": "Email", + "users.email": "E-Mail", "users.postcount": "Anzahl der Beiträge", "users.reputation": "Ansehen", "users.flags": "Meldungen", @@ -50,19 +50,19 @@ "users.banned": "Gebannt", "create.username": "Benutzername", - "create.email": "Email", - "create.email-placeholder": "Email dieses Benutzers", + "create.email": "E-Mail", + "create.email-placeholder": "E-Mail dieses Benutzers", "create.password": "Passwort", "create.password-confirm": "Passwort bestätigen", "temp-ban.length": "Banndauer", - "temp-ban.reason": "Grund (Optional)", + "temp-ban.reason": "Grund (optional)", "temp-ban.hours": "Stunden", "temp-ban.days": "Tage", - "temp-ban.explanation": "Geben Sie die dauer des Bans an. Beachten Sie, dass eine Zeit von 0 als permanent interpretiert wird.", + "temp-ban.explanation": "Geben die dauer des Bans an. Beachte, dass eine Zeit von 0 als permanent interpretiert wird.", - "alerts.confirm-ban": "Wollen Sie diesen Nutzer wirklich permanent bannen?", - "alerts.confirm-ban-multi": "Wollen Sie diese Nutzer wirklich permanent bannen?", + "alerts.confirm-ban": "Möchtest Du diesen Nutzer wirklich permanent bannen?", + "alerts.confirm-ban-multi": "Möchtest Du diese Nutzer wirklich permanent bannen?", "alerts.ban-success": "Benutzer gebannt!", "alerts.button-ban-x": "%1 Nutzer bannen", "alerts.unban-success": "Benutzer entbannt!", @@ -72,12 +72,12 @@ "alerts.make-admin-success": "Die Benutzer sind nun Administratoren.", "alerts.confirm-remove-admin": "Möchtest du wirklich Admins entfernen?", "alerts.remove-admin-success": "Diese(r) Nutzer sind/ist kein(e) Administrator(en) mehr ", - "alerts.confirm-validate-email": "Möchten Sie wirklich die Emails dieser benutzer/dieses benutzers bestätigen?", - "alerts.validate-email-success": "Emails bestätigt", - "alerts.password-reset-confirm": "Möchten Sie wirklich (eine) Passwort-Reset-Email(s) an diese(n) Benutzer schicken?", - "alerts.confirm-delete": "Warnung!
    Wollen Sie wirklich diese(n) Benutzer löschen?
    Diese Aktion kann nicht rückgängig gemacht werden! Nur der Account wird dabei gelöscht. Deren Themen und Beiträge bleiben dabei erhalten.", + "alerts.confirm-validate-email": "Möchtest Du wirklich die E-Mails dieser Benutzer/dieses Benutzers bestätigen?", + "alerts.validate-email-success": "E-Mails bestätigt", + "alerts.password-reset-confirm": "Möchtest Du wirklich (eine) Passwort-Reset-Email(s) an diese(n) Benutzer schicken?", + "alerts.confirm-delete": "Warnung!
    Möchtest Du wirklich diese(n) Benutzer löschen?
    Diese Aktion kann nicht rückgängig gemacht werden! Nur der Account wird dabei gelöscht. Deren Themen und Beiträge bleiben dabei erhalten.", "alerts.delete-success": "Benutzer gelöscht!", - "alerts.confirm-purge": "Warnung!
    Sind Sie sicher, dass Sie diese Nutzer und deren Beiträge löschen wollen?
    Diese Aktion kann nicht rückgängig gemacht werden! Alle Nutzerdaten und Beiträge werden dabei gelöscht!", + "alerts.confirm-purge": "Warnung!
    Bist Du sicher, dass Du diese Nutzer und deren Beiträge löschen willst?
    Diese Aktion kann nicht rückgängig gemacht werden! Alle Nutzerdaten und Beiträge werden dabei gelöscht!", "alerts.create": "Nutzer Erstellen", "alerts.button-create": "Erstellen", "alerts.button-cancel": "Abbrechen", @@ -85,7 +85,7 @@ "alerts.error-x": "Fehler

    %1

    ", "alerts.create-success": "Nutzer erstellt", - "alerts.prompt-email": "Email:", + "alerts.prompt-email": "E-Mail:", "alerts.email-sent-to": "Eine Einladungsemail wurde an %1 gesendet", "alerts.x-users-found": "%1 Nutzer gefunden! Die Suche dauerte %2ms." } \ No newline at end of file diff --git a/public/language/de/admin/settings/advanced.json b/public/language/de/admin/settings/advanced.json index 29a3d7c701..fbe879e289 100644 --- a/public/language/de/admin/settings/advanced.json +++ b/public/language/de/admin/settings/advanced.json @@ -10,7 +10,7 @@ "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", - "traffic.help": "NodeBB wird mit einem module geliefert, welches automatisch anfragen in \"High-Traffic\" situationen blockiert. Sie können diese Einstellungen hier Fine-Tunen, auch wenn die Standardeinstellungen einen guten Anfang darstellen sollten", + "traffic.help": "NodeBB wird mit einem Modul geliefert, welches automatisch anfragen in High-Traffic Situationen blockiert. Du kannst diese Einstellungen hier ändern, auch wenn die Standardeinstellungen einen guten Anfang darstellen sollten", "traffic.enable": "Traffic Management aktivieren", "traffic.event-lag": "Eventschleifenverzögerungsschwelle (in Millisekunden)", "traffic.event-lag-help": "Das Heruntersetzen dieses Werts reduziert die Ladezeiten, aber wird auch dafür sorgen, dass die \"Übermäßige Belastung\" nachricht öfter angezeigt wird. (Neustart erforderlich)", diff --git a/public/language/de/admin/settings/cookies.json b/public/language/de/admin/settings/cookies.json index 6d818fd893..0e87e306d3 100644 --- a/public/language/de/admin/settings/cookies.json +++ b/public/language/de/admin/settings/cookies.json @@ -1,5 +1,5 @@ { - "eu-consent": "EU-Konsens", + "eu-consent": "EU Cookie Zustimmung", "consent.enabled": "Aktiviert", "consent.message": "Benachrichtigung", "consent.acceptance": "Akzeptierungsnachricht", diff --git a/public/language/de/admin/settings/email.json b/public/language/de/admin/settings/email.json index 83b3683e91..da2109f9df 100644 --- a/public/language/de/admin/settings/email.json +++ b/public/language/de/admin/settings/email.json @@ -1,25 +1,25 @@ { - "email-settings": "Email-Einstellungen", + "email-settings": "E-Mail Einstellungen", "address": "E-Mail Adresse", - "address-help": "Die folgende Email-Adresse ist die Emai-Adresse, welche dem Empfänger im \"Von\" und \"Antworten\" Bereich sehen wird.", + "address-help": "Die folgende E-Mail Adresse ist die E-Mail Adresse, welche dem Empfänger im \"Von\" und \"Antworten\" Bereich sehen wird.", "from": "Name des Absenders", - "from-help": "Der Name des Absenders, welcher in der Email angezeigt werden soll.", + "from-help": "Der Name des Absenders, welcher in der E-Mail angezeigt werden soll.", "gmail-routing": "Gmail Routing", "gmail-routing-help1": "Es gab Berichte bezüglich des \"Gmail Routing\", welches nicht auf Accounts mit erhöhten Sicherheitseinstellungen funktionierte (Standardeinstellung). In diesem Fall müssen sie ihren Gmail Account konfigurieren weniger sichere Apps zu erlauben.", "gmail-routing-help2": "Für mehr informationen zu diesem Workaround, konsultieren Sie bitte diesen NodeMailer Artikel über dieses Problem. Eine alternative wäre ein Plugin von Drittherstellern wie SendGrid, mailgun etc. zu verwenden. Verfügbare Plugins durchsuchen.", - "gmail-transport": "Emails über einen Gmail/Google Apps account verschicken", + "gmail-transport": "E-Mails über einen Gmail/Google Apps Account verschicken", "gmail-transport.username": "Benutzername", - "gmail-transport.username-help": "Geben Sie die volle Email-Adresse hier ein, insbesondere wenn Sie eine Google-Apps verwaltete domain verwenden.", + "gmail-transport.username-help": "Gib die vollständige E-Mail Adresse hier ein, insbesondere wenn Du eine Google-Apps verwaltete Domain verwendest.", "gmail-transport.password": "Passwort", - "template": "Email Vorlage bearbeiten", - "template.select": "Email Vorlage auswählen", + "template": "E-Mail Vorlage bearbeiten", + "template.select": "E-Mail Vorlage auswählen", "template.revert": "Original wiederherstellen", - "testing": "Emailtests", - "testing.select": "Wählen Sie die Email Vorlage", - "testing.send": "Test-Email versenden", - "testing.send-help": "Die Test-Email wird an die Email des momentan eingeloggten Nutzers geschickt.", - "subscriptions": "Email Abonnements", - "subscriptions.disable": "Email-Benachrichtigungsmails deaktivieren", + "testing": "E-Mail Test", + "testing.select": "Wählen Sie die E-Mail Vorlage", + "testing.send": "Test-E-Mail versenden", + "testing.send-help": "Die Test-E-Mail wird an die E-Mail Adresse des momentan eingeloggten Nutzers geschickt.", + "subscriptions": "E-Mail Abonnements", + "subscriptions.disable": "E-Mail-Benachrichtigung deaktivieren", "subscriptions.hour": "Sende Zeit", "subscriptions.hour-help": "Bitte geben Sie eine Nummer ein, welche die Stunde repräsentiert zu welcher geplante Emails versandt werden sollen (z.B. 0 für Mitternacht, 17 für 5 Uhr Nachmittags). Beachten Sie, dass die Zeit auf der Serverzeit basiert und daher nicht umbedingt mit ihrer Systemzeit übereinstimmen muss.
    Die ungefähre Serverzeit ist:
    Die nächste tägliche Sendung ist um geplant" } \ No newline at end of file diff --git a/public/language/de/admin/settings/general.json b/public/language/de/admin/settings/general.json index ca83119258..014843ced2 100644 --- a/public/language/de/admin/settings/general.json +++ b/public/language/de/admin/settings/general.json @@ -1,25 +1,25 @@ { - "site-settings": "Seiteneinstellungen", - "title": "Seiten Titel", - "title.name": "Ihr Community Name", - "title.show-in-header": "Seitentitel im Header anzeigen", + "site-settings": "Forum Einstellungen", + "title": "Forum Titel", + "title.name": "Name Deiner Community", + "title.show-in-header": "Titel im Header anzeigen", "browser-title": "Browser Titel", - "browser-title-help": "Wenn kein Browser-Titel spezifiziert wurde, wird der Seitentitel verwendet", + "browser-title-help": "Wenn kein Browser Titel spezifiziert wurde, wird der Forum Titel verwendet", "title-layout": "Titel Layout", - "title-layout-help": "Definieren Sie, wie der Browser Titel Strukturiert wird d.h.z.B. {pageTitle} | {browserTitle}", - "description.placeholder": "Eine kurze Beschreibung ihrer Community", - "description": "Seitenbeschreibung", - "keywords": "Seiten-Schlüsselworte", - "keywords-placeholder": "Schlüsselworte, die ihre Community beschreiben, mit komma getrennt", - "logo": "Seiten-Logo", + "title-layout-help": "Definiert wie der Browser Titel gebildet wird, z.B. {pageTitle} | {browserTitle}", + "description.placeholder": "Eine kurze Beschreibung der Community", + "description": "Forum Beschreibung", + "keywords": "Forum Schlüsselworte", + "keywords-placeholder": "Schlüsselworte, die ihre Community beschreiben, mit Komma getrennt", + "logo": "Forum Logo", "logo.image": "Bild", - "logo.image-placeholder": "Pfad zu einem Logo, welches im Kopfbereich des Forums angezeigt werden soll", + "logo.image-placeholder": "Pfad zu einem Logo, welches im Header des Forums angezeigt werden soll", "logo.upload": "Hochladen", "logo.url": "URL", - "logo.url-placeholder": "Die URL des Seiten-Logos", + "logo.url-placeholder": "Die URL des Logos", "logo.url-help": "Wenn das Logo angeklickt wird, wird der Nutzer an diese Adresse weitergeleitet. Wenn das Feld leer gelassen wird, wird der Nutzer zur Startseite geleitet.", - "logo.alt-text": "Alternativer Text", - "log.alt-text-placeholder": "Alternativer text, falls das Bild nicht angezeigt werden kann", + "logo.alt-text": "Alt Text", + "log.alt-text-placeholder": "Alternativer Text, falls das Bild nicht angezeigt werden kann", "favicon": "Favicon", "favicon.upload": "Hochladen", "touch-icon": "Homescreen/Touch Icon", diff --git a/public/language/de/admin/settings/group.json b/public/language/de/admin/settings/group.json index bce69d2f53..99f4b5ea31 100644 --- a/public/language/de/admin/settings/group.json +++ b/public/language/de/admin/settings/group.json @@ -1,8 +1,8 @@ { "general": "Allgemein", "private-groups": "Private Gruppen", - "private-groups.help": "Wenn aktiviert, benötigt das beitreten einer Gruppe die Bestätigung des jeweiligen Besitzers(Standard: aktiviert)", - "private-groups.warning": "Vorsicht! Wenn diese Option deaktiviert ist, und Sie private Gruppen haben, werden diese automatisch Öffentlich.", + "private-groups.help": "Wenn aktiviert, erfordert das Beitreten einer Gruppe die Bestätigung des jeweiligen Besitzers(Standard: aktiviert)", + "private-groups.warning": "Vorsicht! Wenn diese Option deaktiviert ist, und es private Gruppen gibt, werden diese automatisch öffentlich.", "allow-creation": "Erstellung von Gruppen erlauben", "allow-creation-help": "Wenn aktiviert können Nutzer Gruppen erstellen (Standard: deaktiviert)", "max-name-length": "Maximale Länge von Gruppennamen", diff --git a/public/language/de/admin/settings/guest.json b/public/language/de/admin/settings/guest.json index b515580c87..d3189508fd 100644 --- a/public/language/de/admin/settings/guest.json +++ b/public/language/de/admin/settings/guest.json @@ -1,7 +1,7 @@ { "handles": "Gastzugang", "handles.enabled": "Gastzugänge erlauben", - "handles.enabled-help": "Diese option offenbart ein neues Feld, welches Gästen erlaubt einen Nutzernamen zu wählen, welcher Sie mit jedem Beitrag assoziiert den sie erstellen. Wenn diese option deaktiviert ist, werden sie einfach \"Gast\" genannt", + "handles.enabled-help": "Diese Option offenbart ein neues Feld, welches Gästen erlaubt einen Nutzernamen zu wählen, welcher sie mit jedem Beitrag assoziiert den sie erstellen. Wenn diese Option deaktiviert ist, werden sie einfach \"Gast\" genannt", "privileges": "Gast-Berechtigungen", "privileges.can-search": "Gästen erlauben das Forum zu durchsuchen ohne eingeloggt zu sein", "privileges.can-search-users": "Gästen erlauben nach Benutzern zu suchen ohne eingeloggt zu sein" diff --git a/public/language/de/admin/settings/notifications.json b/public/language/de/admin/settings/notifications.json index 38a30ccbb1..3aab343267 100644 --- a/public/language/de/admin/settings/notifications.json +++ b/public/language/de/admin/settings/notifications.json @@ -1,5 +1,5 @@ { "notifications": "Benachrichtigungen", "welcome-notification": "Wilkommensnachricht", - "welcome-notification-link": "Wilkommensnachrichtslink" + "welcome-notification-link": "Wilkommensnachricht-Link" } \ No newline at end of file diff --git a/public/language/de/admin/settings/pagination.json b/public/language/de/admin/settings/pagination.json index 33bed37f3f..cf2e6456f1 100644 --- a/public/language/de/admin/settings/pagination.json +++ b/public/language/de/admin/settings/pagination.json @@ -1,9 +1,9 @@ { - "pagination": "Seitennummerierungseinstellungen", - "enable": "Themen in Seiten einteilen anstatt unendlich weit zu scrollen", + "pagination": "Seitennummerierungs Einstellungen", + "enable": "Themen in Seiten einteilen anstatt endlos zu scrollen", "topics": "Themen Seitennummerierung", "posts-per-page": "Beiträge pro Seite", "categories": "Kategorie Seitennummerierung", "topics-per-page": "Themen pro Seite", - "initial-num-load": "Ursprüngliche anzahl an Themen, die bei Ungelesen, Aktuell und beliebt geladen werden sollen" + "initial-num-load": "Ursprüngliche Anzahl an Themen, die bei ungelesen, aktuell und beliebt geladen werden sollen" } \ No newline at end of file diff --git a/public/language/de/admin/settings/sockets.json b/public/language/de/admin/settings/sockets.json index 2fa6475bef..d86c0b2d3a 100644 --- a/public/language/de/admin/settings/sockets.json +++ b/public/language/de/admin/settings/sockets.json @@ -1,6 +1,6 @@ { - "reconnection": "Neuverbindungseinstellungen", - "max-attempts": "Maximale Anzahl von Neuverbindungsversuchen", + "reconnection": "Reconnection Einstellungen", + "max-attempts": "Maximale Anzahl von Reconnection-Versuchen", "default-placeholder": "Standard: %1", - "delay": "Neuverbindungsverzögerung" + "delay": "Reconnection-Verzögerung" } \ No newline at end of file diff --git a/public/language/de/admin/settings/web-crawler.json b/public/language/de/admin/settings/web-crawler.json index c5808d268e..5ef1848372 100644 --- a/public/language/de/admin/settings/web-crawler.json +++ b/public/language/de/admin/settings/web-crawler.json @@ -1,10 +1,10 @@ { "crawlability-settings": "Crawlability Einstellung", - "robots-txt": "Benutzerdefinierte Robots.txtLeer lassen für Standardeinstellung", - "sitemap-feed-settings": "Seitenübersicht & Feed Einstellungen", + "robots-txt": "Benutzerdefinierte robots.txt Leer lassen für Standardeinstellung", + "sitemap-feed-settings": "Sitemap & Feed Einstellungen", "disable-rss-feeds": "Deaktiviere RSS Feeds", - "disable-sitemap-xml": "Deaktiviere Seitenübersicht.xml", - "sitemap-topics": "Anzahl der Themen die auf der Seitenübersicht angezeigt werden", - "clear-sitemap-cache": "Leere Seitenübersicht Cache", - "view-sitemap": "Zeige Seitenübersicht" + "disable-sitemap-xml": "Deaktiviere sitemap.xml", + "sitemap-topics": "Anzahl der Themen die auf der Sitemap angezeigt werden", + "clear-sitemap-cache": "Sitemap Cache leeren", + "view-sitemap": "Zeige Sitemap" } \ No newline at end of file diff --git a/public/language/de/login.json b/public/language/de/login.json index acbd636dce..92887d0c2d 100644 --- a/public/language/de/login.json +++ b/public/language/de/login.json @@ -1,5 +1,5 @@ { - "username-email": "Benutzername / E-Mail-Adresse", + "username-email": "Benutzername / Email-Adresse", "username": "Benutzername", "email": "E-Mail", "remember_me": "Eingeloggt bleiben?", From 114f416ec017ddd142378f91fcdd36f72d075189 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 3 May 2017 15:30:42 -0400 Subject: [PATCH 271/316] remove dupe code --- src/user/data.js | 79 +++++++++++++++++++----------------------------- 1 file changed, 31 insertions(+), 48 deletions(-) diff --git a/src/user/data.js b/src/user/data.js index 09dfb8bb12..ca8dbd758f 100644 --- a/src/user/data.js +++ b/src/user/data.js @@ -26,6 +26,10 @@ module.exports = function (User) { }; User.getUsersFields = function (uids, fields, callback) { + if (!Array.isArray(uids) || !uids.length) { + return callback(null, []); + } + var fieldsToRemove = []; function addField(field) { if (fields.indexOf(field) === -1) { @@ -34,23 +38,6 @@ module.exports = function (User) { } } - // Eliminate duplicates and build ref table - var uniqueUids = uids.filter(function (uid, index) { - return index === uids.indexOf(uid); - }); - var ref = uniqueUids.reduce(function (memo, cur, idx) { - memo[cur] = idx; - return memo; - }, {}); - - if (!Array.isArray(uniqueUids) || !uniqueUids.length) { - return callback(null, []); - } - - var keys = uniqueUids.map(function (uid) { - return 'user:' + uid; - }); - if (fields.indexOf('uid') === -1) { fields.push('uid'); } @@ -64,21 +51,17 @@ module.exports = function (User) { addField('lastonline'); } + var uniqueUids = uids.filter(function (uid, index) { + return index === uids.indexOf(uid); + }); + async.waterfall([ function (next) { - db.getObjectsFields(keys, fields, function (err, users) { - if (err) { - return callback(err); - } - - users = uids.map(function (uid) { - return users[ref[uid]]; - }); - - next(null, users); - }); + db.getObjectsFields(uidsToUserKeys(uniqueUids), fields, next); }, function (users, next) { + users = uidsToUsers(uids, uniqueUids, users); + modifyUserData(users, fieldsToRemove, next); }, ], callback); @@ -100,39 +83,39 @@ module.exports = function (User) { return callback(null, []); } - // Eliminate duplicates and build ref table var uniqueUids = uids.filter(function (uid, index) { return index === uids.indexOf(uid); }); - var ref = uniqueUids.reduce(function (memo, cur, idx) { - memo[cur] = idx; - return memo; - }, {}); - - var keys = uniqueUids.map(function (uid) { - return 'user:' + uid; - }); async.waterfall([ function (next) { - db.getObjects(keys, function (err, users) { - if (err) { - return callback(err); - } - - users = uids.map(function (uid) { - return users[ref[uid]]; - }); - - next(null, users); - }); + db.getObjects(uidsToUserKeys(uniqueUids), next); }, function (users, next) { + users = uidsToUsers(uids, uniqueUids, users); + modifyUserData(users, [], next); }, ], callback); }; + function uidsToUsers(uids, uniqueUids, usersData) { + var ref = uniqueUids.reduce(function (memo, cur, idx) { + memo[cur] = idx; + return memo; + }, {}); + var users = uids.map(function (uid) { + return usersData[ref[uid]]; + }); + return users; + } + + function uidsToUserKeys(uids) { + return uids.map(function (uid) { + return 'user:' + uid; + }); + } + function modifyUserData(users, fieldsToRemove, callback) { users.forEach(function (user) { if (!user) { From e7d076a084961f7e59842ad125dcc8812612e379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 3 May 2017 16:36:34 -0400 Subject: [PATCH 272/316] closes #5634 --- package.json | 2 +- src/controllers/admin/cache.js | 7 ++ src/user/settings.js | 146 ++++++++++++++++++----------- src/views/admin/advanced/cache.tpl | 18 ++++ 4 files changed, 119 insertions(+), 54 deletions(-) diff --git a/package.json b/package.json index 6f2494c833..45869d7eb7 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "json-2-csv": "^2.0.22", "less": "^2.0.0", "logrotate-stream": "^0.2.3", - "lru-cache": "4.0.1", + "lru-cache": "4.0.2", "mime": "^1.3.4", "minimist": "^1.1.1", "mkdirp": "~0.5.0", diff --git a/src/controllers/admin/cache.js b/src/controllers/admin/cache.js index dce16818f8..dea5f0345b 100644 --- a/src/controllers/admin/cache.js +++ b/src/controllers/admin/cache.js @@ -5,6 +5,7 @@ var cacheController = {}; cacheController.get = function (req, res) { var postCache = require('../../posts/cache'); var groupCache = require('../../groups').cache; + var userSettingsCache = require('../../user').settingsCache; var avgPostSize = 0; var percentFull = 0; @@ -21,6 +22,12 @@ cacheController.get = function (req, res) { percentFull: percentFull, avgPostSize: avgPostSize, }, + userSettingsCache: { + length: userSettingsCache.length, + max: userSettingsCache.max, + itemCount: userSettingsCache.itemCount, + percentFull: ((userSettingsCache.length / userSettingsCache.max) * 100).toFixed(2), + }, groupCache: { length: groupCache.length, max: groupCache.max, diff --git a/src/user/settings.js b/src/user/settings.js index bef20e0087..76da620d32 100644 --- a/src/user/settings.js +++ b/src/user/settings.js @@ -2,83 +2,121 @@ 'use strict'; var async = require('async'); + var meta = require('../meta'); var db = require('../database'); var plugins = require('../plugins'); +var LRU = require('lru-cache'); + +var cache = LRU({ + max: 500, + length: function () { return 1; }, + maxAge: 1000 * 60 * 60, +}); + module.exports = function (User) { + User.settingsCache = cache; + User.getSettings = function (uid, callback) { if (!parseInt(uid, 10)) { return onSettingsLoaded(0, {}, callback); } - db.getObject('user:' + uid + ':settings', function (err, settings) { - if (err) { - return callback(err); - } + var cached = cache.get('user:' + uid + ':settings'); + if (cached) { + return onSettingsLoaded(uid, cached || {}, callback); + } - onSettingsLoaded(uid, settings || {}, callback); - }); + async.waterfall([ + function (next) { + db.getObject('user:' + uid + ':settings', next); + }, + function (settings, next) { + settings = settings || {}; + settings.uid = uid; + cache.set('user:' + uid + ':settings', settings); + onSettingsLoaded(uid, settings || {}, next); + }, + ], callback); }; User.getMultipleUserSettings = function (uids, callback) { + function getFromCache(next) { + var settings = uids.map(function (uid) { + return cache.get('user:' + uid + ':settings') || {}; + }); + async.map(settings, function (setting, next) { + onSettingsLoaded(setting.uid, setting, next); + }, next); + } + + if (!Array.isArray(uids) || !uids.length) { return callback(null, []); } - var keys = uids.map(function (uid) { - return 'user:' + uid + ':settings'; + var nonCachedUids = uids.filter(function (uid) { + return !cache.has('user:' + uid + ':settings'); }); - db.getObjects(keys, function (err, settings) { - if (err) { - return callback(err); - } - - for (var i = 0; i < settings.length; i += 1) { - settings[i] = settings[i] || {}; - settings[i].uid = uids[i]; - } + if (!nonCachedUids.length) { + return getFromCache(callback); + } - async.map(settings, function (setting, next) { - onSettingsLoaded(setting.uid, setting, next); - }, callback); + var keys = nonCachedUids.map(function (uid) { + return 'user:' + uid + ':settings'; }); + + async.waterfall([ + function (next) { + db.getObjects(keys, next); + }, + function (settings, next) { + settings.forEach(function (userSettings, index) { + userSettings = userSettings || {}; + userSettings.uid = nonCachedUids[index]; + cache.set('user:' + userSettings.uid + ':settings', userSettings); + }); + + getFromCache(next); + }, + ], callback); }; function onSettingsLoaded(uid, settings, callback) { - plugins.fireHook('filter:user.getSettings', { uid: uid, settings: settings }, function (err, data) { - if (err) { - return callback(err); - } - - settings = data.settings; - - var defaultTopicsPerPage = parseInt(meta.config.topicsPerPage, 10) || 20; - var defaultPostsPerPage = parseInt(meta.config.postsPerPage, 10) || 20; - - settings.showemail = parseInt(getSetting(settings, 'showemail', 0), 10) === 1; - settings.showfullname = parseInt(getSetting(settings, 'showfullname', 0), 10) === 1; - settings.openOutgoingLinksInNewTab = parseInt(getSetting(settings, 'openOutgoingLinksInNewTab', 0), 10) === 1; - settings.dailyDigestFreq = getSetting(settings, 'dailyDigestFreq', 'off'); - settings.usePagination = parseInt(getSetting(settings, 'usePagination', 0), 10) === 1; - settings.topicsPerPage = Math.min(settings.topicsPerPage ? parseInt(settings.topicsPerPage, 10) : defaultTopicsPerPage, defaultTopicsPerPage); - settings.postsPerPage = Math.min(settings.postsPerPage ? parseInt(settings.postsPerPage, 10) : defaultPostsPerPage, defaultPostsPerPage); - settings.userLang = settings.userLang || meta.config.defaultLang || 'en-GB'; - settings.topicPostSort = getSetting(settings, 'topicPostSort', 'oldest_to_newest'); - settings.categoryTopicSort = getSetting(settings, 'categoryTopicSort', 'newest_to_oldest'); - settings.followTopicsOnCreate = parseInt(getSetting(settings, 'followTopicsOnCreate', 1), 10) === 1; - settings.followTopicsOnReply = parseInt(getSetting(settings, 'followTopicsOnReply', 0), 10) === 1; - settings.sendChatNotifications = parseInt(getSetting(settings, 'sendChatNotifications', 0), 10) === 1; - settings.sendPostNotifications = parseInt(getSetting(settings, 'sendPostNotifications', 0), 10) === 1; - settings.restrictChat = parseInt(getSetting(settings, 'restrictChat', 0), 10) === 1; - settings.topicSearchEnabled = parseInt(getSetting(settings, 'topicSearchEnabled', 0), 10) === 1; - settings.delayImageLoading = parseInt(getSetting(settings, 'delayImageLoading', 1), 10) === 1; - settings.bootswatchSkin = settings.bootswatchSkin || meta.config.bootswatchSkin || 'default'; - settings.scrollToMyPost = parseInt(getSetting(settings, 'scrollToMyPost', 1), 10) === 1; - - callback(null, settings); - }); + async.waterfall([ + function (next) { + plugins.fireHook('filter:user.getSettings', { uid: uid, settings: settings }, next); + }, + function (data, next) { + settings = data.settings; + + var defaultTopicsPerPage = parseInt(meta.config.topicsPerPage, 10) || 20; + var defaultPostsPerPage = parseInt(meta.config.postsPerPage, 10) || 20; + + settings.showemail = parseInt(getSetting(settings, 'showemail', 0), 10) === 1; + settings.showfullname = parseInt(getSetting(settings, 'showfullname', 0), 10) === 1; + settings.openOutgoingLinksInNewTab = parseInt(getSetting(settings, 'openOutgoingLinksInNewTab', 0), 10) === 1; + settings.dailyDigestFreq = getSetting(settings, 'dailyDigestFreq', 'off'); + settings.usePagination = parseInt(getSetting(settings, 'usePagination', 0), 10) === 1; + settings.topicsPerPage = Math.min(settings.topicsPerPage ? parseInt(settings.topicsPerPage, 10) : defaultTopicsPerPage, defaultTopicsPerPage); + settings.postsPerPage = Math.min(settings.postsPerPage ? parseInt(settings.postsPerPage, 10) : defaultPostsPerPage, defaultPostsPerPage); + settings.userLang = settings.userLang || meta.config.defaultLang || 'en-GB'; + settings.topicPostSort = getSetting(settings, 'topicPostSort', 'oldest_to_newest'); + settings.categoryTopicSort = getSetting(settings, 'categoryTopicSort', 'newest_to_oldest'); + settings.followTopicsOnCreate = parseInt(getSetting(settings, 'followTopicsOnCreate', 1), 10) === 1; + settings.followTopicsOnReply = parseInt(getSetting(settings, 'followTopicsOnReply', 0), 10) === 1; + settings.sendChatNotifications = parseInt(getSetting(settings, 'sendChatNotifications', 0), 10) === 1; + settings.sendPostNotifications = parseInt(getSetting(settings, 'sendPostNotifications', 0), 10) === 1; + settings.restrictChat = parseInt(getSetting(settings, 'restrictChat', 0), 10) === 1; + settings.topicSearchEnabled = parseInt(getSetting(settings, 'topicSearchEnabled', 0), 10) === 1; + settings.delayImageLoading = parseInt(getSetting(settings, 'delayImageLoading', 1), 10) === 1; + settings.bootswatchSkin = settings.bootswatchSkin || meta.config.bootswatchSkin || 'default'; + settings.scrollToMyPost = parseInt(getSetting(settings, 'scrollToMyPost', 1), 10) === 1; + next(null, settings); + }, + ], callback); } function getSetting(settings, key, defaultValue) { @@ -138,6 +176,7 @@ module.exports = function (User) { User.updateDigestSetting(uid, data.dailyDigestFreq, next); }, function (next) { + cache.del('user:' + uid + ':settings'); User.getSettings(uid, next); }, ], callback); @@ -160,8 +199,9 @@ module.exports = function (User) { User.setSetting = function (uid, key, value, callback) { if (!parseInt(uid, 10)) { - return callback(); + return setImmediate(callback); } + cache.del('user:' + uid + ':settings'); db.setObjectField('user:' + uid + ':settings', key, value, callback); }; }; diff --git a/src/views/admin/advanced/cache.tpl b/src/views/admin/advanced/cache.tpl index 126cba8db0..72854a34b0 100644 --- a/src/views/admin/advanced/cache.tpl +++ b/src/views/admin/advanced/cache.tpl @@ -26,6 +26,24 @@ +
    +
    User Settings Cache
    +
    + +
    + {userSettingsCache.itemCount}
    + +
    + {userSettingsCache.length} / {userSettingsCache.max}
    + +
    +
    + [[admin/advanced/cache:percent-full, {userSettingsCache.percentFull}]] +
    +
    +
    +
    +
    Group Cache
    From ea6f981686b16e26041f3c1811c7d70e2572660b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 3 May 2017 17:13:15 -0400 Subject: [PATCH 273/316] closes #5638 --- public/language/en-GB/admin/manage/groups.json | 1 + src/views/admin/manage/groups.tpl | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/public/language/en-GB/admin/manage/groups.json b/public/language/en-GB/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/en-GB/admin/manage/groups.json +++ b/public/language/en-GB/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/src/views/admin/manage/groups.tpl b/src/views/admin/manage/groups.tpl index 98e08be2e7..44c4187604 100644 --- a/src/views/admin/manage/groups.tpl +++ b/src/views/admin/manage/groups.tpl @@ -9,6 +9,7 @@
    + @@ -24,6 +25,9 @@ +
    [[admin/manage/groups:name]]
    From 9ebbd65544eec7c3b721318cbdb2a57c7a6a94d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 3 May 2017 17:24:31 -0400 Subject: [PATCH 274/316] up user settings cache size --- src/user/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/user/settings.js b/src/user/settings.js index 76da620d32..9aa247bd79 100644 --- a/src/user/settings.js +++ b/src/user/settings.js @@ -10,7 +10,7 @@ var plugins = require('../plugins'); var LRU = require('lru-cache'); var cache = LRU({ - max: 500, + max: 1000, length: function () { return 1; }, maxAge: 1000 * 60 * 60, }); From f6d22b7ea59fbfc8938e397fde8ad629ac7aefd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 3 May 2017 17:49:01 -0400 Subject: [PATCH 275/316] up widget essentials --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 45869d7eb7..7e668c42ec 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "nodebb-theme-lavender": "4.0.0", "nodebb-theme-persona": "4.2.10", "nodebb-theme-vanilla": "5.2.1", - "nodebb-widget-essentials": "2.0.13", + "nodebb-widget-essentials": "3.0.0", "nodemailer": "2.6.4", "nodemailer-sendmail-transport": "1.0.0", "nodemailer-smtp-transport": "^2.4.1", From 6a822f729d6114611d8e9c2287049a549d68fc10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 4 May 2017 13:19:28 -0400 Subject: [PATCH 276/316] add timestamp to topic edit hook --- src/posts/edit.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/posts/edit.js b/src/posts/edit.js index 8b48e6835e..99cc8e1692 100644 --- a/src/posts/edit.js +++ b/src/posts/edit.js @@ -85,7 +85,7 @@ module.exports = function (Posts) { async.parallel({ topic: function (next) { - topics.getTopicFields(tid, ['cid', 'title'], next); + topics.getTopicFields(tid, ['cid', 'title', 'timestamp'], next); }, isMain: function (next) { Posts.isMain(data.pid, next); @@ -136,6 +136,7 @@ module.exports = function (Posts) { function (tags, next) { topicData.tags = data.tags; topicData.oldTitle = results.topic.title; + topicData.timestamp = results.topic.timestamp; plugins.fireHook('action:topic.edit', { topic: topicData, uid: data.uid }); next(null, { tid: tid, From 6f8e7343e66c35a518a807f9e1272f6813a450a3 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 4 May 2017 14:08:18 -0400 Subject: [PATCH 277/316] fix for "invalid date" re:threading when replying to yourself --- public/src/client/topic/replies.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/src/client/topic/replies.js b/public/src/client/topic/replies.js index bdb2cc1519..961d57a927 100644 --- a/public/src/client/topic/replies.js +++ b/public/src/client/topic/replies.js @@ -86,10 +86,14 @@ define('forum/topic/replies', ['navigator', 'components', 'forum/topic/posts'], var countEl = replyCount.find('[component="post/reply-count/text"]'); var avatars = replyCount.find('[component="post/reply-count/avatars"]'); var count = Math.max(0, parseInt(countEl.attr('data-replies'), 10) + inc); + var timestamp = replyCount.find('.timeago').attr('title', post.timestampISO); + countEl.attr('data-replies', count); replyCount.toggleClass('hidden', !count); countEl.translateText('[[topic:replies_to_this_post, ' + count + ']]'); avatars.addClass('hasMore'); + + timestamp.timeago(); } return Replies; From 986e52897da7ea5a435978189071370f85a96ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 4 May 2017 13:52:12 -0400 Subject: [PATCH 278/316] fix crash if locale is undefined --- public/src/modules/helpers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/modules/helpers.js b/public/src/modules/helpers.js index 3f02a86758..5a8e998f5c 100644 --- a/public/src/modules/helpers.js +++ b/public/src/modules/helpers.js @@ -177,7 +177,7 @@ }; helpers.localeToHTML = function (locale) { - return locale.replace('_', '-'); + return locale ? locale.replace('_', '-') : ''; }; helpers.renderTopicImage = function (topicObj) { From 670a5d9ca01e252e573f11b9cf8d2e80b7ecda73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 4 May 2017 14:37:03 -0400 Subject: [PATCH 279/316] fix timestamp not showing for replies, remove allowFuture @julianlam --- public/src/client/topic/replies.js | 2 +- public/src/overrides.js | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/public/src/client/topic/replies.js b/public/src/client/topic/replies.js index 961d57a927..7d9b9474bc 100644 --- a/public/src/client/topic/replies.js +++ b/public/src/client/topic/replies.js @@ -93,7 +93,7 @@ define('forum/topic/replies', ['navigator', 'components', 'forum/topic/posts'], countEl.translateText('[[topic:replies_to_this_post, ' + count + ']]'); avatars.addClass('hasMore'); - timestamp.timeago(); + timestamp.data('timeago', null).timeago(); } return Replies; diff --git a/public/src/overrides.js b/public/src/overrides.js index b74d93efba..8f2878c79c 100644 --- a/public/src/overrides.js +++ b/public/src/overrides.js @@ -117,8 +117,6 @@ if (typeof window !== 'undefined') { $.timeago.settings.cutoff = 1000 * 60 * 60 * 24 * (parseInt(config.timeagoCutoff, 10) || 30); } - $.timeago.settings.allowFuture = true; - $.fn.timeago = function () { var els = $(this); From 14f3907aa40cd6ce44c7f19ede4bcc8d60da88cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 4 May 2017 16:32:51 -0400 Subject: [PATCH 280/316] change the way group member posts are stored --- src/database/redis/sorted/add.js | 3 ++ src/groups/delete.js | 1 + src/groups/posts.js | 59 +++++++++++++++++++++++++++----- src/groups/user.js | 15 +++++++- src/posts/create.js | 4 +++ src/posts/delete.js | 27 +++++++++++++++ 6 files changed, 99 insertions(+), 10 deletions(-) diff --git a/src/database/redis/sorted/add.js b/src/database/redis/sorted/add.js index e60d079eac..5a21639be4 100644 --- a/src/database/redis/sorted/add.js +++ b/src/database/redis/sorted/add.js @@ -33,6 +33,9 @@ module.exports = function (redisClient, module) { module.sortedSetsAdd = function (keys, score, value, callback) { callback = callback || function () {}; + if (!Array.isArray(keys) || !keys.length) { + return callback(); + } var multi = redisClient.multi(); for (var i = 0; i < keys.length; i += 1) { diff --git a/src/groups/delete.js b/src/groups/delete.js index 3ee69722c7..6494eeb0ba 100644 --- a/src/groups/delete.js +++ b/src/groups/delete.js @@ -26,6 +26,7 @@ module.exports = function (Groups) { async.apply(db.delete, 'group:' + groupName + ':pending'), async.apply(db.delete, 'group:' + groupName + ':invited'), async.apply(db.delete, 'group:' + groupName + ':owners'), + async.apply(db.delete, 'group:' + groupName + ':member:pids'), async.apply(db.deleteObjectField, 'groupslug:groupname', utils.slugify(groupName)), function (next) { db.getSortedSetRange('groups:createtime', 0, -1, function (err, groups) { diff --git a/src/groups/posts.js b/src/groups/posts.js index 2fc40cac37..d8d4b8ab4c 100644 --- a/src/groups/posts.js +++ b/src/groups/posts.js @@ -7,21 +7,62 @@ var privileges = require('../privileges'); var posts = require('../posts'); module.exports = function (Groups) { - Groups.getLatestMemberPosts = function (groupName, max, uid, callback) { + + Groups.onNewPostMade = function (postData, callback) { + if (!parseInt(postData.uid, 10)) { + return setImmediate(callback); + } + + var groupNames; async.waterfall([ function (next) { - Groups.getMembers(groupName, 0, 100, next); + Groups.getUserGroupMembership('groups:visible:createtime', [postData.uid], next); }, - function (uids, next) { - if (!Array.isArray(uids) || !uids.length) { - return callback(null, []); - } - var keys = uids.map(function (uid) { - return 'uid:' + uid + ':posts'; + function (_groupNames, next) { + groupNames = _groupNames[0]; + + var keys = groupNames.map(function (groupName) { + return 'group:' + groupName + ':member:pids'; }); - db.getSortedSetRevRange(keys, 0, max - 1, next); + + db.sortedSetsAdd(keys, postData.timestamp, postData.pid, next); + }, + function (next) { + async.each(groupNames, function (groupName, next) { + truncateMemberPosts(groupName, next); + }, next); + }, + ], callback); + }; + + function truncateMemberPosts(groupName, callback) { + async.waterfall([ + function (next) { + db.getSortedSetRevRange('group:' + groupName + ':member:pids', 10, 10, next); + }, + function (lastPid, next) { + lastPid = lastPid[0]; + if (!parseInt(lastPid, 10)) { + return callback(); + } + db.sortedSetScore('group:' + groupName + ':member:pids', lastPid, next); + }, + function (score, next) { + db.sortedSetsRemoveRangeByScore(['group:' + groupName + ':member:pids'], '-inf', score, next); + }, + ], callback); + } + + Groups.getLatestMemberPosts = function (groupName, max, uid, callback) { + async.waterfall([ + function (next) { + db.getSortedSetRevRange('group:' + groupName + ':member:pids', 0, max - 1, next); }, function (pids, next) { + if (!Array.isArray(pids) || !pids.length) { + return callback(null, []); + } + privileges.posts.filter('read', pids, uid, next); }, function (pids, next) { diff --git a/src/groups/user.js b/src/groups/user.js index 9a68478ade..1f580f57cc 100644 --- a/src/groups/user.js +++ b/src/groups/user.js @@ -22,6 +22,19 @@ module.exports = function (Groups) { }; Groups.getUserGroupsFromSet = function (set, uids, callback) { + async.waterfall([ + function (next) { + Groups.getUserGroupMembership(set, uids, next); + }, + function (memberOf, next) { + async.map(memberOf, function(memberOf, next) { + Groups.getGroupsData(memberOf, next); + }, next); + }, + ], callback); + }; + + Groups.getUserGroupMembership = function(set, uids, callback) { async.waterfall([ function (next) { db.getSortedSetRevRange(set, 0, -1, next); @@ -40,7 +53,7 @@ module.exports = function (Groups) { } }); - Groups.getGroupsData(memberOf, next); + next(null, memberOf); }, ], next); }, next); diff --git a/src/posts/create.js b/src/posts/create.js index 09c4e9c9b7..75b0f8321e 100644 --- a/src/posts/create.js +++ b/src/posts/create.js @@ -9,6 +9,7 @@ var plugins = require('../plugins'); var user = require('../user'); var topics = require('../topics'); var categories = require('../categories'); +var groups = require('../groups'); var utils = require('../utils'); module.exports = function (Posts) { @@ -82,6 +83,9 @@ module.exports = function (Posts) { categories.onNewPostMade(topicData.cid, topicData.pinned, postData, next); }); }, + function (next) { + groups.onNewPostMade(postData, next); + }, function (next) { db.sortedSetAdd('posts:pid', timestamp, postData.pid, next); }, diff --git a/src/posts/delete.js b/src/posts/delete.js index a2874770af..eb5730bcd0 100644 --- a/src/posts/delete.js +++ b/src/posts/delete.js @@ -6,6 +6,7 @@ var _ = require('underscore'); var db = require('../database'); var topics = require('../topics'); var user = require('../user'); +var groups = require('../groups'); var notifications = require('../notifications'); var plugins = require('../plugins'); @@ -141,6 +142,9 @@ module.exports = function (Posts) { function (next) { deletePostFromReplies(pid, next); }, + function (next) { + deletePostFromGroups(pid, next); + }, function (next) { db.sortedSetsRemove(['posts:pid', 'posts:flagged'], pid, next); }, @@ -294,4 +298,27 @@ module.exports = function (Posts) { ], callback); }); } + + function deletePostFromGroups(pid, callback) { + async.waterfall([ + function (next) { + Posts.getPostField(pid, 'uid', next); + }, + function (uid, next) { + if (!parseInt(uid, 10)) { + return callback(); + } + groups.getUserGroupMembership('groups:visible:createtime', [uid], next); + }, + function (groupNames, next) { + groupNames = groupNames[0]; + var keys = groupNames.map(function (groupName) { + return 'group:' + groupName + ':member:pids'; + }); + + db.sortedSetsRemove(keys, pid, next); + }, + ], callback); + + } }; From 17d0759672e112a6cb8bbec00d8e13bc7b19ee5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 4 May 2017 16:39:47 -0400 Subject: [PATCH 281/316] fix lint --- src/groups/posts.js | 1 - src/groups/user.js | 4 ++-- src/posts/delete.js | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/groups/posts.js b/src/groups/posts.js index d8d4b8ab4c..bd829f3d58 100644 --- a/src/groups/posts.js +++ b/src/groups/posts.js @@ -7,7 +7,6 @@ var privileges = require('../privileges'); var posts = require('../posts'); module.exports = function (Groups) { - Groups.onNewPostMade = function (postData, callback) { if (!parseInt(postData.uid, 10)) { return setImmediate(callback); diff --git a/src/groups/user.js b/src/groups/user.js index 1f580f57cc..fb524a87b9 100644 --- a/src/groups/user.js +++ b/src/groups/user.js @@ -27,14 +27,14 @@ module.exports = function (Groups) { Groups.getUserGroupMembership(set, uids, next); }, function (memberOf, next) { - async.map(memberOf, function(memberOf, next) { + async.map(memberOf, function (memberOf, next) { Groups.getGroupsData(memberOf, next); }, next); }, ], callback); }; - Groups.getUserGroupMembership = function(set, uids, callback) { + Groups.getUserGroupMembership = function (set, uids, callback) { async.waterfall([ function (next) { db.getSortedSetRevRange(set, 0, -1, next); diff --git a/src/posts/delete.js b/src/posts/delete.js index eb5730bcd0..61793f68df 100644 --- a/src/posts/delete.js +++ b/src/posts/delete.js @@ -319,6 +319,5 @@ module.exports = function (Posts) { db.sortedSetsRemove(keys, pid, next); }, ], callback); - } }; From 94735f49453820487d1d65cc0051b539447fd149 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Fri, 5 May 2017 09:23:04 +0000 Subject: [PATCH 282/316] Latest translations and fallbacks --- public/language/cs/pages.json | 10 ++-- public/language/pt-PT/admin/admin.json | 2 +- .../language/ru/admin/advanced/database.json | 24 ++++---- .../language/ru/admin/appearance/skins.json | 12 ++-- .../language/ru/admin/appearance/themes.json | 18 +++--- .../language/ru/admin/development/logger.json | 4 +- public/language/ru/admin/extend/plugins.json | 60 +++++++++---------- public/language/ru/admin/extend/rewards.json | 14 ++--- public/language/ru/user.json | 6 +- 9 files changed, 75 insertions(+), 75 deletions(-) diff --git a/public/language/cs/pages.json b/public/language/cs/pages.json index 0619e12dcc..99962b5908 100644 --- a/public/language/cs/pages.json +++ b/public/language/cs/pages.json @@ -5,21 +5,21 @@ "popular-week": "Oblíbená témata pro tento týden", "popular-month": "Oblíbená témata pro tento měsíc", "popular-alltime": "Oblíbená témata za celou dobu", - "recent": "Aktuální témata", - "flagged-content": "Flagged Content", - "ip-blacklist": "IP Blacklist", + "recent": "Současná témata", + "flagged-content": "Nahlášený obsah", + "ip-blacklist": "Černá listina IP adres", "users/online": "Uživatelé online", "users/latest": "Nejnovější uživatelé", "users/sort-posts": "Uživatelé s nejvíce příspěvky", "users/sort-reputation": "Uživatelé s nejlepší reputací", - "users/banned": "Zabanovaní uživatelé", + "users/banned": "Zablokovaní uživatelé", "users/most-flags": "Most flagged users", "users/search": "Hledání uživatele", "notifications": "Upozornění", "tags": "Tagy", "tag": "Téma označeno pod \"%1\"", "register": "Zaregistrovat účet", - "registration-complete": "Registration complete", + "registration-complete": "Registrace dokončena", "login": "Přihlásit se ke svému účtu", "reset": "Obnovit heslo k účtu", "categories": "Kategorie", diff --git a/public/language/pt-PT/admin/admin.json b/public/language/pt-PT/admin/admin.json index 9c01f56006..349b832a5c 100644 --- a/public/language/pt-PT/admin/admin.json +++ b/public/language/pt-PT/admin/admin.json @@ -3,5 +3,5 @@ "alert.confirm-restart": "Are you sure you wish to restart NodeBB?", "acp-title": "%1 | NodeBB Admin Control Panel", - "settings-header-contents": "Contents" + "settings-header-contents": "Conteúdo" } \ No newline at end of file diff --git a/public/language/ru/admin/advanced/database.json b/public/language/ru/admin/advanced/database.json index 134287bc14..bb106b1eb4 100644 --- a/public/language/ru/admin/advanced/database.json +++ b/public/language/ru/admin/advanced/database.json @@ -1,8 +1,8 @@ { - "x-b": "%1 b", - "x-mb": "%1 mb", - "uptime-seconds": "Uptime in Seconds", - "uptime-days": "Uptime in Days", + "x-b": "%1 байт", + "x-mb": "%1 мегабайт", + "uptime-seconds": "Время работы в секундах", + "uptime-days": "Время работы в днях", "mongo": "Mongo", "mongo.version": "Версия MongoDB", @@ -10,24 +10,24 @@ "mongo.collections": "Коллекции", "mongo.objects": "Документы", "mongo.avg-object-size": "Средний размер документа", - "mongo.data-size": "Data Size", - "mongo.storage-size": "Storage Size", + "mongo.data-size": "Размер данных", + "mongo.storage-size": "Размер хранилища", "mongo.index-size": "Index Size", "mongo.file-size": "Размер файла", "mongo.resident-memory": "Resident Memory", - "mongo.virtual-memory": "Virtual Memory", + "mongo.virtual-memory": "Виртуальная память", "mongo.mapped-memory": "Mapped Memory", "mongo.raw-info": "Сырые данные о MongoDB", "redis": "Redis", "redis.version": "Версия Redis", - "redis.connected-clients": "Connected Clients", + "redis.connected-clients": "Подключенные клиенты", "redis.connected-slaves": "Connected Slaves", - "redis.blocked-clients": "Blocked Clients", - "redis.used-memory": "Used Memory", + "redis.blocked-clients": "Заблокированные клиенты", + "redis.used-memory": "Используемая Память", "redis.memory-frag-ratio": "Memory Fragmentation Ratio", - "redis.total-connections-recieved": "Total Connections Received", - "redis.total-commands-processed": "Total Commands Processed", + "redis.total-connections-recieved": "Общее число подключений получено", + "redis.total-commands-processed": "Команд обработано в общем", "redis.iops": "Instantaneous Ops. Per Second", "redis.keyspace-hits": "Keyspace Hits", "redis.keyspace-misses": "Keyspace Misses", diff --git a/public/language/ru/admin/appearance/skins.json b/public/language/ru/admin/appearance/skins.json index 4db6fbdd8a..ccf0259de4 100644 --- a/public/language/ru/admin/appearance/skins.json +++ b/public/language/ru/admin/appearance/skins.json @@ -1,9 +1,9 @@ { - "loading": "Loading Skins...", - "homepage": "Homepage", - "select-skin": "Select Skin", - "current-skin": "Current Skin", - "skin-updated": "Skin Updated", - "applied-success": "%1 skin was succesfully applied", + "loading": "Загрузка стилей", + "homepage": "Домашняя страница", + "select-skin": "Выбрать стиль", + "current-skin": "Текущий стиль", + "skin-updated": "Стиль обновлен", + "applied-success": "%1 тема была успешно применена", "revert-success": "Skin reverted to base colours" } \ No newline at end of file diff --git a/public/language/ru/admin/appearance/themes.json b/public/language/ru/admin/appearance/themes.json index 3148a01337..b4d1b87426 100644 --- a/public/language/ru/admin/appearance/themes.json +++ b/public/language/ru/admin/appearance/themes.json @@ -1,11 +1,11 @@ { - "checking-for-installed": "Checking for installed themes...", - "homepage": "Homepage", - "select-theme": "Select Theme", - "current-theme": "Current Theme", - "no-themes": "No installed themes found", - "revert-confirm": "Are you sure you wish to restore the default NodeBB theme?", - "theme-changed": "Theme Changed", - "revert-success": "You have successfully reverted your NodeBB back to it's default theme.", - "restart-to-activate": "Please restart your NodeBB to fully activate this theme" + "checking-for-installed": "Проверка установленных тем", + "homepage": "Домашняя страница", + "select-theme": "Выбрать тему", + "current-theme": "Текущая тема", + "no-themes": "Не найдено установленные темы", + "revert-confirm": "Вы уверены, что хотите восстановить стандартную NodeBB тему?", + "theme-changed": "Тема сменена", + "revert-success": "Вы успешно вернули ваш NodeBB обратно к его стандартной теме.", + "restart-to-activate": "Пожалуйста, перезапустите ваш NodeBB, чтобы полностью активировать эту тему" } \ No newline at end of file diff --git a/public/language/ru/admin/development/logger.json b/public/language/ru/admin/development/logger.json index 6ab9558149..efbb214bf9 100644 --- a/public/language/ru/admin/development/logger.json +++ b/public/language/ru/admin/development/logger.json @@ -2,9 +2,9 @@ "logger-settings": "Logger Settings", "description": "By enabling the check boxes, you will receive logs to your terminal. If you specify a path, logs will then be saved to a file instead. HTTP logging is useful for collecting statistics about who, when, and what people access on your forum. In addition to logging HTTP requests, we can also log socket.io events. Socket.io logging, in combination with redis-cli monitor, can be very helpful for learning NodeBB's internals.", "explanation": "Simply check/uncheck the logging settings to enable or disable logging on the fly. No restart needed.", - "enable-http": "Enable HTTP logging", + "enable-http": "Включить HTTP логирование", "enable-socket": "Enable socket.io event logging", - "file-path": "Path to log file", + "file-path": "Путь до файла логов", "file-path-placeholder": "/path/to/log/file.log ::: leave blank to log to your terminal", "control-panel": "Logger Control Panel", diff --git a/public/language/ru/admin/extend/plugins.json b/public/language/ru/admin/extend/plugins.json index 1661a987b7..257c0b051c 100644 --- a/public/language/ru/admin/extend/plugins.json +++ b/public/language/ru/admin/extend/plugins.json @@ -1,45 +1,45 @@ { - "installed": "Installed", - "active": "Active", - "inactive": "Inactive", - "out-of-date": "Out of Date", - "none-found": "No plugins found.", - "none-active": "No Active Plugins", - "find-plugins": "Find Plugins", + "installed": "Установленные", + "active": "Активные", + "inactive": "Неактивные", + "out-of-date": "Устаревшие", + "none-found": "Плагины не найдены.", + "none-active": "Нет активных плагинов", + "find-plugins": "Найти плагины", - "plugin-search": "Plugin Search", - "plugin-search-placeholder": "Search for plugin...", - "reorder-plugins": "Re-order Plugins", - "order-active": "Order Active Plugins", - "dev-interested": "Interested in writing plugins for NodeBB?", - "docs-info": "Full documentation regarding plugin authoring can be found in the NodeBB Docs Portal.", + "plugin-search": "Поиск плагинов", + "plugin-search-placeholder": "Искать плагин...", + "reorder-plugins": "Изменить порядок плагинов", + "order-active": "Упорядочить активные плагины", + "dev-interested": "Заинтересованы в написании плагинов для NodeBB?", + "docs-info": "Полную документацию по разработки плагинов можно найти на NodeBB Docs Portal", "order.description": "Certain plugins work ideally when they are initialised before/after other plugins.", "order.explanation": "Plugins load in the order specified here, from top to bottom", - "plugin-item.themes": "Themes", + "plugin-item.themes": "Темы", "plugin-item.deactivate": "Deactivate", "plugin-item.activate": "Activate", - "plugin-item.install": "Install", - "plugin-item.uninstall": "Uninstall", - "plugin-item.settings": "Settings", - "plugin-item.installed": "Installed", - "plugin-item.latest": "Latest", - "plugin-item.upgrade": "Upgrade", + "plugin-item.install": "Установить", + "plugin-item.uninstall": "Удалить", + "plugin-item.settings": "Настройки", + "plugin-item.installed": "Установленные", + "plugin-item.latest": "Недавние", + "plugin-item.upgrade": "Обновить", "plugin-item.more-info": "For more information:", - "plugin-item.unknown": "Unknown", + "plugin-item.unknown": "Неизвестно", "plugin-item.unknown-explanation": "The state of this plugin could not be determined, possibly due to a misconfiguration error.", - "alert.enabled": "Plugin Enabled", - "alert.disabled": "Plugin Disabled", - "alert.upgraded": "Plugin Upgraded", - "alert.installed": "Plugin Installed", - "alert.uninstalled": "Plugin Uninstalled", + "alert.enabled": "Плагин включен", + "alert.disabled": "Плагин выключен", + "alert.upgraded": "Плагин обновлен", + "alert.installed": "Плагин установлен", + "alert.uninstalled": "Плагин удален", "alert.activate-success": "Please restart your NodeBB to fully activate this plugin", - "alert.deactivate-success": "Plugin successfully deactivated", - "alert.upgrade-success": "Please reload your NodeBB to fully upgrade this plugin", - "alert.install-success": "Plugin successfully installed, please activate the plugin.", - "alert.uninstall-success": "The plugin has been successfully deactivated and uninstalled.", + "alert.deactivate-success": "Плагин успешно отключен", + "alert.upgrade-success": "Пожалуйста перезапустите ваш NodeBB, чтобы полностью обновить этот плагин", + "alert.install-success": "Плагин успешно установлен, пожалуйста активируйте этот плагин.", + "alert.uninstall-success": "Плагин успешно отключен и удален.", "alert.suggest-error": "

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

    Server returned (%1): %2
    ", "alert.package-manager-unreachable": "

    NodeBB could not reach the package manager, an upgrade is not suggested at this time.

    ", "alert.incompatible": "

    Your version of NodeBB (v%1) is only cleared to upgrade to v%2 of this plugin. Please update your NodeBB if you wish to install a newer version of this plugin.

    ", diff --git a/public/language/ru/admin/extend/rewards.json b/public/language/ru/admin/extend/rewards.json index 5383a90b33..64ccd4cc38 100644 --- a/public/language/ru/admin/extend/rewards.json +++ b/public/language/ru/admin/extend/rewards.json @@ -1,17 +1,17 @@ { - "rewards": "Rewards", + "rewards": "Награды", "condition-if-users": "If User's", "condition-is": "Is:", "condition-then": "Then:", "max-claims": "Amount of times reward is claimable", "zero-infinite": "Enter 0 for infinite", - "delete": "Delete", - "enable": "Enable", - "disable": "Disable", + "delete": "Удалить", + "enable": "Включить", + "disable": "Выключить", "control-panel": "Rewards Control", - "new-reward": "New Reward", + "new-reward": "Новая награда", - "alert.delete-success": "Successfully deleted reward", + "alert.delete-success": "Награды успешно удалены", "alert.no-inputs-found": "Illegal reward - no inputs found!", - "alert.save-success": "Successfully saved rewards" + "alert.save-success": "Награды успешно сохранены" } \ No newline at end of file diff --git a/public/language/ru/user.json b/public/language/ru/user.json index ff7c1b9c73..f256c020aa 100644 --- a/public/language/ru/user.json +++ b/public/language/ru/user.json @@ -65,8 +65,8 @@ "remove_uploaded_picture": "Удалить фото", "upload_cover_picture": "Загрузить обложку профиля", "remove_cover_picture_confirm": "Вы уверены, что хотите удалить изображение обложки?", - "crop_picture": "Crop picture", - "upload_cropped_picture": "Crop and upload", + "crop_picture": "Вырезать картинку", + "upload_cropped_picture": "Вырезать и загрузить", "settings": "Настройки", "show_email": "Показывать мою элетронную почту", "show_fullname": "Показывать полное имя", @@ -131,5 +131,5 @@ "info.email-history": "История изменения электронной почты", "info.moderation-note": "Примечание модератора", "info.moderation-note.success": "Примечание модератора сохранено", - "info.moderation-note.add": "Add note" + "info.moderation-note.add": "Добавить примечание" } \ No newline at end of file From 5673769c8ca8dddcf96a6141cb589fae8a24512f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 5 May 2017 12:28:41 -0400 Subject: [PATCH 283/316] add back removed rtrim method --- public/src/utils.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/public/src/utils.js b/public/src/utils.js index 8638d06750..8f2dcb8e87 100644 --- a/public/src/utils.js +++ b/public/src/utils.js @@ -429,5 +429,11 @@ }; } + if (typeof String.prototype.rtrim !== 'function') { + String.prototype.rtrim = function () { + return this.replace(/\s+$/g, ''); + }; + } + return utils; })); From 17ca1d33fbea6cf21ec1e8a0b5253a760e3ce4a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 5 May 2017 15:33:00 -0400 Subject: [PATCH 284/316] faster sitemap --- src/sitemap.js | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/sitemap.js b/src/sitemap.js index 8cd91fec6b..8ed9971e7b 100644 --- a/src/sitemap.js +++ b/src/sitemap.js @@ -19,31 +19,25 @@ var sitemap = { }; sitemap.render = function (callback) { - var numTopics = parseInt(meta.config.sitemapTopics, 10) || 500; + var topicsPerPage = parseInt(meta.config.sitemapTopics, 10) || 500; var returnData = { url: nconf.get('url'), topics: [], }; - var numPages; async.waterfall([ - async.apply(db.getSortedSetRange, 'topics:recent', 0, 1000), - function (tids, next) { - privileges.topics.filterTids('read', tids, 0, next); + function (next) { + db.getObjectField('global', 'topicCount', next); }, - ], function (err, tids) { - if (err) { - numPages = 1; - } else { - numPages = Math.ceil(tids.length / numTopics); - } - - for (var x = 1; x <= numPages; x += 1) { - returnData.topics.push(x); - } + function (topicCount, next) { + var numPages = Math.max(0, topicCount / topicsPerPage); + for (var x = 1; x <= numPages; x += 1) { + returnData.topics.push(x); + } - callback(null, returnData); - }); + next(null, returnData); + }, + ], callback); }; sitemap.getPages = function (callback) { From c330218920ff4138e498c9f4b40dc90083f42240 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 5 May 2017 19:15:37 -0400 Subject: [PATCH 285/316] revoke sessions as soon as user is banned --- src/socket.io/user/ban.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/socket.io/user/ban.js b/src/socket.io/user/ban.js index 084307c017..8d31a2b341 100644 --- a/src/socket.io/user/ban.js +++ b/src/socket.io/user/ban.js @@ -36,6 +36,9 @@ module.exports = function (SocketUser) { }); next(); }, + function (next) { + user.auth.revokeAllSessions(uid, next); + }, ], next); }, callback); }; From 76a2b4800b60523387dce2c21d5db96a6cfa352d Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 5 May 2017 19:20:37 -0400 Subject: [PATCH 286/316] add back timeago.allowFuture so we can notify when a ban will expire --- public/src/overrides.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/src/overrides.js b/public/src/overrides.js index 8f2878c79c..b74d93efba 100644 --- a/public/src/overrides.js +++ b/public/src/overrides.js @@ -117,6 +117,8 @@ if (typeof window !== 'undefined') { $.timeago.settings.cutoff = 1000 * 60 * 60 * 24 * (parseInt(config.timeagoCutoff, 10) || 30); } + $.timeago.settings.allowFuture = true; + $.fn.timeago = function () { var els = $(this); From b3bd70235a8ed012b8eba6fea5d377af96922f74 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 5 May 2017 19:31:49 -0400 Subject: [PATCH 287/316] refactor ban messaging; add ban duration to message; use bootbox instead of alert --- public/language/en-GB/error.json | 1 + public/src/app.js | 13 ------------- public/src/sockets.js | 13 +++++++++++-- src/middleware/header.js | 2 +- src/socket.io/user/ban.js | 16 +++++++++++++++- 5 files changed, 28 insertions(+), 17 deletions(-) diff --git a/public/language/en-GB/error.json b/public/language/en-GB/error.json index 7473b8bc62..5c30bae07c 100644 --- a/public/language/en-GB/error.json +++ b/public/language/en-GB/error.json @@ -37,6 +37,7 @@ "user-banned": "User banned", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.", "ban-expiry-missing": "Please provide an end date for this ban", diff --git a/public/src/app.js b/public/src/app.js index 61e5deac50..75a68c9da5 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -10,7 +10,6 @@ app.cacheBuster = null; (function () { var showWelcomeMessage = !!utils.params().loggedin; - var showBannedMessage = !!utils.params().banned && app.user && app.user.uid === 0; templates.setGlobal('config', config); @@ -266,11 +265,6 @@ app.cacheBuster = null; title: '[[global:welcome_back]] ' + app.user.username + '!', message: '[[global:you_have_successfully_logged_in]]', }, - banned: { - format: 'modal', - title: '[[error:user-banned]]', - message: '[[error:user-banned-reason, ' + utils.params().banned + ']]', - }, }; function showAlert(type) { @@ -303,13 +297,6 @@ app.cacheBuster = null; showAlert('login'); }); } - - if (showBannedMessage) { - showBannedMessage = false; - $(document).ready(function () { - showAlert('banned'); - }); - } }; app.openChat = function (roomId, uid) { diff --git a/public/src/sockets.js b/public/src/sockets.js index 6b22ce9ac9..6b11f49e4f 100644 --- a/public/src/sockets.js +++ b/public/src/sockets.js @@ -121,7 +121,16 @@ app.isConnected = false; app.isConnected = false; } - function onEventBanned() { - window.location.href = config.relative_path + '/'; + function onEventBanned(data) { + var message = data.until ? '[[error:user-banned-reason-until, ' + $.timeago(data.until) + ', ' + data.reason + ']]' : '[[error:user-banned-reason, ' + data.reason + ']]'; + + bootbox.alert({ + title: '[[error:user-banned]]', + message: message, + closeButton: false, + callback: function () { + window.location.href = config.relative_path + '/'; + }, + }); } }()); diff --git a/src/middleware/header.js b/src/middleware/header.js index af1a2e0285..f620195a25 100644 --- a/src/middleware/header.js +++ b/src/middleware/header.js @@ -105,7 +105,7 @@ module.exports = function (middleware) { function (results, next) { if (results.banned) { req.logout(); - return res.redirect('/?banned=' + (results.banReason || 'no-reason')); + return res.redirect('/'); } results.user.isAdmin = results.isAdmin; diff --git a/src/socket.io/user/ban.js b/src/socket.io/user/ban.js index 8d31a2b341..99ef11d96f 100644 --- a/src/socket.io/user/ban.js +++ b/src/socket.io/user/ban.js @@ -7,6 +7,7 @@ var websockets = require('../index'); var events = require('../../events'); var privileges = require('../../privileges'); var plugins = require('../../plugins'); +var translator = require('../../translator'); module.exports = function (SocketUser) { SocketUser.banUsers = function (socket, data, callback) { @@ -99,7 +100,20 @@ module.exports = function (SocketUser) { user.ban(uid, until, reason, next); }, function (next) { - websockets.in('uid_' + uid).emit('event:banned'); + console.log(reason); + if (!reason) { + return translator.translate('[[user:info.banned-no-reason]]', function (translated) { + next(false, translated); + }); + } + + next(false, reason); + }, + function (_reason, next) { + websockets.in('uid_' + uid).emit('event:banned', { + until: until, + reason: _reason, + }); next(); }, ], callback); From 41b4ef859f941a1f182e9166dd4128e836d458a9 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 5 May 2017 19:50:50 -0400 Subject: [PATCH 288/316] add ban messaging when logged in --- src/controllers/authentication.js | 38 +++++++++++++++++++------------ src/socket.io/user/ban.js | 1 - src/user/info.js | 2 +- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js index 54d8d99660..77f0fb46b5 100644 --- a/src/controllers/authentication.js +++ b/src/controllers/authentication.js @@ -13,6 +13,7 @@ var user = require('../user'); var plugins = require('../plugins'); var utils = require('../utils'); var Password = require('../password'); +var translator = require('../translator'); var sockets = require('../socket.io'); @@ -379,24 +380,13 @@ authenticationController.localLogin = function (req, username, password, next) { if (!result.isAdmin && parseInt(meta.config.allowLocalLogin, 10) === 0) { return next(new Error('[[error:local-login-disabled]]')); } + if (!userData || !userData.password) { return next(new Error('[[error:invalid-user-data]]')); } + if (result.banned) { - // Retrieve ban reason and show error - return user.getLatestBanInfo(uid, function (err, banInfo) { - if (err) { - if (err.message === 'no-ban-info') { - next(new Error('[[error:user-banned]]')); - } else { - next(err); - } - } else if (banInfo.reason) { - next(new Error('[[error:user-banned-reason, ' + banInfo.reason + ']]')); - } else { - next(new Error('[[error:user-banned]]')); - } - }); + return banUser(uid, next); } Password.compare(password, userData.password, next); @@ -437,5 +427,25 @@ authenticationController.logout = function (req, res, next) { } }; +function banUser(uid, next) { + user.getLatestBanInfo(uid, function (err, banInfo) { + if (err) { + if (err.message === 'no-ban-info') { + return next(new Error('[[error:user-banned]]')); + } + + return next(err); + } + + if (!banInfo.reason) { + translator.translate('[[user:info.banned-no-reason]]', function (translated) { + banInfo.reason = translated; + next(new Error(banInfo.expiry ? '[[error:user-banned-reason-until, ' + banInfo.expiry_readable + ', ' + banInfo.reason + ']]' : '[[error:user-banned-reason, ' + banInfo.reason + ']]')); + }); + } else { + next(new Error(banInfo.expiry ? '[[error:user-banned-reason-until, ' + banInfo.expiry_readable + ', ' + banInfo.reason + ']]' : '[[error:user-banned-reason, ' + banInfo.reason + ']]')); + } + }); +} module.exports = authenticationController; diff --git a/src/socket.io/user/ban.js b/src/socket.io/user/ban.js index 99ef11d96f..cbc15d3270 100644 --- a/src/socket.io/user/ban.js +++ b/src/socket.io/user/ban.js @@ -100,7 +100,6 @@ module.exports = function (SocketUser) { user.ban(uid, until, reason, next); }, function (next) { - console.log(reason); if (!reason) { return translator.translate('[[user:info.banned-no-reason]]', function (translated) { next(false, translated); diff --git a/src/user/info.js b/src/user/info.js index d3ad7d765d..755c7cff8c 100644 --- a/src/user/info.js +++ b/src/user/info.js @@ -41,7 +41,7 @@ module.exports = function (User) { uid: uid, timestamp: timestamp, expiry: parseInt(expiry, 10), - expiry_readable: new Date(parseInt(expiry, 10)).toString().replace(/:/g, '%3A'), + expiry_readable: new Date(parseInt(expiry, 10)).toString(), reason: validator.escape(String(reason)), }); }); From 2a405ad439fb5ab5ee840277d64ca3454be6b433 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 5 May 2017 20:11:18 -0400 Subject: [PATCH 289/316] fixed permaban on redis --- src/user/bans.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/user/bans.js b/src/user/bans.js index dc70d012dd..1795fc64e9 100644 --- a/src/user/bans.js +++ b/src/user/bans.js @@ -67,7 +67,7 @@ module.exports = function (User) { } // If they are banned, see if the ban has expired - var stillBanned = !userData['banned:expire'] || Date.now() < userData['banned:expire']; + var stillBanned = !parseInt(userData['banned:expire'], 10) || Date.now() < parseInt(userData['banned:expire'], 10); if (stillBanned) { return next(null, true); From 480352c650e92ec419a1af0718a30428b70e22e4 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Sat, 6 May 2017 02:37:00 -0400 Subject: [PATCH 290/316] send the banned user an email with reason + time limit if applies --- public/language/en-GB/email.json | 5 +++++ src/socket.io/user/ban.js | 18 ++++++++++++++++++ src/views/emails/banned.tpl | 18 ++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 src/views/emails/banned.tpl diff --git a/public/language/en-GB/email.json b/public/language/en-GB/email.json index e893709772..a55f4f1f52 100644 --- a/public/language/en-GB/email.json +++ b/public/language/en-GB/email.json @@ -44,5 +44,10 @@ "unsub.cta": "Click here to alter those settings", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", + "closing": "Thanks!" } \ No newline at end of file diff --git a/src/socket.io/user/ban.js b/src/socket.io/user/ban.js index cbc15d3270..191b98767e 100644 --- a/src/socket.io/user/ban.js +++ b/src/socket.io/user/ban.js @@ -3,10 +3,12 @@ var async = require('async'); var user = require('../../user'); +var meta = require('../../meta'); var websockets = require('../index'); var events = require('../../events'); var privileges = require('../../privileges'); var plugins = require('../../plugins'); +var emailer = require('../../emailer'); var translator = require('../../translator'); module.exports = function (SocketUser) { @@ -97,6 +99,22 @@ module.exports = function (SocketUser) { if (isAdmin) { return next(new Error('[[error:cant-ban-other-admins]]')); } + + user.getUserField(uid, 'username', next); + }, + function (username, next) { + var siteTitle = meta.config.title || 'NodeBB'; + var data = { + subject: '[[email:banned.subject, ' + siteTitle + ']]', + site_title: siteTitle, + username: username, + until: until ? new Date(until).toString() : false, + reason: reason, + }; + + emailer.send('banned', uid, data, next); + }, + function (next) { user.ban(uid, until, reason, next); }, function (next) { diff --git a/src/views/emails/banned.tpl b/src/views/emails/banned.tpl new file mode 100644 index 0000000000..2384fe1549 --- /dev/null +++ b/src/views/emails/banned.tpl @@ -0,0 +1,18 @@ +

    + [[email:banned.text1, {username}, {site_title}]] + + [[email:banned.text2, {until}]] + +

    + + +

    + [[email:banned.text3]] +

    + +

    + {reason} +

    + + + \ No newline at end of file From 4511814c18088ad859eaa9cd968e63db4e6c6785 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Sat, 6 May 2017 09:22:16 +0000 Subject: [PATCH 291/316] Latest translations and fallbacks --- public/language/ja/admin/settings/general.json | 2 +- public/language/ja/groups.json | 2 +- public/language/ja/notifications.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/language/ja/admin/settings/general.json b/public/language/ja/admin/settings/general.json index cd2480f642..dfce753a98 100644 --- a/public/language/ja/admin/settings/general.json +++ b/public/language/ja/admin/settings/general.json @@ -28,5 +28,5 @@ "outgoing-links": "外部サイトへのリンク", "outgoing-links.warning-page": "送信リンクの警告ページを使用", "search-default-sort-by": "デフォルトのソートを検索", - "outgoing-links.whitelist": "Domains to whitelist for bypassing the warning page" + "outgoing-links.whitelist": "警告ページをバイパスするためのホワイトリストへのドメイン" } \ No newline at end of file diff --git a/public/language/ja/groups.json b/public/language/ja/groups.json index 275f3acd0f..53d1cc1cfb 100644 --- a/public/language/ja/groups.json +++ b/public/language/ja/groups.json @@ -27,7 +27,7 @@ "details.disableJoinRequests": "参加申請を無効にする", "details.grant": "寄贈/取り消す管理権限", "details.kick": "キック", - "details.kick_confirm": "Are you sure you want to remove this member from the group?", + "details.kick_confirm": "このメンバーをグループから削除", "details.owner_options": "グループの管理", "details.group_name": "グループ名", "details.member_count": "メンバー数", diff --git a/public/language/ja/notifications.json b/public/language/ja/notifications.json index 8b461c3a83..bd93397a09 100644 --- a/public/language/ja/notifications.json +++ b/public/language/ja/notifications.json @@ -40,7 +40,7 @@ "user_started_following_you_multiple": "%1 と %2 または他のユーザーがあなたをフォローしました。", "new_register": "%1が登録リクエストを送りました。", "new_register_multiple": "%1の登録リクエストがレビュー待ちです。", - "flag_assigned_to_you": "Flag %1 has been assigned to you", + "flag_assigned_to_you": "フラグ %1はあなたに割当てられました", "email-confirmed": "Eメールが確認されました", "email-confirmed-message": "メールアドレス検証をして頂き、ありがとうございます。あなたのアカウントは完全にアクティブになりました。", "email-confirm-error-message": "あなたのEメールアドレス検証に問題があります。コードが無効か、期限切れです。", From 5e464f14660d1a1dcd71edf3b9f42fd0e3a98130 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Sat, 6 May 2017 20:41:49 +0000 Subject: [PATCH 292/316] Latest translations and fallbacks --- public/language/ar/admin/advanced/database.json | 1 + public/language/ar/admin/manage/groups.json | 1 + public/language/ar/admin/settings/advanced.json | 2 +- public/language/ar/admin/settings/post.json | 2 ++ public/language/ar/admin/settings/user.json | 1 + public/language/ar/email.json | 4 ++++ public/language/ar/error.json | 3 ++- public/language/ar/modules.json | 1 + public/language/ar/user.json | 1 + public/language/bg/admin/advanced/database.json | 1 + public/language/bg/admin/manage/groups.json | 1 + public/language/bg/admin/settings/advanced.json | 2 +- public/language/bg/admin/settings/post.json | 2 ++ public/language/bg/admin/settings/user.json | 1 + public/language/bg/email.json | 4 ++++ public/language/bg/error.json | 3 ++- public/language/bg/modules.json | 1 + public/language/bg/user.json | 1 + public/language/bn/admin/advanced/database.json | 1 + public/language/bn/admin/manage/groups.json | 1 + public/language/bn/admin/settings/advanced.json | 2 +- public/language/bn/admin/settings/post.json | 2 ++ public/language/bn/admin/settings/user.json | 1 + public/language/bn/email.json | 4 ++++ public/language/bn/error.json | 3 ++- public/language/bn/modules.json | 1 + public/language/bn/user.json | 1 + public/language/cs/admin/advanced/database.json | 1 + public/language/cs/admin/manage/groups.json | 1 + public/language/cs/admin/settings/advanced.json | 2 +- public/language/cs/admin/settings/post.json | 2 ++ public/language/cs/admin/settings/user.json | 1 + public/language/cs/email.json | 4 ++++ public/language/cs/error.json | 3 ++- public/language/cs/modules.json | 1 + public/language/cs/user.json | 1 + public/language/da/admin/advanced/database.json | 1 + public/language/da/admin/manage/groups.json | 1 + public/language/da/admin/settings/advanced.json | 2 +- public/language/da/admin/settings/post.json | 2 ++ public/language/da/admin/settings/user.json | 1 + public/language/da/email.json | 4 ++++ public/language/da/error.json | 3 ++- public/language/da/modules.json | 1 + public/language/da/user.json | 1 + public/language/de/admin/advanced/database.json | 1 + public/language/de/admin/manage/groups.json | 1 + public/language/de/admin/settings/advanced.json | 2 +- public/language/de/admin/settings/post.json | 2 ++ public/language/de/admin/settings/user.json | 1 + public/language/de/email.json | 4 ++++ public/language/de/error.json | 3 ++- public/language/de/modules.json | 1 + public/language/de/user.json | 1 + public/language/el/admin/advanced/database.json | 1 + public/language/el/admin/manage/groups.json | 1 + public/language/el/admin/settings/advanced.json | 2 +- public/language/el/admin/settings/post.json | 2 ++ public/language/el/admin/settings/user.json | 1 + public/language/el/email.json | 4 ++++ public/language/el/error.json | 3 ++- public/language/el/modules.json | 1 + public/language/el/user.json | 1 + public/language/en-US/admin/advanced/database.json | 1 + public/language/en-US/admin/manage/groups.json | 1 + public/language/en-US/admin/settings/advanced.json | 2 +- public/language/en-US/admin/settings/post.json | 2 ++ public/language/en-US/admin/settings/user.json | 1 + public/language/en-US/email.json | 4 ++++ public/language/en-US/error.json | 3 ++- public/language/en-US/modules.json | 1 + public/language/en-US/user.json | 1 + public/language/en-x-pirate/admin/advanced/database.json | 1 + public/language/en-x-pirate/admin/manage/groups.json | 1 + public/language/en-x-pirate/admin/settings/advanced.json | 2 +- public/language/en-x-pirate/admin/settings/post.json | 2 ++ public/language/en-x-pirate/admin/settings/user.json | 1 + public/language/en-x-pirate/email.json | 4 ++++ public/language/en-x-pirate/error.json | 3 ++- public/language/en-x-pirate/modules.json | 1 + public/language/en-x-pirate/user.json | 1 + public/language/es/admin/advanced/database.json | 1 + public/language/es/admin/manage/groups.json | 1 + public/language/es/admin/settings/advanced.json | 2 +- public/language/es/admin/settings/post.json | 2 ++ public/language/es/admin/settings/user.json | 1 + public/language/es/email.json | 4 ++++ public/language/es/error.json | 3 ++- public/language/es/modules.json | 1 + public/language/es/user.json | 1 + public/language/et/admin/advanced/database.json | 1 + public/language/et/admin/manage/groups.json | 1 + public/language/et/admin/settings/advanced.json | 2 +- public/language/et/admin/settings/post.json | 2 ++ public/language/et/admin/settings/user.json | 1 + public/language/et/email.json | 4 ++++ public/language/et/error.json | 3 ++- public/language/et/modules.json | 1 + public/language/et/user.json | 1 + public/language/fa-IR/admin/advanced/database.json | 1 + public/language/fa-IR/admin/manage/groups.json | 1 + public/language/fa-IR/admin/settings/advanced.json | 2 +- public/language/fa-IR/admin/settings/post.json | 2 ++ public/language/fa-IR/admin/settings/user.json | 1 + public/language/fa-IR/email.json | 4 ++++ public/language/fa-IR/error.json | 3 ++- public/language/fa-IR/modules.json | 1 + public/language/fa-IR/user.json | 1 + public/language/fi/admin/advanced/database.json | 1 + public/language/fi/admin/manage/groups.json | 1 + public/language/fi/admin/settings/advanced.json | 2 +- public/language/fi/admin/settings/post.json | 2 ++ public/language/fi/admin/settings/user.json | 1 + public/language/fi/email.json | 4 ++++ public/language/fi/error.json | 3 ++- public/language/fi/modules.json | 1 + public/language/fi/user.json | 1 + public/language/fr/admin/advanced/database.json | 1 + public/language/fr/admin/manage/groups.json | 1 + public/language/fr/admin/settings/advanced.json | 2 +- public/language/fr/admin/settings/post.json | 2 ++ public/language/fr/admin/settings/user.json | 1 + public/language/fr/email.json | 4 ++++ public/language/fr/error.json | 3 ++- public/language/fr/modules.json | 1 + public/language/fr/user.json | 1 + public/language/gl/admin/advanced/database.json | 1 + public/language/gl/admin/manage/groups.json | 1 + public/language/gl/admin/settings/advanced.json | 2 +- public/language/gl/admin/settings/post.json | 2 ++ public/language/gl/admin/settings/user.json | 1 + public/language/gl/email.json | 4 ++++ public/language/gl/error.json | 3 ++- public/language/gl/modules.json | 1 + public/language/gl/user.json | 1 + public/language/he/admin/advanced/database.json | 1 + public/language/he/admin/manage/groups.json | 1 + public/language/he/admin/settings/advanced.json | 2 +- public/language/he/admin/settings/post.json | 2 ++ public/language/he/admin/settings/user.json | 1 + public/language/he/email.json | 4 ++++ public/language/he/error.json | 3 ++- public/language/he/modules.json | 1 + public/language/he/user.json | 1 + public/language/hu/admin/advanced/database.json | 1 + public/language/hu/admin/manage/groups.json | 1 + public/language/hu/admin/settings/advanced.json | 2 +- public/language/hu/admin/settings/post.json | 2 ++ public/language/hu/admin/settings/user.json | 1 + public/language/hu/email.json | 4 ++++ public/language/hu/error.json | 3 ++- public/language/hu/modules.json | 1 + public/language/hu/user.json | 1 + public/language/id/admin/advanced/database.json | 1 + public/language/id/admin/manage/groups.json | 1 + public/language/id/admin/settings/advanced.json | 2 +- public/language/id/admin/settings/post.json | 2 ++ public/language/id/admin/settings/user.json | 1 + public/language/id/email.json | 4 ++++ public/language/id/error.json | 3 ++- public/language/id/modules.json | 1 + public/language/id/user.json | 1 + public/language/it/admin/advanced/database.json | 1 + public/language/it/admin/manage/groups.json | 1 + public/language/it/admin/settings/advanced.json | 2 +- public/language/it/admin/settings/post.json | 2 ++ public/language/it/admin/settings/user.json | 1 + public/language/it/email.json | 4 ++++ public/language/it/error.json | 3 ++- public/language/it/modules.json | 1 + public/language/it/user.json | 1 + public/language/ja/admin/advanced/database.json | 1 + public/language/ja/admin/manage/groups.json | 1 + public/language/ja/admin/settings/advanced.json | 2 +- public/language/ja/admin/settings/post.json | 2 ++ public/language/ja/admin/settings/user.json | 1 + public/language/ja/email.json | 4 ++++ public/language/ja/error.json | 3 ++- public/language/ja/modules.json | 1 + public/language/ja/user.json | 1 + public/language/ko/admin/advanced/database.json | 1 + public/language/ko/admin/manage/groups.json | 1 + public/language/ko/admin/settings/advanced.json | 2 +- public/language/ko/admin/settings/post.json | 2 ++ public/language/ko/admin/settings/user.json | 1 + public/language/ko/email.json | 4 ++++ public/language/ko/error.json | 3 ++- public/language/ko/modules.json | 1 + public/language/ko/user.json | 1 + public/language/lt/admin/advanced/database.json | 1 + public/language/lt/admin/manage/groups.json | 1 + public/language/lt/admin/settings/advanced.json | 2 +- public/language/lt/admin/settings/post.json | 2 ++ public/language/lt/admin/settings/user.json | 1 + public/language/lt/email.json | 4 ++++ public/language/lt/error.json | 3 ++- public/language/lt/modules.json | 1 + public/language/lt/user.json | 1 + public/language/ms/admin/advanced/database.json | 1 + public/language/ms/admin/manage/groups.json | 1 + public/language/ms/admin/settings/advanced.json | 2 +- public/language/ms/admin/settings/post.json | 2 ++ public/language/ms/admin/settings/user.json | 1 + public/language/ms/email.json | 4 ++++ public/language/ms/error.json | 3 ++- public/language/ms/modules.json | 1 + public/language/ms/user.json | 1 + public/language/nb/admin/advanced/database.json | 1 + public/language/nb/admin/manage/groups.json | 1 + public/language/nb/admin/settings/advanced.json | 2 +- public/language/nb/admin/settings/post.json | 2 ++ public/language/nb/admin/settings/user.json | 1 + public/language/nb/email.json | 4 ++++ public/language/nb/error.json | 3 ++- public/language/nb/modules.json | 1 + public/language/nb/user.json | 1 + public/language/nl/admin/advanced/database.json | 1 + public/language/nl/admin/manage/groups.json | 1 + public/language/nl/admin/settings/advanced.json | 2 +- public/language/nl/admin/settings/post.json | 2 ++ public/language/nl/admin/settings/user.json | 1 + public/language/nl/email.json | 4 ++++ public/language/nl/error.json | 3 ++- public/language/nl/modules.json | 1 + public/language/nl/user.json | 1 + public/language/pl/admin/advanced/database.json | 1 + public/language/pl/admin/manage/groups.json | 1 + public/language/pl/admin/settings/advanced.json | 2 +- public/language/pl/admin/settings/post.json | 2 ++ public/language/pl/admin/settings/user.json | 1 + public/language/pl/email.json | 4 ++++ public/language/pl/error.json | 3 ++- public/language/pl/modules.json | 1 + public/language/pl/user.json | 1 + public/language/pt-BR/admin/advanced/database.json | 1 + public/language/pt-BR/admin/manage/groups.json | 1 + public/language/pt-BR/admin/settings/advanced.json | 2 +- public/language/pt-BR/admin/settings/post.json | 2 ++ public/language/pt-BR/admin/settings/user.json | 1 + public/language/pt-BR/email.json | 4 ++++ public/language/pt-BR/error.json | 3 ++- public/language/pt-BR/modules.json | 1 + public/language/pt-BR/user.json | 1 + public/language/pt-PT/admin/advanced/database.json | 1 + public/language/pt-PT/admin/manage/groups.json | 1 + public/language/pt-PT/admin/settings/advanced.json | 2 +- public/language/pt-PT/admin/settings/post.json | 2 ++ public/language/pt-PT/admin/settings/user.json | 1 + public/language/pt-PT/email.json | 4 ++++ public/language/pt-PT/error.json | 3 ++- public/language/pt-PT/modules.json | 1 + public/language/pt-PT/user.json | 1 + public/language/ro/admin/advanced/database.json | 1 + public/language/ro/admin/manage/groups.json | 1 + public/language/ro/admin/settings/advanced.json | 2 +- public/language/ro/admin/settings/post.json | 2 ++ public/language/ro/admin/settings/user.json | 1 + public/language/ro/email.json | 4 ++++ public/language/ro/error.json | 3 ++- public/language/ro/modules.json | 1 + public/language/ro/user.json | 1 + public/language/ru/admin/advanced/database.json | 1 + public/language/ru/admin/manage/groups.json | 1 + public/language/ru/admin/settings/advanced.json | 2 +- public/language/ru/admin/settings/post.json | 2 ++ public/language/ru/admin/settings/user.json | 1 + public/language/ru/email.json | 4 ++++ public/language/ru/error.json | 3 ++- public/language/ru/modules.json | 1 + public/language/ru/user.json | 1 + public/language/rw/admin/advanced/database.json | 1 + public/language/rw/admin/manage/groups.json | 1 + public/language/rw/admin/settings/advanced.json | 2 +- public/language/rw/admin/settings/post.json | 2 ++ public/language/rw/admin/settings/user.json | 1 + public/language/rw/email.json | 4 ++++ public/language/rw/error.json | 3 ++- public/language/rw/modules.json | 1 + public/language/rw/user.json | 1 + public/language/sc/admin/advanced/database.json | 1 + public/language/sc/admin/manage/groups.json | 1 + public/language/sc/admin/settings/advanced.json | 2 +- public/language/sc/admin/settings/post.json | 2 ++ public/language/sc/admin/settings/user.json | 1 + public/language/sc/email.json | 4 ++++ public/language/sc/error.json | 3 ++- public/language/sc/modules.json | 1 + public/language/sc/user.json | 1 + public/language/sk/admin/advanced/database.json | 1 + public/language/sk/admin/manage/groups.json | 1 + public/language/sk/admin/settings/advanced.json | 2 +- public/language/sk/admin/settings/post.json | 2 ++ public/language/sk/admin/settings/user.json | 1 + public/language/sk/email.json | 4 ++++ public/language/sk/error.json | 3 ++- public/language/sk/modules.json | 1 + public/language/sk/user.json | 1 + public/language/sl/admin/advanced/database.json | 1 + public/language/sl/admin/manage/groups.json | 1 + public/language/sl/admin/settings/advanced.json | 2 +- public/language/sl/admin/settings/post.json | 2 ++ public/language/sl/admin/settings/user.json | 1 + public/language/sl/email.json | 4 ++++ public/language/sl/error.json | 3 ++- public/language/sl/modules.json | 1 + public/language/sl/user.json | 1 + public/language/sr/admin/advanced/database.json | 1 + public/language/sr/admin/manage/groups.json | 1 + public/language/sr/admin/settings/advanced.json | 2 +- public/language/sr/admin/settings/post.json | 2 ++ public/language/sr/admin/settings/user.json | 1 + public/language/sr/email.json | 4 ++++ public/language/sr/error.json | 3 ++- public/language/sr/modules.json | 1 + public/language/sr/user.json | 1 + public/language/sv/admin/advanced/database.json | 1 + public/language/sv/admin/manage/groups.json | 1 + public/language/sv/admin/settings/advanced.json | 2 +- public/language/sv/admin/settings/post.json | 2 ++ public/language/sv/admin/settings/user.json | 1 + public/language/sv/email.json | 4 ++++ public/language/sv/error.json | 3 ++- public/language/sv/modules.json | 1 + public/language/sv/user.json | 1 + public/language/th/admin/advanced/database.json | 1 + public/language/th/admin/manage/groups.json | 1 + public/language/th/admin/settings/advanced.json | 2 +- public/language/th/admin/settings/post.json | 2 ++ public/language/th/admin/settings/user.json | 1 + public/language/th/email.json | 4 ++++ public/language/th/error.json | 3 ++- public/language/th/modules.json | 1 + public/language/th/user.json | 1 + public/language/tr/admin/advanced/database.json | 1 + public/language/tr/admin/manage/groups.json | 1 + public/language/tr/admin/settings/advanced.json | 2 +- public/language/tr/admin/settings/post.json | 2 ++ public/language/tr/admin/settings/user.json | 1 + public/language/tr/email.json | 4 ++++ public/language/tr/error.json | 3 ++- public/language/tr/modules.json | 1 + public/language/tr/user.json | 1 + public/language/uk/admin/advanced/database.json | 1 + public/language/uk/admin/manage/groups.json | 1 + public/language/uk/admin/settings/advanced.json | 2 +- public/language/uk/admin/settings/post.json | 2 ++ public/language/uk/admin/settings/user.json | 1 + public/language/uk/email.json | 4 ++++ public/language/uk/error.json | 3 ++- public/language/uk/modules.json | 1 + public/language/uk/user.json | 1 + public/language/vi/admin/advanced/database.json | 1 + public/language/vi/admin/manage/groups.json | 1 + public/language/vi/admin/settings/advanced.json | 2 +- public/language/vi/admin/settings/post.json | 2 ++ public/language/vi/admin/settings/user.json | 1 + public/language/vi/email.json | 4 ++++ public/language/vi/error.json | 3 ++- public/language/vi/modules.json | 1 + public/language/vi/user.json | 1 + public/language/zh-CN/admin/advanced/database.json | 1 + public/language/zh-CN/admin/manage/groups.json | 1 + public/language/zh-CN/admin/settings/advanced.json | 2 +- public/language/zh-CN/admin/settings/post.json | 2 ++ public/language/zh-CN/admin/settings/user.json | 1 + public/language/zh-CN/email.json | 4 ++++ public/language/zh-CN/error.json | 3 ++- public/language/zh-CN/modules.json | 1 + public/language/zh-CN/user.json | 1 + public/language/zh-TW/admin/advanced/database.json | 1 + public/language/zh-TW/admin/manage/groups.json | 1 + public/language/zh-TW/admin/settings/advanced.json | 2 +- public/language/zh-TW/admin/settings/post.json | 2 ++ public/language/zh-TW/admin/settings/user.json | 1 + public/language/zh-TW/email.json | 4 ++++ public/language/zh-TW/error.json | 3 ++- public/language/zh-TW/modules.json | 1 + public/language/zh-TW/user.json | 1 + 378 files changed, 588 insertions(+), 84 deletions(-) diff --git a/public/language/ar/admin/advanced/database.json b/public/language/ar/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/ar/admin/advanced/database.json +++ b/public/language/ar/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/ar/admin/manage/groups.json b/public/language/ar/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/ar/admin/manage/groups.json +++ b/public/language/ar/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/ar/admin/settings/advanced.json b/public/language/ar/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/ar/admin/settings/advanced.json +++ b/public/language/ar/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/ar/admin/settings/post.json b/public/language/ar/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/ar/admin/settings/post.json +++ b/public/language/ar/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/ar/admin/settings/user.json b/public/language/ar/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/ar/admin/settings/user.json +++ b/public/language/ar/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/ar/email.json b/public/language/ar/email.json index 67171b01a6..8e0fba7424 100644 --- a/public/language/ar/email.json +++ b/public/language/ar/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "تم إشعارك بهذه المشاركة بناءً على الخيارات التي سبق وأن حددتها.", "test.text1": "هذه رسالة تجريبية للتأكد من صحة إعدادت الرسائل الإلكترونية في منتدى NodeBB خاصتك.", "unsub.cta": "انقر هنا لتغيير تلك الإعدادات", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "شكرًا لك!" } \ No newline at end of file diff --git a/public/language/ar/error.json b/public/language/ar/error.json index 61d2a6e3a6..0a05be7777 100644 --- a/public/language/ar/error.json +++ b/public/language/ar/error.json @@ -30,6 +30,7 @@ "password-too-long": "كلمة السر طويلة ", "user-banned": "المستخدم محظور", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "عذرا, يجب أن تنتظر 1% ثواني قبل قيامك بأول مشاركة", "blacklisted-ip": "نأسف، لقد تم حظرك من استخدام وتصفح المنتدى. إذا كنت تعتقد أن هذا خطأ رجاءًا اتصل بالإدارة. ", "ban-expiry-missing": "رجاءًا ضع تاريخ نهاية الحظر. ", @@ -104,7 +105,7 @@ "chat-disabled": "نظام المحادثة معطل.", "too-many-messages": "لقد أرسلت الكثير من الرسائل، الرجاء اﻹنتظار قليلاً", "invalid-chat-message": "الرسالة غير صالحة.", - "chat-message-too-long": "الرسالة طويلة.", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "غير مصرح لك بتعديل الرسالة.", "cant-remove-last-user": "لأيمكنك إزالت اخر مستخدم.", "cant-delete-chat-message": "غير مصرح لك بحذف الرسالة.", diff --git a/public/language/ar/modules.json b/public/language/ar/modules.json index 1e844226a2..c975207938 100644 --- a/public/language/ar/modules.json +++ b/public/language/ar/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 أشهر", "chat.delete_message_confirm": "هل أنت متأكد من أنك تريد حذف هذه الرسالة؟", "chat.add-users-to-room": "Add users to room", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "اكتب", "composer.show_preview": "عرض المعاينة", "composer.hide_preview": "إخفاء المعاينة", diff --git a/public/language/ar/user.json b/public/language/ar/user.json index fc26490197..92841dfd93 100644 --- a/public/language/ar/user.json +++ b/public/language/ar/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "اسم المستخدم الذي اخترته سبق أخذه، لذا تم تغييره قليلا. أن الآن مسجل تحت الاسم %1", "password_same_as_username": "Your password is the same as your username, please select another password.", "password_same_as_email": "Your password is the same as your email, please select another password.", + "weak_password": "Weak password.", "upload_picture": "ارفع الصورة", "upload_a_picture": "رفع صورة", "remove_uploaded_picture": "Remove Uploaded Picture", diff --git a/public/language/bg/admin/advanced/database.json b/public/language/bg/admin/advanced/database.json index 051b6ad293..21ed28da0d 100644 --- a/public/language/bg/admin/advanced/database.json +++ b/public/language/bg/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 Б", "x-mb": "%1 МБ", + "x-gb": "%1 gb", "uptime-seconds": "Активно време в секунди", "uptime-days": "Активно време в дни", diff --git a/public/language/bg/admin/manage/groups.json b/public/language/bg/admin/manage/groups.json index 0eb53e4d0c..92398de456 100644 --- a/public/language/bg/admin/manage/groups.json +++ b/public/language/bg/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Име на групата", "description": "Описание на групата", + "member-count": "Member Count", "system": "Системна група", "edit": "Редактиране", "search-placeholder": "Търсене", diff --git a/public/language/bg/admin/settings/advanced.json b/public/language/bg/admin/settings/advanced.json index 44998c70b0..7b42194a17 100644 --- a/public/language/bg/admin/settings/advanced.json +++ b/public/language/bg/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Задайте „ALLOW-FROM“, за да поставите NodeBB в „iFrame“", "headers.powered-by": "Персонализиране на заглавната част „Захранван от“, която се изпраща от NodeBB", "headers.acao": "Произход за разрешаване на управлението на достъпа", - "headers.acao-help": "За да забраните достъпа до всички уеб сайтове, оставете празно или задайте null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Методи за разрешаване на управлението на достъпа", "headers.acah": "Заглавки за разрешаване на управлението на достъпа", "traffic-management": "Управление на трафика", diff --git a/public/language/bg/admin/settings/post.json b/public/language/bg/admin/settings/post.json index 7a867fd34a..024daae4e4 100644 --- a/public/language/bg/admin/settings/post.json +++ b/public/language/bg/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Настройки за непрочетените", "unread.cutoff": "Възраст на публикациите, след която те не се показват в непрочетените (в брой дни)", "unread.min-track-last": "Минимален брой публикации в темата, след което да започва следене на последно прочетената", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Настройки за подписите", "signature.disable": "Забраняване на подписите", "signature.no-links": "Забраняване на поставянето на връзки в подписите", diff --git a/public/language/bg/admin/settings/user.json b/public/language/bg/admin/settings/user.json index 33efa26d71..68b8a33e1f 100644 --- a/public/language/bg/admin/settings/user.json +++ b/public/language/bg/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Минимална дължина на потребителското име", "max-username-length": "Максимална дължина на потребителското име", "min-password-length": "Минимална дължина на паролата", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Максимална дължина на информацията на потребителите за себе си", "terms-of-use": "Условия за ползване на форума (Оставете празно и няма да има такива)", "user-search": "Търсене на потребители", diff --git a/public/language/bg/email.json b/public/language/bg/email.json index 9ee562dd11..f426ce0316 100644 --- a/public/language/bg/email.json +++ b/public/language/bg/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "Това известие за публикация беше изпратено до Вас поради настройките Ви за абонаментите.", "test.text1": "Това е пробно е-писмо, за да потвърдим, че изпращачът на е-поща е правилно настроен за Вашия NodeBB.", "unsub.cta": "Натиснете тук, за да промените тези настройки", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Благодарим Ви!" } \ No newline at end of file diff --git a/public/language/bg/error.json b/public/language/bg/error.json index fa06dea5a9..c8a4867da8 100644 --- a/public/language/bg/error.json +++ b/public/language/bg/error.json @@ -30,6 +30,7 @@ "password-too-long": "Паролата е твърде дълга", "user-banned": "Потребителят е блокиран", "user-banned-reason": "За съжаление, този акаунт е блокиран (Причина: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Съжаляваме, но трябва да изчакате поне %1 секунда/и, преди да направите първата си публикация", "blacklisted-ip": "Съжаляваме, но Вашият IP адрес е забранен за ползване в тази общност. Ако смятате, че това е грешка, моля, свържете се с администратор.", "ban-expiry-missing": "Моля, задайте крайна дата за това блокиране", @@ -104,7 +105,7 @@ "chat-disabled": "Системата за разговори е изключена", "too-many-messages": "Изпратили сте твърде много съобщения. Моля, изчакайте малко.", "invalid-chat-message": "Неправилно съобщение", - "chat-message-too-long": "Съобщението е твърде дълго", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "Нямате право да редактирате това съобщение", "cant-remove-last-user": "Не можете да премахнете последния потребител", "cant-delete-chat-message": "Нямате право да изтриете това съобщение", diff --git a/public/language/bg/modules.json b/public/language/bg/modules.json index b459ebaa2a..0f66f729c6 100644 --- a/public/language/bg/modules.json +++ b/public/language/bg/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 месеца", "chat.delete_message_confirm": "Наистина ли искате да изтриете това съобщение?", "chat.add-users-to-room": "Добавяне на потребители към стаята", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Писане", "composer.show_preview": "Показване на прегледа", "composer.hide_preview": "Скриване на прегледа", diff --git a/public/language/bg/user.json b/public/language/bg/user.json index 3f47b9db1b..5cc184b612 100644 --- a/public/language/bg/user.json +++ b/public/language/bg/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Потребителското име, което искате, е заето и затова ние го променихме малко. Вие ще се наричате %1", "password_same_as_username": "Паролата е същата като потребителското Ви име. Моля, изберете друга парола.", "password_same_as_email": "Паролата е същата като е-пощата Ви. Моля, изберете друга парола.", + "weak_password": "Weak password.", "upload_picture": "Качване на снимка", "upload_a_picture": "Качване на снимка", "remove_uploaded_picture": "Премахване на качената снимка", diff --git a/public/language/bn/admin/advanced/database.json b/public/language/bn/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/bn/admin/advanced/database.json +++ b/public/language/bn/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/bn/admin/manage/groups.json b/public/language/bn/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/bn/admin/manage/groups.json +++ b/public/language/bn/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/bn/admin/settings/advanced.json b/public/language/bn/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/bn/admin/settings/advanced.json +++ b/public/language/bn/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/bn/admin/settings/post.json b/public/language/bn/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/bn/admin/settings/post.json +++ b/public/language/bn/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/bn/admin/settings/user.json b/public/language/bn/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/bn/admin/settings/user.json +++ b/public/language/bn/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/bn/email.json b/public/language/bn/email.json index 525460a206..c4b24d267c 100644 --- a/public/language/bn/email.json +++ b/public/language/bn/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "আপনার সাবস্ক্রিপশন সেটিংসের কারনে আপনার এই বার্তাটি পাঠানো হয়েছে", "test.text1": "আপনি সঠিকভাবে নোডবিবির জন্য মেইলার সেটাপ করেছেন কিনা নিশ্চিত করার জন্য এই টেষ্ট ইমেইল পাঠানো হয়েছে", "unsub.cta": "সেটিংসগুলো পরিবর্তন করতে এখানে ক্লিক করুন", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "ধন্যবাদ!" } \ No newline at end of file diff --git a/public/language/bn/error.json b/public/language/bn/error.json index 1141892e4a..5c97eecb55 100644 --- a/public/language/bn/error.json +++ b/public/language/bn/error.json @@ -30,6 +30,7 @@ "password-too-long": "Password too long", "user-banned": "ব্যবহারকারী নিষিদ্ধ", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.", "ban-expiry-missing": "Please provide an end date for this ban", @@ -104,7 +105,7 @@ "chat-disabled": "Chat system disabled", "too-many-messages": "You have sent too many messages, please wait awhile.", "invalid-chat-message": "Invalid chat message", - "chat-message-too-long": "Chat message is too long", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "You are not allowed to edit this message", "cant-remove-last-user": "You can't remove the last user", "cant-delete-chat-message": "You are not allowed to delete this message", diff --git a/public/language/bn/modules.json b/public/language/bn/modules.json index 29b4a81e69..c512813734 100644 --- a/public/language/bn/modules.json +++ b/public/language/bn/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "৩ মাস", "chat.delete_message_confirm": "Are you sure you wish to delete this message?", "chat.add-users-to-room": "Add users to room", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Compose", "composer.show_preview": "Show Preview", "composer.hide_preview": "Hide Preview", diff --git a/public/language/bn/user.json b/public/language/bn/user.json index 8e85a9dcca..765d2d9b60 100644 --- a/public/language/bn/user.json +++ b/public/language/bn/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "আপনি যে ইউজারনেম চাচ্ছিলেন সেটি ইতিমধ্যে নেয়া হয়ে গেছে, কাজেই আমরা এটি কিঞ্চিং পরিবর্তন করেছি। আপনি এখন %1 হিসেবে পরিচিত", "password_same_as_username": "Your password is the same as your username, please select another password.", "password_same_as_email": "Your password is the same as your email, please select another password.", + "weak_password": "Weak password.", "upload_picture": "ছবি আপলোড করুন", "upload_a_picture": "ছবি (একটি) আপলোড করুন", "remove_uploaded_picture": "আপলোড করা ছবিটি সরিয়ে নাও", diff --git a/public/language/cs/admin/advanced/database.json b/public/language/cs/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/cs/admin/advanced/database.json +++ b/public/language/cs/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/cs/admin/manage/groups.json b/public/language/cs/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/cs/admin/manage/groups.json +++ b/public/language/cs/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/cs/admin/settings/advanced.json b/public/language/cs/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/cs/admin/settings/advanced.json +++ b/public/language/cs/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/cs/admin/settings/post.json b/public/language/cs/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/cs/admin/settings/post.json +++ b/public/language/cs/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/cs/admin/settings/user.json b/public/language/cs/admin/settings/user.json index 607a2fbd92..c1684da402 100644 --- a/public/language/cs/admin/settings/user.json +++ b/public/language/cs/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimální délka uživatelského jména", "max-username-length": "Maximální délka uživatelského jména", "min-password-length": "Minimální délka hesla", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximální délka hesla", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/cs/email.json b/public/language/cs/email.json index 1070b21754..544e2ab30f 100644 --- a/public/language/cs/email.json +++ b/public/language/cs/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "Toto upozornění na příspěvek vám bylo odesláno na základě vašeho nastavení odběru.", "test.text1": "Tento testovací e-mail slouží k ověření, že je e-mailer správně nastaven pro práci s NodeBB.", "unsub.cta": "Chcete-li změnit tyto nastavení, klikněte zde.", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Díky!" } \ No newline at end of file diff --git a/public/language/cs/error.json b/public/language/cs/error.json index d0959ae770..41ca155c7a 100644 --- a/public/language/cs/error.json +++ b/public/language/cs/error.json @@ -30,6 +30,7 @@ "password-too-long": "Heslo je příliš dlouhé", "user-banned": "Uživatel byl zakázán", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.", "ban-expiry-missing": "Please provide an end date for this ban", @@ -104,7 +105,7 @@ "chat-disabled": "Chat system disabled", "too-many-messages": "You have sent too many messages, please wait awhile.", "invalid-chat-message": "Invalid chat message", - "chat-message-too-long": "Chat message is too long", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "You are not allowed to edit this message", "cant-remove-last-user": "You can't remove the last user", "cant-delete-chat-message": "You are not allowed to delete this message", diff --git a/public/language/cs/modules.json b/public/language/cs/modules.json index 84afa3b052..3bc708e825 100644 --- a/public/language/cs/modules.json +++ b/public/language/cs/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 měsíce", "chat.delete_message_confirm": "Jste si jisti že chcete odstranit tuto zprávu?", "chat.add-users-to-room": "Přidat uživatele do místnosti", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Napsat", "composer.show_preview": "Ukázat náhled", "composer.hide_preview": "Skrýt náhled", diff --git a/public/language/cs/user.json b/public/language/cs/user.json index 1e31313032..63560d8b42 100644 --- a/public/language/cs/user.json +++ b/public/language/cs/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Zvolené uživatelské jméno je již zabrané, takže jsme ho trochu upravili. Nyní jste znám jako %1", "password_same_as_username": "Vaše heslo je stejné jako vaše přihlašovací jméno. Zvolte si prosím jiné heslo.", "password_same_as_email": "Vaše heslo je stejné jako váš e-mail. Zvolte si prosím jiné heslo.", + "weak_password": "Weak password.", "upload_picture": "Nahrát obrázek", "upload_a_picture": "Nahrát obrázek", "remove_uploaded_picture": "Odstranit nahraný obrázek", diff --git a/public/language/da/admin/advanced/database.json b/public/language/da/admin/advanced/database.json index 59742a0158..42ae5af00c 100644 --- a/public/language/da/admin/advanced/database.json +++ b/public/language/da/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Oppetid i Sekunder", "uptime-days": "Oppetid i Dage", diff --git a/public/language/da/admin/manage/groups.json b/public/language/da/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/da/admin/manage/groups.json +++ b/public/language/da/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/da/admin/settings/advanced.json b/public/language/da/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/da/admin/settings/advanced.json +++ b/public/language/da/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/da/admin/settings/post.json b/public/language/da/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/da/admin/settings/post.json +++ b/public/language/da/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/da/admin/settings/user.json b/public/language/da/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/da/admin/settings/user.json +++ b/public/language/da/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/da/email.json b/public/language/da/email.json index d5591d698d..afd7577d19 100644 --- a/public/language/da/email.json +++ b/public/language/da/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "Denne indlægs notifikation var sendt pga. dine abonnering indstillinger.", "test.text1": "Dette er en test email for at kontrollere, at den udgående email server er opsat korrekt i forhold til din NodeBB installation.", "unsub.cta": "Klik her for at ændre disse indstillinger", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Tak!" } \ No newline at end of file diff --git a/public/language/da/error.json b/public/language/da/error.json index 331a56891e..271adb8fd3 100644 --- a/public/language/da/error.json +++ b/public/language/da/error.json @@ -30,6 +30,7 @@ "password-too-long": "Kodeord er for langt", "user-banned": "Bruger er bortvist", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Beklager, du er nødt til at vente %1 sekund(er) før du opretter dit indlæg", "blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.", "ban-expiry-missing": "Please provide an end date for this ban", @@ -104,7 +105,7 @@ "chat-disabled": "Chat system er deaktiveret", "too-many-messages": "Du har sendt for mange beskeder, vent venligt lidt.", "invalid-chat-message": "Ugyldig chat besked", - "chat-message-too-long": "Chat beskeden er for lang", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "Du har ikke tilladelse til at redigere denne besked", "cant-remove-last-user": "Du kan ikke fjerne den sidste bruger", "cant-delete-chat-message": "Du har ikke tilladelse til at slette denne besked", diff --git a/public/language/da/modules.json b/public/language/da/modules.json index 819631f988..7a96180041 100644 --- a/public/language/da/modules.json +++ b/public/language/da/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 måneder", "chat.delete_message_confirm": "Er du sikker på at du vil slette denne besked?", "chat.add-users-to-room": "Tilføj brugere til chatrum", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Skriv", "composer.show_preview": "Vis forhåndsvisning", "composer.hide_preview": "Fjern forhåndsvisning", diff --git a/public/language/da/user.json b/public/language/da/user.json index 36f08d5a53..efe3d846f8 100644 --- a/public/language/da/user.json +++ b/public/language/da/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Det valgte brugernavn er allerede taget, så vi har ændret det en smule. Du hedder nu %1", "password_same_as_username": "Din adgangskode er det samme som dit brugernavn, vælg venligst en anden adgangskode.", "password_same_as_email": "Dit kodeord er det samme som din email, venligst vælg et andet kodeord", + "weak_password": "Weak password.", "upload_picture": "Upload billede", "upload_a_picture": "Upload et billede", "remove_uploaded_picture": "Fjern uploaded billede", diff --git a/public/language/de/admin/advanced/database.json b/public/language/de/admin/advanced/database.json index 55cc4e0dc8..a2b3963632 100644 --- a/public/language/de/admin/advanced/database.json +++ b/public/language/de/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 B", "x-mb": "%1 MB", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Sekunden", "uptime-days": "Uptime in Tagen", diff --git a/public/language/de/admin/manage/groups.json b/public/language/de/admin/manage/groups.json index e77558609b..f6df5ca99e 100644 --- a/public/language/de/admin/manage/groups.json +++ b/public/language/de/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Gruppenname", "description": "Gruppenbeschreibung", + "member-count": "Member Count", "system": "System-Gruppe", "edit": "Ändern", "search-placeholder": "Suchen", diff --git a/public/language/de/admin/settings/advanced.json b/public/language/de/admin/settings/advanced.json index fbe879e289..cca15102cc 100644 --- a/public/language/de/admin/settings/advanced.json +++ b/public/language/de/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "ALLOW-FROM setzen um NodeBB in einem iFrame zu platzieren", "headers.powered-by": "Anpassen des \"Powered By\" Headers von NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "Um den Zugriff auf alle Seiten zu blockieren, leer lassen oder auf null setzen", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/de/admin/settings/post.json b/public/language/de/admin/settings/post.json index 3c4d0b97d6..5844dfe9eb 100644 --- a/public/language/de/admin/settings/post.json +++ b/public/language/de/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Ungelesen-Einstellungen", "unread.cutoff": "Ungelesen-Limit (in Tagen)", "unread.min-track-last": "Minimale Anzahl an Beiträgen pro Thema bevor die letzte Sichtung mitgeschrieben wird", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signatureinstellungen", "signature.disable": "Signaturen deaktivieren", "signature.no-links": "Links in signaturen deaktivieren", diff --git a/public/language/de/admin/settings/user.json b/public/language/de/admin/settings/user.json index c0309d47fb..b5bfb291c9 100644 --- a/public/language/de/admin/settings/user.json +++ b/public/language/de/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimale länge des Benutzernamens", "max-username-length": "Maximale länge des Benutzernamens", "min-password-length": "Minimale länge des Passwortes", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximale länge von Über Mich", "terms-of-use": "Forum Nutzungsbedingungen (Leer lassen um es zu deaktivieren)", "user-search": "Benutzersuche", diff --git a/public/language/de/email.json b/public/language/de/email.json index 7ba24003a7..490001d405 100644 --- a/public/language/de/email.json +++ b/public/language/de/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "Diese Mitteilung wurde dir aufgrund deiner Abonnement-Einstellungen gesendet.", "test.text1": "Dies ist eine Test-E-Mail, um zu überprüfen, ob der E-Mailer deines NodeBB korrekt eingestellt wurde.", "unsub.cta": "Klicke hier, um diese Einstellungen zu ändern.", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Danke!" } \ No newline at end of file diff --git a/public/language/de/error.json b/public/language/de/error.json index 105b48a3a8..ea969f8284 100644 --- a/public/language/de/error.json +++ b/public/language/de/error.json @@ -30,6 +30,7 @@ "password-too-long": "Passwort ist zu lang", "user-banned": "Benutzer ist gesperrt", "user-banned-reason": "Entschuldige, dieses Konto wurde gebannt (Grund: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Entschuldigung, du musst %1 Sekunde(n) warten, bevor du deinen ersten Beitrag schreiben kannst.", "blacklisted-ip": "Deine IP-Adresse ist für diese Plattform gesperrt. Sollte dies ein Irrtum sein, dann kontaktiere bitte einen Administrator.", "ban-expiry-missing": "Bitte gebe ein Enddatum für diesen Ban an", @@ -104,7 +105,7 @@ "chat-disabled": "Das Chatsystem deaktiviert", "too-many-messages": "Du hast zu viele Nachrichten versandt, bitte warte eine Weile.", "invalid-chat-message": "Ungültige Nachricht", - "chat-message-too-long": "Die Nachricht ist zu lang", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "Du darfst diese Nachricht nicht ändern", "cant-remove-last-user": "Du kannst den letzten Benutzer nicht entfernen", "cant-delete-chat-message": "Du darfst diese Nachricht nicht löschen", diff --git a/public/language/de/modules.json b/public/language/de/modules.json index 47cf83d5c9..a1cf19cea0 100644 --- a/public/language/de/modules.json +++ b/public/language/de/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 Monate", "chat.delete_message_confirm": "Bist du sicher, dass du diese Nachricht löschen möchtest?", "chat.add-users-to-room": "Benutzer zum Raum hinzufügen", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Verfassen", "composer.show_preview": "Vorschau zeigen", "composer.hide_preview": "Vorschau ausblenden", diff --git a/public/language/de/user.json b/public/language/de/user.json index 3de966f842..afcbb48c96 100644 --- a/public/language/de/user.json +++ b/public/language/de/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Der gewünschte Benutzername ist bereits vergeben, deshalb haben wir ihn ein wenig verändert. Du bist jetzt unter dem Namen %1 bekannt.", "password_same_as_username": "Dein Passwort entspricht deinem Benutzernamen, bitte wähle ein anderes Passwort.", "password_same_as_email": "Dein Passwort entspricht deiner E-Mail-Adresse, bitte wähle ein anderes Passwort.", + "weak_password": "Weak password.", "upload_picture": "Bild hochladen", "upload_a_picture": "Ein Bild hochladen", "remove_uploaded_picture": "Hochgeladenes Bild entfernen", diff --git a/public/language/el/admin/advanced/database.json b/public/language/el/admin/advanced/database.json index a900083d84..d4ba886f65 100644 --- a/public/language/el/admin/advanced/database.json +++ b/public/language/el/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/el/admin/manage/groups.json b/public/language/el/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/el/admin/manage/groups.json +++ b/public/language/el/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/el/admin/settings/advanced.json b/public/language/el/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/el/admin/settings/advanced.json +++ b/public/language/el/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/el/admin/settings/post.json b/public/language/el/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/el/admin/settings/post.json +++ b/public/language/el/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/el/admin/settings/user.json b/public/language/el/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/el/admin/settings/user.json +++ b/public/language/el/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/el/email.json b/public/language/el/email.json index 3f5adb3021..8cdc4b057d 100644 --- a/public/language/el/email.json +++ b/public/language/el/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "This post notification was sent to you due to your subscription settings.", "test.text1": "Αυτό είναι ένα δοκιμαστικό email για να επιβεβαιώσουμε ότι ο emailer έχει στηθεί σωστά για το NodeBB.", "unsub.cta": "Κάνε κλικ εδώ για να αλλάξεις αυτές τις ρυθμίσεις", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Ευχαριστούμε!" } \ No newline at end of file diff --git a/public/language/el/error.json b/public/language/el/error.json index 170110489c..cb553a38d1 100644 --- a/public/language/el/error.json +++ b/public/language/el/error.json @@ -30,6 +30,7 @@ "password-too-long": "Password too long", "user-banned": "Ο Χρήστης είναι αποκλεισμένος/η", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.", "ban-expiry-missing": "Please provide an end date for this ban", @@ -104,7 +105,7 @@ "chat-disabled": "Chat system disabled", "too-many-messages": "You have sent too many messages, please wait awhile.", "invalid-chat-message": "Invalid chat message", - "chat-message-too-long": "Chat message is too long", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "You are not allowed to edit this message", "cant-remove-last-user": "You can't remove the last user", "cant-delete-chat-message": "You are not allowed to delete this message", diff --git a/public/language/el/modules.json b/public/language/el/modules.json index e2d45cd043..275b03ce5a 100644 --- a/public/language/el/modules.json +++ b/public/language/el/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 Months", "chat.delete_message_confirm": "Are you sure you wish to delete this message?", "chat.add-users-to-room": "Add users to room", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Compose", "composer.show_preview": "Show Preview", "composer.hide_preview": "Hide Preview", diff --git a/public/language/el/user.json b/public/language/el/user.json index b2e54c9479..dd17f88ac4 100644 --- a/public/language/el/user.json +++ b/public/language/el/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Το όνομα χρήστη που ζήτησες χρησιμοποιείται ήδη, οπότε το τροποποιήσαμε λίγο. Πλέον είσαι γνωστός/ή ώς %1", "password_same_as_username": "Your password is the same as your username, please select another password.", "password_same_as_email": "Your password is the same as your email, please select another password.", + "weak_password": "Weak password.", "upload_picture": "Ανέβασμα φωτογραφίας", "upload_a_picture": "Ανέβασε μια φωτογραφία", "remove_uploaded_picture": "Remove Uploaded Picture", diff --git a/public/language/en-US/admin/advanced/database.json b/public/language/en-US/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/en-US/admin/advanced/database.json +++ b/public/language/en-US/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/en-US/admin/manage/groups.json b/public/language/en-US/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/en-US/admin/manage/groups.json +++ b/public/language/en-US/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/en-US/admin/settings/advanced.json b/public/language/en-US/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/en-US/admin/settings/advanced.json +++ b/public/language/en-US/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/en-US/admin/settings/post.json b/public/language/en-US/admin/settings/post.json index 832372a941..7ee040cf20 100644 --- a/public/language/en-US/admin/settings/post.json +++ b/public/language/en-US/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/en-US/admin/settings/user.json b/public/language/en-US/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/en-US/admin/settings/user.json +++ b/public/language/en-US/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/en-US/email.json b/public/language/en-US/email.json index 691e6309a2..c1e17018fa 100644 --- a/public/language/en-US/email.json +++ b/public/language/en-US/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "This post notification was sent to you due to your subscription settings.", "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", "unsub.cta": "Click here to alter those settings", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Thanks!" } \ No newline at end of file diff --git a/public/language/en-US/error.json b/public/language/en-US/error.json index 3149dadc15..35eaf8cbc6 100644 --- a/public/language/en-US/error.json +++ b/public/language/en-US/error.json @@ -30,6 +30,7 @@ "password-too-long": "Password too long", "user-banned": "User banned", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.", "ban-expiry-missing": "Please provide an end date for this ban", @@ -104,7 +105,7 @@ "chat-disabled": "Chat system disabled", "too-many-messages": "You have sent too many messages, please wait awhile.", "invalid-chat-message": "Invalid chat message", - "chat-message-too-long": "Chat message is too long", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "You are not allowed to edit this message", "cant-remove-last-user": "You can't remove the last user", "cant-delete-chat-message": "You are not allowed to delete this message", diff --git a/public/language/en-US/modules.json b/public/language/en-US/modules.json index e2d45cd043..275b03ce5a 100644 --- a/public/language/en-US/modules.json +++ b/public/language/en-US/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 Months", "chat.delete_message_confirm": "Are you sure you wish to delete this message?", "chat.add-users-to-room": "Add users to room", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Compose", "composer.show_preview": "Show Preview", "composer.hide_preview": "Hide Preview", diff --git a/public/language/en-US/user.json b/public/language/en-US/user.json index 6b2c7e133c..19f71e5e6a 100644 --- a/public/language/en-US/user.json +++ b/public/language/en-US/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "The username you requested was already taken, so we have altered it slightly. You are now known as %1", "password_same_as_username": "Your password is the same as your username, please select another password.", "password_same_as_email": "Your password is the same as your email, please select another password.", + "weak_password": "Weak password.", "upload_picture": "Upload picture", "upload_a_picture": "Upload a picture", "remove_uploaded_picture": "Remove Uploaded Picture", diff --git a/public/language/en-x-pirate/admin/advanced/database.json b/public/language/en-x-pirate/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/en-x-pirate/admin/advanced/database.json +++ b/public/language/en-x-pirate/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/en-x-pirate/admin/manage/groups.json b/public/language/en-x-pirate/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/en-x-pirate/admin/manage/groups.json +++ b/public/language/en-x-pirate/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/en-x-pirate/admin/settings/advanced.json b/public/language/en-x-pirate/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/en-x-pirate/admin/settings/advanced.json +++ b/public/language/en-x-pirate/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/en-x-pirate/admin/settings/post.json b/public/language/en-x-pirate/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/en-x-pirate/admin/settings/post.json +++ b/public/language/en-x-pirate/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/en-x-pirate/admin/settings/user.json b/public/language/en-x-pirate/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/en-x-pirate/admin/settings/user.json +++ b/public/language/en-x-pirate/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/en-x-pirate/email.json b/public/language/en-x-pirate/email.json index 1ffc2ef7a7..d46ef9d972 100644 --- a/public/language/en-x-pirate/email.json +++ b/public/language/en-x-pirate/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "This post notification was sent to you due to your subscription settings.", "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", "unsub.cta": "Click here to alter those settings", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Thanks!" } \ No newline at end of file diff --git a/public/language/en-x-pirate/error.json b/public/language/en-x-pirate/error.json index 3149dadc15..35eaf8cbc6 100644 --- a/public/language/en-x-pirate/error.json +++ b/public/language/en-x-pirate/error.json @@ -30,6 +30,7 @@ "password-too-long": "Password too long", "user-banned": "User banned", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.", "ban-expiry-missing": "Please provide an end date for this ban", @@ -104,7 +105,7 @@ "chat-disabled": "Chat system disabled", "too-many-messages": "You have sent too many messages, please wait awhile.", "invalid-chat-message": "Invalid chat message", - "chat-message-too-long": "Chat message is too long", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "You are not allowed to edit this message", "cant-remove-last-user": "You can't remove the last user", "cant-delete-chat-message": "You are not allowed to delete this message", diff --git a/public/language/en-x-pirate/modules.json b/public/language/en-x-pirate/modules.json index d2c33e827f..8d12a6f964 100644 --- a/public/language/en-x-pirate/modules.json +++ b/public/language/en-x-pirate/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 Months", "chat.delete_message_confirm": "Are you sure you wish to delete this message?", "chat.add-users-to-room": "Add users to room", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Compose", "composer.show_preview": "Show Preview", "composer.hide_preview": "Hide Preview", diff --git a/public/language/en-x-pirate/user.json b/public/language/en-x-pirate/user.json index a789b5d683..b28bcd5ffe 100644 --- a/public/language/en-x-pirate/user.json +++ b/public/language/en-x-pirate/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "The username you requested was already taken, so we have altered it slightly. You are now known as %1", "password_same_as_username": "Your password is the same as your username, please select another password.", "password_same_as_email": "Your password is the same as your email, please select another password.", + "weak_password": "Weak password.", "upload_picture": "Upload picture", "upload_a_picture": "Upload a picture", "remove_uploaded_picture": "Remove Uploaded Picture", diff --git a/public/language/es/admin/advanced/database.json b/public/language/es/admin/advanced/database.json index 91abeec537..9c51814073 100644 --- a/public/language/es/admin/advanced/database.json +++ b/public/language/es/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/es/admin/manage/groups.json b/public/language/es/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/es/admin/manage/groups.json +++ b/public/language/es/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/es/admin/settings/advanced.json b/public/language/es/admin/settings/advanced.json index 31a15df457..794cdb643d 100644 --- a/public/language/es/admin/settings/advanced.json +++ b/public/language/es/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/es/admin/settings/post.json b/public/language/es/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/es/admin/settings/post.json +++ b/public/language/es/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/es/admin/settings/user.json b/public/language/es/admin/settings/user.json index 8e231827ca..395ae98b58 100644 --- a/public/language/es/admin/settings/user.json +++ b/public/language/es/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/es/email.json b/public/language/es/email.json index 6bb5dd06f8..ed986ea853 100644 --- a/public/language/es/email.json +++ b/public/language/es/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "La notificación de este mensaje se te ha enviado debido a tus ajustes de subscripción.", "test.text1": "Este es un email de prueba para verificar que el envío de email está ajustado correctamente para tu NodeBB", "unsub.cta": "Haz click aquí para modificar los ajustes.", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "¡Gracias!" } \ No newline at end of file diff --git a/public/language/es/error.json b/public/language/es/error.json index efd4d26307..f4d5c8fdef 100644 --- a/public/language/es/error.json +++ b/public/language/es/error.json @@ -30,6 +30,7 @@ "password-too-long": "Contraseña muy corta", "user-banned": "Usuario baneado", "user-banned-reason": "Lo siento, esta cuenta ha sido baneada ( Razon: %1 )", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Lo sentimos, es necesario que esperes %1 segundo(s) antes poder hacer tu primera publicación", "blacklisted-ip": "Lo sentimos, tu dirección IP ha sido baneada de esta comunidad. Si crees que debe de haber un error, por favor contacte con un administrador.", "ban-expiry-missing": "Por favor pon una fecha de fin del ban", @@ -104,7 +105,7 @@ "chat-disabled": "El sistema de chat está deshabilitado", "too-many-messages": "Has enviado demasiados mensajes, por favor espera un poco.", "invalid-chat-message": "Mensaje de Chat inválido", - "chat-message-too-long": "Mensaje de Chat es demasiado largo", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "No tienes permiso para editar este mensaje", "cant-remove-last-user": "No puedes eliminar el último usuario", "cant-delete-chat-message": "No tienes permiso para eliminar este mensaje", diff --git a/public/language/es/modules.json b/public/language/es/modules.json index c373361c97..ce157b6c08 100644 --- a/public/language/es/modules.json +++ b/public/language/es/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 meses", "chat.delete_message_confirm": "¿Estás seguro de que deseas eliminar este mensaje?", "chat.add-users-to-room": "Añadir usuarios a la sala", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Crear", "composer.show_preview": "Ver Previsualización", "composer.hide_preview": "Ocultar Previsualización", diff --git a/public/language/es/user.json b/public/language/es/user.json index ed9d53fa1e..cb3779855d 100644 --- a/public/language/es/user.json +++ b/public/language/es/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "El nombre de usuario que has solicitada ya está siendo usado, por tanto lo hemos alterado ligeramente. Ahora eres conocido como %1.", "password_same_as_username": "Tu Constraseña es igual al nombre de Usuario, por favor seleccione otra Constraseña.", "password_same_as_email": "Tu contraseña es igual que tu dirección de correo, por favor elige otra contraseña.", + "weak_password": "Weak password.", "upload_picture": "Subir foto", "upload_a_picture": "Subir una foto", "remove_uploaded_picture": "Borrar Imagen subida", diff --git a/public/language/et/admin/advanced/database.json b/public/language/et/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/et/admin/advanced/database.json +++ b/public/language/et/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/et/admin/manage/groups.json b/public/language/et/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/et/admin/manage/groups.json +++ b/public/language/et/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/et/admin/settings/advanced.json b/public/language/et/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/et/admin/settings/advanced.json +++ b/public/language/et/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/et/admin/settings/post.json b/public/language/et/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/et/admin/settings/post.json +++ b/public/language/et/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/et/admin/settings/user.json b/public/language/et/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/et/admin/settings/user.json +++ b/public/language/et/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/et/email.json b/public/language/et/email.json index 03fa17fc9f..38d3a3bb82 100644 --- a/public/language/et/email.json +++ b/public/language/et/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "See postituse teavitus on saadetud teile tellimuse seadistuse tõttu.", "test.text1": "See on test e-mail kinnitamaks, et emailer on korrektselt seadistatud sinu NodeBB jaoks.", "unsub.cta": "Vajuta siia, et muuta neid seadeid", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Aitäh!" } \ No newline at end of file diff --git a/public/language/et/error.json b/public/language/et/error.json index 41087e9e52..79979ceb3a 100644 --- a/public/language/et/error.json +++ b/public/language/et/error.json @@ -30,6 +30,7 @@ "password-too-long": "Parool liiga pikk", "user-banned": "Kasutaja bannitud", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Vabandust, te peate ootama %1 sekund(it) enne esimese postituse loomist.", "blacklisted-ip": "Vabandust! Sinu IP-aadress on siin kogukonnas keelatud. Kui arvad, et see on eksitus, palun leia kontakti administraatoriga.", "ban-expiry-missing": "Palun sisesta keelu lõpukuupäev", @@ -104,7 +105,7 @@ "chat-disabled": "Vestlus süsteem keelatud", "too-many-messages": "Oled saatnud liiga palju sõnumeid, oota natukene.", "invalid-chat-message": "Vigane vestluse sõnum", - "chat-message-too-long": "Vestluse sõnum on liiga pikk", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "Sul ei ole lubatud antud sõnumit muuta", "cant-remove-last-user": "Sa ei saa viimast kasutajat eemaldada", "cant-delete-chat-message": "Sul ei ole lubatud antud sõnumit kustutada", diff --git a/public/language/et/modules.json b/public/language/et/modules.json index e844e2088e..35c5f88ef8 100644 --- a/public/language/et/modules.json +++ b/public/language/et/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 Kuud", "chat.delete_message_confirm": "Oled kindel, et soovid selle sõnumi kustutada?", "chat.add-users-to-room": "Lisa kasutajaid ruumi", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Koosta", "composer.show_preview": "Kuva eelvaadet", "composer.hide_preview": "Peida eelvaade", diff --git a/public/language/et/user.json b/public/language/et/user.json index fe708b751e..ee6f98ef5b 100644 --- a/public/language/et/user.json +++ b/public/language/et/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Kasutajanimi mida soovisid, ei olnud saadaval, seeg muutsime seda natukene. Sinu uus kasutajanimi on nüüd: %1", "password_same_as_username": "Su parool kattub su kasutajanimega, palun vali mõni muu parool.", "password_same_as_email": "Su parool kattub su e-mailiga, palun vali mõni muu parool.", + "weak_password": "Weak password.", "upload_picture": "Laadi pilt", "upload_a_picture": "Lae pilt üles", "remove_uploaded_picture": "Eemalda üleslaetud pilt", diff --git a/public/language/fa-IR/admin/advanced/database.json b/public/language/fa-IR/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/fa-IR/admin/advanced/database.json +++ b/public/language/fa-IR/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/fa-IR/admin/manage/groups.json b/public/language/fa-IR/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/fa-IR/admin/manage/groups.json +++ b/public/language/fa-IR/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/fa-IR/admin/settings/advanced.json b/public/language/fa-IR/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/fa-IR/admin/settings/advanced.json +++ b/public/language/fa-IR/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/fa-IR/admin/settings/post.json b/public/language/fa-IR/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/fa-IR/admin/settings/post.json +++ b/public/language/fa-IR/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/fa-IR/admin/settings/user.json b/public/language/fa-IR/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/fa-IR/admin/settings/user.json +++ b/public/language/fa-IR/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/fa-IR/email.json b/public/language/fa-IR/email.json index 121cfadc55..b040de56ee 100644 --- a/public/language/fa-IR/email.json +++ b/public/language/fa-IR/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "این اطلاعیه ی پستی که برای شما فرستاده شده به علت تنظیمات اشترک شماست.", "test.text1": "این یک ایمیل امتحانی جهت تایید اینکه فرستنده ایمیل برای انجمن NodeBB شما به درستی تنظیم و نصب شده است", "unsub.cta": "برای ویرایش آن تنظیمات اینجا کلیک کنید", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "سپاس!" } \ No newline at end of file diff --git a/public/language/fa-IR/error.json b/public/language/fa-IR/error.json index b0bfe33133..92eff6f85f 100644 --- a/public/language/fa-IR/error.json +++ b/public/language/fa-IR/error.json @@ -30,6 +30,7 @@ "password-too-long": "کلمه عبور بسیار طولانیست", "user-banned": "کاربر اخراج شد", "user-banned-reason": "با عرض پوزش، این حساب کاربری از انجمن اخراج شده است (دلیل: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "با عرض پوزش، شما باید %1 ثانیه پیش از فرستادن پست نخست خود صبر کنید", "blacklisted-ip": "با عرض پوزش فراوان، نشانی آی پی شما در این انجمن مسدود شده است، اگر فکر می‌کنید اشتباهی رخ داده با مدیریت انجمن تماس بگیرید.", "ban-expiry-missing": "لطفا تاریخ پایان برای این مسدود کردن ارائه دهید", @@ -104,7 +105,7 @@ "chat-disabled": "سیستم گفتمان غیرفعال شده است", "too-many-messages": "شما پیامهای خیلی زیادی فرستاده اید، لطفا مدتی صبر نمایید", "invalid-chat-message": "پیام نامعتبر", - "chat-message-too-long": "پیام طولانی تر از حد مجاز است", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "شما اجازه ی ویرایش این پیام را ندارید", "cant-remove-last-user": "شما نمی توانید آخرین کاربر را حذف کنید", "cant-delete-chat-message": "شما اجازه حذف این پیام را ندارید.", diff --git a/public/language/fa-IR/modules.json b/public/language/fa-IR/modules.json index 5756198c40..0031f86bd2 100644 --- a/public/language/fa-IR/modules.json +++ b/public/language/fa-IR/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 ماه", "chat.delete_message_confirm": "آیا مطمئن هستید که می خواهید این پیام را حذف کنید؟", "chat.add-users-to-room": "اضافه کردن کاربر به این گفتگو", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "ارسال", "composer.show_preview": "نمایش پیش‌نمایش", "composer.hide_preview": "مخفی کردن پیش‌نمایش", diff --git a/public/language/fa-IR/user.json b/public/language/fa-IR/user.json index c053582c92..b2d3fc29a4 100644 --- a/public/language/fa-IR/user.json +++ b/public/language/fa-IR/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "نام کاربری درخواستی شما در حال حاضر گرفته شده است، بنابراین ما آن را کمی تغییر داده‌ایم. شما هم‌اکنون با نام %1null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/fi/admin/settings/post.json b/public/language/fi/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/fi/admin/settings/post.json +++ b/public/language/fi/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/fi/admin/settings/user.json b/public/language/fi/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/fi/admin/settings/user.json +++ b/public/language/fi/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/fi/email.json b/public/language/fi/email.json index 105dc065a1..33ba62f68d 100644 --- a/public/language/fi/email.json +++ b/public/language/fi/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "This post notification was sent to you due to your subscription settings.", "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", "unsub.cta": "Click here to alter those settings", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Thanks!" } \ No newline at end of file diff --git a/public/language/fi/error.json b/public/language/fi/error.json index 072e879a4a..75e5d3609b 100644 --- a/public/language/fi/error.json +++ b/public/language/fi/error.json @@ -30,6 +30,7 @@ "password-too-long": "Password too long", "user-banned": "Käyttäjä on estetty", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Anteeksi, mutta sinun täytyy odottaa %1 sekunti(a) ennen sinun ensimmäisen viestin lähettämistä", "blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.", "ban-expiry-missing": "Please provide an end date for this ban", @@ -104,7 +105,7 @@ "chat-disabled": "Keskustelujärjestelmä on pois käytöstä", "too-many-messages": "You have sent too many messages, please wait awhile.", "invalid-chat-message": "Virheellinen keskusteluviesti", - "chat-message-too-long": "Keskusteluviesti on liian pitkä", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "You are not allowed to edit this message", "cant-remove-last-user": "You can't remove the last user", "cant-delete-chat-message": "You are not allowed to delete this message", diff --git a/public/language/fi/modules.json b/public/language/fi/modules.json index 880e41a742..95ae6fc34e 100644 --- a/public/language/fi/modules.json +++ b/public/language/fi/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 kuukautta", "chat.delete_message_confirm": "Are you sure you wish to delete this message?", "chat.add-users-to-room": "Add users to room", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Compose", "composer.show_preview": "Show Preview", "composer.hide_preview": "Hide Preview", diff --git a/public/language/fi/user.json b/public/language/fi/user.json index 38f88a106a..3651e69e04 100644 --- a/public/language/fi/user.json +++ b/public/language/fi/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Pyytämäsi käyttäjänimi oli jo varattu, joten muutimme sitä hieman. Käyttäjänimesi on siis nyt %1", "password_same_as_username": "Your password is the same as your username, please select another password.", "password_same_as_email": "Your password is the same as your email, please select another password.", + "weak_password": "Weak password.", "upload_picture": "Lataa kuva", "upload_a_picture": "Lataa kuva", "remove_uploaded_picture": "Remove Uploaded Picture", diff --git a/public/language/fr/admin/advanced/database.json b/public/language/fr/admin/advanced/database.json index 31ec94e572..ee2103cf4f 100644 --- a/public/language/fr/admin/advanced/database.json +++ b/public/language/fr/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 octets", "x-mb": "%1 Mo", + "x-gb": "%1 gb", "uptime-seconds": "Disponibilité en secondes", "uptime-days": "Disponibilité en jours", diff --git a/public/language/fr/admin/manage/groups.json b/public/language/fr/admin/manage/groups.json index 82c389292d..ec1898fbcb 100644 --- a/public/language/fr/admin/manage/groups.json +++ b/public/language/fr/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Nom du groupe", "description": "Description du groupe", + "member-count": "Member Count", "system": "Groupe système", "edit": "Éditer", "search-placeholder": "Rechercher", diff --git a/public/language/fr/admin/settings/advanced.json b/public/language/fr/admin/settings/advanced.json index cdd7373c15..42fa98183f 100644 --- a/public/language/fr/admin/settings/advanced.json +++ b/public/language/fr/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Définissez ALLOW-FROM pour afficher NodeBB dans un iFrame", "headers.powered-by": "Personnaliser l'en-tête \"Propulsé par\" envoyé par NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "Pour interdire l'accès à tous les sites, laisser vide ou définissez comme null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "\nAccess-Control-Allow-Methods", "headers.acah": "\nAccess-Control-Allow-Headers", "traffic-management": "Gestion du trafic", diff --git a/public/language/fr/admin/settings/post.json b/public/language/fr/admin/settings/post.json index fdec928a98..5fb72189c8 100644 --- a/public/language/fr/admin/settings/post.json +++ b/public/language/fr/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Paramètres des messages non lus", "unread.cutoff": "Nombre de jours pour les messages non-lus", "unread.min-track-last": "Nombre minimum de messages dans le sujet avant de garder en mémoire le dernier message lu", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Paramètres de signature", "signature.disable": "Désactiver les signatures", "signature.no-links": "Désactiver les liens en signature", diff --git a/public/language/fr/admin/settings/user.json b/public/language/fr/admin/settings/user.json index c37e8adbee..d6c9fb0c23 100644 --- a/public/language/fr/admin/settings/user.json +++ b/public/language/fr/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Longueur minimum du nom d'utilisateur", "max-username-length": "Longueur maxmum du nom d'utilisateur", "min-password-length": "Longueur minimum du mot de passe", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Longueur maximum du À propos de moi", "terms-of-use": "Conditions générales d'utilisation du forum (Laisser vide pour désactiver)", "user-search": "Rechercher un utilisateur", diff --git a/public/language/fr/email.json b/public/language/fr/email.json index 477db2a967..73634e14f3 100644 --- a/public/language/fr/email.json +++ b/public/language/fr/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "La notification de ce message vous a été envoyé en raison de vos paramètres d'abonnement.", "test.text1": "Ceci est un e-mail de test pour vérifier que l'e-mailer est correctement configuré pour NodeBB.", "unsub.cta": "Cliquez ici pour modifier ces paramètres", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Merci !" } \ No newline at end of file diff --git a/public/language/fr/error.json b/public/language/fr/error.json index 8bf9995e41..a158d6f83b 100644 --- a/public/language/fr/error.json +++ b/public/language/fr/error.json @@ -30,6 +30,7 @@ "password-too-long": "Mot de passe trop long", "user-banned": "Utilisateur banni", "user-banned-reason": "Désolé, ce compte a été banni (Raison : %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Désolé, vous devez attendre encore %1 seconde(s) avant d'envoyer votre premier message", "blacklisted-ip": "Désolé, votre adresse IP a été bannie de cette communauté. Si vous pensez que c'est une erreur, veuillez contacter un administrateur.", "ban-expiry-missing": "Veuillez entrer une date de fin de banissement.", @@ -104,7 +105,7 @@ "chat-disabled": "Système de chat désactivé", "too-many-messages": "Vous avez envoyé trop de messages, veuillez patienter un instant.", "invalid-chat-message": "Message de Chat invalide", - "chat-message-too-long": "Le message de Chat est trop long", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "Vous n'avez pas l'autorisation de modifier ce message", "cant-remove-last-user": "Vous ne pouvez pas supprimer le dernier utilisateur", "cant-delete-chat-message": "Vous n'avez pas l'autorisation de supprimer ce message", diff --git a/public/language/fr/modules.json b/public/language/fr/modules.json index c41811fd84..5f3c253db7 100644 --- a/public/language/fr/modules.json +++ b/public/language/fr/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 Mois", "chat.delete_message_confirm": "Êtes-vous sûr de vouloir supprimer ce message ?", "chat.add-users-to-room": "Ajouter des participants", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Écrire", "composer.show_preview": "Afficher l'aperçu", "composer.hide_preview": "Masquer l'aperçu", diff --git a/public/language/fr/user.json b/public/language/fr/user.json index 0895c10716..f09cefe60b 100644 --- a/public/language/fr/user.json +++ b/public/language/fr/user.json @@ -60,6 +60,7 @@ "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", "password_same_as_username": "Votre mot de passe est identique à votre nom d'utilisateur. Veuillez en choisir un autre.", "password_same_as_email": "Votre mot de passe est identique à votre adresse email. Veuillez en choisir un autre.", + "weak_password": "Weak password.", "upload_picture": "Envoyer l'image", "upload_a_picture": "Envoyer une image", "remove_uploaded_picture": "Supprimer l'image envoyée", diff --git a/public/language/gl/admin/advanced/database.json b/public/language/gl/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/gl/admin/advanced/database.json +++ b/public/language/gl/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/gl/admin/manage/groups.json b/public/language/gl/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/gl/admin/manage/groups.json +++ b/public/language/gl/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/gl/admin/settings/advanced.json b/public/language/gl/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/gl/admin/settings/advanced.json +++ b/public/language/gl/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/gl/admin/settings/post.json b/public/language/gl/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/gl/admin/settings/post.json +++ b/public/language/gl/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/gl/admin/settings/user.json b/public/language/gl/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/gl/admin/settings/user.json +++ b/public/language/gl/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/gl/email.json b/public/language/gl/email.json index 7bcedbf37e..0aee60f82d 100644 --- a/public/language/gl/email.json +++ b/public/language/gl/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "Esta notificación de mensaxe foiche enviada polas túas opcións de subscrición.", "test.text1": "Esta é unha mensaxe de proba para verificar que o envío de correo está configurado correctamente para o seu NodeBB.", "unsub.cta": "Pica aquí para cambiar os axustes", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Grazas!" } \ No newline at end of file diff --git a/public/language/gl/error.json b/public/language/gl/error.json index 86c84601f6..f6fd1bca9b 100644 --- a/public/language/gl/error.json +++ b/public/language/gl/error.json @@ -30,6 +30,7 @@ "password-too-long": "Contrasinal moi longa", "user-banned": "Usuario expulsado", "user-banned-reason": "Desculpa, esta conta foi baneada (Razón: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Desculpa, agarda %1 second(s) antes de facer a túa primeira publicación.", "blacklisted-ip": "Sentímolo, o teu enderezo IP foi baneado desta comunidade. Se crees que se debe a un erro, por favor, contacte cun administrador.", "ban-expiry-missing": "Por favor, engade unha data de fin do ban", @@ -104,7 +105,7 @@ "chat-disabled": "Charlas desactivadas", "too-many-messages": "Estás a enviar moitas mensaxes, por favor, agarda un anaco. ", "invalid-chat-message": "Mensaxe inválida", - "chat-message-too-long": "Mensaxe moi longa", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "Non tes permitido editar esta mensaxe.", "cant-remove-last-user": "Non podes quitar o último usuario", "cant-delete-chat-message": "Non tes permitido borrar esta mensaxe.", diff --git a/public/language/gl/modules.json b/public/language/gl/modules.json index 0a9c2cca4f..8172d7dae3 100644 --- a/public/language/gl/modules.json +++ b/public/language/gl/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 Meses", "chat.delete_message_confirm": "Estás seguro de que desexas eliminar esta mensaxe?", "chat.add-users-to-room": "Engadir usuarios á sala", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Elaborar", "composer.show_preview": "Amosar vista previa", "composer.hide_preview": "Agochar vista previa", diff --git a/public/language/gl/user.json b/public/language/gl/user.json index 0405ac4e05..4019fabb5c 100644 --- a/public/language/gl/user.json +++ b/public/language/gl/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Ese nome de usuario xa estaba collido, así que o modificamos lixeiramente. Agora o teu nome é %1 ", "password_same_as_username": "O teu contrasinal e o teu nome de usuario son os mesmos, por favor, escolle outro contrasinal.", "password_same_as_email": "O teu contrasinal é igual que o teu enderezo electrónico, por favor, escolle outro contrasinal.", + "weak_password": "Weak password.", "upload_picture": "Subir foto", "upload_a_picture": "Subir unha foto", "remove_uploaded_picture": "Borrar unha foto subida", diff --git a/public/language/he/admin/advanced/database.json b/public/language/he/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/he/admin/advanced/database.json +++ b/public/language/he/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/he/admin/manage/groups.json b/public/language/he/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/he/admin/manage/groups.json +++ b/public/language/he/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/he/admin/settings/advanced.json b/public/language/he/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/he/admin/settings/advanced.json +++ b/public/language/he/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/he/admin/settings/post.json b/public/language/he/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/he/admin/settings/post.json +++ b/public/language/he/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/he/admin/settings/user.json b/public/language/he/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/he/admin/settings/user.json +++ b/public/language/he/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/he/email.json b/public/language/he/email.json index 48ff8f4a50..aa2579e87a 100644 --- a/public/language/he/email.json +++ b/public/language/he/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "התראת הפוסט הזו נשלחה אליך על-פי הגדרות החשבון שלך.", "test.text1": "זהו אימייל ניסיון על מנת לוודא שהגדרות המייל בוצעו כהלכה בהגדרות NodeBB.", "unsub.cta": "לחץ כאן לשנות הגדרות אלו", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "תודה!" } \ No newline at end of file diff --git a/public/language/he/error.json b/public/language/he/error.json index b883e71aff..16430c13d1 100644 --- a/public/language/he/error.json +++ b/public/language/he/error.json @@ -30,6 +30,7 @@ "password-too-long": "הסיסמה ארוכה מדי", "user-banned": "המשתמש מושעה", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "אנא המתן %1 שניות לפני פרסום ההודעה", "blacklisted-ip": "מצטערים, אך הורחקת מקהילה זו. אם הנך סבור שמדובר בטעות, אנא צור קשר עם מנהלי הקהילה.", "ban-expiry-missing": "אנא ספק תאריך סיום להרחקה זו.", @@ -104,7 +105,7 @@ "chat-disabled": "מערכת הצ'אט לא פעילה", "too-many-messages": "שלחת יותר מדי הודעות, אנא המתן לזמן מה.", "invalid-chat-message": "הודעת צ'אט לא תקינה", - "chat-message-too-long": "הודעת הצ'אט ארוכה מדי", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "אתה לא רשאי לערוך הודעה זו", "cant-remove-last-user": "אינך יכול למחוק את המשתמש האחרון", "cant-delete-chat-message": "אתה לא רשאי למחוק הודעה זו", diff --git a/public/language/he/modules.json b/public/language/he/modules.json index 87f4a45685..6e41dad53f 100644 --- a/public/language/he/modules.json +++ b/public/language/he/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 חודשים", "chat.delete_message_confirm": "האם אתה בטוח שברצונך למחוק הודעה זו?", "chat.add-users-to-room": "הוסף משתמשים לצ'אט", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "צור", "composer.show_preview": "הצג תצוגה מקדימה", "composer.hide_preview": "הסתר תצוגה מקדימה", diff --git a/public/language/he/user.json b/public/language/he/user.json index 0b96a2075e..d5ad68e33b 100644 --- a/public/language/he/user.json +++ b/public/language/he/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "שם המשתמש שבחרת כבר תפוס, אז שינינו אותו מעט. שם המשתמש שלך כעת הוא %1", "password_same_as_username": "הסיסמה שלך זהה לשם המשתמש, אנא בחר סיסמה שונה.", "password_same_as_email": "הסיסמה שלך זהה לכתובת המייל שלך, אנא בחר סיסמה שונה.", + "weak_password": "Weak password.", "upload_picture": "העלה תמונה", "upload_a_picture": "העלה תמונה", "remove_uploaded_picture": "מחק את התמונה שהועלתה", diff --git a/public/language/hu/admin/advanced/database.json b/public/language/hu/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/hu/admin/advanced/database.json +++ b/public/language/hu/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/hu/admin/manage/groups.json b/public/language/hu/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/hu/admin/manage/groups.json +++ b/public/language/hu/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/hu/admin/settings/advanced.json b/public/language/hu/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/hu/admin/settings/advanced.json +++ b/public/language/hu/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/hu/admin/settings/post.json b/public/language/hu/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/hu/admin/settings/post.json +++ b/public/language/hu/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/hu/admin/settings/user.json b/public/language/hu/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/hu/admin/settings/user.json +++ b/public/language/hu/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/hu/email.json b/public/language/hu/email.json index 67f241f750..548aba2ed3 100644 --- a/public/language/hu/email.json +++ b/public/language/hu/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "Ez a hozzászólás-értesítés a feliratkozási beállításaid miatt lett kiküldve.", "test.text1": "Ez egy teszt levél, ami által ellenőrizzük, hogy a levelező helyesen lett beállítva a NodeBB-ben.", "unsub.cta": "Kattintson ide a beállítások módosításához", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Köszönjük!" } \ No newline at end of file diff --git a/public/language/hu/error.json b/public/language/hu/error.json index 499fba5713..2741e13087 100644 --- a/public/language/hu/error.json +++ b/public/language/hu/error.json @@ -30,6 +30,7 @@ "password-too-long": "Password too long", "user-banned": "Kitiltott felhasználó", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.", "ban-expiry-missing": "Please provide an end date for this ban", @@ -104,7 +105,7 @@ "chat-disabled": "Chat system disabled", "too-many-messages": "Túl sok üzenetet küldtél, kérlek várj egy picit.", "invalid-chat-message": "Invalid chat message", - "chat-message-too-long": "Chat message is too long", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "You are not allowed to edit this message", "cant-remove-last-user": "You can't remove the last user", "cant-delete-chat-message": "You are not allowed to delete this message", diff --git a/public/language/hu/modules.json b/public/language/hu/modules.json index 15a176a383..2421ff6436 100644 --- a/public/language/hu/modules.json +++ b/public/language/hu/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 hónap", "chat.delete_message_confirm": "Are you sure you wish to delete this message?", "chat.add-users-to-room": "Add users to room", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Compose", "composer.show_preview": "Show Preview", "composer.hide_preview": "Hide Preview", diff --git a/public/language/hu/user.json b/public/language/hu/user.json index 2870533d8f..c1e48843a5 100644 --- a/public/language/hu/user.json +++ b/public/language/hu/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "A kívánt felhasználónév már foglalt, így változtatnunk kellett rajta egy kicsit. Mostantól %1 név alatt vagy ismert.", "password_same_as_username": "A jelszavad megegyezik a felhasználóneveddel, kérlek válassz másik jelszót.", "password_same_as_email": "A jelszavad megegyezik az e-mail címeddel, kérlek válassz másik jelszót.", + "weak_password": "Weak password.", "upload_picture": "Kép feltöltése", "upload_a_picture": "Egy kép feltöltése", "remove_uploaded_picture": "Feltöltött kép eltávolítása", diff --git a/public/language/id/admin/advanced/database.json b/public/language/id/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/id/admin/advanced/database.json +++ b/public/language/id/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/id/admin/manage/groups.json b/public/language/id/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/id/admin/manage/groups.json +++ b/public/language/id/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/id/admin/settings/advanced.json b/public/language/id/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/id/admin/settings/advanced.json +++ b/public/language/id/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/id/admin/settings/post.json b/public/language/id/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/id/admin/settings/post.json +++ b/public/language/id/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/id/admin/settings/user.json b/public/language/id/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/id/admin/settings/user.json +++ b/public/language/id/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/id/email.json b/public/language/id/email.json index 9e79021f89..9289a89134 100644 --- a/public/language/id/email.json +++ b/public/language/id/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "This post notification was sent to you due to your subscription settings.", "test.text1": "Ini hanya email percobaan untuk menverifkasi pengiriman email telah diatur oleh NodeBB secara benar", "unsub.cta": "Klik di sini untuk mengubah pengaturan-pengaturan tersebut.", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Terima kasih!" } \ No newline at end of file diff --git a/public/language/id/error.json b/public/language/id/error.json index 716db0823d..18c53d69f5 100644 --- a/public/language/id/error.json +++ b/public/language/id/error.json @@ -30,6 +30,7 @@ "password-too-long": "Password too long", "user-banned": "Pengguna dibanned", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.", "ban-expiry-missing": "Please provide an end date for this ban", @@ -104,7 +105,7 @@ "chat-disabled": "Chat system disabled", "too-many-messages": "You have sent too many messages, please wait awhile.", "invalid-chat-message": "Invalid chat message", - "chat-message-too-long": "Chat message is too long", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "You are not allowed to edit this message", "cant-remove-last-user": "You can't remove the last user", "cant-delete-chat-message": "You are not allowed to delete this message", diff --git a/public/language/id/modules.json b/public/language/id/modules.json index 681551cb0b..194e9c7428 100644 --- a/public/language/id/modules.json +++ b/public/language/id/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 Bulan", "chat.delete_message_confirm": "Are you sure you wish to delete this message?", "chat.add-users-to-room": "Add users to room", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Compose", "composer.show_preview": "Show Preview", "composer.hide_preview": "Hide Preview", diff --git a/public/language/id/user.json b/public/language/id/user.json index d039f98129..60cbd283fe 100644 --- a/public/language/id/user.json +++ b/public/language/id/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Nama pengguna yang kamu inginkan telah diambil, jadi kami merubahnya sedikit. Kamu saat ini dikenal sebagai %1", "password_same_as_username": "Your password is the same as your username, please select another password.", "password_same_as_email": "Your password is the same as your email, please select another password.", + "weak_password": "Weak password.", "upload_picture": "Unggah gambar/foto", "upload_a_picture": "Unggah sebuah gambar/foto", "remove_uploaded_picture": "Remove Uploaded Picture", diff --git a/public/language/it/admin/advanced/database.json b/public/language/it/admin/advanced/database.json index 53404f0915..7430868748 100644 --- a/public/language/it/admin/advanced/database.json +++ b/public/language/it/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in secondi", "uptime-days": "Uptime in giorni", diff --git a/public/language/it/admin/manage/groups.json b/public/language/it/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/it/admin/manage/groups.json +++ b/public/language/it/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/it/admin/settings/advanced.json b/public/language/it/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/it/admin/settings/advanced.json +++ b/public/language/it/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/it/admin/settings/post.json b/public/language/it/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/it/admin/settings/post.json +++ b/public/language/it/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/it/admin/settings/user.json b/public/language/it/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/it/admin/settings/user.json +++ b/public/language/it/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/it/email.json b/public/language/it/email.json index f30f93b9f5..1cf2f5ad7b 100644 --- a/public/language/it/email.json +++ b/public/language/it/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "Questo post ti è stato notificato in base alle tue impostazioni di sottoscrizione.", "test.text1": "Questa è una email di test per verificare che il servizio di invio email è configurato correttamente sul tuo NodeBB.", "unsub.cta": "Clicca qui per modificare queste impostazioni", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Grazie!" } \ No newline at end of file diff --git a/public/language/it/error.json b/public/language/it/error.json index 0aba02e938..e87e872b70 100644 --- a/public/language/it/error.json +++ b/public/language/it/error.json @@ -30,6 +30,7 @@ "password-too-long": "Password troppo lunga", "user-banned": "Utente bannato", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Devi attendere %1 secondi prima di creare il tuo primo post", "blacklisted-ip": "Purtroppo il tuo indirizzo IP è stato bannato da questa community. Se credi che ci sia stato un errore contatta un amministratore.", "ban-expiry-missing": "Per favore fornisci una data finale per questo ban", @@ -104,7 +105,7 @@ "chat-disabled": "Il sistema di chat è stato disabilitato", "too-many-messages": "Hai inviato troppi messaggi, aspetta un attimo.", "invalid-chat-message": "Messaggio chat non valido", - "chat-message-too-long": "Il messaggio chat è troppo lungo", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "Non ti è permesso di modificare questo messaggio", "cant-remove-last-user": "Non puoi rimuovere l'ultimo utente", "cant-delete-chat-message": "Non ti è permesso di eliminare questo messaggio", diff --git a/public/language/it/modules.json b/public/language/it/modules.json index 7e4e0fa0ce..5fcef5d582 100644 --- a/public/language/it/modules.json +++ b/public/language/it/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 Mesi", "chat.delete_message_confirm": "Sei sicuro di voler eliminare questo messaggio?", "chat.add-users-to-room": "Aggiungi utenti alla stanza", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Componi", "composer.show_preview": "Visualizza Anteprima", "composer.hide_preview": "Nascondi Anteprima", diff --git a/public/language/it/user.json b/public/language/it/user.json index d994858692..8bbe9c5ea3 100644 --- a/public/language/it/user.json +++ b/public/language/it/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Il nome utente che hai richiesto era già stato utilizzato, quindi lo abbiamo modificato leggermente. Ora il tuo è %1", "password_same_as_username": "La tua password è uguale al tuo username, per piacere scegli un'altra password", "password_same_as_email": "La tua password sembra coincidere con la tua email, per favore fornisci un'altra password.", + "weak_password": "Weak password.", "upload_picture": "Carica foto", "upload_a_picture": "Carica una foto", "remove_uploaded_picture": "Elimina foto caricata", diff --git a/public/language/ja/admin/advanced/database.json b/public/language/ja/admin/advanced/database.json index 4dac0a3eab..26f19fa233 100644 --- a/public/language/ja/admin/advanced/database.json +++ b/public/language/ja/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "秒単位の稼働時間", "uptime-days": "日単位の稼働時間", diff --git a/public/language/ja/admin/manage/groups.json b/public/language/ja/admin/manage/groups.json index 56aaeaa1d4..c0f55920fd 100644 --- a/public/language/ja/admin/manage/groups.json +++ b/public/language/ja/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "グループ名", "description": "グループの説明", + "member-count": "Member Count", "system": "システムグループ", "edit": "編集", "search-placeholder": "検索", diff --git a/public/language/ja/admin/settings/advanced.json b/public/language/ja/admin/settings/advanced.json index f54cb6d4d7..f7ffbdbaf2 100644 --- a/public/language/ja/admin/settings/advanced.json +++ b/public/language/ja/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "NodeBBをインラインフレーム内に配置するようALLOW-FROMを設定する", "headers.powered-by": "NodeBBから送信された「Powered By」ヘッダーをカスタマイズする", "headers.acao": "アクセス-制御-有効-原点", - "headers.acao-help": "すべてのサイトへのアクセスを拒否するには空のままにするか、nullに設定します", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "アクセス-制御-有効-メソッド", "headers.acah": "アクセス-制御-有効-ヘッダー", "traffic-management": "トラフィック管理", diff --git a/public/language/ja/admin/settings/post.json b/public/language/ja/admin/settings/post.json index b82142e0e2..2eea0ed739 100644 --- a/public/language/ja/admin/settings/post.json +++ b/public/language/ja/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "未読の設定", "unread.cutoff": "未読のカットオフ日", "unread.min-track-last": "最後に読み込みを行う前に追跡するスレッドの最小投稿数", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "署名の設定", "signature.disable": "署名を無効にする", "signature.no-links": "署名内のリンクを無効にする", diff --git a/public/language/ja/admin/settings/user.json b/public/language/ja/admin/settings/user.json index ee78fda0f4..1048a1863c 100644 --- a/public/language/ja/admin/settings/user.json +++ b/public/language/ja/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "ユーザー名の最小文字数", "max-username-length": "ユーザー名の最大文字数", "min-password-length": "パスワードの最小文字数", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "概要の最大文字数", "terms-of-use": "フォーラム利用規約(空白のままにしておくと無効になります)", "user-search": "ユーザーを検索", diff --git a/public/language/ja/email.json b/public/language/ja/email.json index 461b393f30..8dcf90bb5c 100644 --- a/public/language/ja/email.json +++ b/public/language/ja/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "この投稿の通知はあなたの申し込み設定により送られました。", "test.text1": "このメールはNodeBBのメーラー(emailer)が正しく設定されているか確認をするためのメールです。", "unsub.cta": "ここをクリックして設定を変更する", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "ありがとうございます!" } \ No newline at end of file diff --git a/public/language/ja/error.json b/public/language/ja/error.json index d01ac87df2..52df59a6d4 100644 --- a/public/language/ja/error.json +++ b/public/language/ja/error.json @@ -30,6 +30,7 @@ "password-too-long": "パスワードが長すぎます", "user-banned": "ユーザーは停止されています", "user-banned-reason": "申し訳ありませんが、このアカウントは停止されています。 (理由: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "申し訳ありません。登録後に投稿を行うには%1秒お待ち下さい。", "blacklisted-ip": "申し訳ありませんがあなたのIPアドレスは当コミュニティで停止されています。もし誤ったエラーだと思われる場合は管理者にお問い合わせください。", "ban-expiry-missing": "この停止の終了日を入力してください。", @@ -104,7 +105,7 @@ "chat-disabled": "Chat system disabled", "too-many-messages": "You have sent too many messages, please wait awhile.", "invalid-chat-message": "Invalid chat message", - "chat-message-too-long": "Chat message is too long", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "You are not allowed to edit this message", "cant-remove-last-user": "You can't remove the last user", "cant-delete-chat-message": "あなたはこのメッセージを削除する権限を持っていません。", diff --git a/public/language/ja/modules.json b/public/language/ja/modules.json index 938c217701..64678bf638 100644 --- a/public/language/ja/modules.json +++ b/public/language/ja/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3ヶ月", "chat.delete_message_confirm": "本当にこのメッセージを削除しますか?", "chat.add-users-to-room": "部屋にユーザーを追加", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "構成", "composer.show_preview": "プレビュー表示", "composer.hide_preview": "プレビュー非表示", diff --git a/public/language/ja/user.json b/public/language/ja/user.json index 0f144ba5ef..4889823207 100644 --- a/public/language/ja/user.json +++ b/public/language/ja/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "このユーザー名はすでに使用されています。いまのユーザー名は %1 です。", "password_same_as_username": "パスワードがユーザー名と同じですから、他のパスワードを使って下さい。", "password_same_as_email": "パスワードがメールアドレスと同じです。他のパスワードを使って下さい。", + "weak_password": "Weak password.", "upload_picture": "画像をアップロード", "upload_a_picture": "画像をアップロード", "remove_uploaded_picture": "アップした写真を取り消します", diff --git a/public/language/ko/admin/advanced/database.json b/public/language/ko/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/ko/admin/advanced/database.json +++ b/public/language/ko/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/ko/admin/manage/groups.json b/public/language/ko/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/ko/admin/manage/groups.json +++ b/public/language/ko/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/ko/admin/settings/advanced.json b/public/language/ko/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/ko/admin/settings/advanced.json +++ b/public/language/ko/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/ko/admin/settings/post.json b/public/language/ko/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/ko/admin/settings/post.json +++ b/public/language/ko/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/ko/admin/settings/user.json b/public/language/ko/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/ko/admin/settings/user.json +++ b/public/language/ko/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/ko/email.json b/public/language/ko/email.json index 07a668917b..79259b2a40 100644 --- a/public/language/ko/email.json +++ b/public/language/ko/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "이 게시물 알림은 사용자의 구독 설정에 따라 전송되었습니다.", "test.text1": "이 시험용 메일은 NodeBB에 설정된 메일 송신자가 정상적으로 메일을 송신할 수 있는지 시험할 목적으로 발송되었습니다.", "unsub.cta": "설정을 변경하려면 여기를 클릭하세요.", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "감사합니다!" } \ No newline at end of file diff --git a/public/language/ko/error.json b/public/language/ko/error.json index 72110085cc..f67dae56bb 100644 --- a/public/language/ko/error.json +++ b/public/language/ko/error.json @@ -30,6 +30,7 @@ "password-too-long": "패스워드가 너무 깁니다.", "user-banned": "차단된 사용자입니다.", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "죄송합니다, 첫 번째 게시물은 %1 초 후에 작성할 수 있습니다.", "blacklisted-ip": "죄송하지만, 당신의 IP는 이 커뮤니티로부터 차단되었습니다. 만약 에러라는 생각이 드신다면 관리자에게 연락해주세요.", "ban-expiry-missing": "Please provide an end date for this ban", @@ -104,7 +105,7 @@ "chat-disabled": "대화 기능을 사용하지 않습니다.", "too-many-messages": "짧은 시간동안 너무 많은 메시지를 전송하였습니다. 잠시 후에 다시 시도하세요.", "invalid-chat-message": "올바르지 않은 메시지입니다.", - "chat-message-too-long": "메시지가 너무 깁니다.", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "편집 할 수 있는 권한이 없습니다.", "cant-remove-last-user": "마지막 사용자를 삭제할 수 없습니다.", "cant-delete-chat-message": "메세지를 지울 권한이 없습니다.", diff --git a/public/language/ko/modules.json b/public/language/ko/modules.json index 10420c4f75..e85fbc9763 100644 --- a/public/language/ko/modules.json +++ b/public/language/ko/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3개월", "chat.delete_message_confirm": "이 대화를 삭제하시겠습니까?", "chat.add-users-to-room": "유저 추가하기", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "작성", "composer.show_preview": "미리보기", "composer.hide_preview": "미리보기 숨김", diff --git a/public/language/ko/user.json b/public/language/ko/user.json index db32ec7755..f54cadc53e 100644 --- a/public/language/ko/user.json +++ b/public/language/ko/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "새 사용자 이름이 이미 존재하여 %1로 저장되었습니다.", "password_same_as_username": "비밀번호가 사용자명과 동일합니다. 다른 비밀번호를 입력하세요.", "password_same_as_email": "비밀번호가 이메일 주소와 동일합니다. 다른 비밀번호를 입력하세요.", + "weak_password": "Weak password.", "upload_picture": "사진 업로드", "upload_a_picture": "사진 업로드", "remove_uploaded_picture": "등록된 사진을 삭제", diff --git a/public/language/lt/admin/advanced/database.json b/public/language/lt/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/lt/admin/advanced/database.json +++ b/public/language/lt/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/lt/admin/manage/groups.json b/public/language/lt/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/lt/admin/manage/groups.json +++ b/public/language/lt/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/lt/admin/settings/advanced.json b/public/language/lt/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/lt/admin/settings/advanced.json +++ b/public/language/lt/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/lt/admin/settings/post.json b/public/language/lt/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/lt/admin/settings/post.json +++ b/public/language/lt/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/lt/admin/settings/user.json b/public/language/lt/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/lt/admin/settings/user.json +++ b/public/language/lt/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/lt/email.json b/public/language/lt/email.json index d4f24c89e5..699f8cacd1 100644 --- a/public/language/lt/email.json +++ b/public/language/lt/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "Šios žinutės perspėjimas buvo išsiųstas į tavo prenumeratos nustatymus", "test.text1": "Ši žinutė yra bandomoji kad įsitikint, kad vartotojas teisingai nustatė nustatymus tavo NodeBB", "unsub.cta": "Spauskite čia norėdami pakeisti šiuos nustatymus", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Ačiū!" } \ No newline at end of file diff --git a/public/language/lt/error.json b/public/language/lt/error.json index fb9718f86e..aa16eedd88 100644 --- a/public/language/lt/error.json +++ b/public/language/lt/error.json @@ -30,6 +30,7 @@ "password-too-long": "Password too long", "user-banned": "Vartotojas užblokuotas", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Atsiprašome, jūs įpareigoti palaukti %1 sekunde(s) prieš rašant pirmą pranešimą", "blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.", "ban-expiry-missing": "Please provide an end date for this ban", @@ -104,7 +105,7 @@ "chat-disabled": "Chat system disabled", "too-many-messages": "Išsiuntėte per daug pranešimų, kurį laiką prašome palaukti.", "invalid-chat-message": "Invalid chat message", - "chat-message-too-long": "Chat message is too long", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "You are not allowed to edit this message", "cant-remove-last-user": "You can't remove the last user", "cant-delete-chat-message": "You are not allowed to delete this message", diff --git a/public/language/lt/modules.json b/public/language/lt/modules.json index dd0afcdac7..0e102bc984 100644 --- a/public/language/lt/modules.json +++ b/public/language/lt/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 mėnesiai", "chat.delete_message_confirm": "Are you sure you wish to delete this message?", "chat.add-users-to-room": "Add users to room", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Sukomponuoti", "composer.show_preview": "Rodyti pavyzdį", "composer.hide_preview": "Slėpti pavyzdį", diff --git a/public/language/lt/user.json b/public/language/lt/user.json index 3e146a76fe..b02ecfbf0e 100644 --- a/public/language/lt/user.json +++ b/public/language/lt/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Jūsų norimas vartotojo vardas jau užimtas, todėl mes jį šiek tiek pakeitėme. Dabar jūs esate žinomas kaip %1", "password_same_as_username": "Jūsų slaptažodis sutampa su Jūsų vartotojo vardu. Dėl saugumo, prašome naudoti kitą slaptažodį.", "password_same_as_email": "Jūsų slaptažodis sutampa su Jūsų el. pašto adresu. Dėl saugumo, prašome naudoti kitą slaptažodį.", + "weak_password": "Weak password.", "upload_picture": "Įkelti paveikslėlį", "upload_a_picture": "Įkelti paveikslėlį", "remove_uploaded_picture": "Ištrinti paveikslėlį", diff --git a/public/language/ms/admin/advanced/database.json b/public/language/ms/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/ms/admin/advanced/database.json +++ b/public/language/ms/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/ms/admin/manage/groups.json b/public/language/ms/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/ms/admin/manage/groups.json +++ b/public/language/ms/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/ms/admin/settings/advanced.json b/public/language/ms/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/ms/admin/settings/advanced.json +++ b/public/language/ms/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/ms/admin/settings/post.json b/public/language/ms/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/ms/admin/settings/post.json +++ b/public/language/ms/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/ms/admin/settings/user.json b/public/language/ms/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/ms/admin/settings/user.json +++ b/public/language/ms/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/ms/email.json b/public/language/ms/email.json index eb5affbabd..804155fd84 100644 --- a/public/language/ms/email.json +++ b/public/language/ms/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "Kiriman pemberitahuan ini dihantar berdasarkan tetapan langganan anda.", "test.text1": "Ini adalah percubaan email untuk mengesahkan emailer ditetap dengan betul di NodeBB.", "unsub.cta": "Klik sini untuk mengubah tetapan itu", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Terima Kasih!" } \ No newline at end of file diff --git a/public/language/ms/error.json b/public/language/ms/error.json index 25582f7d3e..34f838b4b4 100644 --- a/public/language/ms/error.json +++ b/public/language/ms/error.json @@ -30,6 +30,7 @@ "password-too-long": "Kata laluan terlalu panjang", "user-banned": "Pengguna diharamkan", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Maaf, anda dikehendaki menunggu %1 saat() sebelum membuat kiriman pertama anda", "blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.", "ban-expiry-missing": "Please provide an end date for this ban", @@ -104,7 +105,7 @@ "chat-disabled": "Sistem borak tidak diaktifkan", "too-many-messages": "Anda menghantar terlalu banyak pesanan, sila tunggu seketika.", "invalid-chat-message": "Mesej borak tidak sah", - "chat-message-too-long": "Mesej borak terlalu panjang", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "Anda tidak dibenarkan menyunting mesej ini", "cant-remove-last-user": "Anda tidak boleh membuang pengguna akhir", "cant-delete-chat-message": "Anda tidak dibenarkan memadamkan mesej ini", diff --git a/public/language/ms/modules.json b/public/language/ms/modules.json index 4e3f9bf58b..67c96b75a2 100644 --- a/public/language/ms/modules.json +++ b/public/language/ms/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 Bulan", "chat.delete_message_confirm": "Are you sure you wish to delete this message?", "chat.add-users-to-room": "Add users to room", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Tulis", "composer.show_preview": "Pra-lihat", "composer.hide_preview": "Sorok pra-lihat", diff --git a/public/language/ms/user.json b/public/language/ms/user.json index a07b860972..bc1074bc5e 100644 --- a/public/language/ms/user.json +++ b/public/language/ms/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Nama pengguna yang anda minta telah digunakan oleh orang lain, jadi kami telah mengubahsuaikannya sedikit. Anda kini dikenali sebagai %1", "password_same_as_username": "Kata laluan anda adalah sama seperti nama pengguna, sila pilih kata laluan yang lain", "password_same_as_email": "Your password is the same as your email, please select another password.", + "weak_password": "Weak password.", "upload_picture": "Muatnaik gambar", "upload_a_picture": "Muatnaik sekeping gambar", "remove_uploaded_picture": "Buang Gambar Yang Dimuatnaik", diff --git a/public/language/nb/admin/advanced/database.json b/public/language/nb/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/nb/admin/advanced/database.json +++ b/public/language/nb/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/nb/admin/manage/groups.json b/public/language/nb/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/nb/admin/manage/groups.json +++ b/public/language/nb/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/nb/admin/settings/advanced.json b/public/language/nb/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/nb/admin/settings/advanced.json +++ b/public/language/nb/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/nb/admin/settings/post.json b/public/language/nb/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/nb/admin/settings/post.json +++ b/public/language/nb/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/nb/admin/settings/user.json b/public/language/nb/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/nb/admin/settings/user.json +++ b/public/language/nb/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/nb/email.json b/public/language/nb/email.json index 6377d85fbf..916aa5880b 100644 --- a/public/language/nb/email.json +++ b/public/language/nb/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "Dette innleggsvarselet ble sendt til deg basert på dine innstillinger for abonnering.", "test.text1": "Dette er en test e-post for å verifisere at e-postsystemet i NodeBB fungerer som det skal.", "unsub.cta": "Klikk her for å endre disse innstillingene", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Takk!" } \ No newline at end of file diff --git a/public/language/nb/error.json b/public/language/nb/error.json index 5e25fa5557..8c63511a83 100644 --- a/public/language/nb/error.json +++ b/public/language/nb/error.json @@ -30,6 +30,7 @@ "password-too-long": "Password too long", "user-banned": "Bruker utestengt", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Beklager, du må vente %1 sekund(er) før du oppretter ditt første innlegg", "blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.", "ban-expiry-missing": "Please provide an end date for this ban", @@ -104,7 +105,7 @@ "chat-disabled": "Chat system disabled", "too-many-messages": "Du har sendt for mange meldinger, vennligst vent en stund.", "invalid-chat-message": "Ugyldig samtalemelding", - "chat-message-too-long": "Samtalemeldingen er for lang", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "You are not allowed to edit this message", "cant-remove-last-user": "You can't remove the last user", "cant-delete-chat-message": "You are not allowed to delete this message", diff --git a/public/language/nb/modules.json b/public/language/nb/modules.json index 1f6bcd2c44..72c205d3b1 100644 --- a/public/language/nb/modules.json +++ b/public/language/nb/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 måneder", "chat.delete_message_confirm": "Are you sure you wish to delete this message?", "chat.add-users-to-room": "Add users to room", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Komponer", "composer.show_preview": "Vis forhåndsvisning", "composer.hide_preview": "Skjul forhåndsvisning", diff --git a/public/language/nb/user.json b/public/language/nb/user.json index af209863a7..5a71bd8a5e 100644 --- a/public/language/nb/user.json +++ b/public/language/nb/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Brukernavnet du ønsket er opptatt, så vi har endret ditt litt. Du er nå kjent som %1", "password_same_as_username": "Ditt passord er det samme som ditt brukernavn, vennligst velg et annet passord.", "password_same_as_email": "Your password is the same as your email, please select another password.", + "weak_password": "Weak password.", "upload_picture": "Last opp bilde", "upload_a_picture": "Last opp et bilde", "remove_uploaded_picture": "Fjern Opplastet Bilde", diff --git a/public/language/nl/admin/advanced/database.json b/public/language/nl/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/nl/admin/advanced/database.json +++ b/public/language/nl/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/nl/admin/manage/groups.json b/public/language/nl/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/nl/admin/manage/groups.json +++ b/public/language/nl/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/nl/admin/settings/advanced.json b/public/language/nl/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/nl/admin/settings/advanced.json +++ b/public/language/nl/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/nl/admin/settings/post.json b/public/language/nl/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/nl/admin/settings/post.json +++ b/public/language/nl/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/nl/admin/settings/user.json b/public/language/nl/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/nl/admin/settings/user.json +++ b/public/language/nl/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/nl/email.json b/public/language/nl/email.json index c010b77769..d94f164e9e 100644 --- a/public/language/nl/email.json +++ b/public/language/nl/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "Deze notificatie is door ons verzonden vanwege gebruikersinstellingen voor abonnementen en berichten.", "test.text1": "Dit is een testbericht om te verifiëren dat NodeBB de e-mailberichtservice correct heeft opgezet.", "unsub.cta": "Klik hier om deze instellingen te wijzigen", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Bedankt!" } \ No newline at end of file diff --git a/public/language/nl/error.json b/public/language/nl/error.json index 850ebe7db9..588d2d3a1b 100644 --- a/public/language/nl/error.json +++ b/public/language/nl/error.json @@ -30,6 +30,7 @@ "password-too-long": "Wachtwoord is te lang", "user-banned": "Gebruiker verbannen", "user-banned-reason": "Sorry, dit account is verbannen (Reden: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Helaas, het is een vereiste om %1 seconde(n) te wachten voordat het eerste bericht geplaatst kan worden.", "blacklisted-ip": "Sorry, uw IP-adres is verbannen uit deze community. Als u meent dat dit onterecht is, neem dan contact op met een beheerder.", "ban-expiry-missing": "Geef een einddatum op voor deze ban.", @@ -104,7 +105,7 @@ "chat-disabled": "Chat systeem uitgeschakeld", "too-many-messages": "Je hebt in korte tijd veel berichten verstuurd, als je even wacht mag je weer berichten sturen.", "invalid-chat-message": "Ongeldig bericht", - "chat-message-too-long": "Het chatbericht is te lang", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "Het is niet toegestaan om dit bericht aan te passen", "cant-remove-last-user": "Je kan de laatste gebruiker niet verwijderen", "cant-delete-chat-message": "Het is niet toegestaan om dit bericht te verwijderen", diff --git a/public/language/nl/modules.json b/public/language/nl/modules.json index 59d0396464..aac1dd231b 100644 --- a/public/language/nl/modules.json +++ b/public/language/nl/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 maanden", "chat.delete_message_confirm": "Weet je zeker dat je dit bericht wilt verwijderen?", "chat.add-users-to-room": "Voeg gebruikers toe aan deze chat room", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Samenstellen", "composer.show_preview": "Voorbeeldweergave", "composer.hide_preview": "Verberg voorbeeld", diff --git a/public/language/nl/user.json b/public/language/nl/user.json index 9c753904f6..12b92dc7dd 100644 --- a/public/language/nl/user.json +++ b/public/language/nl/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Helaas, de gewenste gebruikersnaam is al door iemand in gebruik genomen dus vandaar een kleine aanpassing naar %1 doorgevoerd", "password_same_as_username": "Je wachtwoord is hetzelfde als je gebruikersnaam. Kies een ander wachtwoord.", "password_same_as_email": "Je wachtwoord is hetzelfde als je email, kies alsjeblieft een ander wachtwoord.", + "weak_password": "Weak password.", "upload_picture": "Upload afbeelding", "upload_a_picture": "Upload een afbeelding", "remove_uploaded_picture": "Verwijder gëuploade foto", diff --git a/public/language/pl/admin/advanced/database.json b/public/language/pl/admin/advanced/database.json index c4e29ca7c1..dd365e5b0b 100644 --- a/public/language/pl/admin/advanced/database.json +++ b/public/language/pl/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime w sekundach", "uptime-days": "Uptime w dniach", diff --git a/public/language/pl/admin/manage/groups.json b/public/language/pl/admin/manage/groups.json index 3de74fad4a..b5438179bd 100644 --- a/public/language/pl/admin/manage/groups.json +++ b/public/language/pl/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Nazwa Grupy", "description": "Opis Grupy", + "member-count": "Member Count", "system": "System Grup", "edit": "Edytuj", "search-placeholder": "Szukaj", diff --git a/public/language/pl/admin/settings/advanced.json b/public/language/pl/admin/settings/advanced.json index d931155ad3..6269f725a1 100644 --- a/public/language/pl/admin/settings/advanced.json +++ b/public/language/pl/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/pl/admin/settings/post.json b/public/language/pl/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/pl/admin/settings/post.json +++ b/public/language/pl/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/pl/admin/settings/user.json b/public/language/pl/admin/settings/user.json index 94df909293..ca27eca846 100644 --- a/public/language/pl/admin/settings/user.json +++ b/public/language/pl/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/pl/email.json b/public/language/pl/email.json index cf511906ed..65434dd2d4 100644 --- a/public/language/pl/email.json +++ b/public/language/pl/email.json @@ -32,5 +32,9 @@ "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", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Dziękujemy!" } \ No newline at end of file diff --git a/public/language/pl/error.json b/public/language/pl/error.json index 5da5652888..9e1bcf4ae4 100644 --- a/public/language/pl/error.json +++ b/public/language/pl/error.json @@ -30,6 +30,7 @@ "password-too-long": "Hasło jest za długie", "user-banned": "Użytkownik zbanowany", "user-banned-reason": "Twoje konto zostało zablokowane (Powód: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Przepraszamy, musisz odczekać %1 sekund(y) przed utworzeniem pierwszego posta", "blacklisted-ip": "Twój adres IP został zablokowany na tej społeczności. Jeśli uważasz to za błąd, zgłoś to administratorowi", "ban-expiry-missing": "Wprowadź datę końca blokady", @@ -104,7 +105,7 @@ "chat-disabled": "System rozmów jest wyłączony", "too-many-messages": "Wysłałeś zbyt wiele wiadomości, prosimy chwilę poczekać.", "invalid-chat-message": "Nieprawidłowa wiadomość", - "chat-message-too-long": "Wiadomość jest zbyt długa", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "Nie jesteś upoważniony do edycji tej wiadomości", "cant-remove-last-user": "You can't remove the last user", "cant-delete-chat-message": "Nie jesteś upoważniony do usunięcia tej wiadomości", diff --git a/public/language/pl/modules.json b/public/language/pl/modules.json index ac5af73422..1afc9a68da 100644 --- a/public/language/pl/modules.json +++ b/public/language/pl/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 miesiące", "chat.delete_message_confirm": "Jesteś pewny, że chcesz usunąć tą wiadomość?", "chat.add-users-to-room": "Dodaj użytkownika do pokoju czatu", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Twórz", "composer.show_preview": "Pokaż Podgląd", "composer.hide_preview": "Ukryj Podgląd", diff --git a/public/language/pl/user.json b/public/language/pl/user.json index 75547f5c27..795470daa5 100644 --- a/public/language/pl/user.json +++ b/public/language/pl/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Wybrany login jest już zajęty, więc zmieniliśmy go trochę. Proponujemy %1", "password_same_as_username": "Twoje hasło jest takie samo jak nazwa użytkownika, prosimy wybrać inne hasło.", "password_same_as_email": "Twoje hasło jest takie samo jak adres e-mail, prosimy wybrać inne hasło.", + "weak_password": "Weak password.", "upload_picture": "Prześlij zdjęcie", "upload_a_picture": "Prześlij zdjęcie", "remove_uploaded_picture": "Usuń Przesłane Zdjęcie", diff --git a/public/language/pt-BR/admin/advanced/database.json b/public/language/pt-BR/admin/advanced/database.json index e887b1069a..a7299086b2 100644 --- a/public/language/pt-BR/admin/advanced/database.json +++ b/public/language/pt-BR/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Tempo Rodando em Segundos", "uptime-days": "Tempo Rodando em Dias", diff --git a/public/language/pt-BR/admin/manage/groups.json b/public/language/pt-BR/admin/manage/groups.json index c1d4a34f88..6ec69b4ac5 100644 --- a/public/language/pt-BR/admin/manage/groups.json +++ b/public/language/pt-BR/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Nome do Grupo", "description": "Descrição do Grupo", + "member-count": "Member Count", "system": "Grupo do Sistema", "edit": "Editar", "search-placeholder": "Procurar", diff --git a/public/language/pt-BR/admin/settings/advanced.json b/public/language/pt-BR/admin/settings/advanced.json index 8d37677031..87f5545286 100644 --- a/public/language/pt-BR/admin/settings/advanced.json +++ b/public/language/pt-BR/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Defina ALLOW-FROM para Colocar o NodeBB em um iFrame", "headers.powered-by": "Personalizar o cabeçalho de \"Powered By\" enviado pelo NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "Para impedir acesso à todos os sites, deixe em branco ou define como null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Administração de Tráfego", diff --git a/public/language/pt-BR/admin/settings/post.json b/public/language/pt-BR/admin/settings/post.json index 81a18b002d..902000e386 100644 --- a/public/language/pt-BR/admin/settings/post.json +++ b/public/language/pt-BR/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Configurações de Não-Lidos", "unread.cutoff": "Data de corte de não-lidos", "unread.min-track-last": "Mínimo de posts no tópico antes de rastrear o último lido", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Configurações de Assinatura", "signature.disable": "Desabilitar assinaturas", "signature.no-links": "Desabilitar links em assinaturas", diff --git a/public/language/pt-BR/admin/settings/user.json b/public/language/pt-BR/admin/settings/user.json index afeb270c5b..9ccdd6baa5 100644 --- a/public/language/pt-BR/admin/settings/user.json +++ b/public/language/pt-BR/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Tamanho Mínimo do Nome de Usuário", "max-username-length": "Tamanho Máximo do Nome de Usuário", "min-password-length": "Tamanho Mínimo da Senha", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Tamanho Máximo do Sobre Mim", "terms-of-use": "Termos de Uso do Fórum (Deixar em branco para desabilitar)", "user-search": "Pesquisa de Usuário", diff --git a/public/language/pt-BR/email.json b/public/language/pt-BR/email.json index 64ef3928a9..766152ee05 100644 --- a/public/language/pt-BR/email.json +++ b/public/language/pt-BR/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "Esta notificação de postagem foi enviada para você devido as suas configurações de assinatura.", "test.text1": "Este é um e-mail de teste, para verificar que o enviador de emails está corretamente configurado no seu NodeBB.", "unsub.cta": "Clique aqui para alterar estas configurações", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Obrigado!" } \ No newline at end of file diff --git a/public/language/pt-BR/error.json b/public/language/pt-BR/error.json index 14e3448ae3..ba2053e547 100644 --- a/public/language/pt-BR/error.json +++ b/public/language/pt-BR/error.json @@ -30,6 +30,7 @@ "password-too-long": "A senha é muito grande", "user-banned": "Usuário banido", "user-banned-reason": "Desculpa, esta conta foi banida (Motivo: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Desculpe, é necessário que você aguarde %1 segundo(s) antes de fazer o seu primeiro post.", "blacklisted-ip": "Desculpe, o seu endereço IP foi banido desta comunidade. Se você acha que isso é um engano, por favor contate um administrador.", "ban-expiry-missing": "Por favor forneça uma data para o fim deste banimento", @@ -104,7 +105,7 @@ "chat-disabled": "O sistema de chat foi desabilitado", "too-many-messages": "Você enviou muitas mensagens, por favor aguarde um momento.", "invalid-chat-message": "Mensagem de chat inválida", - "chat-message-too-long": "A mensagem de chat é muito longa", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "Você não tem permissão para editar esta mensagem", "cant-remove-last-user": "Você não pode excluir o último usuário", "cant-delete-chat-message": "Você não possui permissão para deletar esta mensagem", diff --git a/public/language/pt-BR/modules.json b/public/language/pt-BR/modules.json index ff7568f89a..0a2425ce03 100644 --- a/public/language/pt-BR/modules.json +++ b/public/language/pt-BR/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 Meses", "chat.delete_message_confirm": "Tem certeza que deseja excluir esta mensagem?", "chat.add-users-to-room": "Adicionar usuários à sala", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Compor", "composer.show_preview": "Exibir Pré-visualização", "composer.hide_preview": "Esconder Pré-visualização", diff --git a/public/language/pt-BR/user.json b/public/language/pt-BR/user.json index 28a5741924..e242fc41b4 100644 --- a/public/language/pt-BR/user.json +++ b/public/language/pt-BR/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "O nome de usuário que você escolheu já existia, então nós o alteramos um pouquinho. Agora você é conhecido como %1", "password_same_as_username": "A sua senha é igual ao seu nome de usuário, por favor escolha outra senha.", "password_same_as_email": "Tua senha é a mesma que o teu email, por favor escolha outra senha.", + "weak_password": "Weak password.", "upload_picture": "Carregar Foto", "upload_a_picture": "Carregue uma Foto", "remove_uploaded_picture": "Remover Foto Enviada", diff --git a/public/language/pt-PT/admin/advanced/database.json b/public/language/pt-PT/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/pt-PT/admin/advanced/database.json +++ b/public/language/pt-PT/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/pt-PT/admin/manage/groups.json b/public/language/pt-PT/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/pt-PT/admin/manage/groups.json +++ b/public/language/pt-PT/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/pt-PT/admin/settings/advanced.json b/public/language/pt-PT/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/pt-PT/admin/settings/advanced.json +++ b/public/language/pt-PT/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/pt-PT/admin/settings/post.json b/public/language/pt-PT/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/pt-PT/admin/settings/post.json +++ b/public/language/pt-PT/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/pt-PT/admin/settings/user.json b/public/language/pt-PT/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/pt-PT/admin/settings/user.json +++ b/public/language/pt-PT/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/pt-PT/email.json b/public/language/pt-PT/email.json index 8f347ea8b4..deddc31cb2 100644 --- a/public/language/pt-PT/email.json +++ b/public/language/pt-PT/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "Esta notificação foi envidada devido às tuas definições de subscrição.", "test.text1": "Este é um e-mail de teste para verificar que o emailer está configurado corretamente para o teu NodeBB.", "unsub.cta": "Clica aqui para alterares essas definições", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Obrigado!" } \ No newline at end of file diff --git a/public/language/pt-PT/error.json b/public/language/pt-PT/error.json index d8ee847d2f..d87e005288 100644 --- a/public/language/pt-PT/error.json +++ b/public/language/pt-PT/error.json @@ -30,6 +30,7 @@ "password-too-long": "Palavra-passe muito longa", "user-banned": "Utilizador banido", "user-banned-reason": "Desculpa, esta conta foi banida (Razão: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Desculpa, é necessário que esperes %1 segundo(s) antes de fazeres a tua primeira publicação", "blacklisted-ip": "Desculpa, o teu endereço IP foi banido desta comunidade. Se sentes que isto é um erro, por favor contacta o administrador.", "ban-expiry-missing": "Por favor providencia uma data para o fim deste banimento", @@ -104,7 +105,7 @@ "chat-disabled": "Sistema de conversas desativado", "too-many-messages": "Enviaste demasiadas mensagens, por favor espera um pouco.", "invalid-chat-message": "Mensagem de chat inválida", - "chat-message-too-long": "Mensagem de chat demasiado longa", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "Não tens permissão para editar esta mensagem", "cant-remove-last-user": "Não podes remover o último utilizador", "cant-delete-chat-message": "Não tens permissão para eliminar esta mensagem", diff --git a/public/language/pt-PT/modules.json b/public/language/pt-PT/modules.json index 6164606984..5c7ffcaca4 100644 --- a/public/language/pt-PT/modules.json +++ b/public/language/pt-PT/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 meses", "chat.delete_message_confirm": "Tens a certeza que desejas apagar esta mensagem?", "chat.add-users-to-room": "Adicionar utilizadores à sala", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Compor", "composer.show_preview": "Mostrar pré-visualização", "composer.hide_preview": "Ocultar pré-visualização", diff --git a/public/language/pt-PT/user.json b/public/language/pt-PT/user.json index 0f686ef666..6e5b6981b3 100644 --- a/public/language/pt-PT/user.json +++ b/public/language/pt-PT/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "O nome de utilizador que escolheste já está em utilização por isso alteramo-lo ligeiramente. És agora conhecido como %1", "password_same_as_username": "A tua palavra-passe é igual ao teu nome de utilizador. Por favor, escolhe outra palavra-passe.", "password_same_as_email": "A tua palavra-passe é a mesma que o teu e-mail. Por favor, escolhe outra palavra-passe.", + "weak_password": "Weak password.", "upload_picture": "Carregar imagem", "upload_a_picture": "Carregar uma imagem", "remove_uploaded_picture": "Remover imagem carregada", diff --git a/public/language/ro/admin/advanced/database.json b/public/language/ro/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/ro/admin/advanced/database.json +++ b/public/language/ro/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/ro/admin/manage/groups.json b/public/language/ro/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/ro/admin/manage/groups.json +++ b/public/language/ro/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/ro/admin/settings/advanced.json b/public/language/ro/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/ro/admin/settings/advanced.json +++ b/public/language/ro/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/ro/admin/settings/post.json b/public/language/ro/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/ro/admin/settings/post.json +++ b/public/language/ro/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/ro/admin/settings/user.json b/public/language/ro/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/ro/admin/settings/user.json +++ b/public/language/ro/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/ro/email.json b/public/language/ro/email.json index e07033fd97..1fa8e4f2a8 100644 --- a/public/language/ro/email.json +++ b/public/language/ro/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "This post notification was sent to you due to your subscription settings.", "test.text1": "Acesta este un email de test pentru a verica dacă mailul este setat corect pentru NodeBB-ul tău.", "unsub.cta": "Apasă aici pentru a modifica acele setări", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Mulțumesc!" } \ No newline at end of file diff --git a/public/language/ro/error.json b/public/language/ro/error.json index 71c3b5cc7e..82112f70c0 100644 --- a/public/language/ro/error.json +++ b/public/language/ro/error.json @@ -30,6 +30,7 @@ "password-too-long": "Parola prea lunga.", "user-banned": "Utilizator banat", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Imi pare rau dar trebuie sa astepti %1 secunda(e) pentru a posta prima oara.", "blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.", "ban-expiry-missing": "Please provide an end date for this ban", @@ -104,7 +105,7 @@ "chat-disabled": "Chat system disabled", "too-many-messages": "You have sent too many messages, please wait awhile.", "invalid-chat-message": "Invalid chat message", - "chat-message-too-long": "Chat message is too long", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "You are not allowed to edit this message", "cant-remove-last-user": "You can't remove the last user", "cant-delete-chat-message": "You are not allowed to delete this message", diff --git a/public/language/ro/modules.json b/public/language/ro/modules.json index 47ab86acd9..c1a2cb2b4f 100644 --- a/public/language/ro/modules.json +++ b/public/language/ro/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 Luni", "chat.delete_message_confirm": "Are you sure you wish to delete this message?", "chat.add-users-to-room": "Add users to room", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Scrie", "composer.show_preview": "Show Preview", "composer.hide_preview": "Hide Preview", diff --git a/public/language/ro/user.json b/public/language/ro/user.json index ccb1d8ebf7..205c7bfdbf 100644 --- a/public/language/ro/user.json +++ b/public/language/ro/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Numele de utilizator pe care l-ai cerut este deja luat, așa că l-am modificat puțin. Acum ești cunoscut ca %1", "password_same_as_username": "Your password is the same as your username, please select another password.", "password_same_as_email": "Your password is the same as your email, please select another password.", + "weak_password": "Weak password.", "upload_picture": "Uploadează poză", "upload_a_picture": "Uploadează o poză", "remove_uploaded_picture": "Remove Uploaded Picture", diff --git a/public/language/ru/admin/advanced/database.json b/public/language/ru/admin/advanced/database.json index bb106b1eb4..50e8aae0aa 100644 --- a/public/language/ru/admin/advanced/database.json +++ b/public/language/ru/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 байт", "x-mb": "%1 мегабайт", + "x-gb": "%1 gb", "uptime-seconds": "Время работы в секундах", "uptime-days": "Время работы в днях", diff --git a/public/language/ru/admin/manage/groups.json b/public/language/ru/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/ru/admin/manage/groups.json +++ b/public/language/ru/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/ru/admin/settings/advanced.json b/public/language/ru/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/ru/admin/settings/advanced.json +++ b/public/language/ru/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/ru/admin/settings/post.json b/public/language/ru/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/ru/admin/settings/post.json +++ b/public/language/ru/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/ru/admin/settings/user.json b/public/language/ru/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/ru/admin/settings/user.json +++ b/public/language/ru/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/ru/email.json b/public/language/ru/email.json index 09daba1521..d791e5a61f 100644 --- a/public/language/ru/email.json +++ b/public/language/ru/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "Вы получили это уведомление согласно вашим настройкам подписки.", "test.text1": "Это тестовое сообщение для проверки почтового сервиса.", "unsub.cta": "Изменить настройки", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Спасибо!" } \ No newline at end of file diff --git a/public/language/ru/error.json b/public/language/ru/error.json index 7b2d73ccc4..c1aecb322e 100644 --- a/public/language/ru/error.json +++ b/public/language/ru/error.json @@ -30,6 +30,7 @@ "password-too-long": "Пароль слишком длинный", "user-banned": "Участник заблокирован", "user-banned-reason": "Учетная запись заблокирована (Причина: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Вы можете написать своё первое сообщение через %1 сек.", "blacklisted-ip": "Извините, ваш IP адрес был забанен этим сообществом. Если вы считаете, что это ошибка, пожалуйста, свяжитесь с администратором.", "ban-expiry-missing": "Пожалуйста, укажите дату окончания этой блокировки", @@ -104,7 +105,7 @@ "chat-disabled": "Чат выключен", "too-many-messages": "Для отправки нового сообщения необходимо подождать, т.к. вы отправили слишком много сообщений подряд.", "invalid-chat-message": "Ошибка в сообщении", - "chat-message-too-long": "Слишком длинное сообщение. Пожалуйста, сократите своё сообщение, чтобы можно было его отправить.", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "К сожалению, у вас нет доступа для редактирования этого сообщения", "cant-remove-last-user": "Удалить последнего участника невозможно.", "cant-delete-chat-message": "К сожалению, у вас нет доступа для удаления этого сообщения", diff --git a/public/language/ru/modules.json b/public/language/ru/modules.json index 2ebe6eb869..442137ca92 100644 --- a/public/language/ru/modules.json +++ b/public/language/ru/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 месяца", "chat.delete_message_confirm": "Вы уверены, что хотите удалить это сообщение?", "chat.add-users-to-room": "Добавить участников в комнату", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Редактор сообщений", "composer.show_preview": "Показать предпросмотр сообщения", "composer.hide_preview": "Скрыть предпросмотр", diff --git a/public/language/ru/user.json b/public/language/ru/user.json index f256c020aa..33bd08a5b1 100644 --- a/public/language/ru/user.json +++ b/public/language/ru/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Логин, который вы запросили, уже занят. Мы его немного изменили. Теперь ваш логин %1", "password_same_as_username": "Ваш пароль совпадает с именем пользователя, это очень небезопасно. Пожалуйста укажите другой пароль.", "password_same_as_email": "Ваш пароль совпадает с элетронной почтой, это очень небезопасно. Пожалуйста, укажите другой пароль.", + "weak_password": "Weak password.", "upload_picture": "Загрузить фото", "upload_a_picture": "Загрузить фото", "remove_uploaded_picture": "Удалить фото", diff --git a/public/language/rw/admin/advanced/database.json b/public/language/rw/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/rw/admin/advanced/database.json +++ b/public/language/rw/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/rw/admin/manage/groups.json b/public/language/rw/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/rw/admin/manage/groups.json +++ b/public/language/rw/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/rw/admin/settings/advanced.json b/public/language/rw/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/rw/admin/settings/advanced.json +++ b/public/language/rw/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/rw/admin/settings/post.json b/public/language/rw/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/rw/admin/settings/post.json +++ b/public/language/rw/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/rw/admin/settings/user.json b/public/language/rw/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/rw/admin/settings/user.json +++ b/public/language/rw/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/rw/email.json b/public/language/rw/email.json index d26954b90b..aca6b1b8e1 100644 --- a/public/language/rw/email.json +++ b/public/language/rw/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "Iri tangazo rijyanye n'ibyashyizwe ku rubuga waryohererejwe kubera ko wabihisemo mu byo uzajya umenyeshwa", "test.text1": "Iyi message ni igerageza kugirango harebwe niba emailer ya NodeBB yarateguwe neza", "unsub.cta": "Kanda hano kugirango uhindure uko bizajya bigenda", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Murakoze!" } \ No newline at end of file diff --git a/public/language/rw/error.json b/public/language/rw/error.json index f367b35002..3e6c5cdeff 100644 --- a/public/language/rw/error.json +++ b/public/language/rw/error.json @@ -30,6 +30,7 @@ "password-too-long": "Password too long", "user-banned": "Umuntu wirukanwe", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Wihangena kuko usabwa gutegereza amasegonda (isegonda) %1 mbere yo gushyiraho ikintu cyawe cya mbere", "blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.", "ban-expiry-missing": "Please provide an end date for this ban", @@ -104,7 +105,7 @@ "chat-disabled": "Chat system disabled", "too-many-messages": "Wohereje ubutumwa bwinshi cyane. Ba utegerejeho gato. ", "invalid-chat-message": "Invalid chat message", - "chat-message-too-long": "Chat message is too long", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "You are not allowed to edit this message", "cant-remove-last-user": "You can't remove the last user", "cant-delete-chat-message": "You are not allowed to delete this message", diff --git a/public/language/rw/modules.json b/public/language/rw/modules.json index 09d8c88fdd..8a808163d5 100644 --- a/public/language/rw/modules.json +++ b/public/language/rw/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "Amezi 3", "chat.delete_message_confirm": "Wiringiye neza ko ushaka gusiba ubu butumwa?", "chat.add-users-to-room": "Ongera abantu mu gikari", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Andika", "composer.show_preview": "Bona Uko Biza Gusa", "composer.hide_preview": "Hisha Uko Biza Gusa", diff --git a/public/language/rw/user.json b/public/language/rw/user.json index fc35772e1f..96bc2e84f9 100644 --- a/public/language/rw/user.json +++ b/public/language/rw/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Izina ushaka kujya ukoresha twasanze ryarafashwe. Ntugire impungenge kuko twakuboneye iryo byenda kumera kimwe. Uzaba uzwi ku izina rya %1", "password_same_as_username": "Ijambobanga ryawe rirasa neza n'izina ukoresha; hitamo irindi jambobanga.", "password_same_as_email": "Ijambobanga ryawe rirasa neza na email yawe; hitamo irindi jambobanga.", + "weak_password": "Weak password.", "upload_picture": "Gushyiraho ifoto", "upload_a_picture": "Shyiraho ifoto", "remove_uploaded_picture": "Kuraho Ifoto", diff --git a/public/language/sc/admin/advanced/database.json b/public/language/sc/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/sc/admin/advanced/database.json +++ b/public/language/sc/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/sc/admin/manage/groups.json b/public/language/sc/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/sc/admin/manage/groups.json +++ b/public/language/sc/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/sc/admin/settings/advanced.json b/public/language/sc/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/sc/admin/settings/advanced.json +++ b/public/language/sc/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/sc/admin/settings/post.json b/public/language/sc/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/sc/admin/settings/post.json +++ b/public/language/sc/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/sc/admin/settings/user.json b/public/language/sc/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/sc/admin/settings/user.json +++ b/public/language/sc/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/sc/email.json b/public/language/sc/email.json index 691e6309a2..c1e17018fa 100644 --- a/public/language/sc/email.json +++ b/public/language/sc/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "This post notification was sent to you due to your subscription settings.", "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", "unsub.cta": "Click here to alter those settings", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Thanks!" } \ No newline at end of file diff --git a/public/language/sc/error.json b/public/language/sc/error.json index 3149dadc15..35eaf8cbc6 100644 --- a/public/language/sc/error.json +++ b/public/language/sc/error.json @@ -30,6 +30,7 @@ "password-too-long": "Password too long", "user-banned": "User banned", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.", "ban-expiry-missing": "Please provide an end date for this ban", @@ -104,7 +105,7 @@ "chat-disabled": "Chat system disabled", "too-many-messages": "You have sent too many messages, please wait awhile.", "invalid-chat-message": "Invalid chat message", - "chat-message-too-long": "Chat message is too long", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "You are not allowed to edit this message", "cant-remove-last-user": "You can't remove the last user", "cant-delete-chat-message": "You are not allowed to delete this message", diff --git a/public/language/sc/modules.json b/public/language/sc/modules.json index 11a87b51d8..e360cae07e 100644 --- a/public/language/sc/modules.json +++ b/public/language/sc/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 Months", "chat.delete_message_confirm": "Are you sure you wish to delete this message?", "chat.add-users-to-room": "Add users to room", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Compose", "composer.show_preview": "Show Preview", "composer.hide_preview": "Hide Preview", diff --git a/public/language/sc/user.json b/public/language/sc/user.json index d88af312d8..b72ed9f9c2 100644 --- a/public/language/sc/user.json +++ b/public/language/sc/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "The username you requested was already taken, so we have altered it slightly. You are now known as %1", "password_same_as_username": "Your password is the same as your username, please select another password.", "password_same_as_email": "Your password is the same as your email, please select another password.", + "weak_password": "Weak password.", "upload_picture": "Càrriga immàgine", "upload_a_picture": "Càrriga un'immàgine", "remove_uploaded_picture": "Remove Uploaded Picture", diff --git a/public/language/sk/admin/advanced/database.json b/public/language/sk/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/sk/admin/advanced/database.json +++ b/public/language/sk/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/sk/admin/manage/groups.json b/public/language/sk/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/sk/admin/manage/groups.json +++ b/public/language/sk/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/sk/admin/settings/advanced.json b/public/language/sk/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/sk/admin/settings/advanced.json +++ b/public/language/sk/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/sk/admin/settings/post.json b/public/language/sk/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/sk/admin/settings/post.json +++ b/public/language/sk/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/sk/admin/settings/user.json b/public/language/sk/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/sk/admin/settings/user.json +++ b/public/language/sk/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/sk/email.json b/public/language/sk/email.json index 27676d7a37..11e7372009 100644 --- a/public/language/sk/email.json +++ b/public/language/sk/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "Toto oznámenie o príspevkoch ste prijali na základe Vašich nastavení účtu.", "test.text1": "Toto je skúšobný e-mail na overenie funkčnosti e-mailovej aplikácie Vášho NodeBB fóra.", "unsub.cta": "Kliknite sem pre zmenu týchto nastavení", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Ďakujeme!" } \ No newline at end of file diff --git a/public/language/sk/error.json b/public/language/sk/error.json index 93505ca3a5..76f54ce089 100644 --- a/public/language/sk/error.json +++ b/public/language/sk/error.json @@ -30,6 +30,7 @@ "password-too-long": "Heslo je príliš dlhé", "user-banned": "Užívateľ je zablokovaný", "user-banned-reason": "Prepáčte, tento účet bol zablokovaný (Dôvod: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Prepáčte, musíte počkať %1 sekúnd(y) predtým, ako vytvoríte svoj prvý príspevok", "blacklisted-ip": "Prepáčte, ale vaša IP adresa bola na tejto komunite zablokovaná. Ak sa cítite poškodený, prosím kontaktujte správcu.", "ban-expiry-missing": "Prosím uveďte dátum ukončenia tohto zablokovania", @@ -104,7 +105,7 @@ "chat-disabled": "Systém konverzácií je zablokovaný", "too-many-messages": "Odoslali ste príliš veľa správ, počkajte chvíľu prosím.", "invalid-chat-message": "Neplatná správa konverzácie", - "chat-message-too-long": "Správa v konverzácií je príliš dlhá", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "Nemáte oprávnenie k úprave tejto správy", "cant-remove-last-user": "Nemôžete odstrániť posledného užívateľa", "cant-delete-chat-message": "Nemáte oprávanie k odstráneniu tejto správy", diff --git a/public/language/sk/modules.json b/public/language/sk/modules.json index 191d9f2abb..880a4a0128 100644 --- a/public/language/sk/modules.json +++ b/public/language/sk/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 mesiace", "chat.delete_message_confirm": "Ste si istý, že chcete odstrániť túto správu?", "chat.add-users-to-room": "Pridať užívateľa do miestnosti", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Zostaviť", "composer.show_preview": "Zobraziť náhľad", "composer.hide_preview": "Skryť náhľad", diff --git a/public/language/sk/user.json b/public/language/sk/user.json index 98e87e05f3..c107383e30 100644 --- a/public/language/sk/user.json +++ b/public/language/sk/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Vaše požadované prihlasovacie meno je už obsadené, tak sme si ho dovolili mierne upraviť. Budeme Vás evidovať ako %1", "password_same_as_username": "Vaše heslo sa zhoduje s Vaším používateľským menom, prosím zvoľte iné heslo.", "password_same_as_email": "Vaše heslo sa zhoduje s Vaším e-mailom, prosím zvoľte iné heslo.", + "weak_password": "Weak password.", "upload_picture": "Nahrať obrázok", "upload_a_picture": "Nahrať obrázok", "remove_uploaded_picture": "Vymazať nahraný obrázok", diff --git a/public/language/sl/admin/advanced/database.json b/public/language/sl/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/sl/admin/advanced/database.json +++ b/public/language/sl/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/sl/admin/manage/groups.json b/public/language/sl/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/sl/admin/manage/groups.json +++ b/public/language/sl/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/sl/admin/settings/advanced.json b/public/language/sl/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/sl/admin/settings/advanced.json +++ b/public/language/sl/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/sl/admin/settings/post.json b/public/language/sl/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/sl/admin/settings/post.json +++ b/public/language/sl/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/sl/admin/settings/user.json b/public/language/sl/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/sl/admin/settings/user.json +++ b/public/language/sl/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/sl/email.json b/public/language/sl/email.json index 768c9c03c2..7934ff9bfb 100644 --- a/public/language/sl/email.json +++ b/public/language/sl/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "Obvestilo o objavi vam je bilo poslano zaradi nastavitev vaše naročnine.", "test.text1": "To je testno elektronsko sporočilo za preverjanje pravilnosti nastavitev podsistema za pošiljanje NodeBB poštnih sporočil.", "unsub.cta": "Kliknite tu za spremembo nastavitev.", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Hvala!" } \ No newline at end of file diff --git a/public/language/sl/error.json b/public/language/sl/error.json index 00452a7de0..6c876e99d4 100644 --- a/public/language/sl/error.json +++ b/public/language/sl/error.json @@ -30,6 +30,7 @@ "password-too-long": "Geslo je predolgo.", "user-banned": "Uporabnik je izločen.", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": " Pred svojo prvo objavo počakajte %1 s.", "blacklisted-ip": "Vaš IP-naslov je izločen. Povprašajte skrbnika za več informacij.", "ban-expiry-missing": "Vnesite končni datum za to izločitev.", @@ -104,7 +105,7 @@ "chat-disabled": "Klepet je onemogočen.", "too-many-messages": "Poslali ste preveč sporočil, prosimo, počakajte nekaj časa.", "invalid-chat-message": "Neveljavno sporočilo klepeta", - "chat-message-too-long": "Sporočilo klepeta je predolgo.", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "Nimate dovoljenja za urejanje tega sporočila.", "cant-remove-last-user": "Zadnjega uporabnika ne morete odstraniti.", "cant-delete-chat-message": "NImate dovoljenja za izbris tega sporočila.", diff --git a/public/language/sl/modules.json b/public/language/sl/modules.json index 860b680e7e..18b2281ba4 100644 --- a/public/language/sl/modules.json +++ b/public/language/sl/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 meseci", "chat.delete_message_confirm": "Ali ste prepričani, da želite izbrisati to sporočilo?", "chat.add-users-to-room": "Dodaj uporabnike v sobo.", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Sestavljanje", "composer.show_preview": "Pokaži predogled", "composer.hide_preview": "Skrij predogled", diff --git a/public/language/sl/user.json b/public/language/sl/user.json index 479046c340..bdb0cba806 100644 --- a/public/language/sl/user.json +++ b/public/language/sl/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Predlagano uporabniško ime je že zasedeno, zato smo ga rahlo spremenili. Sedaj vas poznamo kot %1", "password_same_as_username": "Vaše geslo je enako kot vaše uporabniško ime, prosim izberite drugačno geslo.", "password_same_as_email": "Vaše geslo je enako kot vaše e-poštni naslov, prosim izberite drugačno geslo.", + "weak_password": "Weak password.", "upload_picture": "Naloži fotografijo", "upload_a_picture": "Naloži fotografijo", "remove_uploaded_picture": "Odstrani preneseno sliko ", diff --git a/public/language/sr/admin/advanced/database.json b/public/language/sr/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/sr/admin/advanced/database.json +++ b/public/language/sr/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/sr/admin/manage/groups.json b/public/language/sr/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/sr/admin/manage/groups.json +++ b/public/language/sr/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/sr/admin/settings/advanced.json b/public/language/sr/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/sr/admin/settings/advanced.json +++ b/public/language/sr/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/sr/admin/settings/post.json b/public/language/sr/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/sr/admin/settings/post.json +++ b/public/language/sr/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/sr/admin/settings/user.json b/public/language/sr/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/sr/admin/settings/user.json +++ b/public/language/sr/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/sr/email.json b/public/language/sr/email.json index d47a4abd13..5d9fdf5c8f 100644 --- a/public/language/sr/email.json +++ b/public/language/sr/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "Ово обавештење вам је послато услед вашег подешавања претплате.", "test.text1": "Ово је пробно е-писмо за проверу исправности поставки е-поштара у NodeBB.", "unsub.cta": "Кликните овде да измените та подешавања", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Хвала!" } \ No newline at end of file diff --git a/public/language/sr/error.json b/public/language/sr/error.json index fb4e7993a4..28f93ab7fc 100644 --- a/public/language/sr/error.json +++ b/public/language/sr/error.json @@ -30,6 +30,7 @@ "password-too-long": "Шифра је предугачка.", "user-banned": "Корисник је забрањен", "user-banned-reason": "Овај налог је забрањен (Разлог: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Жао нам је, морате сачекати %1 секунде/и пре него што објавите прву поруку", "blacklisted-ip": "Жао нам је, ваша IP је забрањена у овој заједници. Ако мислите да је ово грешка, контактирајте администратора.", "ban-expiry-missing": "Наведите крајњи датум за ову забрану", @@ -104,7 +105,7 @@ "chat-disabled": "Ћаскања су онемогућена", "too-many-messages": "Послали сте превише порука, сачекајте мало.", "invalid-chat-message": "Неважећа порука", - "chat-message-too-long": "Порука је предугачка", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "Није вам дозвољено да уређујете ову поруку", "cant-remove-last-user": "Не можете уклонити последњег корисника", "cant-delete-chat-message": "Није вам дозвољено да избришете ову поруку", diff --git a/public/language/sr/modules.json b/public/language/sr/modules.json index 2c4b9cb728..b8ec9e3332 100644 --- a/public/language/sr/modules.json +++ b/public/language/sr/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 месеца", "chat.delete_message_confirm": "Да ли сте сигурни да желите да избришете ову поруку?", "chat.add-users-to-room": "Додајте кориснике у собу", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Писање поруке", "composer.show_preview": "Прикажи преглед", "composer.hide_preview": "Сакриј преглед", diff --git a/public/language/sr/user.json b/public/language/sr/user.json index f65600abaf..af5e8f9291 100644 --- a/public/language/sr/user.json +++ b/public/language/sr/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Корисничко име које сте захтевали је већ заузето па смо је мало изменили. Сада сте знани као %1", "password_same_as_username": "Ваша лозинка је иста као ваше име, изаберите другу лозинку", "password_same_as_email": "Ваша лозинка је иста као ваша е-пошта, изаберите другу лозинку", + "weak_password": "Weak password.", "upload_picture": "Отпремање слике", "upload_a_picture": "Отпреми слику", "remove_uploaded_picture": "Уклоните отпремљену слику", diff --git a/public/language/sv/admin/advanced/database.json b/public/language/sv/admin/advanced/database.json index f7db6220ee..b88ca6fc82 100644 --- a/public/language/sv/admin/advanced/database.json +++ b/public/language/sv/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", diff --git a/public/language/sv/admin/manage/groups.json b/public/language/sv/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/sv/admin/manage/groups.json +++ b/public/language/sv/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/sv/admin/settings/advanced.json b/public/language/sv/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/sv/admin/settings/advanced.json +++ b/public/language/sv/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/sv/admin/settings/post.json b/public/language/sv/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/sv/admin/settings/post.json +++ b/public/language/sv/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/sv/admin/settings/user.json b/public/language/sv/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/sv/admin/settings/user.json +++ b/public/language/sv/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/sv/email.json b/public/language/sv/email.json index 6780dc6f92..db54326e29 100644 --- a/public/language/sv/email.json +++ b/public/language/sv/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "Det här meddelandet fick du på grund av dina inställningar för prenumeration. ", "test.text1": "\nDet här är ett testmeddelande som verifierar att e-posten är korrekt installerad för din NodeBB. ", "unsub.cta": "Klicka här för att ändra inställningarna", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Tack!" } \ No newline at end of file diff --git a/public/language/sv/error.json b/public/language/sv/error.json index f976d6e657..2e3ea4bfcb 100644 --- a/public/language/sv/error.json +++ b/public/language/sv/error.json @@ -30,6 +30,7 @@ "password-too-long": "Lösenordet är för långt", "user-banned": "Användare bannlyst", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "När du är ny medlem måste du vänta %1 sekund(er) innan du gör ditt första inlägg", "blacklisted-ip": "Din IP-adress har blivit bannlyst från det här forumet. Om du tror att det beror på ett misstag, vad god kontakta en administratör. ", "ban-expiry-missing": "Ange ett slutdatum för denna banning", @@ -104,7 +105,7 @@ "chat-disabled": "Chatten är inaktiverad", "too-many-messages": "Du har skickat för många meddelanden, var god vänta", "invalid-chat-message": "Ogiltigt chattmeddelande", - "chat-message-too-long": "Chattmeddelande är för långt", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "Du har inte rättigheter att redigera det här meddelandet", "cant-remove-last-user": "Du kan inte ta bort den sista användaren", "cant-delete-chat-message": "Du har inte rättigheter att radera det här meddelandet", diff --git a/public/language/sv/modules.json b/public/language/sv/modules.json index d0bb0ec1ea..17a3c003a0 100644 --- a/public/language/sv/modules.json +++ b/public/language/sv/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 månader", "chat.delete_message_confirm": "Är du säker på att du vill radera det här meddelandet?", "chat.add-users-to-room": "Addera användare till rum", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Komponera", "composer.show_preview": "Visa förhandsgranskning", "composer.hide_preview": "Dölj förhandsgranskning", diff --git a/public/language/sv/user.json b/public/language/sv/user.json index bffff33e87..2c5494af3b 100644 --- a/public/language/sv/user.json +++ b/public/language/sv/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Användarnamnet är redan upptaget, så vi förändrade det lite. Du kallas nu för %1", "password_same_as_username": "Ditt lösenord är samma som ditt användarnamn, välj ett annat lösenord.", "password_same_as_email": "Ditt lösenord är detsamma som din e-postadress. Var god välj ett annat lösenord.", + "weak_password": "Weak password.", "upload_picture": "Ladda upp bild", "upload_a_picture": "Ladda upp en bild", "remove_uploaded_picture": "Ta bort uppladdad bild", diff --git a/public/language/th/admin/advanced/database.json b/public/language/th/admin/advanced/database.json index eef9a4e6d4..ee4f15b2b6 100644 --- a/public/language/th/admin/advanced/database.json +++ b/public/language/th/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "ระยะเวลาทำงานต่อเนื่องเป็นวินาที", "uptime-days": "ระยะเวลาทำงานต่อเนื่องเป็นวัน", diff --git a/public/language/th/admin/manage/groups.json b/public/language/th/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/th/admin/manage/groups.json +++ b/public/language/th/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/th/admin/settings/advanced.json b/public/language/th/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/th/admin/settings/advanced.json +++ b/public/language/th/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/th/admin/settings/post.json b/public/language/th/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/th/admin/settings/post.json +++ b/public/language/th/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/th/admin/settings/user.json b/public/language/th/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/th/admin/settings/user.json +++ b/public/language/th/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/th/email.json b/public/language/th/email.json index 6881135565..f6e3cd7e5b 100644 --- a/public/language/th/email.json +++ b/public/language/th/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "This post notification was sent to you due to your subscription settings.", "test.text1": "นี่คืออีเมลทดสอบเพื่อยืนยันว่าระบบอีเมลมีการตั้งค่าที่ถูกต้องสำหรับ NodeBB ของคุณ", "unsub.cta": "กดตรงนี้เพื่อเปลี่ยนแปลงการตั้งค่า", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "ขอบคุณ!" } \ No newline at end of file diff --git a/public/language/th/error.json b/public/language/th/error.json index 96df4a204c..4d5cf25465 100644 --- a/public/language/th/error.json +++ b/public/language/th/error.json @@ -30,6 +30,7 @@ "password-too-long": "Password too long", "user-banned": "User banned", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.", "ban-expiry-missing": "Please provide an end date for this ban", @@ -104,7 +105,7 @@ "chat-disabled": "Chat system disabled", "too-many-messages": "You have sent too many messages, please wait awhile.", "invalid-chat-message": "Invalid chat message", - "chat-message-too-long": "Chat message is too long", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "You are not allowed to edit this message", "cant-remove-last-user": "You can't remove the last user", "cant-delete-chat-message": "You are not allowed to delete this message", diff --git a/public/language/th/modules.json b/public/language/th/modules.json index 59c3a2e66e..00a675c2f2 100644 --- a/public/language/th/modules.json +++ b/public/language/th/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 Months", "chat.delete_message_confirm": "Are you sure you wish to delete this message?", "chat.add-users-to-room": "Add users to room", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Compose", "composer.show_preview": "Show Preview", "composer.hide_preview": "Hide Preview", diff --git a/public/language/th/user.json b/public/language/th/user.json index c6dc2304d1..345583ac68 100644 --- a/public/language/th/user.json +++ b/public/language/th/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "ชื้อผู้ใช้นี้ถูกใช้แล้ว เราทำการแก้ไขชื่อผู้ใช้ของคุณเล็กน้อยเป็น %1", "password_same_as_username": "คุณใช้รหัสผ่านเดียวกับชื่อผู้ใช้ กรุณาเปลี่ยนรหัสผ่านใหม่", "password_same_as_email": "คุณใช้รหัสผ่านเดียวกับอีเมล กรุณาเปลี่ยนรหัสผ่านใหม่", + "weak_password": "Weak password.", "upload_picture": "อัพโหลดรูป", "upload_a_picture": "อัพโหลดรูป", "remove_uploaded_picture": "ลบภาพที่อัพโหลดไว้", diff --git a/public/language/tr/admin/advanced/database.json b/public/language/tr/admin/advanced/database.json index 2d7196eebd..25989418d7 100644 --- a/public/language/tr/admin/advanced/database.json +++ b/public/language/tr/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Saniyede Bir Çalışma Zamanı", "uptime-days": "Günde Bir Çalışma Zamanı", diff --git a/public/language/tr/admin/manage/groups.json b/public/language/tr/admin/manage/groups.json index fff1ef9df0..f076da3bde 100644 --- a/public/language/tr/admin/manage/groups.json +++ b/public/language/tr/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Grup Adı", "description": "Grup Açıklaması", + "member-count": "Member Count", "system": "System Group", "edit": "Düzenle", "search-placeholder": "Ara", diff --git a/public/language/tr/admin/settings/advanced.json b/public/language/tr/admin/settings/advanced.json index ed8438eb34..fb9f6cd486 100644 --- a/public/language/tr/admin/settings/advanced.json +++ b/public/language/tr/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/tr/admin/settings/post.json b/public/language/tr/admin/settings/post.json index 71d021e580..1a4cb89f77 100644 --- a/public/language/tr/admin/settings/post.json +++ b/public/language/tr/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/tr/admin/settings/user.json b/public/language/tr/admin/settings/user.json index dd7154fc35..0aa97f45e8 100644 --- a/public/language/tr/admin/settings/user.json +++ b/public/language/tr/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/tr/email.json b/public/language/tr/email.json index 4353efd42a..4cf508e292 100644 --- a/public/language/tr/email.json +++ b/public/language/tr/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "Bu yazı bildirimi size abonelik ayarlarınız nedeni ile gönderilmiştir.", "test.text1": "Bu ileti NodeBB e-posta ayarlarınızın doğru çalışıp çalışmadığını kontrol etmek için gönderildi.", "unsub.cta": "Buraya tıklayarak ayarlarınızı değiştirebilirsiniz.", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Teşekkürler!" } \ No newline at end of file diff --git a/public/language/tr/error.json b/public/language/tr/error.json index 457af00f3e..16c3a33815 100644 --- a/public/language/tr/error.json +++ b/public/language/tr/error.json @@ -30,6 +30,7 @@ "password-too-long": "Parola çok uzun", "user-banned": "Kullanıcı Yasaklı", "user-banned-reason": "Maalesef, bu hesap yasaklandı (Sebep:% 1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Özür dileriz, ilk iletinizi yapmadan önce %1 saniye beklemeniz gerekiyor", "blacklisted-ip": "Üzgünüz, IP adresiniz, bu toplulukta yasaklandı. Bunun bir hata olduğunu düşünüyorsanız, bir yönetici ile irtibata geçiniz.", "ban-expiry-missing": "Bu yasak için bir bitiş tarihi girin", @@ -104,7 +105,7 @@ "chat-disabled": "Sohbet özelliği kapalı", "too-many-messages": "Ardı ardına çok fazla mesaj yolladınız, lütfen biraz bekleyiniz.", "invalid-chat-message": "Geçersiz sohbet mesajı", - "chat-message-too-long": "Sohbet mesajı çok uzun", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "Bu mesajı düzenlemek için izin verilmez", "cant-remove-last-user": "Son kullanıcıyı silemezsiniz", "cant-delete-chat-message": "Bu mesajı silmek için izin verilmez", diff --git a/public/language/tr/modules.json b/public/language/tr/modules.json index e0b251b7bc..a96b07be46 100644 --- a/public/language/tr/modules.json +++ b/public/language/tr/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 Ay", "chat.delete_message_confirm": "Bu mesajı silmek istediğinden emin misin?", "chat.add-users-to-room": "Odaya Kullanıcı Ekle", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Yaz", "composer.show_preview": "Önizleme Göster", "composer.hide_preview": "Önizleme Sakla", diff --git a/public/language/tr/user.json b/public/language/tr/user.json index 05dcd10d0a..5733c0530f 100644 --- a/public/language/tr/user.json +++ b/public/language/tr/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "İstediğiniz kullanıcı ismi zaten alınmış, bu yüzden biraz degiştirdik. Şimdiki kullanıcı isminiz %1", "password_same_as_username": "Parolanız kullanıcı adınız ile aynı, lütfen başka bir parola seçiniz.", "password_same_as_email": "Şifreniz mail adresiniz ile aynı lütfen başka bir şifre seçin.", + "weak_password": "Weak password.", "upload_picture": "Resim Yükle", "upload_a_picture": "Bir Resim Yükle", "remove_uploaded_picture": "Yüklenmiş fotoğrafı kaldır", diff --git a/public/language/uk/admin/advanced/database.json b/public/language/uk/admin/advanced/database.json index b7ff1ae639..66b2956c90 100644 --- a/public/language/uk/admin/advanced/database.json +++ b/public/language/uk/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 б", "x-mb": "%1 мб", + "x-gb": "%1 gb", "uptime-seconds": "Uptime в секундах", "uptime-days": "Uptime в днях", diff --git a/public/language/uk/admin/manage/groups.json b/public/language/uk/admin/manage/groups.json index 8287ef6da8..9758b901c8 100644 --- a/public/language/uk/admin/manage/groups.json +++ b/public/language/uk/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Назва групи", "description": "Опис групи", + "member-count": "Member Count", "system": "Системна група", "edit": "Редагувати", "search-placeholder": "Пошук", diff --git a/public/language/uk/admin/settings/advanced.json b/public/language/uk/admin/settings/advanced.json index 6f27dfebd2..4f96f25c9b 100644 --- a/public/language/uk/admin/settings/advanced.json +++ b/public/language/uk/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Задати ALLOW-FROM для розміщення NodeBB в iFrame", "headers.powered-by": "Налаштувати заголовок \"Powered By\", котрий відправляє NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "Щоб заборонити доступ для всіх сайтів, залиште пустим або вкажіть null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Керування трафіком", diff --git a/public/language/uk/admin/settings/post.json b/public/language/uk/admin/settings/post.json index 77034cbf9c..ea770e1463 100644 --- a/public/language/uk/admin/settings/post.json +++ b/public/language/uk/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Налаштування непрочитаних", "unread.cutoff": "За скільки днів показувати непрочитані", "unread.min-track-last": "Мінімальна кількість постів у темі перш ніж відслідковувати останні прочитані", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Налаштування підписів", "signature.disable": "Вимкнути підписи", "signature.no-links": "Вимкнути посилання в підписах", diff --git a/public/language/uk/admin/settings/user.json b/public/language/uk/admin/settings/user.json index 263df1745b..dc463f9ecd 100644 --- a/public/language/uk/admin/settings/user.json +++ b/public/language/uk/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Мінімальна довжина імені користувача", "max-username-length": "Максимальна довжина імені користувача", "min-password-length": "Мінімальна довжина пароля", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Максимальна довжина розділу \"Про мене\"", "terms-of-use": "Умови користування форумом (Залиште пустим, щоб вимкнути)", "user-search": "Пошук користувачів", diff --git a/public/language/uk/email.json b/public/language/uk/email.json index 386af1ca97..236a1f3978 100644 --- a/public/language/uk/email.json +++ b/public/language/uk/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "Це поштове повідомлення було вислано вам, згідно ваших налаштувань підписки", "test.text1": "Це пробний лист для верифікації поштової служби. Всі налаштування вірні для NodeBB.", "unsub.cta": "Натисніть тут, щоб змінити ці налаштування", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Дякуємо!" } \ No newline at end of file diff --git a/public/language/uk/error.json b/public/language/uk/error.json index 286eb36767..2cc9650c8e 100644 --- a/public/language/uk/error.json +++ b/public/language/uk/error.json @@ -30,6 +30,7 @@ "password-too-long": "Пароль задовгий", "user-banned": "Користувача забанено", "user-banned-reason": "Вибачте, але цей акаунт було забанено (Причина: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Вибачте, але вам необхідно зачекати %1 секунд(и), перед першим постом", "blacklisted-ip": "Вибачте, але ваша IP-адреса була забанена в цій спільноті. Якщо ви гадаєте, що це сталось помилково, зв'яжіться з адміністратором.", "ban-expiry-missing": "Вкажіть, будь ласка, кінцеву дату бану", @@ -104,7 +105,7 @@ "chat-disabled": "Чат вимкнено", "too-many-messages": "Ви надіслали забагато повідомлень, зачекайте трішки.", "invalid-chat-message": "Невірне повідомлення чату", - "chat-message-too-long": "Повідомлення чату задовге", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "Ви не можете редагувати повідомлення", "cant-remove-last-user": "Ви не можете видалити останнього користувача", "cant-delete-chat-message": "Ви не можете видалити це повідомлення", diff --git a/public/language/uk/modules.json b/public/language/uk/modules.json index f005377372..6cb7697cc5 100644 --- a/public/language/uk/modules.json +++ b/public/language/uk/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 місяці", "chat.delete_message_confirm": "Ви впевнені, що хочете видалити це повідомлення?", "chat.add-users-to-room": "Додати користувачів до кімнати", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Редактор повідомлень", "composer.show_preview": "Показати попередній перегляд", "composer.hide_preview": "Сховати попередній перегляд", diff --git a/public/language/uk/user.json b/public/language/uk/user.json index bad807379e..0d5e93857f 100644 --- a/public/language/uk/user.json +++ b/public/language/uk/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Ім'я користувача, що ви обрали, вже було зайняте, то ж ми його трішки змінили. Ви тепер відомі як %1", "password_same_as_username": "Ваш пароль співпадає з іменем користувача. Оберіть інший пароль, будь ласка.", "password_same_as_email": "Ваш пароль співпадає з електронною поштою. Оберіть інший пароль, будь ласка.", + "weak_password": "Weak password.", "upload_picture": "Завантажити зображення", "upload_a_picture": "Завантажити зображення", "remove_uploaded_picture": "Видалити завантажене зображення", diff --git a/public/language/vi/admin/advanced/database.json b/public/language/vi/admin/advanced/database.json index fe446d1759..7dd15423dc 100644 --- a/public/language/vi/admin/advanced/database.json +++ b/public/language/vi/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "Thời gian hoạt động(giây)", "uptime-days": "Thời gian hoạt động(Ngày)", diff --git a/public/language/vi/admin/manage/groups.json b/public/language/vi/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/vi/admin/manage/groups.json +++ b/public/language/vi/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/vi/admin/settings/advanced.json b/public/language/vi/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/vi/admin/settings/advanced.json +++ b/public/language/vi/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/vi/admin/settings/post.json b/public/language/vi/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/vi/admin/settings/post.json +++ b/public/language/vi/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/vi/admin/settings/user.json b/public/language/vi/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/vi/admin/settings/user.json +++ b/public/language/vi/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/vi/email.json b/public/language/vi/email.json index 30b3fb658a..9cab607e01 100644 --- a/public/language/vi/email.json +++ b/public/language/vi/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "Thông báo bài viết này được gửi cho bạn dựa tên thiết lập nhận thông báo của bạn", "test.text1": "Đây là email kiểm tra xem chức năng gửi mail trên hệ thống NodeBB của bạn có hoạt động tốt hay không.", "unsub.cta": "Nhấn vào đây để thay đổi cài đặt.", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "Xin cảm ơn!" } \ No newline at end of file diff --git a/public/language/vi/error.json b/public/language/vi/error.json index 5e45cc0ee1..a833820d5c 100644 --- a/public/language/vi/error.json +++ b/public/language/vi/error.json @@ -30,6 +30,7 @@ "password-too-long": "Mật khẩu quá dài", "user-banned": "Tài khoản bị ban", "user-banned-reason": "Xin lỗi, tài khoản này đã bị khóa (Lí do: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Rất tiếc, bạn phải chờ %1 giây để đăng bài viết đầu tiên.", "blacklisted-ip": "Rất tiếc, địa chỉ IP của bạn đã bị cấm khỏi cộng đồng. Nếu bạn cảm thấy có gì không đúng, hãy liên lạc với người quản trị.", "ban-expiry-missing": "Vui lòng cung cấp ngày hết hạn của lệnh cấm", @@ -104,7 +105,7 @@ "chat-disabled": "Hệ thống chat đã bị vô hiệu hoá", "too-many-messages": "Bạn đã gửi quá nhiều tin nhắn, vui lòng đợi trong giây lát.", "invalid-chat-message": "Tin nhắn không hợp lệ", - "chat-message-too-long": "Tin nhắn quá dài", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "Bạn không được phép chỉnh sửa tin nhắn này", "cant-remove-last-user": "Bạn không thể xoá thành viên cuối cùng", "cant-delete-chat-message": "Bạn không được phép xoá tin nhắn này", diff --git a/public/language/vi/modules.json b/public/language/vi/modules.json index a8c41951a3..b700406deb 100644 --- a/public/language/vi/modules.json +++ b/public/language/vi/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3 tháng", "chat.delete_message_confirm": "Bạn có chắc chắn bạn muốn xoá tin nhắn này chứ?", "chat.add-users-to-room": "Thêm người vào phòng", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "Soạn thảo", "composer.show_preview": "Hiện Xem trước", "composer.hide_preview": "Ẩn Xem trước", diff --git a/public/language/vi/user.json b/public/language/vi/user.json index 7603788230..c369f3af56 100644 --- a/public/language/vi/user.json +++ b/public/language/vi/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "Tên truy cập này đã tồn tại, vì vậy chúng tôi đã sửa đổi nó một chút. Tên truy cập của bạn giờ là %1", "password_same_as_username": "Mật khẩu của bạn trùng với tên đăng nhập, vui lòng chọn một mật khẩu khác.", "password_same_as_email": "Mật khẩu của bạn trùng với email của bạn, hãy chọn mật khẩu khác.", + "weak_password": "Weak password.", "upload_picture": "Tải lên hình ảnh", "upload_a_picture": "Tải lên một hình ảnh", "remove_uploaded_picture": "Xoá ảnh đã tải lên", diff --git a/public/language/zh-CN/admin/advanced/database.json b/public/language/zh-CN/admin/advanced/database.json index 5519709794..825469bb2d 100644 --- a/public/language/zh-CN/admin/advanced/database.json +++ b/public/language/zh-CN/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "运行秒数", "uptime-days": "运行天数", diff --git a/public/language/zh-CN/admin/manage/groups.json b/public/language/zh-CN/admin/manage/groups.json index 131199f22c..4fee922f44 100644 --- a/public/language/zh-CN/admin/manage/groups.json +++ b/public/language/zh-CN/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "群组名", "description": "群组描述", + "member-count": "Member Count", "system": "系统群组", "edit": "编辑", "search-placeholder": "搜索", diff --git a/public/language/zh-CN/admin/settings/advanced.json b/public/language/zh-CN/admin/settings/advanced.json index eb9bced36e..4cc5377e39 100644 --- a/public/language/zh-CN/admin/settings/advanced.json +++ b/public/language/zh-CN/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "设置 ALLOW-FROM 来放置 NodeBB 于 iFrame 中", "headers.powered-by": "自定义由 NodeBB 发送的 \"Powered By\" 头部 ", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "要拒绝所有网站访问?在这留空或者设置成 null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "流量管理", diff --git a/public/language/zh-CN/admin/settings/post.json b/public/language/zh-CN/admin/settings/post.json index 857f72b98e..6f77d26602 100644 --- a/public/language/zh-CN/admin/settings/post.json +++ b/public/language/zh-CN/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "未读设置", "unread.cutoff": "未读截止天数", "unread.min-track-last": "跟踪最后阅读之前的主题最小帖子", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "签名设置", "signature.disable": "禁用签名", "signature.no-links": "禁用签名中的链接", diff --git a/public/language/zh-CN/admin/settings/user.json b/public/language/zh-CN/admin/settings/user.json index 7b13c89b95..d147da2ccc 100644 --- a/public/language/zh-CN/admin/settings/user.json +++ b/public/language/zh-CN/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "最小用户名长度", "max-username-length": "最大用户名长度", "min-password-length": "最小密码长度", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "自我介绍的最大长度", "terms-of-use": "论坛使用条款 (留空即可禁用)", "user-search": "用户搜索", diff --git a/public/language/zh-CN/email.json b/public/language/zh-CN/email.json index 823d26070f..f247ee4b1f 100644 --- a/public/language/zh-CN/email.json +++ b/public/language/zh-CN/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "根据您的订阅设置,为您发送此回帖提醒。", "test.text1": "这是一封测试邮件,用来验证 NodeBB 的邮件配置是否设置正确。", "unsub.cta": "点击这里修改这些设置", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "谢谢!" } \ No newline at end of file diff --git a/public/language/zh-CN/error.json b/public/language/zh-CN/error.json index a59beaf451..89f130c7ff 100644 --- a/public/language/zh-CN/error.json +++ b/public/language/zh-CN/error.json @@ -30,6 +30,7 @@ "password-too-long": "密码太长", "user-banned": "用户已禁止", "user-banned-reason": "抱歉,此帐号已经被封禁 (原因:%1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "抱歉,您需要等待 %1 秒后,才可以发帖!", "blacklisted-ip": "对不起,您的 IP 地址已被社区禁用。如果您认为这是一个错误,请与管理员联系。", "ban-expiry-missing": "请提供此次禁言结束日期", @@ -104,7 +105,7 @@ "chat-disabled": "聊天系统已关闭", "too-many-messages": "您发送了太多消息,请稍等片刻。", "invalid-chat-message": "无效的聊天信息", - "chat-message-too-long": "聊天信息太长", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "您不能编辑这条信息", "cant-remove-last-user": "您不能移除这个用户", "cant-delete-chat-message": "您不允许删除这条消息", diff --git a/public/language/zh-CN/modules.json b/public/language/zh-CN/modules.json index 235ec2c80b..32241beaba 100644 --- a/public/language/zh-CN/modules.json +++ b/public/language/zh-CN/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3个月", "chat.delete_message_confirm": "确认删除此消息吗?", "chat.add-users-to-room": "向此聊天室中添加成员", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "编写帮助", "composer.show_preview": "显示预览", "composer.hide_preview": "隐藏预览", diff --git a/public/language/zh-CN/user.json b/public/language/zh-CN/user.json index 1f7842a16c..8e8c90ada7 100644 --- a/public/language/zh-CN/user.json +++ b/public/language/zh-CN/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "您申请的用户名已被占用,所以我们稍作更改。您现在的用户名是 %1", "password_same_as_username": "您的密码与用户名相同,请选择另外的密码。", "password_same_as_email": "您的密码与邮箱相同,请选择另外的密码。", + "weak_password": "Weak password.", "upload_picture": "上传头像", "upload_a_picture": "上传头像", "remove_uploaded_picture": "删除已上传的头像", diff --git a/public/language/zh-TW/admin/advanced/database.json b/public/language/zh-TW/admin/advanced/database.json index 860a040b3d..66a71edae9 100644 --- a/public/language/zh-TW/admin/advanced/database.json +++ b/public/language/zh-TW/admin/advanced/database.json @@ -1,6 +1,7 @@ { "x-b": "%1 b", "x-mb": "%1 mb", + "x-gb": "%1 gb", "uptime-seconds": "正常運作秒數", "uptime-days": "正常運作天數", diff --git a/public/language/zh-TW/admin/manage/groups.json b/public/language/zh-TW/admin/manage/groups.json index b5e526aacf..c019ec9823 100644 --- a/public/language/zh-TW/admin/manage/groups.json +++ b/public/language/zh-TW/admin/manage/groups.json @@ -1,6 +1,7 @@ { "name": "Group Name", "description": "Group Description", + "member-count": "Member Count", "system": "System Group", "edit": "Edit", "search-placeholder": "Search", diff --git a/public/language/zh-TW/admin/settings/advanced.json b/public/language/zh-TW/admin/settings/advanced.json index b023528d04..05a1929cf0 100644 --- a/public/language/zh-TW/admin/settings/advanced.json +++ b/public/language/zh-TW/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty or set to null", + "headers.acao-help": "To deny access to all sites, leave empty", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/zh-TW/admin/settings/post.json b/public/language/zh-TW/admin/settings/post.json index f293e554d9..aca8b39d64 100644 --- a/public/language/zh-TW/admin/settings/post.json +++ b/public/language/zh-TW/admin/settings/post.json @@ -29,6 +29,8 @@ "unread": "Unread Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", + "recent": "Recent Settings", + "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", "signature.disable": "Disable signatures", "signature.no-links": "Disable links in signatures", diff --git a/public/language/zh-TW/admin/settings/user.json b/public/language/zh-TW/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/zh-TW/admin/settings/user.json +++ b/public/language/zh-TW/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/zh-TW/email.json b/public/language/zh-TW/email.json index d34fae331b..3e95aee620 100644 --- a/public/language/zh-TW/email.json +++ b/public/language/zh-TW/email.json @@ -32,5 +32,9 @@ "notif.post.unsub.info": "本張貼通知按你的訂閱設置發送給你。", "test.text1": "這是一個測試電子郵件,用於確認你的NodeBB郵件功能是否設置正確。", "unsub.cta": "點擊此處來更改這些設置", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", "closing": "感謝!" } \ No newline at end of file diff --git a/public/language/zh-TW/error.json b/public/language/zh-TW/error.json index 8b3fa47a81..9ac4e1f494 100644 --- a/public/language/zh-TW/error.json +++ b/public/language/zh-TW/error.json @@ -30,6 +30,7 @@ "password-too-long": "密碼太長", "user-banned": "該使用者已被停用", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "抱歉,發表你第一篇文章須要等待 %1 秒", "blacklisted-ip": "抱歉,你的IP位置已經被這個社群禁用了。如果你覺得這是一個失誤,請連絡管理員。", "ban-expiry-missing": "請提供這個禁用的到期時間", @@ -104,7 +105,7 @@ "chat-disabled": "聊天系統被禁止", "too-many-messages": "你已經送出過多的訊息,請稍等一下。", "invalid-chat-message": "無效的聊天訊息", - "chat-message-too-long": "聊天訊息太長", + "chat-message-too-long": "Chat messages can not be longer than %1 characters.", "cant-edit-chat-message": "你不被允許編輯這條訊息", "cant-remove-last-user": "你不能移除最後的使用者", "cant-delete-chat-message": "你不被允許刪除這條訊息", diff --git a/public/language/zh-TW/modules.json b/public/language/zh-TW/modules.json index 753ee18d2e..b3fef620d5 100644 --- a/public/language/zh-TW/modules.json +++ b/public/language/zh-TW/modules.json @@ -20,6 +20,7 @@ "chat.three_months": "3個月", "chat.delete_message_confirm": "你確定要刪除這個訊息?", "chat.add-users-to-room": "將使用者加入聊天室中", + "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", "composer.compose": "撰寫", "composer.show_preview": "顯示預覽", "composer.hide_preview": "隱藏預覽", diff --git a/public/language/zh-TW/user.json b/public/language/zh-TW/user.json index e0010019f8..3a1874a4a4 100644 --- a/public/language/zh-TW/user.json +++ b/public/language/zh-TW/user.json @@ -60,6 +60,7 @@ "username_taken_workaround": "你想要註冊的帳號已經被使用了,所以我們將它略作改變。你現在的帳號名稱是 %1", "password_same_as_username": "你的密碼和帳號是一樣的,請選擇另一組密碼。", "password_same_as_email": "你的密碼和電子郵件是一樣的,請選擇另一組密碼。", + "weak_password": "Weak password.", "upload_picture": "上傳頭像", "upload_a_picture": "上傳一張照片", "remove_uploaded_picture": "移除上傳的圖片", From 27b362fab227edefd4acbe6b27a7822ec9b78833 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Mon, 8 May 2017 09:22:52 +0000 Subject: [PATCH 293/316] Latest translations and fallbacks --- .../language/bg/admin/advanced/database.json | 2 +- public/language/bg/admin/manage/groups.json | 2 +- .../language/bg/admin/settings/advanced.json | 2 +- public/language/bg/admin/settings/post.json | 4 ++-- public/language/bg/admin/settings/user.json | 2 +- public/language/bg/email.json | 8 ++++---- public/language/bg/error.json | 4 ++-- public/language/bg/modules.json | 2 +- public/language/bg/user.json | 2 +- public/language/sk/admin/manage/flags.json | 8 ++++---- public/language/sk/admin/manage/users.json | 4 ++-- public/language/sk/notifications.json | 2 +- public/language/sk/user.json | 2 +- public/language/sr/email.json | 8 ++++---- public/language/sr/error.json | 20 +++++++++---------- public/language/sr/modules.json | 2 +- public/language/sr/user.json | 2 +- public/language/tr/admin/settings/chat.json | 2 +- public/language/tr/notifications.json | 2 +- public/language/tr/user.json | 2 +- 20 files changed, 41 insertions(+), 41 deletions(-) diff --git a/public/language/bg/admin/advanced/database.json b/public/language/bg/admin/advanced/database.json index 21ed28da0d..acc6fde389 100644 --- a/public/language/bg/admin/advanced/database.json +++ b/public/language/bg/admin/advanced/database.json @@ -1,7 +1,7 @@ { "x-b": "%1 Б", "x-mb": "%1 МБ", - "x-gb": "%1 gb", + "x-gb": "%1 ГБ", "uptime-seconds": "Активно време в секунди", "uptime-days": "Активно време в дни", diff --git a/public/language/bg/admin/manage/groups.json b/public/language/bg/admin/manage/groups.json index 92398de456..d7e9a46ca2 100644 --- a/public/language/bg/admin/manage/groups.json +++ b/public/language/bg/admin/manage/groups.json @@ -1,7 +1,7 @@ { "name": "Име на групата", "description": "Описание на групата", - "member-count": "Member Count", + "member-count": "Брой на членовете", "system": "Системна група", "edit": "Редактиране", "search-placeholder": "Търсене", diff --git a/public/language/bg/admin/settings/advanced.json b/public/language/bg/admin/settings/advanced.json index 7b42194a17..d99bdfd5b5 100644 --- a/public/language/bg/admin/settings/advanced.json +++ b/public/language/bg/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "Задайте „ALLOW-FROM“, за да поставите NodeBB в „iFrame“", "headers.powered-by": "Персонализиране на заглавната част „Захранван от“, която се изпраща от NodeBB", "headers.acao": "Произход за разрешаване на управлението на достъпа", - "headers.acao-help": "To deny access to all sites, leave empty", + "headers.acao-help": "За да забраните достъпа до всички уеб сайтове, оставете празно", "headers.acam": "Методи за разрешаване на управлението на достъпа", "headers.acah": "Заглавки за разрешаване на управлението на достъпа", "traffic-management": "Управление на трафика", diff --git a/public/language/bg/admin/settings/post.json b/public/language/bg/admin/settings/post.json index 024daae4e4..37b535660f 100644 --- a/public/language/bg/admin/settings/post.json +++ b/public/language/bg/admin/settings/post.json @@ -29,8 +29,8 @@ "unread": "Настройки за непрочетените", "unread.cutoff": "Възраст на публикациите, след която те не се показват в непрочетените (в брой дни)", "unread.min-track-last": "Минимален брой публикации в темата, след което да започва следене на последно прочетената", - "recent": "Recent Settings", - "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", + "recent": "Настройки за скорошните", + "recent.categoryFilter.disable": "Изключване на филтрирането на темите в пренебрегваните категории на страницата /recent", "signature": "Настройки за подписите", "signature.disable": "Забраняване на подписите", "signature.no-links": "Забраняване на поставянето на връзки в подписите", diff --git a/public/language/bg/admin/settings/user.json b/public/language/bg/admin/settings/user.json index 68b8a33e1f..a470c905d0 100644 --- a/public/language/bg/admin/settings/user.json +++ b/public/language/bg/admin/settings/user.json @@ -37,7 +37,7 @@ "min-username-length": "Минимална дължина на потребителското име", "max-username-length": "Максимална дължина на потребителското име", "min-password-length": "Минимална дължина на паролата", - "min-password-strength": "Minimum Password Strength", + "min-password-strength": "Минимална сложност на паролата", "max-about-me-length": "Максимална дължина на информацията на потребителите за себе си", "terms-of-use": "Условия за ползване на форума (Оставете празно и няма да има такива)", "user-search": "Търсене на потребители", diff --git a/public/language/bg/email.json b/public/language/bg/email.json index f426ce0316..aaf94585fd 100644 --- a/public/language/bg/email.json +++ b/public/language/bg/email.json @@ -32,9 +32,9 @@ "notif.post.unsub.info": "Това известие за публикация беше изпратено до Вас поради настройките Ви за абонаментите.", "test.text1": "Това е пробно е-писмо, за да потвърдим, че изпращачът на е-поща е правилно настроен за Вашия NodeBB.", "unsub.cta": "Натиснете тук, за да промените тези настройки", - "banned.subject": "You have been banned from %1", - "banned.text1": "The user %1 has been banned from %2.", - "banned.text2": "This ban will last until %1.", - "banned.text3": "This is the reason why you have been banned:", + "banned.subject": "Вие бяхте блокиран(а) от %1", + "banned.text1": "Потребителят %1 беше блокиран от %2.", + "banned.text2": "Това блокиране ще е в сила до %1.", + "banned.text3": "Това е причината, поради която бяхте блокиран(а):", "closing": "Благодарим Ви!" } \ No newline at end of file diff --git a/public/language/bg/error.json b/public/language/bg/error.json index c8a4867da8..6733bdcca5 100644 --- a/public/language/bg/error.json +++ b/public/language/bg/error.json @@ -30,7 +30,7 @@ "password-too-long": "Паролата е твърде дълга", "user-banned": "Потребителят е блокиран", "user-banned-reason": "За съжаление, този акаунт е блокиран (Причина: %1)", - "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", + "user-banned-reason-until": "За съжаление, този акаунт е блокиран до %1 (Причина: %2)", "user-too-new": "Съжаляваме, но трябва да изчакате поне %1 секунда/и, преди да направите първата си публикация", "blacklisted-ip": "Съжаляваме, но Вашият IP адрес е забранен за ползване в тази общност. Ако смятате, че това е грешка, моля, свържете се с администратор.", "ban-expiry-missing": "Моля, задайте крайна дата за това блокиране", @@ -105,7 +105,7 @@ "chat-disabled": "Системата за разговори е изключена", "too-many-messages": "Изпратили сте твърде много съобщения. Моля, изчакайте малко.", "invalid-chat-message": "Неправилно съобщение", - "chat-message-too-long": "Chat messages can not be longer than %1 characters.", + "chat-message-too-long": "Съобщенията в разговор не може да бъдат по-дълги от %1 знака.", "cant-edit-chat-message": "Нямате право да редактирате това съобщение", "cant-remove-last-user": "Не можете да премахнете последния потребител", "cant-delete-chat-message": "Нямате право да изтриете това съобщение", diff --git a/public/language/bg/modules.json b/public/language/bg/modules.json index 0f66f729c6..fac82100cb 100644 --- a/public/language/bg/modules.json +++ b/public/language/bg/modules.json @@ -20,7 +20,7 @@ "chat.three_months": "3 месеца", "chat.delete_message_confirm": "Наистина ли искате да изтриете това съобщение?", "chat.add-users-to-room": "Добавяне на потребители към стаята", - "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", + "chat.confirm-chat-with-dnd-user": "Този потребител е в състояние „не ме безпокойте“. Наистина ли искате да разговаряте с него?", "composer.compose": "Писане", "composer.show_preview": "Показване на прегледа", "composer.hide_preview": "Скриване на прегледа", diff --git a/public/language/bg/user.json b/public/language/bg/user.json index 5cc184b612..ac0249f530 100644 --- a/public/language/bg/user.json +++ b/public/language/bg/user.json @@ -60,7 +60,7 @@ "username_taken_workaround": "Потребителското име, което искате, е заето и затова ние го променихме малко. Вие ще се наричате %1", "password_same_as_username": "Паролата е същата като потребителското Ви име. Моля, изберете друга парола.", "password_same_as_email": "Паролата е същата като е-пощата Ви. Моля, изберете друга парола.", - "weak_password": "Weak password.", + "weak_password": "Проста парола.", "upload_picture": "Качване на снимка", "upload_a_picture": "Качване на снимка", "remove_uploaded_picture": "Премахване на качената снимка", diff --git a/public/language/sk/admin/manage/flags.json b/public/language/sk/admin/manage/flags.json index bfc488a409..4b11c7990d 100644 --- a/public/language/sk/admin/manage/flags.json +++ b/public/language/sk/admin/manage/flags.json @@ -1,14 +1,14 @@ { "daily": "Daily flags", - "by-user": "Flags by user", - "by-user-search": "Search flagged posts by username", + "by-user": "Označené používateľom", + "by-user-search": "Vyhľadávať označené príspevky podľa používateľa", "category": "Category", "sort-by": "Sort By", - "sort-by.most-flags": "Most Flags", + "sort-by.most-flags": "Najviac označené", "sort-by.most-recent": "Most Recent", "search": "Search", "dismiss-all": "Dismiss All", - "none-flagged": "No flagged posts!", + "none-flagged": "Žiadne označené príspevky!", "posted-in": "Posted in %1", "read-more": "Read More", "flagged-x-times": "This post has been flagged %1 time(s):", diff --git a/public/language/sk/admin/manage/users.json b/public/language/sk/admin/manage/users.json index f1651a814b..1e808c70fa 100644 --- a/public/language/sk/admin/manage/users.json +++ b/public/language/sk/admin/manage/users.json @@ -10,7 +10,7 @@ "temp-ban": "Ban User(s) Temporarily", "unban": "Unban User(s)", "reset-lockout": "Reset Lockout", - "reset-flags": "Reset Flags", + "reset-flags": "Obnoviť označenia", "delete": "Delete User(s)", "purge": "Delete User(s) and Content", "download-csv": "Download CSV", @@ -23,7 +23,7 @@ "pills.top-posters": "Top Posters", "pills.top-rep": "Most Reputation", "pills.inactive": "Inactive", - "pills.flagged": "Most Flagged", + "pills.flagged": "Najviac označované", "pills.banned": "Banned", "pills.search": "User Search", diff --git a/public/language/sk/notifications.json b/public/language/sk/notifications.json index 39ca363782..5bf882e4d1 100644 --- a/public/language/sk/notifications.json +++ b/public/language/sk/notifications.json @@ -16,7 +16,7 @@ "chat": "Konverzácie", "follows": "Nasledovatelia", "upvote": "Zahlasované", - "new-flags": "New Flags", + "new-flags": "Nové označenia", "my-flags": "Flags assigned to me", "bans": "Zablokované", "new_message_from": "Nova spáva od %1", diff --git a/public/language/sk/user.json b/public/language/sk/user.json index c107383e30..b072831a64 100644 --- a/public/language/sk/user.json +++ b/public/language/sk/user.json @@ -60,7 +60,7 @@ "username_taken_workaround": "Vaše požadované prihlasovacie meno je už obsadené, tak sme si ho dovolili mierne upraviť. Budeme Vás evidovať ako %1", "password_same_as_username": "Vaše heslo sa zhoduje s Vaším používateľským menom, prosím zvoľte iné heslo.", "password_same_as_email": "Vaše heslo sa zhoduje s Vaším e-mailom, prosím zvoľte iné heslo.", - "weak_password": "Weak password.", + "weak_password": "Slabé heslo.", "upload_picture": "Nahrať obrázok", "upload_a_picture": "Nahrať obrázok", "remove_uploaded_picture": "Vymazať nahraný obrázok", diff --git a/public/language/sr/email.json b/public/language/sr/email.json index 5d9fdf5c8f..c69628cbbf 100644 --- a/public/language/sr/email.json +++ b/public/language/sr/email.json @@ -32,9 +32,9 @@ "notif.post.unsub.info": "Ово обавештење вам је послато услед вашег подешавања претплате.", "test.text1": "Ово је пробно е-писмо за проверу исправности поставки е-поштара у NodeBB.", "unsub.cta": "Кликните овде да измените та подешавања", - "banned.subject": "You have been banned from %1", - "banned.text1": "The user %1 has been banned from %2.", - "banned.text2": "This ban will last until %1.", - "banned.text3": "This is the reason why you have been banned:", + "banned.subject": "Забрањени сте на %1", + "banned.text1": "Корисник %1 је забрањен на %2.", + "banned.text2": "Ова забрана ће трајати до %1.", + "banned.text3": "Ово је разлог зашто сте забрањени:", "closing": "Хвала!" } \ No newline at end of file diff --git a/public/language/sr/error.json b/public/language/sr/error.json index 28f93ab7fc..f05560c926 100644 --- a/public/language/sr/error.json +++ b/public/language/sr/error.json @@ -30,7 +30,7 @@ "password-too-long": "Шифра је предугачка.", "user-banned": "Корисник је забрањен", "user-banned-reason": "Овај налог је забрањен (Разлог: %1)", - "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", + "user-banned-reason-until": "Овај налог је забрањен до %1 (Разлог: %2)", "user-too-new": "Жао нам је, морате сачекати %1 секунде/и пре него што објавите прву поруку", "blacklisted-ip": "Жао нам је, ваша IP је забрањена у овој заједници. Ако мислите да је ово грешка, контактирајте администратора.", "ban-expiry-missing": "Наведите крајњи датум за ову забрану", @@ -59,15 +59,15 @@ "post-delete-duration-expired-days-hours": "Време у којем вам је дозвољено брисање порука након објављивања: %1 дан. и %2 час.", "cant-delete-topic-has-reply": "Не можете обрисати вашу тему након што је на њу одговорено", "cant-delete-topic-has-replies": "Не можете обрисати вашу тему након што добије %1 одговора", - "content-too-short": "Унесите дужу поруку. Порука мора имати најмање %1 карактера.", - "content-too-long": "Унесите краћу поруку. Порука не сме бити дужа од %1 карактера.", - "title-too-short": "Унесите дужи наслов. Наслов мора имати најмање %1 карактера.", - "title-too-long": "Унесите краћи наслов. Наслов не сме бити дужи од %1 карактера.", + "content-too-short": "Унесите дужу поруку. Порука мора садржати најмање %1 знак(ов)а.", + "content-too-long": "Унесите краћу поруку. Порука не сме бити дужа од %1 знак(ов)а.", + "title-too-short": "Унесите дужи наслов. Наслов мора садржати најмање %1 знак(ов)а.", + "title-too-long": "Унесите краћи наслов. Наслов не сме бити дужи од %1 знак(ов)а.", "category-not-selected": "Није одабрана категорија", "too-many-posts": "Можете објављивати поруке само једном у %1 секунди - сачекајте пре него што покушате поново", "too-many-posts-newbie": "Као нови корисник, можете објављивати поруке само једном у %1 секунди док не достигнете %2 углед - сачекајте пре него што покушате поново", - "tag-too-short": "Унесите дужу ознаку. Ознаке морају имати најмање %1 карактера.", - "tag-too-long": "Унесите краћу ознаку. Ознаке не смеју бити дуже од %1 карактера.", + "tag-too-short": "Унесите дужу ознаку. Ознаке морају садржати најмање %1 знак(ов)а.", + "tag-too-long": "Унесите краћу ознаку. Ознаке не смеју бити дуже од %1 знак(ов)а.", "not-enough-tags": "Нема довољно ознака. Теме морају имати најмање %1 ознаке/а.", "too-many-tags": "Превише ознака. Теме не смеју имати више од %1 ознаке/а.", "still-uploading": "Сачекајте док се отпремања не заврше.", @@ -98,14 +98,14 @@ "topic-thumbnails-are-disabled": "Сличице тема су онемогућене.", "invalid-file": "Неисправна датотека", "uploads-are-disabled": "Отпремања су онемогућена", - "signature-too-long": "Жао нам је, потпис не сме бити дужи од %1 карактера", - "about-me-too-long": "Жао нам је, информације о вама не смеју бити дуже од %1 карактера ", + "signature-too-long": "Жао нам је, потпис не сме бити дужи од %1 знак(ов)а.", + "about-me-too-long": "Жао нам је, информације о вама не смеју бити дуже од %1 знак(ов)а.", "cant-chat-with-yourself": "Не можете ћаскати са самим собом!", "chat-restricted": "Овај корисник је ограничио њихова ћаскања. Морају вас пратити пре него што можете ћаскати са њима.", "chat-disabled": "Ћаскања су онемогућена", "too-many-messages": "Послали сте превише порука, сачекајте мало.", "invalid-chat-message": "Неважећа порука", - "chat-message-too-long": "Chat messages can not be longer than %1 characters.", + "chat-message-too-long": "Поруке ћаскања не могу бити дуже од %1 знакова.", "cant-edit-chat-message": "Није вам дозвољено да уређујете ову поруку", "cant-remove-last-user": "Не можете уклонити последњег корисника", "cant-delete-chat-message": "Није вам дозвољено да избришете ову поруку", diff --git a/public/language/sr/modules.json b/public/language/sr/modules.json index b8ec9e3332..156f96ec4b 100644 --- a/public/language/sr/modules.json +++ b/public/language/sr/modules.json @@ -20,7 +20,7 @@ "chat.three_months": "3 месеца", "chat.delete_message_confirm": "Да ли сте сигурни да желите да избришете ову поруку?", "chat.add-users-to-room": "Додајте кориснике у собу", - "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", + "chat.confirm-chat-with-dnd-user": "Овај корисник је поставио свој статус на \"Не узнемиравај\". Да ли и даље желите да ћаскате са њим?", "composer.compose": "Писање поруке", "composer.show_preview": "Прикажи преглед", "composer.hide_preview": "Сакриј преглед", diff --git a/public/language/sr/user.json b/public/language/sr/user.json index af5e8f9291..3949589e7b 100644 --- a/public/language/sr/user.json +++ b/public/language/sr/user.json @@ -60,7 +60,7 @@ "username_taken_workaround": "Корисничко име које сте захтевали је већ заузето па смо је мало изменили. Сада сте знани као %1", "password_same_as_username": "Ваша лозинка је иста као ваше име, изаберите другу лозинку", "password_same_as_email": "Ваша лозинка је иста као ваша е-пошта, изаберите другу лозинку", - "weak_password": "Weak password.", + "weak_password": "Лозинка је слаба", "upload_picture": "Отпремање слике", "upload_a_picture": "Отпреми слику", "remove_uploaded_picture": "Уклоните отпремљену слику", diff --git a/public/language/tr/admin/settings/chat.json b/public/language/tr/admin/settings/chat.json index a695d56607..384c27fb12 100644 --- a/public/language/tr/admin/settings/chat.json +++ b/public/language/tr/admin/settings/chat.json @@ -4,6 +4,6 @@ "disable-editing": "Sohbet mesajlarını düzenlemeyi/silmeyi kapat", "disable-editing-help": "Administrators and global moderators are exempt from this restriction", "max-length": "Maksimum sohbet mesajı uzunluğu", - "max-room-size": "Maximum number of users in chat rooms", + "max-room-size": "Sohbet odalarındaki maksimum kullanıcı sayısı", "delay": "Time between chat messages in milliseconds" } \ No newline at end of file diff --git a/public/language/tr/notifications.json b/public/language/tr/notifications.json index dc0383981e..b4e317f224 100644 --- a/public/language/tr/notifications.json +++ b/public/language/tr/notifications.json @@ -17,7 +17,7 @@ "follows": "Takip ediyor", "upvote": "Artı Oy", "new-flags": "Yeni Bayrak", - "my-flags": "Flags assigned to me", + "my-flags": "Vekil olarak atandığım bayraklar", "bans": "Yasaklamalar", "new_message_from": "%1 size bir mesaj gönderdi", "upvoted_your_post_in": "%1 iletinizi beğendi. %2.", diff --git a/public/language/tr/user.json b/public/language/tr/user.json index 5733c0530f..f6ac9536a9 100644 --- a/public/language/tr/user.json +++ b/public/language/tr/user.json @@ -60,7 +60,7 @@ "username_taken_workaround": "İstediğiniz kullanıcı ismi zaten alınmış, bu yüzden biraz degiştirdik. Şimdiki kullanıcı isminiz %1", "password_same_as_username": "Parolanız kullanıcı adınız ile aynı, lütfen başka bir parola seçiniz.", "password_same_as_email": "Şifreniz mail adresiniz ile aynı lütfen başka bir şifre seçin.", - "weak_password": "Weak password.", + "weak_password": "Zayıf parola.", "upload_picture": "Resim Yükle", "upload_a_picture": "Bir Resim Yükle", "remove_uploaded_picture": "Yüklenmiş fotoğrafı kaldır", From 6c224c380029267451141ff919f1a67473881b68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 8 May 2017 12:09:25 -0400 Subject: [PATCH 294/316] fix cover:url logic --- src/controllers/accounts/helpers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/accounts/helpers.js b/src/controllers/accounts/helpers.js index df77bce21d..6086bf4cf8 100644 --- a/src/controllers/accounts/helpers.js +++ b/src/controllers/accounts/helpers.js @@ -149,7 +149,7 @@ helpers.getUserDataByUserSlug = function (userslug, callerUID, callback) { userData.birthday = validator.escape(String(userData.birthday || '')); userData.moderationNote = validator.escape(String(userData.moderationNote || '')); - userData['cover:url'] = (nconf.get('relative_path') + userData['cover:url']) || require('../../coverPhoto').getDefaultProfileCover(userData.uid); + userData['cover:url'] = userData['cover:url'] ? (nconf.get('relative_path') + userData['cover:url']) : require('../../coverPhoto').getDefaultProfileCover(userData.uid); userData['cover:position'] = validator.escape(String(userData['cover:position'] || '50% 50%')); userData['username:disableEdit'] = !userData.isAdmin && parseInt(meta.config['username:disableEdit'], 10) === 1; userData['email:disableEdit'] = !userData.isAdmin && parseInt(meta.config['email:disableEdit'], 10) === 1; From 7135d25a4aeb40ad1b5313c516ec766c1f8d1b63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 8 May 2017 12:19:22 -0400 Subject: [PATCH 295/316] closes #5654 --- src/database/redis/sorted/add.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/database/redis/sorted/add.js b/src/database/redis/sorted/add.js index 5a21639be4..b279f5ba76 100644 --- a/src/database/redis/sorted/add.js +++ b/src/database/redis/sorted/add.js @@ -3,6 +3,9 @@ module.exports = function (redisClient, module) { module.sortedSetAdd = function (key, score, value, callback) { callback = callback || function () {}; + if (!key) { + return setImmediate(callback); + } if (Array.isArray(score) && Array.isArray(value)) { return sortedSetAddMulti(key, score, value, callback); } @@ -39,7 +42,9 @@ module.exports = function (redisClient, module) { var multi = redisClient.multi(); for (var i = 0; i < keys.length; i += 1) { - multi.zadd(keys[i], score, value); + if (keys[i]) { + multi.zadd(keys[i], score, value); + } } multi.exec(function (err) { From 972e9a0303e49d17076fcaa3a358a904297981f6 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 8 May 2017 13:56:49 -0400 Subject: [PATCH 296/316] bump version for manual minor release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7e668c42ec..1564dbb3a8 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "nodebb", "license": "GPL-3.0", "description": "NodeBB Forum", - "version": "1.4.6", + "version": "1.5.0", "homepage": "http://www.nodebb.org", "repository": { "type": "git", From aa9b795b3b92e0ee75e4845253e336a6f78c2d05 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 8 May 2017 14:08:17 -0400 Subject: [PATCH 297/316] update theme versions for v1.5.0 release --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 1564dbb3a8..616ad63639 100644 --- a/package.json +++ b/package.json @@ -64,8 +64,8 @@ "nodebb-plugin-spam-be-gone": "0.4.13", "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "4.0.0", - "nodebb-theme-persona": "4.2.10", - "nodebb-theme-vanilla": "5.2.1", + "nodebb-theme-persona": "5.0.0", + "nodebb-theme-vanilla": "6.0.0", "nodebb-widget-essentials": "3.0.0", "nodemailer": "2.6.4", "nodemailer-sendmail-transport": "1.0.0", From b58eb99571a4e5f8a2e9a40c9366db84a4c172d0 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 8 May 2017 14:26:48 -0400 Subject: [PATCH 298/316] re-adding public/uploads to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index dcda0224fc..469b990863 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ pidfile /public/templates /public/sounds +/public/uploads # compiled files /public/stylesheet.css From aca07a0f202dbb96b7045bb32824c8510b3df805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 8 May 2017 14:41:49 -0400 Subject: [PATCH 299/316] fix typo --- src/views/admin/development/info.tpl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/views/admin/development/info.tpl b/src/views/admin/development/info.tpl index 4a6d9b90b3..f594e7a499 100644 --- a/src/views/admin/development/info.tpl +++ b/src/views/admin/development/info.tpl @@ -5,8 +5,8 @@
    - [[admin/development/info:nodes-responded, {nodeCount}, {timeOut}]] - + [[admin/development/info:nodes-responded, {nodeCount}, {timeout}]] + @@ -26,8 +26,8 @@ From 7ac031775a632e0ca89a6ceff20702b9650bb7c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 8 May 2017 14:55:29 -0400 Subject: [PATCH 300/316] locale test change --- test/locale-detect.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/locale-detect.js b/test/locale-detect.js index 84ea5af59c..1bace9fd75 100644 --- a/test/locale-detect.js +++ b/test/locale-detect.js @@ -4,10 +4,12 @@ var assert = require('assert'); var nconf = require('nconf'); var request = require('request'); +var db = require('./mocks/databasemock'); var meta = require('../src/meta'); describe('Language detection', function () { it('should detect the language for a guest', function (done) { + meta.config.autoDetectLang = 1; request(nconf.get('url') + '/api/config', { headers: { 'Accept-Language': 'de-DE,de;q=0.5', @@ -24,7 +26,6 @@ describe('Language detection', function () { it('should do nothing when disabled', function (done) { meta.configs.set('autoDetectLang', 0, function (err) { assert.ifError(err); - request(nconf.get('url') + '/api/config', { headers: { 'Accept-Language': 'de-DE,de;q=0.5', From d333e87b514262c62bf1890c228691663b50d99b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 8 May 2017 15:37:36 -0400 Subject: [PATCH 301/316] update some deps --- package.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 616ad63639..9633474e34 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "coveralls": "istanbul cover _mocha --report lcovonly -- -R dot && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage" }, "dependencies": { - "async": "^2.1.4", + "async": "2.4.0", "ace-builds": "^1.2.6", "autoprefixer": "^6.2.3", "bcryptjs": "~2.3.0", @@ -39,8 +39,8 @@ "express": "^4.14.0", "express-session": "^1.8.2", "express-useragent": "1.0.7", - "html-to-text": "2.1.3", - "ip": "1.1.3", + "html-to-text": "3.2.0", + "ip": "1.1.5", "jimp": "0.2.27", "jquery": "^3.1.0", "json-2-csv": "^2.0.22", @@ -50,7 +50,7 @@ "mime": "^1.3.4", "minimist": "^1.1.1", "mkdirp": "~0.5.0", - "mongodb": "2.2.25", + "mongodb": "2.2.26", "morgan": "^1.3.2", "mousetrap": "^1.5.3", "nconf": "~0.8.2", @@ -84,10 +84,10 @@ "semver": "^5.1.0", "serve-favicon": "^2.1.5", "sitemap": "^1.4.0", - "socket.io": "1.7.2", - "socket.io-client": "1.7.2", - "socket.io-redis": "3.1.0", - "socketio-wildcard": "~0.3.0", + "socket.io": "1.7.4", + "socket.io-client": "1.7.4", + "socket.io-redis": "4.0.0", + "socketio-wildcard": "0.4.0", "string": "^3.0.0", "templates.js": "0.3.10", "toobusy-js": "^0.5.1", From 84ff8bb24af59fa744a3aec4317eb7b5b6006887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 8 May 2017 16:03:32 -0400 Subject: [PATCH 302/316] up autoprefixer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9633474e34..06650cb598 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "dependencies": { "async": "2.4.0", "ace-builds": "^1.2.6", - "autoprefixer": "^6.2.3", + "autoprefixer": "7.0.1", "bcryptjs": "~2.3.0", "body-parser": "^1.9.0", "bootstrap": "^3.3.7", From 1f3840414cdc681bbee14c07d98bf82c7a05c035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 8 May 2017 16:12:21 -0400 Subject: [PATCH 303/316] up bcryptjs --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 06650cb598..04fb86f80e 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "async": "2.4.0", "ace-builds": "^1.2.6", "autoprefixer": "7.0.1", - "bcryptjs": "~2.3.0", + "bcryptjs": "2.4.3", "body-parser": "^1.9.0", "bootstrap": "^3.3.7", "chart.js": "^2.4.0", From ea5927047fdf065cd05ff63572f899b5fd76afa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 8 May 2017 16:18:36 -0400 Subject: [PATCH 304/316] up connect-redis --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 04fb86f80e..bb20a5291b 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "connect-flash": "^0.1.1", "connect-mongo": "1.3.2", "connect-multiparty": "^2.0.0", - "connect-redis": "~3.1.0", + "connect-redis": "3.3.0", "cookie-parser": "^1.3.3", "cron": "^1.0.5", "cropperjs": "^0.8.1", From dfbb488a0fec86dd8f13f102931df2ff1a82c10f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 8 May 2017 16:30:12 -0400 Subject: [PATCH 305/316] up postcss --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index bb20a5291b..671102982e 100644 --- a/package.json +++ b/package.json @@ -72,8 +72,8 @@ "nodemailer-smtp-transport": "^2.4.1", "passport": "^0.3.0", "passport-local": "1.0.0", - "postcss": "^5.0.13", - "postcss-clean": "^1.0.0", + "postcss": "6.0.1", + "postcss-clean": "1.0.2", "promise-polyfill": "^6.0.2", "prompt": "^1.0.0", "redis": "~2.6.2", From a43d6e69667b6a47d61c3bb0c595ccefe037356d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 8 May 2017 16:50:40 -0400 Subject: [PATCH 306/316] up redis --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 671102982e..2b980a0567 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "postcss-clean": "1.0.2", "promise-polyfill": "^6.0.2", "prompt": "^1.0.0", - "redis": "~2.6.2", + "redis": "2.7.1", "request": "2.81.0", "rimraf": "~2.5.0", "rss": "^1.0.0", From 5822aaa4338be7fce1c9bec803f0ec7ed29c83d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 8 May 2017 16:57:36 -0400 Subject: [PATCH 307/316] up rimraf --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2b980a0567..5ed6ce010a 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "prompt": "^1.0.0", "redis": "2.7.1", "request": "2.81.0", - "rimraf": "~2.5.0", + "rimraf": "2.6.1", "rss": "^1.0.0", "sanitize-html": "^1.13.0", "semver": "^5.1.0", From df7f357e535fe2dc2b65deba10b8920496ee832a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 8 May 2017 17:36:59 -0400 Subject: [PATCH 308/316] up validator --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5ed6ce010a..4b5cdb2640 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "uglify-js": "^2.6.0", "underscore": "^1.8.3", "underscore.deep": "^0.5.1", - "validator": "^6.1.0", + "validator": "7.0.0", "winston": "^2.1.0", "xml": "^1.0.1", "xregexp": "~3.1.0", From d49da8c85d84e7e7d2e60908abe79a9450237e6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 8 May 2017 17:46:26 -0400 Subject: [PATCH 309/316] up xregexp --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4b5cdb2640..36b7d2032f 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "validator": "7.0.0", "winston": "^2.1.0", "xml": "^1.0.1", - "xregexp": "~3.1.0", + "xregexp": "3.2.0", "zxcvbn": "^4.4.2" }, "devDependencies": { From f3deef4931a30d49b753e84d6fa2cc5c705188d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 8 May 2017 19:54:53 -0400 Subject: [PATCH 310/316] closes #5656 --- public/src/client/footer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/client/footer.js b/public/src/client/footer.js index dc10e84d1f..25444f0912 100644 --- a/public/src/client/footer.js +++ b/public/src/client/footer.js @@ -7,7 +7,7 @@ define('forum/footer', ['notifications', 'chat', 'components', 'translator'], fu translator.prepareDOM(); function updateUnreadTopicCount(url, count) { - $('#main-nav a[href="' + config.relative_path + url + '"] i') + $('a[href="' + config.relative_path + url + '"] i') .toggleClass('unread-count', count > 0) .attr('data-content', count > 99 ? '99+' : count); } From b18b0db5befc479cee067565d05cfea696feacc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 8 May 2017 20:36:43 -0400 Subject: [PATCH 311/316] notifications.js style --- src/notifications.js | 817 +++++++++++++++++++++---------------------- src/start.js | 2 +- 2 files changed, 409 insertions(+), 410 deletions(-) diff --git a/src/notifications.js b/src/notifications.js index 9034fc54c8..bb002238e6 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -15,500 +15,499 @@ var batch = require('./batch'); var plugins = require('./plugins'); var utils = require('./utils'); -(function (Notifications) { - Notifications.init = function () { - winston.verbose('[notifications.init] Registering jobs.'); - new cron('*/30 * * * *', Notifications.prune, null, true); - }; - - Notifications.get = function (nid, callback) { - Notifications.getMultiple([nid], function (err, notifications) { - callback(err, Array.isArray(notifications) && notifications.length ? notifications[0] : null); - }); - }; +var Notifications = module.exports; + +Notifications.startJobs = function () { + winston.verbose('[notifications.init] Registering jobs.'); + new cron('*/30 * * * *', Notifications.prune, null, true); +}; + +Notifications.get = function (nid, callback) { + Notifications.getMultiple([nid], function (err, notifications) { + callback(err, Array.isArray(notifications) && notifications.length ? notifications[0] : null); + }); +}; + +Notifications.getMultiple = function (nids, callback) { + if (!nids.length) { + return setImmediate(callback, null, []); + } + var keys = nids.map(function (nid) { + return 'notifications:' + nid; + }); + + var notifications; + + async.waterfall([ + function (next) { + db.getObjects(keys, next); + }, + function (_notifications, next) { + notifications = _notifications; + var userKeys = notifications.map(function (notification) { + return notification && notification.from; + }); - Notifications.getMultiple = function (nids, callback) { - if (!nids.length) { - return setImmediate(callback, null, []); - } - var keys = nids.map(function (nid) { - return 'notifications:' + nid; - }); + User.getUsersFields(userKeys, ['username', 'userslug', 'picture'], next); + }, + function (usersData, next) { + notifications.forEach(function (notification, index) { + if (notification) { + notification.datetimeISO = utils.toISOString(notification.datetime); - var notifications; + if (notification.bodyLong) { + notification.bodyLong = S(notification.bodyLong).escapeHTML().s; + } - async.waterfall([ - function (next) { - db.getObjects(keys, next); - }, - function (_notifications, next) { - notifications = _notifications; - var userKeys = notifications.map(function (notification) { - return notification && notification.from; - }); + notification.user = usersData[index]; + if (notification.user) { + notification.image = notification.user.picture || null; + if (notification.user.username === '[[global:guest]]') { + notification.bodyShort = notification.bodyShort.replace(/([\s\S]*?),[\s\S]*?,([\s\S]*?)/, '$1, [[global:guest]], $2'); + } + } else if (notification.image === 'brand:logo' || !notification.image) { + notification.image = meta.config['brand:logo'] || nconf.get('relative_path') + '/logo.png'; + } + } + }); - User.getUsersFields(userKeys, ['username', 'userslug', 'picture'], next); - }, - function (usersData, next) { - notifications.forEach(function (notification, index) { - if (notification) { - notification.datetimeISO = utils.toISOString(notification.datetime); + next(null, notifications); + }, + ], callback); +}; + +Notifications.filterExists = function (nids, callback) { + async.waterfall([ + function (next) { + db.isSortedSetMembers('notifications', nids, next); + }, + function (exists, next) { + nids = nids.filter(function (notifId, idx) { + return exists[idx]; + }); - if (notification.bodyLong) { - notification.bodyLong = S(notification.bodyLong).escapeHTML().s; - } + next(null, nids); + }, + ], callback); +}; - notification.user = usersData[index]; - if (notification.user) { - notification.image = notification.user.picture || null; - if (notification.user.username === '[[global:guest]]') { - notification.bodyShort = notification.bodyShort.replace(/([\s\S]*?),[\s\S]*?,([\s\S]*?)/, '$1, [[global:guest]], $2'); - } - } else if (notification.image === 'brand:logo' || !notification.image) { - notification.image = meta.config['brand:logo'] || nconf.get('relative_path') + '/logo.png'; - } - } - }); +Notifications.findRelated = function (mergeIds, set, callback) { + // A related notification is one in a zset that has the same mergeId + var _nids; - next(null, notifications); - }, - ], callback); - }; + async.waterfall([ + async.apply(db.getSortedSetRevRange, set, 0, -1), + function (nids, next) { + _nids = nids; - Notifications.filterExists = function (nids, callback) { - async.waterfall([ - function (next) { - db.isSortedSetMembers('notifications', nids, next); - }, - function (exists, next) { - nids = nids.filter(function (notifId, idx) { - return exists[idx]; - }); + var keys = nids.map(function (nid) { + return 'notifications:' + nid; + }); - next(null, nids); - }, - ], callback); - }; + db.getObjectsFields(keys, ['mergeId'], next); + }, + ], function (err, sets) { + if (err) { + return callback(err); + } - Notifications.findRelated = function (mergeIds, set, callback) { - // A related notification is one in a zset that has the same mergeId - var _nids; + sets = sets.map(function (set) { + return set.mergeId; + }); - async.waterfall([ - async.apply(db.getSortedSetRevRange, set, 0, -1), - function (nids, next) { - _nids = nids; + callback(null, _nids.filter(function (nid, idx) { + return mergeIds.indexOf(sets[idx]) !== -1; + })); + }); +}; - var keys = nids.map(function (nid) { - return 'notifications:' + nid; - }); +Notifications.create = function (data, callback) { + if (!data.nid) { + return callback(new Error('no-notification-id')); + } + data.importance = data.importance || 5; + db.getObject('notifications:' + data.nid, function (err, oldNotification) { + if (err) { + return callback(err); + } - db.getObjectsFields(keys, ['mergeId'], next); - }, - ], function (err, sets) { - if (err) { - return callback(err); + if (oldNotification) { + if (parseInt(oldNotification.pid, 10) === parseInt(data.pid, 10) && parseInt(oldNotification.importance, 10) > parseInt(data.importance, 10)) { + return callback(null, null); } + } - sets = sets.map(function (set) { - return set.mergeId; - }); - - callback(null, _nids.filter(function (nid, idx) { - return mergeIds.indexOf(sets[idx]) !== -1; - })); + var now = Date.now(); + data.datetime = now; + async.parallel([ + function (next) { + db.sortedSetAdd('notifications', now, data.nid, next); + }, + function (next) { + db.setObject('notifications:' + data.nid, data, next); + }, + ], function (err) { + callback(err, data); }); - }; + }); +}; - Notifications.create = function (data, callback) { - if (!data.nid) { - return callback(new Error('no-notification-id')); - } - data.importance = data.importance || 5; - db.getObject('notifications:' + data.nid, function (err, oldNotification) { - if (err) { - return callback(err); - } +Notifications.push = function (notification, uids, callback) { + callback = callback || function () {}; - if (oldNotification) { - if (parseInt(oldNotification.pid, 10) === parseInt(data.pid, 10) && parseInt(oldNotification.importance, 10) > parseInt(data.importance, 10)) { - return callback(null, null); - } - } + if (!notification || !notification.nid) { + return callback(); + } - var now = Date.now(); - data.datetime = now; - async.parallel([ - function (next) { - db.sortedSetAdd('notifications', now, data.nid, next); - }, - function (next) { - db.setObject('notifications:' + data.nid, data, next); - }, - ], function (err) { - callback(err, data); - }); + if (!Array.isArray(uids)) { + uids = [uids]; + } + + uids = uids.filter(function (uid, index, array) { + return parseInt(uid, 10) && array.indexOf(uid) === index; + }); + + if (!uids.length) { + return callback(); + } + + setTimeout(function () { + batch.processArray(uids, function (uids, next) { + pushToUids(uids, notification, next); + }, { interval: 1000 }, function (err) { + if (err) { + winston.error(err.stack); + } }); - }; + }, 1000); + + callback(); +}; + +function pushToUids(uids, notification, callback) { + var oneWeekAgo = Date.now() - 604800000; + var unreadKeys = []; + var readKeys = []; + + async.waterfall([ + function (next) { + plugins.fireHook('filter:notification.push', { notification: notification, uids: uids }, next); + }, + function (data, next) { + uids = data.uids; + notification = data.notification; + + uids.forEach(function (uid) { + unreadKeys.push('uid:' + uid + ':notifications:unread'); + readKeys.push('uid:' + uid + ':notifications:read'); + }); - Notifications.push = function (notification, uids, callback) { - callback = callback || function () {}; + db.sortedSetsAdd(unreadKeys, notification.datetime, notification.nid, next); + }, + function (next) { + db.sortedSetsRemove(readKeys, notification.nid, next); + }, + function (next) { + db.sortedSetsRemoveRangeByScore(unreadKeys, '-inf', oneWeekAgo, next); + }, + function (next) { + db.sortedSetsRemoveRangeByScore(readKeys, '-inf', oneWeekAgo, next); + }, + function (next) { + var websockets = require('./socket.io'); + if (websockets.server) { + uids.forEach(function (uid) { + websockets.in('uid_' + uid).emit('event:new_notification', notification); + }); + } - if (!notification || !notification.nid) { - return callback(); + plugins.fireHook('action:notification.pushed', { notification: notification, uids: uids }); + next(); + }, + ], callback); +} + +Notifications.pushGroup = function (notification, groupName, callback) { + callback = callback || function () {}; + groups.getMembers(groupName, 0, -1, function (err, members) { + if (err || !Array.isArray(members) || !members.length) { + return callback(err); } - if (!Array.isArray(uids)) { - uids = [uids]; + Notifications.push(notification, members, callback); + }); +}; + +Notifications.pushGroups = function (notification, groupNames, callback) { + callback = callback || function () {}; + groups.getMembersOfGroups(groupNames, function (err, groupMembers) { + if (err) { + return callback(err); } - uids = uids.filter(function (uid, index, array) { - return parseInt(uid, 10) && array.indexOf(uid) === index; - }); + var members = _.unique(_.flatten(groupMembers)); - if (!uids.length) { - return callback(); - } + Notifications.push(notification, members, callback); + }); +}; - setTimeout(function () { - batch.processArray(uids, function (uids, next) { - pushToUids(uids, notification, next); - }, { interval: 1000 }, function (err) { - if (err) { - winston.error(err.stack); - } - }); - }, 1000); +Notifications.rescind = function (nid, callback) { + callback = callback || function () {}; - callback(); - }; + async.parallel([ + async.apply(db.sortedSetRemove, 'notifications', nid), + async.apply(db.delete, 'notifications:' + nid), + ], function (err) { + if (err) { + winston.error('Encountered error rescinding notification (' + nid + '): ' + err.message); + } else { + winston.verbose('[notifications/rescind] Rescinded notification "' + nid + '"'); + } - function pushToUids(uids, notification, callback) { - var oneWeekAgo = Date.now() - 604800000; - var unreadKeys = []; - var readKeys = []; + callback(err, nid); + }); +}; - async.waterfall([ - function (next) { - plugins.fireHook('filter:notification.push', { notification: notification, uids: uids }, next); - }, - function (data, next) { - uids = data.uids; - notification = data.notification; +Notifications.markRead = function (nid, uid, callback) { + callback = callback || function () {}; + if (!parseInt(uid, 10) || !nid) { + return callback(); + } + Notifications.markReadMultiple([nid], uid, callback); +}; - uids.forEach(function (uid) { - unreadKeys.push('uid:' + uid + ':notifications:unread'); - readKeys.push('uid:' + uid + ':notifications:read'); - }); +Notifications.markUnread = function (nid, uid, callback) { + callback = callback || function () {}; + if (!parseInt(uid, 10) || !nid) { + return callback(); + } - db.sortedSetsAdd(unreadKeys, notification.datetime, notification.nid, next); - }, - function (next) { - db.sortedSetsRemove(readKeys, notification.nid, next); - }, - function (next) { - db.sortedSetsRemoveRangeByScore(unreadKeys, '-inf', oneWeekAgo, next); - }, - function (next) { - db.sortedSetsRemoveRangeByScore(readKeys, '-inf', oneWeekAgo, next); - }, - function (next) { - var websockets = require('./socket.io'); - if (websockets.server) { - uids.forEach(function (uid) { - websockets.in('uid_' + uid).emit('event:new_notification', notification); - }); - } + db.getObject('notifications:' + nid, function (err, notification) { + if (err || !notification) { + return callback(err || new Error('[[error:no-notification]]')); + } + notification.datetime = notification.datetime || Date.now(); - plugins.fireHook('action:notification.pushed', { notification: notification, uids: uids }); - next(); - }, + async.parallel([ + async.apply(db.sortedSetRemove, 'uid:' + uid + ':notifications:read', nid), + async.apply(db.sortedSetAdd, 'uid:' + uid + ':notifications:unread', notification.datetime, nid), ], callback); + }); +}; + +Notifications.markReadMultiple = function (nids, uid, callback) { + callback = callback || function () {}; + nids = nids.filter(Boolean); + if (!Array.isArray(nids) || !nids.length) { + return callback(); } - Notifications.pushGroup = function (notification, groupName, callback) { - callback = callback || function () {}; - groups.getMembers(groupName, 0, -1, function (err, members) { - if (err || !Array.isArray(members) || !members.length) { - return callback(err); - } + var notificationKeys = nids.map(function (nid) { + return 'notifications:' + nid; + }); - Notifications.push(notification, members, callback); - }); - }; + async.waterfall([ + async.apply(db.getObjectsFields, notificationKeys, ['mergeId']), + function (mergeIds, next) { + // Isolate mergeIds and find related notifications + mergeIds = mergeIds.map(function (set) { + return set.mergeId; + }).reduce(function (memo, mergeId, idx, arr) { + if (mergeId && idx === arr.indexOf(mergeId)) { + memo.push(mergeId); + } + return memo; + }, []); - Notifications.pushGroups = function (notification, groupNames, callback) { - callback = callback || function () {}; - groups.getMembersOfGroups(groupNames, function (err, groupMembers) { - if (err) { - return callback(err); - } + Notifications.findRelated(mergeIds, 'uid:' + uid + ':notifications:unread', next); + }, + function (relatedNids, next) { + notificationKeys = _.union(nids, relatedNids).map(function (nid) { + return 'notifications:' + nid; + }); + + db.getObjectsFields(notificationKeys, ['nid', 'datetime'], next); + }, + ], function (err, notificationData) { + if (err) { + return callback(err); + } - var members = _.unique(_.flatten(groupMembers)); + // Filter out notifications that didn't exist + notificationData = notificationData.filter(function (notification) { + return notification && notification.nid; + }); - Notifications.push(notification, members, callback); + // Extract nid + nids = notificationData.map(function (notification) { + return notification.nid; }); - }; - Notifications.rescind = function (nid, callback) { - callback = callback || function () {}; + var datetimes = notificationData.map(function (notification) { + return (notification && notification.datetime) || Date.now(); + }); async.parallel([ - async.apply(db.sortedSetRemove, 'notifications', nid), - async.apply(db.delete, 'notifications:' + nid), + function (next) { + db.sortedSetRemove('uid:' + uid + ':notifications:unread', nids, next); + }, + function (next) { + db.sortedSetAdd('uid:' + uid + ':notifications:read', datetimes, nids, next); + }, ], function (err) { - if (err) { - winston.error('Encountered error rescinding notification (' + nid + '): ' + err.message); - } else { - winston.verbose('[notifications/rescind] Rescinded notification "' + nid + '"'); - } - - callback(err, nid); + callback(err); }); - }; + }); +}; - Notifications.markRead = function (nid, uid, callback) { - callback = callback || function () {}; - if (!parseInt(uid, 10) || !nid) { - return callback(); +Notifications.markAllRead = function (uid, callback) { + db.getSortedSetRevRange('uid:' + uid + ':notifications:unread', 0, 99, function (err, nids) { + if (err) { + return callback(err); } - Notifications.markReadMultiple([nid], uid, callback); - }; - Notifications.markUnread = function (nid, uid, callback) { - callback = callback || function () {}; - if (!parseInt(uid, 10) || !nid) { + if (!Array.isArray(nids) || !nids.length) { return callback(); } - db.getObject('notifications:' + nid, function (err, notification) { - if (err || !notification) { - return callback(err || new Error('[[error:no-notification]]')); - } - notification.datetime = notification.datetime || Date.now(); + Notifications.markReadMultiple(nids, uid, callback); + }); +}; - async.parallel([ - async.apply(db.sortedSetRemove, 'uid:' + uid + ':notifications:read', nid), - async.apply(db.sortedSetAdd, 'uid:' + uid + ':notifications:unread', notification.datetime, nid), - ], callback); - }); - }; +Notifications.prune = function () { + var week = 604800000; + + var cutoffTime = Date.now() - week; + + db.getSortedSetRangeByScore('notifications', 0, 500, '-inf', cutoffTime, function (err, nids) { + if (err) { + return winston.error(err.message); + } - Notifications.markReadMultiple = function (nids, uid, callback) { - callback = callback || function () {}; - nids = nids.filter(Boolean); if (!Array.isArray(nids) || !nids.length) { - return callback(); + return; } - var notificationKeys = nids.map(function (nid) { + var keys = nids.map(function (nid) { return 'notifications:' + nid; }); - async.waterfall([ - async.apply(db.getObjectsFields, notificationKeys, ['mergeId']), - function (mergeIds, next) { - // Isolate mergeIds and find related notifications - mergeIds = mergeIds.map(function (set) { - return set.mergeId; - }).reduce(function (memo, mergeId, idx, arr) { - if (mergeId && idx === arr.indexOf(mergeId)) { - memo.push(mergeId); - } - return memo; - }, []); - - Notifications.findRelated(mergeIds, 'uid:' + uid + ':notifications:unread', next); + async.parallel([ + function (next) { + db.sortedSetRemove('notifications', nids, next); }, - function (relatedNids, next) { - notificationKeys = _.union(nids, relatedNids).map(function (nid) { - return 'notifications:' + nid; - }); - - db.getObjectsFields(notificationKeys, ['nid', 'datetime'], next); + function (next) { + db.deleteAll(keys, next); }, - ], function (err, notificationData) { + ], function (err) { if (err) { - return callback(err); + return winston.error('Encountered error pruning notifications: ' + err.message); } - - // Filter out notifications that didn't exist - notificationData = notificationData.filter(function (notification) { - return notification && notification.nid; - }); - - // Extract nid - nids = notificationData.map(function (notification) { - return notification.nid; - }); - - var datetimes = notificationData.map(function (notification) { - return (notification && notification.datetime) || Date.now(); - }); - - async.parallel([ - function (next) { - db.sortedSetRemove('uid:' + uid + ':notifications:unread', nids, next); - }, - function (next) { - db.sortedSetAdd('uid:' + uid + ':notifications:read', datetimes, nids, next); - }, - ], function (err) { - callback(err); - }); }); - }; - - Notifications.markAllRead = function (uid, callback) { - db.getSortedSetRevRange('uid:' + uid + ':notifications:unread', 0, 99, function (err, nids) { - if (err) { - return callback(err); + }); +}; + +Notifications.merge = function (notifications, callback) { + // When passed a set of notification objects, merge any that can be merged + var mergeIds = [ + 'notifications:upvoted_your_post_in', + 'notifications:user_started_following_you', + 'notifications:user_posted_to', + 'notifications:user_flagged_post_in', + 'notifications:user_flagged_user', + 'new_register', + ]; + var isolated; + var differentiators; + var differentiator; + var modifyIndex; + var set; + + notifications = mergeIds.reduce(function (notifications, mergeId) { + isolated = notifications.filter(function (notifObj) { + if (!notifObj || !notifObj.hasOwnProperty('mergeId')) { + return false; } - if (!Array.isArray(nids) || !nids.length) { - return callback(); - } - - Notifications.markReadMultiple(nids, uid, callback); + return notifObj.mergeId.split('|')[0] === mergeId; }); - }; - - Notifications.prune = function () { - var week = 604800000; - var cutoffTime = Date.now() - week; - - db.getSortedSetRangeByScore('notifications', 0, 500, '-inf', cutoffTime, function (err, nids) { - if (err) { - return winston.error(err.message); - } + if (isolated.length <= 1) { + return notifications; // Nothing to merge + } - if (!Array.isArray(nids) || !nids.length) { - return; + // Each isolated mergeId may have multiple differentiators, so process each separately + differentiators = isolated.reduce(function (cur, next) { + differentiator = next.mergeId.split('|')[1] || 0; + if (cur.indexOf(differentiator) === -1) { + cur.push(differentiator); } - var keys = nids.map(function (nid) { - return 'notifications:' + nid; - }); - - async.parallel([ - function (next) { - db.sortedSetRemove('notifications', nids, next); - }, - function (next) { - db.deleteAll(keys, next); - }, - ], function (err) { - if (err) { - return winston.error('Encountered error pruning notifications: ' + err.message); - } - }); - }); - }; - - Notifications.merge = function (notifications, callback) { - // When passed a set of notification objects, merge any that can be merged - var mergeIds = [ - 'notifications:upvoted_your_post_in', - 'notifications:user_started_following_you', - 'notifications:user_posted_to', - 'notifications:user_flagged_post_in', - 'notifications:user_flagged_user', - 'new_register', - ]; - var isolated; - var differentiators; - var differentiator; - var modifyIndex; - var set; - - notifications = mergeIds.reduce(function (notifications, mergeId) { - isolated = notifications.filter(function (notifObj) { - if (!notifObj || !notifObj.hasOwnProperty('mergeId')) { - return false; - } - - return notifObj.mergeId.split('|')[0] === mergeId; - }); + return cur; + }, []); - if (isolated.length <= 1) { - return notifications; // Nothing to merge + differentiators.forEach(function (differentiator) { + if (differentiator === 0 && differentiators.length === 1) { + set = isolated; + } else { + set = isolated.filter(function (notifObj) { + return notifObj.mergeId === (mergeId + '|' + differentiator); + }); } - // Each isolated mergeId may have multiple differentiators, so process each separately - differentiators = isolated.reduce(function (cur, next) { - differentiator = next.mergeId.split('|')[1] || 0; - if (cur.indexOf(differentiator) === -1) { - cur.push(differentiator); - } + modifyIndex = notifications.indexOf(set[0]); + if (modifyIndex === -1 || set.length === 1) { + return notifications; + } - return cur; - }, []); + switch (mergeId) { + // intentional fall-through + case 'notifications:upvoted_your_post_in': + case 'notifications:user_started_following_you': + case 'notifications:user_posted_to': + case 'notifications:user_flagged_post_in': + case 'notifications:user_flagged_user': + var usernames = set.map(function (notifObj) { + return notifObj && notifObj.user && notifObj.user.username; + }).filter(function (username, idx, array) { + return array.indexOf(username) === idx; + }); + var numUsers = usernames.length; - differentiators.forEach(function (differentiator) { - if (differentiator === 0 && differentiators.length === 1) { - set = isolated; - } else { - set = isolated.filter(function (notifObj) { - return notifObj.mergeId === (mergeId + '|' + differentiator); - }); - } + var title = S(notifications[modifyIndex].topicTitle || '').decodeHTMLEntities().s; + var titleEscaped = title.replace(/%/g, '%').replace(/,/g, ','); + titleEscaped = titleEscaped ? (', ' + titleEscaped) : ''; - modifyIndex = notifications.indexOf(set[0]); - if (modifyIndex === -1 || set.length === 1) { - return notifications; + if (numUsers === 2) { + notifications[modifyIndex].bodyShort = '[[' + mergeId + '_dual, ' + usernames.join(', ') + titleEscaped + ']]'; + } else if (numUsers > 2) { + notifications[modifyIndex].bodyShort = '[[' + mergeId + '_multiple, ' + usernames[0] + ', ' + (numUsers - 1) + titleEscaped + ']]'; } - switch (mergeId) { - // intentional fall-through - case 'notifications:upvoted_your_post_in': - case 'notifications:user_started_following_you': - case 'notifications:user_posted_to': - case 'notifications:user_flagged_post_in': - case 'notifications:user_flagged_user': - var usernames = set.map(function (notifObj) { - return notifObj && notifObj.user && notifObj.user.username; - }).filter(function (username, idx, array) { - return array.indexOf(username) === idx; - }); - var numUsers = usernames.length; - - var title = S(notifications[modifyIndex].topicTitle || '').decodeHTMLEntities().s; - var titleEscaped = title.replace(/%/g, '%').replace(/,/g, ','); - titleEscaped = titleEscaped ? (', ' + titleEscaped) : ''; - - if (numUsers === 2) { - notifications[modifyIndex].bodyShort = '[[' + mergeId + '_dual, ' + usernames.join(', ') + titleEscaped + ']]'; - } else if (numUsers > 2) { - notifications[modifyIndex].bodyShort = '[[' + mergeId + '_multiple, ' + usernames[0] + ', ' + (numUsers - 1) + titleEscaped + ']]'; - } + notifications[modifyIndex].path = set[set.length - 1].path; + break; - notifications[modifyIndex].path = set[set.length - 1].path; - break; + case 'new_register': + notifications[modifyIndex].bodyShort = '[[notifications:' + mergeId + '_multiple, ' + set.length + ']]'; + break; + } - case 'new_register': - notifications[modifyIndex].bodyShort = '[[notifications:' + mergeId + '_multiple, ' + set.length + ']]'; - break; + // Filter out duplicates + notifications = notifications.filter(function (notifObj, idx) { + if (!notifObj || !notifObj.mergeId) { + return true; } - // Filter out duplicates - notifications = notifications.filter(function (notifObj, idx) { - if (!notifObj || !notifObj.mergeId) { - return true; - } - - return !(notifObj.mergeId === (mergeId + (differentiator ? '|' + differentiator : '')) && idx !== modifyIndex); - }); + return !(notifObj.mergeId === (mergeId + (differentiator ? '|' + differentiator : '')) && idx !== modifyIndex); }); - - return notifications; - }, notifications); - - plugins.fireHook('filter:notifications.merge', { - notifications: notifications, - }, function (err, data) { - callback(err, data.notifications); }); - }; -}(exports)); + return notifications; + }, notifications); + + plugins.fireHook('filter:notifications.merge', { + notifications: notifications, + }, function (err, data) { + callback(err, data.notifications); + }); +}; diff --git a/src/start.js b/src/start.js index 2a6e53f507..8d4e465a55 100644 --- a/src/start.js +++ b/src/start.js @@ -48,7 +48,7 @@ start.start = function () { require('./socket.io').init(webserver.server); if (nconf.get('isPrimary') === 'true' && !nconf.get('jobsDisabled')) { - require('./notifications').init(); + require('./notifications').startJobs(); require('./user').startJobs(); } From 6fcd12eafe7cf5839d74835afb165720df05c027 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 8 May 2017 20:55:47 -0400 Subject: [PATCH 312/316] fix tests --- test/mocks/databasemock.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/mocks/databasemock.js b/test/mocks/databasemock.js index 412a255dc6..4432f944f1 100644 --- a/test/mocks/databasemock.js +++ b/test/mocks/databasemock.js @@ -146,7 +146,7 @@ var sockets = require('../../src/socket.io'); sockets.init(webserver.server); - require('../../src/notifications').init(); + require('../../src/notifications').startJobs(); require('../../src/user').startJobs(); webserver.listen(next); From db532fbb0d8cd237cec461f6498c68a34032a46b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 8 May 2017 21:23:30 -0400 Subject: [PATCH 313/316] fix user settings cache --- src/user/settings.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/user/settings.js b/src/user/settings.js index 9aa247bd79..de0da0641d 100644 --- a/src/user/settings.js +++ b/src/user/settings.js @@ -2,6 +2,7 @@ 'use strict'; var async = require('async'); +var _ = require('underscore'); var meta = require('../meta'); var db = require('../database'); @@ -25,7 +26,7 @@ module.exports = function (User) { var cached = cache.get('user:' + uid + ':settings'); if (cached) { - return onSettingsLoaded(uid, cached || {}, callback); + return onSettingsLoaded(uid, _.clone(cached || {}), callback); } async.waterfall([ @@ -36,7 +37,7 @@ module.exports = function (User) { settings = settings || {}; settings.uid = uid; cache.set('user:' + uid + ':settings', settings); - onSettingsLoaded(uid, settings || {}, next); + onSettingsLoaded(uid, _.clone(settings || {}), next); }, ], callback); }; @@ -47,7 +48,7 @@ module.exports = function (User) { return cache.get('user:' + uid + ':settings') || {}; }); async.map(settings, function (setting, next) { - onSettingsLoaded(setting.uid, setting, next); + onSettingsLoaded(setting.uid, _.clone(setting), next); }, next); } From 36a2ac0ebde280caacee761a6871b30877bab4f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 8 May 2017 21:28:06 -0400 Subject: [PATCH 314/316] add test for user settings cache issue --- test/user.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/user.js b/test/user.js index 662b82f326..943c348872 100644 --- a/test/user.js +++ b/test/user.js @@ -945,7 +945,11 @@ describe('User', function () { }; socketUser.saveSettings({ uid: testUid }, data, function (err) { assert.ifError(err); - done(); + User.getSettings(testUid, function (err, data) { + assert.ifError(err); + assert.equal(data.usePagination, true); + done(); + }); }); }); From cb339838c03666a45c8bf0a4de68c60785d920f6 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Tue, 9 May 2017 09:23:07 +0000 Subject: [PATCH 315/316] Latest translations and fallbacks --- public/language/cs/flags.json | 2 +- public/language/de/admin/manage/groups.json | 2 +- .../language/de/admin/settings/advanced.json | 2 +- public/language/de/email.json | 4 +- public/language/de/error.json | 4 +- public/language/de/flags.json | 100 ++++++++--------- public/language/de/modules.json | 2 +- public/language/de/user.json | 2 +- public/language/es/flags.json | 98 ++++++++--------- public/language/fr/flags.json | 98 ++++++++--------- public/language/gl/flags.json | 100 ++++++++--------- public/language/ja/flags.json | 102 +++++++++--------- public/language/pt-BR/flags.json | 98 ++++++++--------- public/language/pt-PT/flags.json | 72 ++++++------- public/language/ru/flags.json | 38 +++---- public/language/sk/flags.json | 4 +- public/language/sr/flags.json | 102 +++++++++--------- public/language/tr/flags.json | 102 +++++++++--------- public/language/zh-CN/flags.json | 102 +++++++++--------- public/language/zh-TW/flags.json | 4 +- 20 files changed, 519 insertions(+), 519 deletions(-) diff --git a/public/language/cs/flags.json b/public/language/cs/flags.json index c4bfed6f23..aea0e868e4 100644 --- a/public/language/cs/flags.json +++ b/public/language/cs/flags.json @@ -54,7 +54,7 @@ "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", "modal-reason-spam": "Spam", "modal-reason-offensive": "Offensive", - "modal-reason-custom": "Reason for reporting this content...", + "modal-reason-custom": "Důvod nahlášení tohoto obsahu…", "modal-submit": "Submit Report", "modal-submit-success": "Content has been flagged for moderation." } \ No newline at end of file diff --git a/public/language/de/admin/manage/groups.json b/public/language/de/admin/manage/groups.json index f6df5ca99e..fa7244a3e5 100644 --- a/public/language/de/admin/manage/groups.json +++ b/public/language/de/admin/manage/groups.json @@ -1,7 +1,7 @@ { "name": "Gruppenname", "description": "Gruppenbeschreibung", - "member-count": "Member Count", + "member-count": "Mitglieder Anzahl", "system": "System-Gruppe", "edit": "Ändern", "search-placeholder": "Suchen", diff --git a/public/language/de/admin/settings/advanced.json b/public/language/de/admin/settings/advanced.json index cca15102cc..87c5b2ca21 100644 --- a/public/language/de/admin/settings/advanced.json +++ b/public/language/de/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "ALLOW-FROM setzen um NodeBB in einem iFrame zu platzieren", "headers.powered-by": "Anpassen des \"Powered By\" Headers von NodeBB", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty", + "headers.acao-help": "Um den Zugriff zu allen Seiten zu verbieten, leer lassen.", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "Traffic Management", diff --git a/public/language/de/email.json b/public/language/de/email.json index 490001d405..1ade727750 100644 --- a/public/language/de/email.json +++ b/public/language/de/email.json @@ -32,8 +32,8 @@ "notif.post.unsub.info": "Diese Mitteilung wurde dir aufgrund deiner Abonnement-Einstellungen gesendet.", "test.text1": "Dies ist eine Test-E-Mail, um zu überprüfen, ob der E-Mailer deines NodeBB korrekt eingestellt wurde.", "unsub.cta": "Klicke hier, um diese Einstellungen zu ändern.", - "banned.subject": "You have been banned from %1", - "banned.text1": "The user %1 has been banned from %2.", + "banned.subject": "Du wurdest von %1 gebannt.", + "banned.text1": "Der Benutzer %1 wurde von %2 gebannt.", "banned.text2": "This ban will last until %1.", "banned.text3": "This is the reason why you have been banned:", "closing": "Danke!" diff --git a/public/language/de/error.json b/public/language/de/error.json index ea969f8284..51526a9041 100644 --- a/public/language/de/error.json +++ b/public/language/de/error.json @@ -30,7 +30,7 @@ "password-too-long": "Passwort ist zu lang", "user-banned": "Benutzer ist gesperrt", "user-banned-reason": "Entschuldige, dieses Konto wurde gebannt (Grund: %1)", - "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", + "user-banned-reason-until": "Entschuldigung, dieser Account wurde bis %1 (Reason: %2) gebannt.", "user-too-new": "Entschuldigung, du musst %1 Sekunde(n) warten, bevor du deinen ersten Beitrag schreiben kannst.", "blacklisted-ip": "Deine IP-Adresse ist für diese Plattform gesperrt. Sollte dies ein Irrtum sein, dann kontaktiere bitte einen Administrator.", "ban-expiry-missing": "Bitte gebe ein Enddatum für diesen Ban an", @@ -105,7 +105,7 @@ "chat-disabled": "Das Chatsystem deaktiviert", "too-many-messages": "Du hast zu viele Nachrichten versandt, bitte warte eine Weile.", "invalid-chat-message": "Ungültige Nachricht", - "chat-message-too-long": "Chat messages can not be longer than %1 characters.", + "chat-message-too-long": "Chat Nachricht darf nicht länger als %1 Zeichen sein.", "cant-edit-chat-message": "Du darfst diese Nachricht nicht ändern", "cant-remove-last-user": "Du kannst den letzten Benutzer nicht entfernen", "cant-delete-chat-message": "Du darfst diese Nachricht nicht löschen", diff --git a/public/language/de/flags.json b/public/language/de/flags.json index c4bfed6f23..974ba725ff 100644 --- a/public/language/de/flags.json +++ b/public/language/de/flags.json @@ -1,60 +1,60 @@ { - "state": "State", - "reporter": "Reporter", - "reported-at": "Reported At", - "description": "Description", - "no-flags": "Hooray! No flags found.", - "assignee": "Assignee", - "update": "Update", - "updated": "Updated", - "target-purged": "The content this flag referred to has been purged and is no longer available.", + "state": "Zustand", + "reporter": "Meldender", + "reported-at": "Gemeldet am", + "description": "Beschreibung", + "no-flags": "Hurra! Keine Meldungen gefunden.", + "assignee": "Zugeordneter Benutzer", + "update": "Aktualisieren", + "updated": "Aktualisiert", + "target-purged": "Der Inhalt auf den diese Meldung hingewiesen hat, wurde gelöscht und ist nicht mehr verfügbar.", - "quick-filters": "Quick Filters", - "filter-active": "There are one or more filters active in this list of flags", - "filter-reset": "Remove Filters", - "filters": "Filter Options", - "filter-reporterId": "Reporter UID", - "filter-targetUid": "Flagged UID", - "filter-type": "Flag Type", - "filter-type-all": "All Content", - "filter-type-post": "Post", - "filter-state": "State", - "filter-assignee": "Assignee UID", - "filter-cid": "Category", - "filter-quick-mine": "Assigned to me", - "filter-cid-all": "All categories", - "apply-filters": "Apply Filters", + "quick-filters": "Schnell-Filter", + "filter-active": "Ein oder mehrere Filter sind in dieser Meldungs-Liste aktiv", + "filter-reset": "Filter Entfernen", + "filters": "Filter Optionen", + "filter-reporterId": "Melder UID", + "filter-targetUid": "Gemeldete UID", + "filter-type": "Meldungstyp", + "filter-type-all": "Gesamter Inhalt", + "filter-type-post": "Beitrag", + "filter-state": "Status", + "filter-assignee": "UID des Zugewiesenen", + "filter-cid": "Kategorie", + "filter-quick-mine": "Mir zugewiesen", + "filter-cid-all": "Alle Kategorien", + "apply-filters": "Filter anwenden", - "quick-links": "Quick Links", - "flagged-user": "Flagged User", - "view-profile": "View Profile", - "start-new-chat": "Start New Chat", - "go-to-target": "View Flag Target", + "quick-links": "Schnellnavigation", + "flagged-user": "Gemeldeter Benutzer", + "view-profile": "Profil ansehen", + "start-new-chat": "Neuen Chat beginnen", + "go-to-target": "Meldungsziel ansehen", - "user-view": "View Profile", - "user-edit": "Edit Profile", + "user-view": "Profil ansehen", + "user-edit": "Profil bearbeiten", - "notes": "Flag Notes", - "add-note": "Add Note", - "no-notes": "No shared notes.", + "notes": "Meldungsnotizen", + "add-note": "Notiz hinzufügen", + "no-notes": "Keine geteilten Notizen", - "history": "Flag History", - "back": "Back to Flags List", - "no-history": "No flag history.", + "history": "Meldungsverlauf", + "back": "Zurück zur Meldungsliste", + "no-history": "Kein Meldungsverlauf", - "state-all": "All states", - "state-open": "New/Open", - "state-wip": "Work in Progress", - "state-resolved": "Resolved", - "state-rejected": "Rejected", - "no-assignee": "Not Assigned", - "note-added": "Note Added", + "state-all": "Alle Status", + "state-open": "Neu/Öffnen", + "state-wip": "In Arbeit", + "state-resolved": "Gelöst", + "state-rejected": "Abgelehnt", + "no-assignee": "Nicht zugewiesen", + "note-added": "Notiz hinzugefügt", - "modal-title": "Report Inappropriate Content", - "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-title": "Anstößige Inhalte Melden", + "modal-body": "Bitte geben Sie den Grund an, weshalb Sie %1 %2 melden wollen. Alternativ können Sie einen der Schnell-Meldungs-Knöpfe verwenden, wenn anwendbar.", "modal-reason-spam": "Spam", - "modal-reason-offensive": "Offensive", - "modal-reason-custom": "Reason for reporting this content...", - "modal-submit": "Submit Report", - "modal-submit-success": "Content has been flagged for moderation." + "modal-reason-offensive": "Beleidigend", + "modal-reason-custom": "Grund für die Meldung dieses Inhalts...", + "modal-submit": "Meldung abschicken", + "modal-submit-success": "Der Inhalt wurde gemeldet." } \ No newline at end of file diff --git a/public/language/de/modules.json b/public/language/de/modules.json index a1cf19cea0..bb53b0fb87 100644 --- a/public/language/de/modules.json +++ b/public/language/de/modules.json @@ -20,7 +20,7 @@ "chat.three_months": "3 Monate", "chat.delete_message_confirm": "Bist du sicher, dass du diese Nachricht löschen möchtest?", "chat.add-users-to-room": "Benutzer zum Raum hinzufügen", - "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", + "chat.confirm-chat-with-dnd-user": "Dieser Benutzer hat seinen Status auf DnD(Bitte nicht stören) gesetzt. Möchtest du noch immer mit ihm chatten?", "composer.compose": "Verfassen", "composer.show_preview": "Vorschau zeigen", "composer.hide_preview": "Vorschau ausblenden", diff --git a/public/language/de/user.json b/public/language/de/user.json index afcbb48c96..22dd586842 100644 --- a/public/language/de/user.json +++ b/public/language/de/user.json @@ -60,7 +60,7 @@ "username_taken_workaround": "Der gewünschte Benutzername ist bereits vergeben, deshalb haben wir ihn ein wenig verändert. Du bist jetzt unter dem Namen %1 bekannt.", "password_same_as_username": "Dein Passwort entspricht deinem Benutzernamen, bitte wähle ein anderes Passwort.", "password_same_as_email": "Dein Passwort entspricht deiner E-Mail-Adresse, bitte wähle ein anderes Passwort.", - "weak_password": "Weak password.", + "weak_password": "Schwaches Password.", "upload_picture": "Bild hochladen", "upload_a_picture": "Ein Bild hochladen", "remove_uploaded_picture": "Hochgeladenes Bild entfernen", diff --git a/public/language/es/flags.json b/public/language/es/flags.json index c4bfed6f23..012d3181b0 100644 --- a/public/language/es/flags.json +++ b/public/language/es/flags.json @@ -1,60 +1,60 @@ { - "state": "State", - "reporter": "Reporter", - "reported-at": "Reported At", - "description": "Description", - "no-flags": "Hooray! No flags found.", - "assignee": "Assignee", - "update": "Update", - "updated": "Updated", - "target-purged": "The content this flag referred to has been purged and is no longer available.", + "state": "Estado", + "reporter": "Reportador", + "reported-at": "Reportado en", + "description": "Descripción", + "no-flags": "Yeah! No se encontraron indicadores", + "assignee": "Asignado", + "update": "Actualizar", + "updated": "Actualizado", + "target-purged": "El contenido al que se refiere este indicador ha sido purgado y ya no está disponible.", - "quick-filters": "Quick Filters", - "filter-active": "There are one or more filters active in this list of flags", - "filter-reset": "Remove Filters", - "filters": "Filter Options", - "filter-reporterId": "Reporter UID", - "filter-targetUid": "Flagged UID", - "filter-type": "Flag Type", - "filter-type-all": "All Content", + "quick-filters": "Filtros rapidos", + "filter-active": "Hay uno o más filtros activos en esta lista de indicadores.", + "filter-reset": "Quitar filtros", + "filters": "Opciones de filtros", + "filter-reporterId": "UID del reportador", + "filter-targetUid": "Indicador UID", + "filter-type": "Tipo de indicador", + "filter-type-all": "Todo el contenido", "filter-type-post": "Post", - "filter-state": "State", - "filter-assignee": "Assignee UID", - "filter-cid": "Category", - "filter-quick-mine": "Assigned to me", - "filter-cid-all": "All categories", - "apply-filters": "Apply Filters", + "filter-state": "estado", + "filter-assignee": "UID asignado", + "filter-cid": "Categoria", + "filter-quick-mine": "Asignado a mí", + "filter-cid-all": "Todas las categorias", + "apply-filters": "Aplicar filtros", - "quick-links": "Quick Links", - "flagged-user": "Flagged User", - "view-profile": "View Profile", - "start-new-chat": "Start New Chat", - "go-to-target": "View Flag Target", + "quick-links": "Links rapidos", + "flagged-user": "Usuario marcado", + "view-profile": "Ver perfil", + "start-new-chat": "Empezar nuevo chat", + "go-to-target": "Ver objetivo marcado", - "user-view": "View Profile", - "user-edit": "Edit Profile", + "user-view": "Ver perfil", + "user-edit": "Editar perfil", - "notes": "Flag Notes", - "add-note": "Add Note", - "no-notes": "No shared notes.", + "notes": "Marcar notas", + "add-note": "Añadir nota", + "no-notes": "No hay notas compartidas", - "history": "Flag History", - "back": "Back to Flags List", - "no-history": "No flag history.", + "history": "Historico de marcadores", + "back": "Volver a la lista de marcadores", + "no-history": "No hay registro de marcadores", - "state-all": "All states", - "state-open": "New/Open", - "state-wip": "Work in Progress", - "state-resolved": "Resolved", - "state-rejected": "Rejected", - "no-assignee": "Not Assigned", - "note-added": "Note Added", + "state-all": "Todos los estados", + "state-open": "Nuevo/Abrir", + "state-wip": "Trabajo en proceso", + "state-resolved": "Resuelto", + "state-rejected": "Rechazado", + "no-assignee": "Sin asignar", + "note-added": "Nota añadida", - "modal-title": "Report Inappropriate Content", - "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", - "modal-reason-spam": "Spam", - "modal-reason-offensive": "Offensive", - "modal-reason-custom": "Reason for reporting this content...", - "modal-submit": "Submit Report", + "modal-title": "Reportar contenido inapropiado", + "modal-body": "Por favor especifica tu razón para marcar %1 %2 para revisar. Alternativamente, usa una de los botones de reporte rápido si corresponde.", + "modal-reason-spam": "Correo no deseado", + "modal-reason-offensive": "Ofensivo", + "modal-reason-custom": "Razón para reportar este contenido...", + "modal-submit": "Enviar reporte", "modal-submit-success": "Content has been flagged for moderation." } \ No newline at end of file diff --git a/public/language/fr/flags.json b/public/language/fr/flags.json index c4bfed6f23..c4d031cca4 100644 --- a/public/language/fr/flags.json +++ b/public/language/fr/flags.json @@ -1,60 +1,60 @@ { - "state": "State", - "reporter": "Reporter", - "reported-at": "Reported At", + "state": "Etat", + "reporter": "Rapporteur", + "reported-at": "Reporté à", "description": "Description", - "no-flags": "Hooray! No flags found.", - "assignee": "Assignee", - "update": "Update", - "updated": "Updated", - "target-purged": "The content this flag referred to has been purged and is no longer available.", + "no-flags": "Hourra ! Aucun signalement trouvé.", + "assignee": "Assigné", + "update": "Mise à jour", + "updated": "Mis à jour", + "target-purged": "Le contenu référencé par ce signalement a été supprimé et n'est plus accessible", - "quick-filters": "Quick Filters", - "filter-active": "There are one or more filters active in this list of flags", - "filter-reset": "Remove Filters", - "filters": "Filter Options", - "filter-reporterId": "Reporter UID", - "filter-targetUid": "Flagged UID", - "filter-type": "Flag Type", - "filter-type-all": "All Content", - "filter-type-post": "Post", - "filter-state": "State", - "filter-assignee": "Assignee UID", - "filter-cid": "Category", - "filter-quick-mine": "Assigned to me", - "filter-cid-all": "All categories", - "apply-filters": "Apply Filters", + "quick-filters": "Filtres rapides", + "filter-active": "Il y a un ou plusieurs filtres actifs dans cette liste de signalements", + "filter-reset": "Supprimer les filtres", + "filters": "Options de filtre", + "filter-reporterId": "UID du reporteur", + "filter-targetUid": "UID signalé", + "filter-type": "Type de signalement", + "filter-type-all": "Tout le contenu", + "filter-type-post": "Message", + "filter-state": "Etat", + "filter-assignee": "UID assigné", + "filter-cid": "Catégorie", + "filter-quick-mine": "Assigné à moi", + "filter-cid-all": "Toutes les catégories", + "apply-filters": "Appliquer les filtres", - "quick-links": "Quick Links", - "flagged-user": "Flagged User", - "view-profile": "View Profile", - "start-new-chat": "Start New Chat", - "go-to-target": "View Flag Target", + "quick-links": "Permaliens", + "flagged-user": "Utilisateurs signalés", + "view-profile": "Voir le profil", + "start-new-chat": "Démarrer un nouveau Chat", + "go-to-target": "Voir le signalement cible", - "user-view": "View Profile", - "user-edit": "Edit Profile", + "user-view": "Voir le profil", + "user-edit": "Éditer le profil", - "notes": "Flag Notes", - "add-note": "Add Note", - "no-notes": "No shared notes.", + "notes": "Notes de signalement", + "add-note": "Ajouter une note", + "no-notes": "aucune note partagée", - "history": "Flag History", - "back": "Back to Flags List", - "no-history": "No flag history.", + "history": "Historiques des signalements", + "back": "Revenir à la liste des signalements", + "no-history": "aucun historique de signalements", - "state-all": "All states", - "state-open": "New/Open", - "state-wip": "Work in Progress", - "state-resolved": "Resolved", - "state-rejected": "Rejected", - "no-assignee": "Not Assigned", - "note-added": "Note Added", + "state-all": "Tous les états", + "state-open": "Nouveau/Ouvert", + "state-wip": "En cours", + "state-resolved": "Résolu", + "state-rejected": "Rejeté", + "no-assignee": "Non assigné", + "note-added": "Note ajoutée", - "modal-title": "Report Inappropriate Content", - "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-title": "Signaler un contenu inapproprié", + "modal-body": "Veuillez spécifier votre raison de signaler %1 %2 pour une révision. Vous pouvez utiliser un des boutons de report rapide si c'est plus approprié", "modal-reason-spam": "Spam", - "modal-reason-offensive": "Offensive", - "modal-reason-custom": "Reason for reporting this content...", - "modal-submit": "Submit Report", - "modal-submit-success": "Content has been flagged for moderation." + "modal-reason-offensive": "Choquant", + "modal-reason-custom": "Motif du signalement...", + "modal-submit": "Soumettre le signalement", + "modal-submit-success": "Le contenu a été soumis pour examen." } \ No newline at end of file diff --git a/public/language/gl/flags.json b/public/language/gl/flags.json index c4bfed6f23..0fda38ff5a 100644 --- a/public/language/gl/flags.json +++ b/public/language/gl/flags.json @@ -1,60 +1,60 @@ { - "state": "State", - "reporter": "Reporter", - "reported-at": "Reported At", - "description": "Description", - "no-flags": "Hooray! No flags found.", - "assignee": "Assignee", - "update": "Update", - "updated": "Updated", - "target-purged": "The content this flag referred to has been purged and is no longer available.", + "state": "Estado", + "reporter": "Reportador", + "reported-at": "Reportado en", + "description": "Descripción", + "no-flags": "Un licorca, que non hai nada marcado para revisión.", + "assignee": "Encargado", + "update": "Actualizar", + "updated": "Actualizado", + "target-purged": "O contido marcado foi purgado e xa non está dispoñible", - "quick-filters": "Quick Filters", - "filter-active": "There are one or more filters active in this list of flags", - "filter-reset": "Remove Filters", - "filters": "Filter Options", - "filter-reporterId": "Reporter UID", - "filter-targetUid": "Flagged UID", - "filter-type": "Flag Type", - "filter-type-all": "All Content", - "filter-type-post": "Post", - "filter-state": "State", - "filter-assignee": "Assignee UID", - "filter-cid": "Category", - "filter-quick-mine": "Assigned to me", - "filter-cid-all": "All categories", - "apply-filters": "Apply Filters", + "quick-filters": "Filtros rápidos", + "filter-active": "Hai un ou máis filtros na lista de avisos", + "filter-reset": "Eliminar filtros", + "filters": "Filtrar opcións", + "filter-reporterId": "UID do reportador", + "filter-targetUid": "UID marcada", + "filter-type": "Tipo de aviso", + "filter-type-all": "Todo o contido", + "filter-type-post": "Publicar", + "filter-state": "Estado", + "filter-assignee": "UID do encargado", + "filter-cid": "Categoría", + "filter-quick-mine": "Asignado a min", + "filter-cid-all": "Tódalas categorías", + "apply-filters": "Aplicar filtros", - "quick-links": "Quick Links", - "flagged-user": "Flagged User", - "view-profile": "View Profile", - "start-new-chat": "Start New Chat", - "go-to-target": "View Flag Target", + "quick-links": "Ligazóns rápidas", + "flagged-user": "Usuario marcado", + "view-profile": "Ver perfil", + "start-new-chat": "Comezar novo chat", + "go-to-target": "Ver contido marcado", - "user-view": "View Profile", - "user-edit": "Edit Profile", + "user-view": "Ver perfil", + "user-edit": "Editar perfil", - "notes": "Flag Notes", - "add-note": "Add Note", - "no-notes": "No shared notes.", + "notes": "Notas do aviso", + "add-note": "Engadir nota", + "no-notes": "Ningunha nota foi compartida", - "history": "Flag History", - "back": "Back to Flags List", - "no-history": "No flag history.", + "history": "Historial de avisos", + "back": "Voltar á lista de avisos", + "no-history": "Non hai historial de avisos", - "state-all": "All states", - "state-open": "New/Open", - "state-wip": "Work in Progress", - "state-resolved": "Resolved", - "state-rejected": "Rejected", - "no-assignee": "Not Assigned", - "note-added": "Note Added", + "state-all": "Tódolos estados", + "state-open": "Novo/Abrir", + "state-wip": "Traballo en progreso", + "state-resolved": "Resolto", + "state-rejected": "Rexeitado", + "no-assignee": "Non asignado", + "note-added": "Nota engadida", - "modal-title": "Report Inappropriate Content", - "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-title": "Reportar Contido Inapropiado", + "modal-body": "Por favor, especifique o seu motivo para marcar %1 %2 para revisión. Alternativamente, empregue un dos botóns de reporte rápido se fose pertinente.", "modal-reason-spam": "Spam", - "modal-reason-offensive": "Offensive", - "modal-reason-custom": "Reason for reporting this content...", - "modal-submit": "Submit Report", - "modal-submit-success": "Content has been flagged for moderation." + "modal-reason-offensive": "Ofensivo", + "modal-reason-custom": "Motivo para reportar este contido...", + "modal-submit": "Enviar Reporte", + "modal-submit-success": "Contido marcado para moderación" } \ No newline at end of file diff --git a/public/language/ja/flags.json b/public/language/ja/flags.json index c4bfed6f23..90a2d96569 100644 --- a/public/language/ja/flags.json +++ b/public/language/ja/flags.json @@ -1,60 +1,60 @@ { - "state": "State", - "reporter": "Reporter", - "reported-at": "Reported At", - "description": "Description", - "no-flags": "Hooray! No flags found.", - "assignee": "Assignee", - "update": "Update", - "updated": "Updated", - "target-purged": "The content this flag referred to has been purged and is no longer available.", + "state": "状態", + "reporter": "報告者", + "reported-at": "報告された", + "description": "説明", + "no-flags": "おめでとう!フラグは見つかりませんでした。", + "assignee": "譲受人", + "update": "更新", + "updated": "更新されました", + "target-purged": "このフラグが参照しているコンテンツは切り離されており、利用できません。", - "quick-filters": "Quick Filters", - "filter-active": "There are one or more filters active in this list of flags", - "filter-reset": "Remove Filters", - "filters": "Filter Options", - "filter-reporterId": "Reporter UID", - "filter-targetUid": "Flagged UID", - "filter-type": "Flag Type", - "filter-type-all": "All Content", - "filter-type-post": "Post", - "filter-state": "State", - "filter-assignee": "Assignee UID", - "filter-cid": "Category", - "filter-quick-mine": "Assigned to me", - "filter-cid-all": "All categories", - "apply-filters": "Apply Filters", + "quick-filters": "クイックフィルター", + "filter-active": "このフラグのリストには1つまたは複数のフィルタが有効です。", + "filter-reset": "フィルターを削除", + "filters": "フィルターオプション", + "filter-reporterId": "報告者のユーザーID", + "filter-targetUid": "フラグを立てたユーザーID", + "filter-type": "フラグの種類", + "filter-type-all": "すべてのコンテンツ", + "filter-type-post": "投稿", + "filter-state": "状態", + "filter-assignee": "譲受人のユーザーID", + "filter-cid": "カテゴリ", + "filter-quick-mine": "私に割り当てられました", + "filter-cid-all": "全てのカテゴリ", + "apply-filters": "フィルターを追加", - "quick-links": "Quick Links", - "flagged-user": "Flagged User", - "view-profile": "View Profile", - "start-new-chat": "Start New Chat", - "go-to-target": "View Flag Target", + "quick-links": "クイックリンク", + "flagged-user": "フラグを立てたユーザー", + "view-profile": "プロフィールを見る", + "start-new-chat": "新しいチャットを開始", + "go-to-target": "フラグのターゲットを表示", - "user-view": "View Profile", - "user-edit": "Edit Profile", + "user-view": "プロフィールを見る", + "user-edit": "プロフィールを編集", - "notes": "Flag Notes", - "add-note": "Add Note", - "no-notes": "No shared notes.", + "notes": "ノートにフラグをつける", + "add-note": "ノートを追加", + "no-notes": "共有ノートはありません。", - "history": "Flag History", - "back": "Back to Flags List", - "no-history": "No flag history.", + "history": "フラグ履歴", + "back": "フラグリストに戻る", + "no-history": "フラグ履歴がありません", - "state-all": "All states", - "state-open": "New/Open", - "state-wip": "Work in Progress", - "state-resolved": "Resolved", - "state-rejected": "Rejected", - "no-assignee": "Not Assigned", - "note-added": "Note Added", + "state-all": "全ての状態", + "state-open": "新規/開く", + "state-wip": "進行中の作業", + "state-resolved": "解決済み", + "state-rejected": "拒否済", + "no-assignee": "割り当てられていない", + "note-added": "ノートが追加されました", - "modal-title": "Report Inappropriate Content", - "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", - "modal-reason-spam": "Spam", - "modal-reason-offensive": "Offensive", - "modal-reason-custom": "Reason for reporting this content...", - "modal-submit": "Submit Report", - "modal-submit-success": "Content has been flagged for moderation." + "modal-title": "不適切なコンテンツを報告する", + "modal-body": "レビューのために%1 %2 にフラグを付ける理由を指定してください。または必要に応じてクイックレポートボタンの1つを使用します。", + "modal-reason-spam": "スパム", + "modal-reason-offensive": "攻撃", + "modal-reason-custom": "このコンテンツを報告する理由...", + "modal-submit": "レポートを提出", + "modal-submit-success": "コンテンツはモデレーションにフラグ付けされています。" } \ No newline at end of file diff --git a/public/language/pt-BR/flags.json b/public/language/pt-BR/flags.json index c4bfed6f23..802b042fa3 100644 --- a/public/language/pt-BR/flags.json +++ b/public/language/pt-BR/flags.json @@ -1,60 +1,60 @@ { - "state": "State", - "reporter": "Reporter", - "reported-at": "Reported At", - "description": "Description", - "no-flags": "Hooray! No flags found.", - "assignee": "Assignee", - "update": "Update", - "updated": "Updated", - "target-purged": "The content this flag referred to has been purged and is no longer available.", + "state": "Estado", + "reporter": "Reportado por", + "reported-at": "Reportado Em", + "description": "Descrição", + "no-flags": "Ihuul! Nenhuma sinalização encontrada.", + "assignee": "Cessionário", + "update": "Atualizar", + "updated": "Atualizado", + "target-purged": "O conteúdo ao qual essa sinalização se referia foi removido e não está mais disponível.", - "quick-filters": "Quick Filters", - "filter-active": "There are one or more filters active in this list of flags", - "filter-reset": "Remove Filters", - "filters": "Filter Options", - "filter-reporterId": "Reporter UID", - "filter-targetUid": "Flagged UID", - "filter-type": "Flag Type", - "filter-type-all": "All Content", + "quick-filters": "Filtros Rápidos", + "filter-active": "Há um ou mais filtros ativos nesta lista de sinalizações", + "filter-reset": "Remover Filtros", + "filters": "Opções de Filtro", + "filter-reporterId": "UID do Reportador", + "filter-targetUid": "UID Sinalizado", + "filter-type": "Tipo de Sinalização", + "filter-type-all": "Todo o Conteúdo", "filter-type-post": "Post", - "filter-state": "State", - "filter-assignee": "Assignee UID", - "filter-cid": "Category", - "filter-quick-mine": "Assigned to me", - "filter-cid-all": "All categories", - "apply-filters": "Apply Filters", + "filter-state": "Estado", + "filter-assignee": "UID do Cessionário", + "filter-cid": "Categoria", + "filter-quick-mine": "Procurado à mim", + "filter-cid-all": "Todas as categorias", + "apply-filters": "Aplicar Filtros", - "quick-links": "Quick Links", - "flagged-user": "Flagged User", - "view-profile": "View Profile", - "start-new-chat": "Start New Chat", - "go-to-target": "View Flag Target", + "quick-links": "Links Rápidos", + "flagged-user": "Usuário Sinalizado", + "view-profile": "Ver Perfil", + "start-new-chat": "Iniciar Novo Chat", + "go-to-target": "Ver Sinalizado", - "user-view": "View Profile", - "user-edit": "Edit Profile", + "user-view": "Ver Perfil", + "user-edit": "Editar Perfil", - "notes": "Flag Notes", - "add-note": "Add Note", - "no-notes": "No shared notes.", + "notes": "Notas da Sinalização", + "add-note": "Adicionar Nota", + "no-notes": "Nenhuma nota compartilhada.", - "history": "Flag History", - "back": "Back to Flags List", - "no-history": "No flag history.", + "history": "Histórico de Sinalizações", + "back": "Voltar à Lista de Sinaliações", + "no-history": "Sem histórico de sinalizações.", - "state-all": "All states", - "state-open": "New/Open", - "state-wip": "Work in Progress", - "state-resolved": "Resolved", - "state-rejected": "Rejected", - "no-assignee": "Not Assigned", - "note-added": "Note Added", + "state-all": "Todos os estados", + "state-open": "Novo/Aberto", + "state-wip": "Trabalho em Progresso", + "state-resolved": "Resolvido", + "state-rejected": "Rejeitado", + "no-assignee": "Não Procurado", + "note-added": "Nota Adicionada", - "modal-title": "Report Inappropriate Content", - "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", + "modal-title": "Reportar Conteúdo Inadequado", + "modal-body": "Por favor especifique sua razão para sinalizar %1 %2 para revisão. Alternativamente, use um dos botões de reporte rápido se for aplicável.", "modal-reason-spam": "Spam", - "modal-reason-offensive": "Offensive", - "modal-reason-custom": "Reason for reporting this content...", - "modal-submit": "Submit Report", - "modal-submit-success": "Content has been flagged for moderation." + "modal-reason-offensive": "Ofensivo", + "modal-reason-custom": "Motivo para reportar este conteúdo...", + "modal-submit": "Enviar Reportagem", + "modal-submit-success": "O conteúdo foi sinalizado para moderação." } \ No newline at end of file diff --git a/public/language/pt-PT/flags.json b/public/language/pt-PT/flags.json index c4bfed6f23..8d1d58cbf4 100644 --- a/public/language/pt-PT/flags.json +++ b/public/language/pt-PT/flags.json @@ -2,59 +2,59 @@ "state": "State", "reporter": "Reporter", "reported-at": "Reported At", - "description": "Description", + "description": "Descrição", "no-flags": "Hooray! No flags found.", "assignee": "Assignee", - "update": "Update", - "updated": "Updated", + "update": "Atualizar", + "updated": "Atualizado", "target-purged": "The content this flag referred to has been purged and is no longer available.", - "quick-filters": "Quick Filters", + "quick-filters": "Filtros Rápidos", "filter-active": "There are one or more filters active in this list of flags", - "filter-reset": "Remove Filters", - "filters": "Filter Options", + "filter-reset": "Remover Filtros", + "filters": "Opções dos Filtros", "filter-reporterId": "Reporter UID", "filter-targetUid": "Flagged UID", "filter-type": "Flag Type", - "filter-type-all": "All Content", - "filter-type-post": "Post", + "filter-type-all": "Todo o Conteúdo", + "filter-type-post": "Publicação", "filter-state": "State", "filter-assignee": "Assignee UID", - "filter-cid": "Category", - "filter-quick-mine": "Assigned to me", - "filter-cid-all": "All categories", - "apply-filters": "Apply Filters", + "filter-cid": "Categoria", + "filter-quick-mine": "Atribuído a mim", + "filter-cid-all": "Todas as categorias", + "apply-filters": "Aplicar Filtros", - "quick-links": "Quick Links", - "flagged-user": "Flagged User", - "view-profile": "View Profile", - "start-new-chat": "Start New Chat", - "go-to-target": "View Flag Target", + "quick-links": "Links Rápidos", + "flagged-user": "Utilizador Sinalizado", + "view-profile": "Ver Perfil", + "start-new-chat": "Iniciar Nova Conversa", + "go-to-target": "Ver Alvo da Sinalização", - "user-view": "View Profile", - "user-edit": "Edit Profile", + "user-view": "Ver Perfil", + "user-edit": "Editar Perfil", - "notes": "Flag Notes", - "add-note": "Add Note", - "no-notes": "No shared notes.", + "notes": "Sinalizar Notas", + "add-note": "Adicionar Nota", + "no-notes": "Não existem notas partilhadas.", - "history": "Flag History", - "back": "Back to Flags List", - "no-history": "No flag history.", + "history": "Histórico de Sinalizações", + "back": "Voltar para a Lista de Sinalizações", + "no-history": "Não existe histórico de sinalizações.", - "state-all": "All states", - "state-open": "New/Open", - "state-wip": "Work in Progress", - "state-resolved": "Resolved", - "state-rejected": "Rejected", - "no-assignee": "Not Assigned", - "note-added": "Note Added", + "state-all": "Todos os estados", + "state-open": "Novo/Abrir", + "state-wip": "Trabalho em Progresso", + "state-resolved": "Resolvido", + "state-rejected": "Rejeitado", + "no-assignee": "Não Atribuído", + "note-added": "Nota Adicionada.", "modal-title": "Report Inappropriate Content", "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", "modal-reason-spam": "Spam", - "modal-reason-offensive": "Offensive", - "modal-reason-custom": "Reason for reporting this content...", - "modal-submit": "Submit Report", - "modal-submit-success": "Content has been flagged for moderation." + "modal-reason-offensive": "Ofensivo", + "modal-reason-custom": "Motivo para reportar este conteúdo...", + "modal-submit": "Submeter Relatório", + "modal-submit-success": "Conteúdo sinalizado para moderação." } \ No newline at end of file diff --git a/public/language/ru/flags.json b/public/language/ru/flags.json index c4bfed6f23..6a5776a4f2 100644 --- a/public/language/ru/flags.json +++ b/public/language/ru/flags.json @@ -2,59 +2,59 @@ "state": "State", "reporter": "Reporter", "reported-at": "Reported At", - "description": "Description", + "description": "Описание", "no-flags": "Hooray! No flags found.", "assignee": "Assignee", - "update": "Update", - "updated": "Updated", + "update": "Обновить", + "updated": "Обновлено", "target-purged": "The content this flag referred to has been purged and is no longer available.", "quick-filters": "Quick Filters", "filter-active": "There are one or more filters active in this list of flags", - "filter-reset": "Remove Filters", + "filter-reset": "Убрать фильтры", "filters": "Filter Options", "filter-reporterId": "Reporter UID", "filter-targetUid": "Flagged UID", "filter-type": "Flag Type", - "filter-type-all": "All Content", + "filter-type-all": "Весь контент", "filter-type-post": "Post", "filter-state": "State", "filter-assignee": "Assignee UID", - "filter-cid": "Category", + "filter-cid": "Категория", "filter-quick-mine": "Assigned to me", - "filter-cid-all": "All categories", + "filter-cid-all": "Все категории", "apply-filters": "Apply Filters", "quick-links": "Quick Links", "flagged-user": "Flagged User", - "view-profile": "View Profile", - "start-new-chat": "Start New Chat", + "view-profile": "Просмотреть профиль", + "start-new-chat": "Начать новый чат", "go-to-target": "View Flag Target", - "user-view": "View Profile", - "user-edit": "Edit Profile", + "user-view": "Просмотреть профиль", + "user-edit": "Изменить Профиль", "notes": "Flag Notes", - "add-note": "Add Note", + "add-note": "Добавить примечание", "no-notes": "No shared notes.", "history": "Flag History", "back": "Back to Flags List", "no-history": "No flag history.", - "state-all": "All states", - "state-open": "New/Open", + "state-all": "Все государства", + "state-open": "Новый/Открыть", "state-wip": "Work in Progress", - "state-resolved": "Resolved", - "state-rejected": "Rejected", - "no-assignee": "Not Assigned", + "state-resolved": "Решен", + "state-rejected": "Отклонен", + "no-assignee": "Не назначенный ", "note-added": "Note Added", "modal-title": "Report Inappropriate Content", "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", - "modal-reason-spam": "Spam", + "modal-reason-spam": "Спам", "modal-reason-offensive": "Offensive", "modal-reason-custom": "Reason for reporting this content...", - "modal-submit": "Submit Report", + "modal-submit": "Представить отчет", "modal-submit-success": "Content has been flagged for moderation." } \ No newline at end of file diff --git a/public/language/sk/flags.json b/public/language/sk/flags.json index c4bfed6f23..21bdc1bf31 100644 --- a/public/language/sk/flags.json +++ b/public/language/sk/flags.json @@ -3,7 +3,7 @@ "reporter": "Reporter", "reported-at": "Reported At", "description": "Description", - "no-flags": "Hooray! No flags found.", + "no-flags": "Hurá! Žiadne označenia neboli nájdené.", "assignee": "Assignee", "update": "Update", "updated": "Updated", @@ -26,7 +26,7 @@ "apply-filters": "Apply Filters", "quick-links": "Quick Links", - "flagged-user": "Flagged User", + "flagged-user": "Označený používateľ", "view-profile": "View Profile", "start-new-chat": "Start New Chat", "go-to-target": "View Flag Target", diff --git a/public/language/sr/flags.json b/public/language/sr/flags.json index c4bfed6f23..e60225af70 100644 --- a/public/language/sr/flags.json +++ b/public/language/sr/flags.json @@ -1,60 +1,60 @@ { - "state": "State", - "reporter": "Reporter", - "reported-at": "Reported At", - "description": "Description", - "no-flags": "Hooray! No flags found.", - "assignee": "Assignee", - "update": "Update", - "updated": "Updated", - "target-purged": "The content this flag referred to has been purged and is no longer available.", + "state": "Стање", + "reporter": "Извештач", + "reported-at": "Пријављено", + "description": "Опис", + "no-flags": "Ура! Нема заставица.", + "assignee": "Заступник", + "update": "Ажурирај", + "updated": "Ажурирано", + "target-purged": "Садржај на који се односи ова заставица је очишћен и није више доступан.", - "quick-filters": "Quick Filters", - "filter-active": "There are one or more filters active in this list of flags", - "filter-reset": "Remove Filters", - "filters": "Filter Options", - "filter-reporterId": "Reporter UID", - "filter-targetUid": "Flagged UID", - "filter-type": "Flag Type", - "filter-type-all": "All Content", - "filter-type-post": "Post", - "filter-state": "State", - "filter-assignee": "Assignee UID", - "filter-cid": "Category", - "filter-quick-mine": "Assigned to me", - "filter-cid-all": "All categories", - "apply-filters": "Apply Filters", + "quick-filters": "Брзи филтери", + "filter-active": "Постоји један или више активних филтера на овом списку заставица", + "filter-reset": "Уклони заставице", + "filters": "Опције филтера", + "filter-reporterId": "UID извештача", + "filter-targetUid": "UID означеног", + "filter-type": "Тип заставице", + "filter-type-all": "Сав садржај", + "filter-type-post": "Порука", + "filter-state": "Стање", + "filter-assignee": "UID заступника", + "filter-cid": "Категорија", + "filter-quick-mine": "Додељено мени", + "filter-cid-all": "Све категорије", + "apply-filters": "Примени филтере", - "quick-links": "Quick Links", - "flagged-user": "Flagged User", - "view-profile": "View Profile", - "start-new-chat": "Start New Chat", - "go-to-target": "View Flag Target", + "quick-links": "Брзе везе", + "flagged-user": "Означени корисник", + "view-profile": "Погледај профил", + "start-new-chat": "Започни ново ћаскање", + "go-to-target": "Погледај циљ означавања", - "user-view": "View Profile", - "user-edit": "Edit Profile", + "user-view": "Погледај профил", + "user-edit": "Уреди профил", - "notes": "Flag Notes", - "add-note": "Add Note", - "no-notes": "No shared notes.", + "notes": "Белешке о заставицама", + "add-note": "Додај белешку", + "no-notes": "Нема дељених бележака.", - "history": "Flag History", - "back": "Back to Flags List", - "no-history": "No flag history.", + "history": "Историја заставица", + "back": "Назад на списак заставица", + "no-history": "Нема историје заставица", - "state-all": "All states", - "state-open": "New/Open", - "state-wip": "Work in Progress", - "state-resolved": "Resolved", - "state-rejected": "Rejected", - "no-assignee": "Not Assigned", - "note-added": "Note Added", + "state-all": "Сва стања", + "state-open": "Ново/Отвори", + "state-wip": "Рад у току", + "state-resolved": "Решено", + "state-rejected": "Одбијено", + "no-assignee": "Недодељено", + "note-added": "Белешка је додата", - "modal-title": "Report Inappropriate Content", - "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", - "modal-reason-spam": "Spam", - "modal-reason-offensive": "Offensive", - "modal-reason-custom": "Reason for reporting this content...", - "modal-submit": "Submit Report", - "modal-submit-success": "Content has been flagged for moderation." + "modal-title": "Пријави неприкладан садржај", + "modal-body": "Наведите разлог за означавање %1 %2 за проверу. Алтернативно, користите један од тастера за брзу пријаву ко је применљиво.", + "modal-reason-spam": "Непожељно", + "modal-reason-offensive": "Увредљиво", + "modal-reason-custom": "Разлог за пријаву овог садржаја...", + "modal-submit": "Пошаљи извештај", + "modal-submit-success": "Садржај је означен за модерацију." } \ No newline at end of file diff --git a/public/language/tr/flags.json b/public/language/tr/flags.json index c4bfed6f23..612ccb6500 100644 --- a/public/language/tr/flags.json +++ b/public/language/tr/flags.json @@ -1,60 +1,60 @@ { - "state": "State", - "reporter": "Reporter", - "reported-at": "Reported At", - "description": "Description", - "no-flags": "Hooray! No flags found.", - "assignee": "Assignee", - "update": "Update", - "updated": "Updated", - "target-purged": "The content this flag referred to has been purged and is no longer available.", + "state": "Bildiri", + "reporter": "Muhabir", + "reported-at": "Bildirildi", + "description": "Açıklama", + "no-flags": "Yaşasın! Bayrak bulunamadı.", + "assignee": "Vekil", + "update": "Güncelle", + "updated": "Güncellendi", + "target-purged": "İlgili bayrağın içeriği temizlendi ve artık mevcut değil.", - "quick-filters": "Quick Filters", - "filter-active": "There are one or more filters active in this list of flags", - "filter-reset": "Remove Filters", - "filters": "Filter Options", - "filter-reporterId": "Reporter UID", - "filter-targetUid": "Flagged UID", - "filter-type": "Flag Type", - "filter-type-all": "All Content", - "filter-type-post": "Post", - "filter-state": "State", - "filter-assignee": "Assignee UID", - "filter-cid": "Category", - "filter-quick-mine": "Assigned to me", - "filter-cid-all": "All categories", - "apply-filters": "Apply Filters", + "quick-filters": "Akıllı Filtre", + "filter-active": "Bayraklar listesinde etkin olan bir veya daha fazla filtre var", + "filter-reset": "Filtreleri Kaldır", + "filters": "Filtre Ayarı", + "filter-reporterId": "Muhabir UID", + "filter-targetUid": "Bayraklanan UID", + "filter-type": "Bayrak Tipi", + "filter-type-all": "Bütün İçerik", + "filter-type-post": "İleti", + "filter-state": "Bildiri", + "filter-assignee": "Vekil UID", + "filter-cid": "Kategori", + "filter-quick-mine": "Vekil atandı", + "filter-cid-all": "Bütün Kategoriler", + "apply-filters": "Filtreleri Onayla", - "quick-links": "Quick Links", - "flagged-user": "Flagged User", - "view-profile": "View Profile", - "start-new-chat": "Start New Chat", - "go-to-target": "View Flag Target", + "quick-links": "Akıllı Bağlantılar", + "flagged-user": "Bayraklanan Kullanıcı", + "view-profile": "Profili Gör", + "start-new-chat": "Yeni Sohbet Başlat", + "go-to-target": "Bayrak Hedefini Gör", - "user-view": "View Profile", - "user-edit": "Edit Profile", + "user-view": "Profili Gör", + "user-edit": "Profili Düzenle", - "notes": "Flag Notes", - "add-note": "Add Note", - "no-notes": "No shared notes.", + "notes": "Bayrak Notu", + "add-note": "Not Ekle", + "no-notes": "Not paylaşılmadı", - "history": "Flag History", - "back": "Back to Flags List", - "no-history": "No flag history.", + "history": "Bayrak Geçmişi", + "back": "Bayrak Listesine Geri Dön", + "no-history": "Bayrak geçmişi yok", - "state-all": "All states", - "state-open": "New/Open", - "state-wip": "Work in Progress", - "state-resolved": "Resolved", - "state-rejected": "Rejected", - "no-assignee": "Not Assigned", - "note-added": "Note Added", + "state-all": "Bütün Bildiriler", + "state-open": "Yeni/Açık", + "state-wip": "Yapım Aşamasında", + "state-resolved": "Çözüldü", + "state-rejected": "Reddedildi", + "no-assignee": "Atanmadı", + "note-added": "Not eklendi", - "modal-title": "Report Inappropriate Content", - "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", - "modal-reason-spam": "Spam", - "modal-reason-offensive": "Offensive", - "modal-reason-custom": "Reason for reporting this content...", - "modal-submit": "Submit Report", - "modal-submit-success": "Content has been flagged for moderation." + "modal-title": "Uygunsuz İçeriği Rapor Et", + "modal-body": "%1 %2 için bayraklama nedenini belirtin. Alternatif olarak hızlı rapor butonlarından birinini kullanabilirsin.", + "modal-reason-spam": "Gereksiz", + "modal-reason-offensive": "Saldırgan", + "modal-reason-custom": "Bir içeriği bildirme nedeni...", + "modal-submit": "Raporu Gönder", + "modal-submit-success": "İçerik, denetlemek için bayraklandı." } \ No newline at end of file diff --git a/public/language/zh-CN/flags.json b/public/language/zh-CN/flags.json index c4bfed6f23..0346594fbc 100644 --- a/public/language/zh-CN/flags.json +++ b/public/language/zh-CN/flags.json @@ -1,60 +1,60 @@ { - "state": "State", - "reporter": "Reporter", - "reported-at": "Reported At", - "description": "Description", - "no-flags": "Hooray! No flags found.", - "assignee": "Assignee", - "update": "Update", - "updated": "Updated", - "target-purged": "The content this flag referred to has been purged and is no longer available.", + "state": "状态", + "reporter": "举报人", + "reported-at": "举报于", + "description": "描述", + "no-flags": "啊哈!没发现任何的举报。", + "assignee": "代理人", + "update": "更新", + "updated": "已更新", + "target-purged": "被举报的内容已经被清除,不再可用。", - "quick-filters": "Quick Filters", - "filter-active": "There are one or more filters active in this list of flags", - "filter-reset": "Remove Filters", - "filters": "Filter Options", - "filter-reporterId": "Reporter UID", - "filter-targetUid": "Flagged UID", - "filter-type": "Flag Type", - "filter-type-all": "All Content", - "filter-type-post": "Post", - "filter-state": "State", - "filter-assignee": "Assignee UID", - "filter-cid": "Category", - "filter-quick-mine": "Assigned to me", - "filter-cid-all": "All categories", - "apply-filters": "Apply Filters", + "quick-filters": "快速过滤器", + "filter-active": "该列中有一个或更多激活的过滤器", + "filter-reset": "删除过滤器", + "filters": "过滤器选项", + "filter-reporterId": "举报者UID", + "filter-targetUid": "被举报者 UID", + "filter-type": "举报类型", + "filter-type-all": "所有内容", + "filter-type-post": "帖子", + "filter-state": "状态", + "filter-assignee": "代理人UID", + "filter-cid": "版块", + "filter-quick-mine": "委托给我", + "filter-cid-all": "全部版块", + "apply-filters": "应用过滤器", - "quick-links": "Quick Links", - "flagged-user": "Flagged User", - "view-profile": "View Profile", - "start-new-chat": "Start New Chat", - "go-to-target": "View Flag Target", + "quick-links": "快速链接", + "flagged-user": "被举报的用户", + "view-profile": "查看个人资料", + "start-new-chat": "开始新会话", + "go-to-target": "查看举报目标", - "user-view": "View Profile", - "user-edit": "Edit Profile", + "user-view": "查看资料", + "user-edit": "编辑资料", - "notes": "Flag Notes", - "add-note": "Add Note", - "no-notes": "No shared notes.", + "notes": "举报备注", + "add-note": "添加备注", + "no-notes": "没有共享的备注内容。", - "history": "Flag History", - "back": "Back to Flags List", - "no-history": "No flag history.", + "history": "举报历史", + "back": "返回举报列表", + "no-history": "没有举报历史。", - "state-all": "All states", - "state-open": "New/Open", - "state-wip": "Work in Progress", - "state-resolved": "Resolved", - "state-rejected": "Rejected", - "no-assignee": "Not Assigned", - "note-added": "Note Added", + "state-all": "所有状态", + "state-open": "新建/打开", + "state-wip": "正在处理", + "state-resolved": "已解决", + "state-rejected": "已拒绝", + "no-assignee": "未指派", + "note-added": "备注已添加", - "modal-title": "Report Inappropriate Content", - "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", - "modal-reason-spam": "Spam", - "modal-reason-offensive": "Offensive", - "modal-reason-custom": "Reason for reporting this content...", - "modal-submit": "Submit Report", - "modal-submit-success": "Content has been flagged for moderation." + "modal-title": "举报不适内容", + "modal-body": "请选择或者输入您举报 %1%2 的原因以便版主进行审核。", + "modal-reason-spam": "垃圾信息", + "modal-reason-offensive": "人身攻击", + "modal-reason-custom": "举报此内容的理由……", + "modal-submit": "提交举报", + "modal-submit-success": "已举报此内容。" } \ No newline at end of file diff --git a/public/language/zh-TW/flags.json b/public/language/zh-TW/flags.json index c4bfed6f23..05a722bae2 100644 --- a/public/language/zh-TW/flags.json +++ b/public/language/zh-TW/flags.json @@ -5,7 +5,7 @@ "description": "Description", "no-flags": "Hooray! No flags found.", "assignee": "Assignee", - "update": "Update", + "update": "更新", "updated": "Updated", "target-purged": "The content this flag referred to has been purged and is no longer available.", @@ -17,7 +17,7 @@ "filter-targetUid": "Flagged UID", "filter-type": "Flag Type", "filter-type-all": "All Content", - "filter-type-post": "Post", + "filter-type-post": "文章", "filter-state": "State", "filter-assignee": "Assignee UID", "filter-cid": "Category", From 3ae3d788be3dcf11d6cf0d89a65c22d8a1c4cccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 9 May 2017 13:24:39 -0400 Subject: [PATCH 316/316] up composer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 36b7d2032f..b4a6a96933 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "morgan": "^1.3.2", "mousetrap": "^1.5.3", "nconf": "~0.8.2", - "nodebb-plugin-composer-default": "4.4.6", + "nodebb-plugin-composer-default": "4.4.7", "nodebb-plugin-dbsearch": "2.0.2", "nodebb-plugin-emoji-extended": "1.1.1", "nodebb-plugin-emoji-one": "1.1.5",
    {info.process.pid} {info.process.version} - {info.stats.onlineRegisteredCount} / - {info.stats.onlineGuestCount} / + {info.stats.onlineRegisteredCount} / + {info.stats.onlineGuestCount} / {info.stats.socketCount} {info.git.branch}@{info.git.hash}