From a3cfcd9a48f400fd57f18d1aff1980bee4a0c0a4 Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Fri, 27 Nov 2015 16:55:31 -0700
Subject: [PATCH 01/59] Maximum invites, invites stored
Added the ability for admins to restrict the maximum amount of invites
a user can make. Invites are stored and displayed in the registration
queue admin page.
---
public/language/en@pirate/error.json | 6 ++-
public/language/en_GB/error.json | 6 ++-
public/language/en_US/error.json | 6 ++-
public/src/client/register.js | 4 +-
src/controllers/admin/users.js | 47 ++++++++++++++++++--
src/controllers/users.js | 20 +++++++--
src/socket.io/user.js | 29 +++++++++++-
src/user/invite.js | 59 ++++++++++++++++++++++++-
src/views/admin/manage/registration.tpl | 46 ++++++++++++++++---
src/views/admin/settings/user.tpl | 11 ++++-
10 files changed, 206 insertions(+), 28 deletions(-)
diff --git a/public/language/en@pirate/error.json b/public/language/en@pirate/error.json
index c9751317f1..88c55e7a49 100644
--- a/public/language/en@pirate/error.json
+++ b/public/language/en@pirate/error.json
@@ -87,5 +87,7 @@
"registration-error": "Registration Error",
"parse-error": "Something went wrong while parsing server response",
"wrong-login-type-email": "Please use your email to login",
- "wrong-login-type-username": "Please use your username to login"
-}
\ No newline at end of file
+ "wrong-login-type-username": "Please use your username to login",
+
+ "invite-maximum-met": "You have invited the maximum amount of people (%1 out of %2)."
+}
diff --git a/public/language/en_GB/error.json b/public/language/en_GB/error.json
index 4159d211d8..7842356067 100644
--- a/public/language/en_GB/error.json
+++ b/public/language/en_GB/error.json
@@ -113,5 +113,7 @@
"registration-error": "Registration Error",
"parse-error": "Something went wrong while parsing server response",
"wrong-login-type-email": "Please use your email to login",
- "wrong-login-type-username": "Please use your username to login"
-}
\ No newline at end of file
+ "wrong-login-type-username": "Please use your username to login",
+
+ "invite-maximum-met": "You have invited the maximum amount of people (%1 out of %2)."
+}
diff --git a/public/language/en_US/error.json b/public/language/en_US/error.json
index 2789d31b2d..15c7ee3d72 100644
--- a/public/language/en_US/error.json
+++ b/public/language/en_US/error.json
@@ -87,5 +87,7 @@
"registration-error": "Registration Error",
"parse-error": "Something went wrong while parsing server response",
"wrong-login-type-email": "Please use your email to login",
- "wrong-login-type-username": "Please use your username to login"
-}
\ No newline at end of file
+ "wrong-login-type-username": "Please use your username to login",
+
+ "invite-maximum-met": "You have invited the maximum amount of people (%1 out of %2)."
+}
diff --git a/public/src/client/register.js b/public/src/client/register.js
index ef11e4a4bd..3d0f943da9 100644
--- a/public/src/client/register.js
+++ b/public/src/client/register.js
@@ -28,7 +28,7 @@ define('forum/register', ['csrf', 'translator'], function(csrf, translator) {
var query = utils.params();
if (query.email && query.token) {
- email.val(query.email);
+ email.val(decodeURIComponent(query.email));
$('#token').val(query.token);
}
@@ -160,7 +160,7 @@ define('forum/register', ['csrf', 'translator'], function(csrf, translator) {
socket.emit('user.exists', {
username: username
}, function(err, exists) {
- if(err) {
+ if (err) {
return app.alertError(err.message);
}
diff --git a/src/controllers/admin/users.js b/src/controllers/admin/users.js
index 6c10694ba6..dc5420b401 100644
--- a/src/controllers/admin/users.js
+++ b/src/controllers/admin/users.js
@@ -1,6 +1,7 @@
"use strict";
-var user = require('../../user'),
+var async = require('async'),
+ user = require('../../user'),
meta = require('../../meta');
@@ -31,12 +32,50 @@ usersController.banned = function(req, res, next) {
};
usersController.registrationQueue = function(req, res, next) {
- user.getRegistrationQueue(0, -1, function(err, data) {
+ var invitations;
+ async.parallel({
+ users: function(next) {
+ user.getRegistrationQueue(0, -1, next);
+ },
+ invites: function(next) {
+ async.waterfall([
+ function(next) {
+ user.getAllInvites(next);
+ },
+ function(_invitations, next) {
+ invitations = _invitations;
+ async.map(invitations, function(invites, next) {
+ user.getUserField(invites.uid, 'username', next);
+ }, next);
+ },
+ function(usernames, next) {
+ invitations.forEach(function(invites, index) {
+ invites.username = usernames[index];
+ });
+ async.map(invitations, function(invites, next) {
+ async.map(invites.invitations, user.getUsernameByEmail, next);
+ }, next);
+ },
+ function(usernames, next) {
+ invitations.forEach(function(invites, index) {
+ invites.invitations = invites.invitations.map(function(email, i) {
+ return {
+ email: email,
+ username: usernames[index][i] === '[[global:guest]]' ? '' : usernames[index][i]
+ };
+ });
+ });
+ next(null, invitations);
+ }
+ ], next);
+ }
+ }, function(err, data) {
if (err) {
return next(err);
}
- res.render('admin/manage/registration', {users: data});
- })
+ res.render('admin/manage/registration', data);
+ });
+
};
function getUsers(set, req, res, next) {
diff --git a/src/controllers/users.js b/src/controllers/users.js
index 0087232255..21d1490717 100644
--- a/src/controllers/users.js
+++ b/src/controllers/users.js
@@ -150,14 +150,26 @@ usersController.getUsersForSearch = function(req, res, next) {
};
function render(req, res, data, next) {
- plugins.fireHook('filter:users.build', {req: req, res: res, templateData: data}, function(err, data) {
+ plugins.fireHook('filter:users.build', { req: req, res: res, templateData: data }, function(err, data) {
if (err) {
return next(err);
}
- data.templateData.inviteOnly = meta.config.registrationType === 'invite-only';
- data.templateData['reputation:disabled'] = parseInt(meta.config['reputation:disabled'], 10) === 1;
- res.render('users', data.templateData);
+ if (!req.uid) {
+ return next(new Error('[[error:no-privileges]]'));
+ }
+
+ user.getInvitesNumber(req.uid, function(err, num) {
+ if (err) {
+ return next(err);
+ }
+
+ data.templateData.invites = num;
+ data.templateData.maximumInvites = meta.config.maximumInvites;
+ data.templateData.inviteOnly = meta.config.registrationType === 'invite-only';
+ data.templateData['reputation:disabled'] = parseInt(meta.config['reputation:disabled'], 10) === 1;
+ res.render('users', data.templateData);
+ });
});
}
diff --git a/src/socket.io/user.js b/src/socket.io/user.js
index fcaaa51d30..3f2f72c6f4 100644
--- a/src/socket.io/user.js
+++ b/src/socket.io/user.js
@@ -267,14 +267,39 @@ SocketUser.loadMore = function(socket, data, callback) {
SocketUser.invite = function(socket, email, callback) {
if (!email || !socket.uid) {
- return callback(new Error('[[error:invald-data]]'));
+ return callback(new Error('[[error:invalid-data]]'));
}
if (meta.config.registrationType !== 'invite-only') {
return callback(new Error('[[error:forum-not-invite-only]]'));
}
- user.sendInvitationEmail(socket.uid, email, callback);
+ var max = meta.config.maximumInvites;
+
+ if (max) {
+ async.waterfall([
+ function(next) {
+ user.getInvitesNumber(socket.uid, next);
+ },
+ function(invites, next) {
+ user.isAdministrator(socket.uid, function(err, admin) {
+ next(err, invites, admin);
+ });
+ },
+ function(invites, admin, next) {
+ console.log(admin, invites, max);
+ if (!admin && invites > max) {
+ return next(new Error('[[error:invite-maximum-met, ' + invites + ', ' + max + ']]'));
+ }
+ next();
+ },
+ function(next) {
+ user.sendInvitationEmail(socket.uid, email, next);
+ }
+ ], callback);
+ } else {
+ user.sendInvitationEmail(socket.uid, email, callback);
+ }
};
diff --git a/src/user/invite.js b/src/user/invite.js
index 0fb8ae979b..ef3146b5f4 100644
--- a/src/user/invite.js
+++ b/src/user/invite.js
@@ -16,13 +16,68 @@ var async = require('async'),
module.exports = function(User) {
+ User.getInvites = function(uid, callback) {
+ db.getSetMembers('invitation:uid:' + uid, callback);
+ };
+
+ User.getInvitesNumber = function(uid, callback) {
+ db.setCount('invitation:uid:' + uid, callback);
+ };
+
+ User.getInvitingUsers = function(callback) {
+ db.getSetMembers('invitation:uids', callback);
+ };
+
+ User.getAllInvites = function(callback) {
+ var uids;
+ async.waterfall([
+ User.getInvitingUsers,
+ function(_uids, next) {
+ uids = _uids;
+ async.map(uids, User.getInvites, next);
+ },
+ function(invitations, next) {
+ invitations = invitations.map(function(invites, index) {
+ return {
+ uid: uids[index],
+ invitations: invites
+ };
+ });
+ next(null, invitations);
+ }
+ ], callback);
+ };
+
User.sendInvitationEmail = function(uid, email, callback) {
callback = callback || function() {};
+
var token = utils.generateUUID();
- var registerLink = nconf.get('url') + '/register?token=' + token + '&email=' + email;
+ var registerLink = nconf.get('url') + '/register?token=' + token + '&email=' + encodeURIComponent(email);
var oneDay = 86400000;
+
async.waterfall([
+ function(next) {
+ User.getUidByEmail(email, next);
+ },
+ function(exists, next) {
+ if (exists) {
+ return next(new Error('[[error:email-taken]]'));
+ }
+ next();
+ },
+ function(next) {
+ async.parallel([
+ function(next) {
+ db.setAdd('invitation:uid:' + uid, email, next);
+ },
+ function(next) {
+ db.setAdd('invitation:uids', uid, next);
+ }
+ ], function(err) {
+ next(err);
+ });
+ },
function(next) {
db.set('invitation:email:' + email, token, next);
},
@@ -73,4 +128,4 @@ module.exports = function(User) {
db.delete('invitation:email:' + email, callback);
};
-};
\ No newline at end of file
+};
diff --git a/src/views/admin/manage/registration.tpl b/src/views/admin/manage/registration.tpl
index faa31d7535..278ba279b1 100644
--- a/src/views/admin/manage/registration.tpl
+++ b/src/views/admin/manage/registration.tpl
@@ -1,4 +1,14 @@
-
+
+
+ Queue
+
+
+
+ There are no users in the registration queue.
+ To enable this feature, go to Settings -> User -> Authentication and set
+ Registration Type to "Admin Approval".
+
+
Name
@@ -7,11 +17,6 @@
Time
-
-
- There are no users in the registration queue. To enable this feature go to Settings -> User -> Authentication and set Registration Type to "Admin Approval".
-
-
@@ -50,4 +55,31 @@
-
\ No newline at end of file
+
+
+
+
+ Invitations
+
+
+ Below is a complete list of invitations sent. Use ctrl-f to search through the list by email or username.
+
+ The username will be displayed to the right of the emails for users who have redeemed their invitations.
+
+ 0 for no restriction. Admins get infinite invitations
+ Only applicable for "Invite Only"
+
+
@@ -267,4 +276,4 @@
-
\ No newline at end of file
+
From af8e649246ab3d28456840a34be43eadf5f87712 Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Fri, 27 Nov 2015 17:00:36 -0700
Subject: [PATCH 02/59] Fix indentation in translations
---
public/language/en@pirate/error.json | 2 +-
public/language/en_US/error.json | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/public/language/en@pirate/error.json b/public/language/en@pirate/error.json
index 88c55e7a49..0933126bc8 100644
--- a/public/language/en@pirate/error.json
+++ b/public/language/en@pirate/error.json
@@ -89,5 +89,5 @@
"wrong-login-type-email": "Please use your email to login",
"wrong-login-type-username": "Please use your username to login",
- "invite-maximum-met": "You have invited the maximum amount of people (%1 out of %2)."
+ "invite-maximum-met": "You have invited the maximum amount of people (%1 out of %2)."
}
diff --git a/public/language/en_US/error.json b/public/language/en_US/error.json
index 15c7ee3d72..00f7ed9236 100644
--- a/public/language/en_US/error.json
+++ b/public/language/en_US/error.json
@@ -89,5 +89,5 @@
"wrong-login-type-email": "Please use your email to login",
"wrong-login-type-username": "Please use your username to login",
- "invite-maximum-met": "You have invited the maximum amount of people (%1 out of %2)."
+ "invite-maximum-met": "You have invited the maximum amount of people (%1 out of %2)."
}
From 67905667a9e8c6f94bc0fb89087dbe65914518cc Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Sat, 28 Nov 2015 15:33:17 -0700
Subject: [PATCH 03/59] Added admin-only invites
---
src/controllers/index.js | 2 +-
src/controllers/users.js | 12 +++----
src/socket.io/user.js | 53 +++++++++++++++++--------------
src/views/admin/settings/user.tpl | 1 +
4 files changed, 37 insertions(+), 31 deletions(-)
diff --git a/src/controllers/index.js b/src/controllers/index.js
index f418c16572..e4261730f0 100644
--- a/src/controllers/index.js
+++ b/src/controllers/index.js
@@ -108,7 +108,7 @@ Controllers.register = function(req, res, next) {
async.waterfall([
function(next) {
- if (registrationType === 'invite-only') {
+ if (registrationType === 'invite-only' || registrationType === 'admin-invite-only') {
user.verifyInvitation(req.query, next);
} else {
next();
diff --git a/src/controllers/users.js b/src/controllers/users.js
index 21d1490717..7207150d0d 100644
--- a/src/controllers/users.js
+++ b/src/controllers/users.js
@@ -155,9 +155,11 @@ function render(req, res, data, next) {
return next(err);
}
- if (!req.uid) {
- return next(new Error('[[error:no-privileges]]'));
- }
+ var registrationType = meta.config.registrationType;
+
+ data.templateData.maximumInvites = meta.config.maximumInvites;
+ data.templateData.inviteOnly = registrationType === 'invite-only' || registrationType === 'admin-invite-only';
+ data.templateData['reputation:disabled'] = parseInt(meta.config['reputation:disabled'], 10) === 1;
user.getInvitesNumber(req.uid, function(err, num) {
if (err) {
@@ -165,11 +167,9 @@ function render(req, res, data, next) {
}
data.templateData.invites = num;
- data.templateData.maximumInvites = meta.config.maximumInvites;
- data.templateData.inviteOnly = meta.config.registrationType === 'invite-only';
- data.templateData['reputation:disabled'] = parseInt(meta.config['reputation:disabled'], 10) === 1;
res.render('users', data.templateData);
});
+
});
}
diff --git a/src/socket.io/user.js b/src/socket.io/user.js
index 3f2f72c6f4..14b0c2c828 100644
--- a/src/socket.io/user.js
+++ b/src/socket.io/user.js
@@ -270,36 +270,41 @@ SocketUser.invite = function(socket, email, callback) {
return callback(new Error('[[error:invalid-data]]'));
}
- if (meta.config.registrationType !== 'invite-only') {
+ var registrationType = meta.config.registrationType
+
+ if (registrationType !== 'invite-only' && registrationType !== 'admin-invite-only') {
return callback(new Error('[[error:forum-not-invite-only]]'));
}
var max = meta.config.maximumInvites;
- if (max) {
- async.waterfall([
- function(next) {
- user.getInvitesNumber(socket.uid, next);
- },
- function(invites, next) {
- user.isAdministrator(socket.uid, function(err, admin) {
- next(err, invites, admin);
- });
- },
- function(invites, admin, next) {
- console.log(admin, invites, max);
- if (!admin && invites > max) {
- return next(new Error('[[error:invite-maximum-met, ' + invites + ', ' + max + ']]'));
+ user.isAdministrator(socket.uid, function(err, admin) {
+ if (err) {
+ return callback(err);
+ }
+ if (registrationType === 'admin-invite-only' && !admin) {
+ return callback(new Error('[[error:no-privileges]]'));
+ }
+ if (max) {
+ async.waterfall([
+ function(next) {
+ user.getInvitesNumber(socket.uid, next);
+ },
+ function(invites, next) {
+ if (!admin && invites > max) {
+ return next(new Error('[[error:invite-maximum-met, ' + invites + ', ' + max + ']]'));
+ }
+ next();
+ },
+ function(next) {
+ user.sendInvitationEmail(socket.uid, email, next);
}
- next();
- },
- function(next) {
- user.sendInvitationEmail(socket.uid, email, next);
- }
- ], callback);
- } else {
- user.sendInvitationEmail(socket.uid, email, callback);
- }
+ ], callback);
+ } else {
+ user.sendInvitationEmail(socket.uid, email, callback);
+ }
+ });
+
};
diff --git a/src/views/admin/settings/user.tpl b/src/views/admin/settings/user.tpl
index 6f6b83f78d..bd0e746c91 100644
--- a/src/views/admin/settings/user.tpl
+++ b/src/views/admin/settings/user.tpl
@@ -33,6 +33,7 @@
+
From 259eb585d683957fd62e59d937d16811ceda2d24 Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Sat, 28 Nov 2015 15:38:17 -0700
Subject: [PATCH 04/59] Added template data for admin invite only
---
src/controllers/users.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/controllers/users.js b/src/controllers/users.js
index 7207150d0d..189a98df5c 100644
--- a/src/controllers/users.js
+++ b/src/controllers/users.js
@@ -159,6 +159,7 @@ function render(req, res, data, next) {
data.templateData.maximumInvites = meta.config.maximumInvites;
data.templateData.inviteOnly = registrationType === 'invite-only' || registrationType === 'admin-invite-only';
+ data.templateData.adminInviteOnly = registrationType === 'admin-invite-only';
data.templateData['reputation:disabled'] = parseInt(meta.config['reputation:disabled'], 10) === 1;
user.getInvitesNumber(req.uid, function(err, num) {
From dcbf53bae357444464b3976d3ecba143a15434cd Mon Sep 17 00:00:00 2001
From: barisusakli
Date: Fri, 11 Dec 2015 10:57:13 +0200
Subject: [PATCH 05/59] moved search to dbsearch
---
package.json | 1 -
src/database/mongo.js | 23 ++++--------
src/database/mongo/main.js | 77 --------------------------------------
src/database/redis.js | 11 +-----
src/database/redis/main.js | 27 -------------
5 files changed, 8 insertions(+), 131 deletions(-)
diff --git a/package.json b/package.json
index 0609a3dc22..fee26f6053 100644
--- a/package.json
+++ b/package.json
@@ -56,7 +56,6 @@
"passport": "^0.3.0",
"passport-local": "1.0.0",
"prompt": "^0.2.14",
- "redisearch": "^0.0.6",
"request": "^2.44.0",
"rimraf": "~2.4.2",
"rss": "^1.0.0",
diff --git a/src/database/mongo.js b/src/database/mongo.js
index 0563e02633..5282024f6d 100644
--- a/src/database/mongo.js
+++ b/src/database/mongo.js
@@ -124,7 +124,7 @@
require('./mongo/sorted')(db, module);
require('./mongo/list')(db, module);
- if(nconf.get('mongo:password') && nconf.get('mongo:username')) {
+ if (nconf.get('mongo:password') && nconf.get('mongo:username')) {
db.authenticate(nconf.get('mongo:username'), nconf.get('mongo:password'), function (err) {
if (err) {
winston.error(err.stack);
@@ -138,31 +138,22 @@
}
function createIndices() {
- winston.info('[database] Checking database indices.')
+ winston.info('[database] Checking database indices.');
async.parallel([
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}),
-
- async.apply(createIndex, 'searchtopic', {content: 'text', uid: 1, cid: 1}, {background: true}),
- async.apply(createIndex, 'searchtopic', {id: 1}, {background: true}),
-
- async.apply(createIndex, 'searchpost', {content: 'text', uid: 1, cid: 1}, {background: true}),
- async.apply(createIndex, 'searchpost', {id: 1}, {background: true})
+ async.apply(createIndex, 'objects', {expireAt: 1}, {expireAfterSeconds: 0, background: true})
], function(err) {
- callback(err);
- });
- }
-
- function createIndex(collection, index, options, callback) {
- db.collection(collection).ensureIndex(index, options, function(err) {
if (err) {
winston.error('Error creating index ' + err.message);
}
callback(err);
});
}
+
+ function createIndex(collection, index, options, callback) {
+ db.collection(collection).ensureIndex(index, options, callback);
+ }
});
};
diff --git a/src/database/mongo/main.js b/src/database/mongo/main.js
index f09f13f173..777565df06 100644
--- a/src/database/mongo/main.js
+++ b/src/database/mongo/main.js
@@ -5,83 +5,6 @@ var winston = require('winston');
module.exports = function(db, module) {
var helpers = module.helpers.mongo;
- module.searchIndex = function(key, data, id, callback) {
- callback = callback || function() {};
- id = parseInt(id, 10);
- if (!id) {
- return callback();
- }
- var setData = {
- id: id
- };
- for(var field in data) {
- if (data.hasOwnProperty(field) && data[field]) {
- setData[field] = data[field].toString();
- }
- }
-
- db.collection('search' + key).update({id: id}, {$set: setData}, {upsert:true, w: 1}, function(err) {
- if (err) {
- winston.error('Error indexing ' + err.message);
- }
- callback(err);
- });
- };
-
- module.search = function(key, data, limit, callback) {
- var searchQuery = {};
-
- if (data.content) {
- searchQuery.$text = {$search: data.content};
- }
-
- if (Array.isArray(data.cid) && data.cid.length) {
- data.cid = data.cid.filter(Boolean);
- if (data.cid.length > 1) {
- searchQuery.cid = {$in: data.cid.map(String)};
- } else if (data.cid[0]) {
- searchQuery.cid = data.cid[0].toString();
- }
- }
-
- if (Array.isArray(data.uid) && data.uid.length) {
- data.uid = data.uid.filter(Boolean);
- if (data.uid.length > 1) {
- searchQuery.uid = {$in: data.uid.map(String)};
- } else if (data.uid[0]) {
- searchQuery.uid = data.uid[0].toString();
- }
- }
-
- db.collection('search' + key).find(searchQuery, {limit: limit, fields:{_id: 0, id: 1}}).toArray(function(err, results) {
- if (err) {
- return callback(err);
- }
-
- if (!results || !results.length) {
- return callback(null, []);
- }
-
- var data = results.map(function(item) {
- return item.id;
- });
-
- callback(null, data);
- });
- };
-
- module.searchRemove = function(key, id, callback) {
- callback = callback || helpers.noop;
- id = parseInt(id, 10);
- if (!id) {
- return callback();
- }
-
- db.collection('search' + key).remove({id: id}, function(err, res) {
- callback(err);
- });
- };
-
module.flushdb = function(callback) {
callback = callback || helpers.noop;
db.dropDatabase(callback);
diff --git a/src/database/redis.js b/src/database/redis.js
index da9c49aaa6..239090aa95 100644
--- a/src/database/redis.js
+++ b/src/database/redis.js
@@ -4,16 +4,11 @@
var winston = require('winston'),
nconf = require('nconf'),
- path = require('path'),
semver = require('semver'),
session = require('express-session'),
- utils = require('./../../public/src/utils.js'),
redis,
connectRedis,
- redisSearch,
- redisClient,
- postSearch,
- topicSearch;
+ redisClient;
module.questions = [
{
@@ -43,7 +38,6 @@
try {
redis = require('redis');
connectRedis = require('connect-redis')(session);
- redisSearch = require('redisearch');
} catch (err) {
winston.error('Unable to initialize Redis! Is Redis installed? Error :' + err.message);
process.exit();
@@ -58,9 +52,6 @@
ttl: 60 * 60 * 24 * 14
});
- module.postSearch = redisSearch.createSearch('nodebbpostsearch', redisClient);
- module.topicSearch = redisSearch.createSearch('nodebbtopicsearch', redisClient);
-
require('./redis/main')(redisClient, module);
require('./redis/hash')(redisClient, module);
require('./redis/sets')(redisClient, module);
diff --git a/src/database/redis/main.js b/src/database/redis/main.js
index 503d515569..7fc2890d87 100644
--- a/src/database/redis/main.js
+++ b/src/database/redis/main.js
@@ -1,31 +1,6 @@
"use strict";
module.exports = function(redisClient, module) {
- module.searchIndex = function(key, data, id, callback) {
- var method = key === 'post' ? module.postSearch : module.topicSearch;
-
- method.index(data, id, function(err, res) {
- callback(err);
- });
- };
-
- module.search = function(key, data, limit, callback) {
- var method = key === 'post' ? module.postSearch : module.topicSearch;
-
- method.query(data, 0, limit - 1, callback);
- };
-
- module.searchRemove = function(key, id, callback) {
- callback = callback || function() {};
- if (!id) {
- return callback();
- }
- var method = key === 'post' ? module.postSearch : module.topicSearch;
-
- method.remove(id, function(err, res) {
- callback(err);
- });
- };
module.flushdb = function(callback) {
redisClient.send_command('flushdb', [], function(err) {
@@ -35,8 +10,6 @@ module.exports = function(redisClient, module) {
});
};
-
-
module.exists = function(key, callback) {
redisClient.exists(key, function(err, exists) {
callback(err, exists === 1);
From fff5def99b416f57b030fd77bc98db9d2b6a24a0 Mon Sep 17 00:00:00 2001
From: barisusakli
Date: Fri, 11 Dec 2015 11:00:50 +0200
Subject: [PATCH 06/59] up dbsearch
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index fee26f6053..86cb942c62 100644
--- a/package.json
+++ b/package.json
@@ -40,7 +40,7 @@
"morgan": "^1.3.2",
"nconf": "~0.8.2",
"nodebb-plugin-composer-default": "1.0.24",
- "nodebb-plugin-dbsearch": "0.2.18",
+ "nodebb-plugin-dbsearch": "0.3.0",
"nodebb-plugin-emoji-extended": "0.4.17",
"nodebb-plugin-markdown": "4.0.8",
"nodebb-plugin-mentions": "1.0.12",
From 60e186c8f36a6723000f4e0fdb06c69f62161bcb Mon Sep 17 00:00:00 2001
From: barisusakli
Date: Fri, 11 Dec 2015 11:07:37 +0200
Subject: [PATCH 07/59] refactor api/username api/email
---
src/controllers/api.js | 34 ++++++++--------------------------
1 file changed, 8 insertions(+), 26 deletions(-)
diff --git a/src/controllers/api.js b/src/controllers/api.js
index 74c4d008c8..79cf9dd6d4 100644
--- a/src/controllers/api.js
+++ b/src/controllers/api.js
@@ -179,15 +179,9 @@ apiController.getObject = function(req, res, next) {
apiController.getUserByUID = function(req, res, next) {
var uid = req.params.uid ? req.params.uid : 0;
- getUserByUID(uid, function(err, userData) {
- if (err || !userData) {
- return next(err);
- }
- res.json(userData);
- });
+ getUserByUID(uid, res, next);
};
-
apiController.getUserByUsername = function(req, res, next) {
var username = req.params.username ? req.params.username : 0;
@@ -196,17 +190,11 @@ apiController.getUserByUsername = function(req, res, next) {
user.getUidByUsername(username, next);
},
function(uid, next) {
- getUserByUID(uid, next);
+ getUserByUID(uid, res, next);
}
- ], function(err, userData) {
- if (err || !userData) {
- return next(err);
- }
- res.json(userData);
- });
+ ], next);
};
-
apiController.getUserByEmail = function(req, res, next) {
var email = req.params.email ? req.params.email : 0;
@@ -215,30 +203,24 @@ apiController.getUserByEmail = function(req, res, next) {
user.getUidByEmail(email, next);
},
function(uid, next) {
- getUserByUID(uid, next);
+ getUserByUID(uid, res, next);
}
- ], function(err, userData) {
- if (err || !userData) {
- return next(err);
- }
- res.json(userData);
- });
+ ], next);
};
-
-function getUserByUID(uid, callback) {
+function getUserByUID(uid, res, next) {
async.parallel({
userData: async.apply(user.getUserData, uid),
settings: async.apply(user.getSettings, uid)
}, function(err, results) {
if (err || !results.userData) {
- return callback(err, null);
+ return next(err);
}
results.userData.email = results.settings.showemail ? results.userData.email : undefined;
results.userData.fullname = results.settings.showfullname ? results.userData.fullname : undefined;
- callback(null, results.userData);
+ res.json(results.userData);
});
}
From 8dc947504165a3b217b2b5af6046c3f75505d985 Mon Sep 17 00:00:00 2001
From: barisusakli
Date: Fri, 11 Dec 2015 14:57:47 +0200
Subject: [PATCH 08/59] closes #3941
---
src/controllers/accounts/edit.js | 1 +
src/socket.io/user/profile.js | 13 +++++++++----
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/src/controllers/accounts/edit.js b/src/controllers/accounts/edit.js
index a6b2f7ad55..497564d88a 100644
--- a/src/controllers/accounts/edit.js
+++ b/src/controllers/accounts/edit.js
@@ -19,6 +19,7 @@ editController.get = function(req, res, callback) {
return callback(err);
}
+ userData['username:disableEdit'] = !userData.isAdmin && parseInt(meta.config['username:disableEdit'], 10) === 1;
userData.title = '[[pages:account/edit, ' + userData.username + ']]';
userData.breadcrumbs = helpers.buildBreadcrumbs([{text: userData.username, url: '/user/' + userData.userslug}, {text: '[[user:edit]]'}]);
diff --git a/src/socket.io/user/profile.js b/src/socket.io/user/profile.js
index a245c6e29d..5a365d6ff2 100644
--- a/src/socket.io/user/profile.js
+++ b/src/socket.io/user/profile.js
@@ -117,12 +117,17 @@ module.exports = function(SocketUser) {
return next(new Error('[[error:invalid-data]]'));
}
- if (parseInt(meta.config['username:disableEdit'], 10) === 1) {
+ user.isAdministrator(socket.uid, next);
+ },
+ function(isAdmin, next) {
+ if (!isAdmin && socket.uid !== parseInt(data.uid, 10)) {
+ return next(new Error('[[error:no-privileges]]'));
+ }
+
+ if (!isAdmin && parseInt(meta.config['username:disableEdit'], 10) === 1) {
data.username = oldUserData.username;
}
- user.isAdminOrSelf(socket.uid, data.uid, next);
- },
- function (next) {
+
user.updateProfile(data.uid, data, next);
},
function (userData, next) {
From 42cce3771b32354119c3c02c0953ae4e4e9c6fb3 Mon Sep 17 00:00:00 2001
From: barisusakli
Date: Fri, 11 Dec 2015 15:17:05 +0200
Subject: [PATCH 09/59] closes #3846
---
src/user/admin.js | 48 +++++++++++++++++++++++++++++------------------
1 file changed, 30 insertions(+), 18 deletions(-)
diff --git a/src/user/admin.js b/src/user/admin.js
index 95075b88f1..004e048c93 100644
--- a/src/user/admin.js
+++ b/src/user/admin.js
@@ -1,9 +1,9 @@
'use strict';
-var async = require('async'),
- db = require('./../database');
-
+var async = require('async');
+var db = require('../database');
+var plugins = require('../plugins');
module.exports = function(User) {
@@ -17,7 +17,7 @@ module.exports = function(User) {
User.getIPs = function(uid, stop, callback) {
db.getSortedSetRevRange('uid:' + uid + ':ip', 0, stop, function(err, ips) {
- if(err) {
+ if (err) {
return callback(err);
}
@@ -31,17 +31,17 @@ module.exports = function(User) {
var csvContent = '';
async.waterfall([
- function(next) {
+ function (next) {
db.getSortedSetRangeWithScores('username:uid', 0, -1, next);
},
- function(users, next) {
+ function (users, next) {
var uids = users.map(function(user) {
return user.score;
});
User.getUsersFields(uids, ['uid', 'email', 'username'], next);
},
- function(usersData, next) {
- usersData.forEach(function(user, index) {
+ function (usersData, next) {
+ usersData.forEach(function(user) {
if (user) {
csvContent += user.email + ',' + user.username + ',' + user.uid + '\n';
}
@@ -53,22 +53,34 @@ module.exports = function(User) {
};
User.ban = function(uid, callback) {
- User.setUserField(uid, 'banned', 1, function(err) {
- if (err) {
- return callback(err);
+ async.waterfall([
+ function (next) {
+ User.setUserField(uid, 'banned', 1, next);
+ },
+ function (next) {
+ db.sortedSetAdd('users:banned', Date.now(), uid, next);
+ },
+ function (next) {
+ plugins.fireHook('action:user.banned', {uid: uid});
+ next();
}
- db.sortedSetAdd('users:banned', Date.now(), uid, callback);
- });
+ ], callback);
};
User.unban = function(uid, callback) {
db.delete('uid:' + uid + ':flagged_by');
- User.setUserField(uid, 'banned', 0, function(err) {
- if (err) {
- return callback(err);
+ async.waterfall([
+ function (next) {
+ User.setUserField(uid, 'banned', 0, next);
+ },
+ function (next) {
+ db.sortedSetRemove('users:banned', uid, next);
+ },
+ function (next) {
+ plugins.fireHook('action:user.unbanned', {uid: uid});
+ next();
}
- db.sortedSetRemove('users:banned', uid, callback);
- });
+ ], callback);
};
User.resetFlags = function(uids, callback) {
From d12e8e1116a7ec935254a2b4b1b9559a282a1bb5 Mon Sep 17 00:00:00 2001
From: barisusakli
Date: Fri, 11 Dec 2015 15:24:49 +0200
Subject: [PATCH 10/59] closes #3814
---
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 77a71aba66..9a679cd49a 100644
--- a/src/controllers/accounts/helpers.js
+++ b/src/controllers/accounts/helpers.js
@@ -59,7 +59,7 @@ helpers.getUserDataByUserSlug = function(userslug, callerUID, callback) {
userData.joindateISO = utils.toISOString(userData.joindate);
userData.lastonlineISO = utils.toISOString(userData.lastonline || userData.joindate);
- userData.age = userData.birthday ? Math.floor((new Date().getTime() - new Date(userData.birthday).getTime()) / 31536000000) : '';
+ userData.age = Math.max(0, userData.birthday ? Math.floor((new Date().getTime() - new Date(userData.birthday).getTime()) / 31536000000) : 0);
if (!(isAdmin || self || (userData.email && userSettings.showemail))) {
userData.email = '';
From 2d203d7dcafe6eeb9ae2e22145ab829cd880d210 Mon Sep 17 00:00:00 2001
From: Julian Lam
Date: Fri, 11 Dec 2015 12:07:02 -0500
Subject: [PATCH 11/59] Squashed commit of the following:
Closes #2668
commit 3d4f494ed3257bceda8f6f82057cab83f0f252b3
Author: Julian Lam
Date: Fri Dec 11 12:06:42 2015 -0500
theme minvers for #2668
commit b608ce61854f8195143685bb9753b80d32b26e95
Author: Julian Lam
Date: Fri Dec 11 12:01:03 2015 -0500
Allowing chat modal to edit and delete messages
re: #2668
commit 0104db90a4070582f3938b6929dae35f985bac35
Author: Julian Lam
Date: Fri Dec 11 11:51:23 2015 -0500
Fixed issue where newSet calculations were off
... sometimes.
Also, rendering of edited messages now parses a template partial,
instead of just replacing the content.
commit 5cb6ca600425ca9320c599b32306e93dcc5aa4ce
Author: Julian Lam
Date: Fri Dec 11 11:07:12 2015 -0500
If edited content matches existing content...
... then edit is aborted.
commit 6e7495247b1895589c716db29f919a934087b924
Author: Julian Lam
Date: Fri Dec 11 11:05:08 2015 -0500
some linting and fixed issue where new msgs when deleted would crash server
commit db4a9e40d6dff44569c2437378121db8fdf75cf8
Author: Julian Lam
Date: Tue Dec 8 17:25:56 2015 -0500
Message deletion for #2668, and fixed bug
Fixed bug where chat modal would spawn even though user was sitting
on the /chats page.
commit a5aa2498ab4a8bba02a6daa43a9dbed7b3e37976
Author: Julian Lam
Date: Tue Dec 8 14:55:23 2015 -0500
wiring up the edit button, #2668
commit 5f2afdcf6f2b9eae6b5873ca100149e65e3d385d
Author: Julian Lam
Date: Tue Dec 8 14:20:39 2015 -0500
added indicator to show if and when a message had been edited
commit e8301132d525c1b9fd46c98cdb282ac7ea7a0d7f
Author: Julian Lam
Date: Tue Dec 8 14:06:39 2015 -0500
Allowing editing of chat messages
commit bfd991be1cb1769599f7d5d2b1638e313c3c2dcb
Author: Julian Lam
Date: Tue Dec 8 10:33:49 2015 -0500
Added messageId to messages object return
commit 0306ee6657b3288dd4547c66869d7d4ece0b31ad
Author: Julian Lam
Date: Tue Dec 8 08:20:17 2015 -0500
WIP #2668
---
package.json | 4 +-
public/language/en_GB/error.json | 1 +
public/language/en_GB/modules.json | 1 +
public/src/client/chats.js | 115 ++++++++++++++++---
public/src/modules/chat.js | 15 +++
public/src/modules/components.js | 7 ++
src/messaging.js | 176 ++++++++++++++++++++++++++---
src/socket.io/modules.js | 34 ++++++
8 files changed, 322 insertions(+), 31 deletions(-)
diff --git a/package.json b/package.json
index 86cb942c62..da7883cacc 100644
--- a/package.json
+++ b/package.json
@@ -48,8 +48,8 @@
"nodebb-plugin-spam-be-gone": "0.4.5",
"nodebb-rewards-essentials": "0.0.6",
"nodebb-theme-lavender": "3.0.2",
- "nodebb-theme-persona": "4.0.40",
- "nodebb-theme-vanilla": "5.0.15",
+ "nodebb-theme-persona": "4.0.41",
+ "nodebb-theme-vanilla": "5.0.16",
"nodebb-widget-essentials": "2.0.5",
"nodemailer": "0.7.1",
"npm": "^2.1.4",
diff --git a/public/language/en_GB/error.json b/public/language/en_GB/error.json
index 4159d211d8..4e00dd3470 100644
--- a/public/language/en_GB/error.json
+++ b/public/language/en_GB/error.json
@@ -101,6 +101,7 @@
"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",
+ "cant-edit-chat-message": "You are not allowed to edit this message",
"reputation-system-disabled": "Reputation system is disabled.",
"downvoting-disabled": "Downvoting is disabled",
diff --git a/public/language/en_GB/modules.json b/public/language/en_GB/modules.json
index 6bb97b68a4..5dc56fa8c2 100644
--- a/public/language/en_GB/modules.json
+++ b/public/language/en_GB/modules.json
@@ -15,6 +15,7 @@
"chat.seven_days": "7 Days",
"chat.thirty_days": "30 Days",
"chat.three_months": "3 Months",
+ "chat.delete_message_confirm": "Are you sure you wish to delete this message?",
"composer.compose": "Compose",
"composer.show_preview": "Show Preview",
diff --git a/public/src/client/chats.js b/public/src/client/chats.js
index 437c97978c..0531c40de9 100644
--- a/public/src/client/chats.js
+++ b/public/src/client/chats.js
@@ -63,6 +63,17 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll',
});
});
+ components.get('chat/messages')
+ .on('click', '[data-action="edit"]', function() {
+ var messageId = $(this).parents('[data-mid]').attr('data-mid');
+ var inputEl = components.get('chat/input');
+ Chats.prepEdit(inputEl, messageId);
+ })
+ .on('click', '[data-action="delete"]', function() {
+ var messageId = $(this).parents('[data-mid]').attr('data-mid');
+ Chats.delete(messageId);
+ });
+
$('.recent-chats').on('scroll', function() {
var $this = $(this);
var bottom = ($this[0].scrollHeight - $this.height()) * 0.9;
@@ -95,6 +106,49 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll',
$('[component="chat/input"]').focus();
});
+ Mousetrap.bind('up', function(e) {
+ if (e.target === components.get('chat/input').get(0)) {
+ // Retrieve message id from messages list
+ var message = components.get('chat/messages').find('.chat-message[data-self="1"]').last();
+ var lastMid = message.attr('data-mid');
+ var inputEl = components.get('chat/input');
+
+ Chats.prepEdit(inputEl, lastMid);
+ }
+ });
+ };
+
+ Chats.prepEdit = function(inputEl, messageId) {
+ socket.emit('modules.chats.getRaw', { mid: messageId }, function(err, raw) {
+ // Populate the input field with the raw message content
+ if (inputEl.val().length === 0) {
+ // By setting the `data-mid` attribute, I tell the chat code that I am editing a
+ // message, instead of posting a new one.
+ inputEl.attr('data-mid', messageId).addClass('editing');
+ inputEl.val(raw);
+ }
+ });
+ };
+
+ Chats.delete = function(messageId) {
+ translator.translate('[[modules:chat.delete_message_confirm]]', function(translated) {
+ bootbox.confirm(translated, function(ok) {
+ if (ok) {
+ socket.emit('modules.chats.delete', {
+ messageId: messageId
+ }, function(err) {
+ if (err) {
+ return app.alertError(err.message);
+ }
+
+ // Remove message from list
+ components.get('chat/message', messageId).slideUp('slow', function() {
+ $(this).remove();
+ });
+ });
+ }
+ });
+ });
};
Chats.addSinceHandler = function(toUid, chatContentEl, sinceEl) {
@@ -253,6 +307,22 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll',
socket.on('event:user_status_change', function(data) {
app.updateUserStatus($('.chats-list [data-uid="' + data.uid + '"] [component="user/status"]'), data.status);
});
+
+ socket.on('event:chats.edit', function(data) {
+ var message;
+
+ data.messages.forEach(function(message) {
+ templates.parse('partials/chat_message', {
+ messages: message
+ }, function(html) {
+ body = components.get('chat/message', message.messageId);
+ if (body.length) {
+ body.replaceWith(html);
+ components.get('chat/message', message.messageId).find('.timeago').timeago();
+ }
+ });
+ });
+ });
};
Chats.resizeMainWindow = function() {
@@ -278,7 +348,9 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll',
};
Chats.sendMessage = function(toUid, inputEl) {
- var msg = inputEl.val();
+ var msg = inputEl.val(),
+ mid = inputEl.attr('data-mid');
+
if (msg.length > config.maximumChatMessageLength) {
return app.alertError('[[error:chat-message-too-long]]');
}
@@ -288,20 +360,35 @@ define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll',
}
inputEl.val('');
- socket.emit('modules.chats.send', {
- touid: toUid,
- message: msg
- }, function(err) {
- if (err) {
- if (err.message === '[[error:email-not-confirmed-chat]]') {
- return app.showEmailConfirmWarning(err);
- }
- return app.alertError(err.message);
- }
+ inputEl.removeAttr('data-mid');
- sounds.play('chat-outgoing');
- Chats.notifyTyping(toUid, false);
- });
+ if (!mid) {
+ socket.emit('modules.chats.send', {
+ touid: toUid,
+ message: msg
+ }, function(err) {
+ if (err) {
+ if (err.message === '[[error:email-not-confirmed-chat]]') {
+ return app.showEmailConfirmWarning(err);
+ }
+ return app.alertError(err.message);
+ }
+
+ sounds.play('chat-outgoing');
+ Chats.notifyTyping(toUid, false);
+ });
+ } else {
+ socket.emit('modules.chats.edit', {
+ mid: mid,
+ message: msg
+ }, function(err) {
+ if (err) {
+ return app.alertError(err.message);
+ }
+
+ Chats.notifyTyping(toUid, false);
+ });
+ }
};
Chats.scrollToBottom = function(containerEl) {
diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js
index a9363836cd..27d7b9548e 100644
--- a/public/src/modules/chat.js
+++ b/public/src/modules/chat.js
@@ -19,6 +19,10 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra
});
socket.on('event:chats.receive', function(data) {
+ if (ajaxify.currentPage.match(/^chats/)) {
+ return;
+ }
+
var username = data.message.fromUser.username;
var isSelf = parseInt(data.message.fromUser.uid, 10) === parseInt(app.user.uid, 10);
data.message.self = data.self;
@@ -252,6 +256,17 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra
}
});
+ chatModal.find('[component="chat/messages"]')
+ .on('click', '[data-action="edit"]', function() {
+ var messageId = $(this).parents('[data-mid]').attr('data-mid');
+ var inputEl = chatModal.find('[component="chat/input"]');
+ Chats.prepEdit(inputEl, messageId);
+ })
+ .on('click', '[data-action="delete"]', function() {
+ var messageId = $(this).parents('[data-mid]').attr('data-mid');
+ Chats.delete(messageId);
+ });
+
Chats.addSinceHandler(chatModal.attr('touid'), chatModal.find('.chat-content'), chatModal.find('[data-since]'));
Chats.addSendHandlers(chatModal.attr('touid'), chatModal.find('#chat-message-input'), chatModal.find('#chat-message-send-btn'));
diff --git a/public/src/modules/components.js b/public/src/modules/components.js
index da03bacd16..f4de069e01 100644
--- a/public/src/modules/components.js
+++ b/public/src/modules/components.js
@@ -35,6 +35,13 @@ define('components', function() {
'categories/category': function(name, value) {
return $('[component="categories/category"][data-' + name + '="' + value + '"]');
+ },
+
+ 'chat/message': function(messageId) {
+ return $('[component="chat/message"][data-mid="' + messageId + '"]');
+ },
+ 'chat/message/body': function(messageId) {
+ return $('[component="chat/message"][data-mid="' + messageId + '"] [component="chat/message/body"]');
}
};
diff --git a/src/messaging.js b/src/messaging.js
index e0a39eac59..7d3a89316e 100644
--- a/src/messaging.js
+++ b/src/messaging.js
@@ -106,6 +106,81 @@ var db = require('./database'),
});
};
+ Messaging.editMessage = function(mid, content, callback) {
+ async.series([
+ function(next) {
+ // Verify that the message actually changed
+ Messaging.getMessageField(mid, 'content', function(err, raw) {
+ if (raw === content) {
+ // No dice.
+ return callback();
+ }
+
+ next();
+ });
+ },
+ async.apply(Messaging.setMessageFields, mid, {
+ content: content,
+ edited: Date.now()
+ }),
+ function(next) {
+ Messaging.getMessageFields(mid, ['fromuid', 'touid'], function(err, data) {
+ getMessages([mid], data.fromuid, data.touid, true, function(err, messages) {
+ sockets.in('uid_' + data.fromuid).emit('event:chats.edit', {
+ messages: messages
+ });
+ sockets.in('uid_' + data.touid).emit('event:chats.edit', {
+ messages: messages
+ });
+ next();
+ });
+ });
+ }
+ ], callback);
+ };
+
+ Messaging.deleteMessage = function(mid, callback) {
+ var uids = [];
+ async.series([
+ function(next) {
+ db.getObject('message:' + mid, function(err, messageObj) {
+ messageObj.fromuid = parseInt(messageObj.fromuid, 10);
+ messageObj.touid = parseInt(messageObj.touid, 10);
+ uids.push(messageObj.fromuid, messageObj.touid);
+ uids.sort(function(a, b) {
+ return a > b ? 1 : -1;
+ });
+ next();
+ });
+ },
+ function(next) {
+ next();
+ },
+ function(next) {
+ db.sortedSetRemove('messages:uid:' + uids[0] + ':to:' + uids[1], mid, next);
+ },
+ async.apply(db.delete, 'message:' + mid)
+ ], callback);
+ };
+
+ Messaging.getMessageField = function(mid, field, callback) {
+ Messaging.getMessageFields(mid, [field], function(err, fields) {
+ callback(err, fields[field]);
+ });
+ };
+
+ Messaging.getMessageFields = function(mid, fields, callback) {
+ db.getObjectFields('message:' + mid, fields, callback);
+ };
+
+ Messaging.setMessageField = function(mid, field, content, callback) {
+ db.setObjectField('message:' + mid, field, content, callback);
+ };
+
+ Messaging.setMessageFields = function(mid, data, callback) {
+ db.setObject('message:' + mid, data, callback);
+ };
+
Messaging.getMessages = function(params, callback) {
var fromuid = params.fromuid,
touid = params.touid,
@@ -160,7 +235,12 @@ var db = require('./database'),
async.waterfall([
async.apply(db.getObjects, keys),
function(messages, next) {
- messages = messages.filter(Boolean);
+ messages = messages.map(function(msg, idx) {
+ if (msg) {
+ msg.messageId = parseInt(mids[idx], 10);
+ }
+ return msg;
+ }).filter(Boolean);
async.map(messages, function(message, next) {
var self = parseInt(message.fromuid, 10) === parseInt(fromuid, 10);
message.fromUser = self ? userData[0] : userData[1];
@@ -169,6 +249,10 @@ var db = require('./database'),
message.self = self ? 1 : 0;
message.newSet = false;
+ if (message.hasOwnProperty('edited')) {
+ message.editedISO = new Date(parseInt(message.edited, 10)).toISOString();
+ }
+
Messaging.parse(message.content, message.fromuid, fromuid, userData[1], userData[0], isNew, function(result) {
message.content = result;
message.cleanedContent = S(result).stripTags().decodeHTMLEntities().s;
@@ -177,21 +261,50 @@ var db = require('./database'),
}, next);
},
function(messages, next) {
- // 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 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) {
- // If the previous message was from the other person, this is also a new set
- message.newSet = true;
- }
+ if (messages.length > 1) {
+ // 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 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) {
+ // If the previous message was from the other person, this is also a new set
+ message.newSet = true;
+ }
- return message;
- });
+ return message;
+ });
- next(undefined, messages);
+ next(undefined, messages);
+ } else {
+ // For single messages, we don't know the context, so look up the previous message and compare
+ var uids = [fromuid, touid].sort(function(a, b) { return a > b ? 1 : -1 });
+ var key = 'messages:uid:' + uids[0] + ':to:' + uids[1];
+ async.waterfall([
+ async.apply(db.sortedSetRank, key, messages[0].messageId),
+ function(index, next) {
+ db.getSortedSetRange(key, index-1, index-1, next);
+ },
+ function(mid, next) {
+ Messaging.getMessageFields(mid, ['fromuid', 'timestamp'], next);
+ }
+ ], function(err, fields) {
+ if (err) {
+ return next(err);
+ }
+
+ if (
+ (parseInt(messages[0].timestamp, 10) > parseInt(fields.timestamp, 10) + (1000*60*5)) ||
+ (parseInt(messages[0].fromuid, 10) !== parseInt(fields.fromuid, 10))
+ ) {
+ // If it's been 5 minutes, this is a new set of messages
+ messages[0].newSet = true;
+ }
+
+ next(undefined, messages);
+ });
+ }
}
], callback);
});
@@ -277,7 +390,7 @@ var db = require('./database'),
count: 1,
markRead: false
}, function(err, teaser) {
- var teaser = teaser[0];
+ teaser = teaser[0];
teaser.content = S(teaser.content).stripTags().decodeHTMLEntities().s;
next(err, teaser);
});
@@ -415,6 +528,39 @@ var db = require('./database'),
], callback);
};
+ Messaging.canEdit = function(messageId, uid, callback) {
+ if (parseInt(meta.config.disableChat) === 1) {
+ return callback(null, false);
+ }
+
+ async.waterfall([
+ function (next) {
+ user.getUserFields(uid, ['banned', 'email:confirmed'], next);
+ },
+ function (userData, next) {
+ if (parseInt(userData.banned, 10) === 1) {
+ return callback(null, false);
+ }
+
+ if (parseInt(meta.config.requireEmailConfirmation, 10) === 1 && parseInt(userData['email:confirmed'], 10) !== 1) {
+ return callback(null, false);
+ }
+
+ Messaging.getMessageField(messageId, 'fromuid', next);
+ },
+ function(fromUid, next) {
+ if (parseInt(fromUid, 10) === parseInt(uid, 10)) {
+ return callback(null, true);
+ }
+
+ user.isAdministrator(uid, next);
+ },
+ function(isAdmin, next) {
+ next(null, isAdmin);
+ }
+ ], callback);
+ };
+
function sendNotifications(fromuid, touid, messageObj, callback) {
user.isOnline(touid, function(err, isOnline) {
if (err || isOnline) {
diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js
index 08b8a4a26a..7b6ed82778 100644
--- a/src/socket.io/modules.js
+++ b/src/socket.io/modules.js
@@ -29,6 +29,14 @@ SocketModules.chats.get = function(socket, data, callback) {
}, callback);
};
+SocketModules.chats.getRaw = function(socket, data, callback) {
+ if(!data || !data.hasOwnProperty('mid')) {
+ return callback(new Error('[[error:invalid-data]]'));
+ }
+
+ Messaging.getMessageField(data.mid, 'content', callback);
+};
+
SocketModules.chats.send = function(socket, data, callback) {
if (!data) {
return callback(new Error('[[error:invalid-data]]'));
@@ -62,6 +70,32 @@ SocketModules.chats.send = function(socket, data, callback) {
});
};
+SocketModules.chats.edit = function(socket, data, callback) {
+ if (!data) {
+ return callback(new Error('[[error:invalid-data]]'));
+ }
+
+ Messaging.canEdit(data.mid, socket.uid, function(err, allowed) {
+ if (allowed) {
+ Messaging.editMessage(data.mid, data.message, callback);
+ } else {
+ return callback(new Error('[[error:cant-edit-chat-message]]'));
+ }
+ });
+};
+
+SocketModules.chats.delete = function(socket, data, callback) {
+ if (!data) {
+ return callback(new Error('[[error:invalid-data]]'));
+ }
+
+ Messaging.canEdit(data.messageId, socket.uid, function(err, allowed) {
+ if (allowed) {
+ Messaging.deleteMessage(data.messageId, callback);
+ }
+ });
+}
+
SocketModules.chats.canMessage = function(socket, toUid, callback) {
Messaging.canMessage(socket.uid, toUid, function(err, allowed) {
callback(!allowed ? new Error('[[error:chat-restricted]]') : undefined);
From 3b6b36cfa6417fc0bc2aeb284a8b7e2d368b1102 Mon Sep 17 00:00:00 2001
From: Julian Lam
Date: Fri, 11 Dec 2015 19:50:21 -0500
Subject: [PATCH 12/59] fixing possible crash
---
src/messaging.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/messaging.js b/src/messaging.js
index 7d3a89316e..81758f43b7 100644
--- a/src/messaging.js
+++ b/src/messaging.js
@@ -390,7 +390,7 @@ var db = require('./database'),
count: 1,
markRead: false
}, function(err, teaser) {
- teaser = teaser[0];
+ var teaser = teaser[0];
teaser.content = S(teaser.content).stripTags().decodeHTMLEntities().s;
next(err, teaser);
});
From bcbc8608a4664d1c83181ed03e513306919e6f0f Mon Sep 17 00:00:00 2001
From: Julian Lam
Date: Fri, 11 Dec 2015 20:04:27 -0500
Subject: [PATCH 13/59] newSet calculations bugfix
---
src/messaging.js | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/src/messaging.js b/src/messaging.js
index 81758f43b7..bff12313ba 100644
--- a/src/messaging.js
+++ b/src/messaging.js
@@ -284,7 +284,13 @@ var db = require('./database'),
async.waterfall([
async.apply(db.sortedSetRank, key, messages[0].messageId),
function(index, next) {
- db.getSortedSetRange(key, index-1, index-1, next);
+ // Continue only if this isn't the first message in sorted set
+ if (index > 0) {
+ db.getSortedSetRange(key, index-1, index-1, next);
+ } else {
+ messages[0].newSet = true;
+ return next(undefined, messages);
+ }
},
function(mid, next) {
Messaging.getMessageFields(mid, ['fromuid', 'timestamp'], next);
@@ -390,7 +396,7 @@ var db = require('./database'),
count: 1,
markRead: false
}, function(err, teaser) {
- var teaser = teaser[0];
+ teaser = teaser[0];
teaser.content = S(teaser.content).stripTags().decodeHTMLEntities().s;
next(err, teaser);
});
From b4c8301596ea7a97992c168d58a078ffdea732d2 Mon Sep 17 00:00:00 2001
From: Aziz Khoury
Date: Sat, 12 Dec 2015 13:07:51 -0500
Subject: [PATCH 14/59] [minor] meta tag, content-type shouldnt be escaped
so `text/html; charset=UTF-8` and not `text/html; charset=UTF-8`
---
src/meta/tags.js | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/meta/tags.js b/src/meta/tags.js
index 55e5bd32d2..8495b827a6 100644
--- a/src/meta/tags.js
+++ b/src/meta/tags.js
@@ -17,7 +17,8 @@ module.exports = function(Meta) {
content: 'width=device-width, initial-scale=1.0, user-scalable=no'
}, {
name: 'content-type',
- content: 'text/html; charset=UTF-8'
+ content: 'text/html; charset=UTF-8',
+ noEscape: true
}, {
name: 'apple-mobile-web-app-capable',
content: 'yes'
@@ -124,4 +125,4 @@ module.exports = function(Meta) {
});
}
}
-};
\ No newline at end of file
+};
From 518552de648bc131e53c295dcb9fc970f874c616 Mon Sep 17 00:00:00 2001
From: barisusakli
Date: Sun, 13 Dec 2015 17:07:36 +0200
Subject: [PATCH 15/59] dont calculate pid index on new replies
---
src/topics/create.js | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/src/topics/create.js b/src/topics/create.js
index 828214df28..af576b9d03 100644
--- a/src/topics/create.js
+++ b/src/topics/create.js
@@ -251,11 +251,6 @@ module.exports = function(Topics) {
Topics.follow(postData.tid, uid);
}
- posts.getPidIndex(postData.pid, postData.tid, settings.topicPostSort, next);
- },
- function(postIndex, next) {
- postData.index = postIndex;
-
if (parseInt(uid, 10)) {
Topics.notifyFollowers(postData, uid);
user.setUserField(uid, 'lastonline', Date.now());
@@ -297,6 +292,7 @@ module.exports = function(Topics) {
function (results, next) {
postData.user = results.userInfo[0];
postData.topic = results.topicInfo;
+ postData.index = parseInt(results.topicInfo.postcount, 10) - 1;
// Username override for guests, if enabled
if (parseInt(meta.config.allowGuestHandles, 10) === 1 && parseInt(postData.uid, 10) === 0 && data.handle) {
From 24a302ea1acdec84101e3694aa358b82e67e4d9b Mon Sep 17 00:00:00 2001
From: barisusakli
Date: Sun, 13 Dec 2015 18:06:14 +0200
Subject: [PATCH 16/59] closes #3947
---
src/socket.io/topics/move.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/socket.io/topics/move.js b/src/socket.io/topics/move.js
index 42c82f6b8f..80fc2e3772 100644
--- a/src/socket.io/topics/move.js
+++ b/src/socket.io/topics/move.js
@@ -55,7 +55,7 @@ module.exports = function(SocketTopics) {
async.waterfall([
function (next) {
- privileges.categories.canMoveAllTopics(data.currentCid, data.cid, data.uid, next);
+ privileges.categories.canMoveAllTopics(data.currentCid, data.cid, socket.uid, next);
},
function (canMove, next) {
if (!canMove) {
From 1cf2ad339a7a56268d37d765034502c0c791f38d Mon Sep 17 00:00:00 2001
From: Julian Lam
Date: Mon, 14 Dec 2015 09:28:15 -0500
Subject: [PATCH 17/59] latest translations and fallbacks
---
public/language/ar/error.json | 1 +
public/language/ar/modules.json | 1 +
public/language/bg/error.json | 1 +
public/language/bg/modules.json | 3 +-
public/language/bg/topic.json | 12 ++++----
public/language/bg/user.json | 4 +--
public/language/bn/error.json | 1 +
public/language/bn/modules.json | 1 +
public/language/cs/error.json | 1 +
public/language/cs/modules.json | 1 +
public/language/da/error.json | 1 +
public/language/da/modules.json | 1 +
public/language/de/error.json | 1 +
public/language/de/modules.json | 1 +
public/language/de/pages.json | 2 +-
public/language/el/error.json | 1 +
public/language/el/modules.json | 1 +
public/language/en@pirate/error.json | 7 ++---
public/language/en@pirate/modules.json | 1 +
public/language/en_US/error.json | 7 ++---
public/language/en_US/modules.json | 1 +
public/language/es/error.json | 1 +
public/language/es/modules.json | 1 +
public/language/et/error.json | 1 +
public/language/et/modules.json | 1 +
public/language/fa_IR/category.json | 2 +-
public/language/fa_IR/error.json | 39 ++++++++++++------------
public/language/fa_IR/global.json | 8 ++---
public/language/fa_IR/groups.json | 6 ++--
public/language/fa_IR/login.json | 2 +-
public/language/fa_IR/modules.json | 13 ++++----
public/language/fa_IR/notifications.json | 6 ++--
public/language/fa_IR/pages.json | 22 ++++++-------
public/language/fa_IR/topic.json | 18 +++++------
public/language/fa_IR/user.json | 16 +++++-----
public/language/fi/error.json | 1 +
public/language/fi/modules.json | 1 +
public/language/fr/error.json | 1 +
public/language/fr/modules.json | 1 +
public/language/gl/error.json | 1 +
public/language/gl/modules.json | 1 +
public/language/he/error.json | 1 +
public/language/he/modules.json | 1 +
public/language/hu/error.json | 1 +
public/language/hu/modules.json | 1 +
public/language/id/error.json | 1 +
public/language/id/modules.json | 1 +
public/language/it/error.json | 1 +
public/language/it/modules.json | 1 +
public/language/ja/error.json | 1 +
public/language/ja/modules.json | 1 +
public/language/ko/error.json | 1 +
public/language/ko/modules.json | 1 +
public/language/lt/error.json | 1 +
public/language/lt/modules.json | 1 +
public/language/ms/error.json | 1 +
public/language/ms/modules.json | 1 +
public/language/nb/error.json | 1 +
public/language/nb/modules.json | 1 +
public/language/nl/error.json | 1 +
public/language/nl/modules.json | 1 +
public/language/pl/error.json | 1 +
public/language/pl/modules.json | 1 +
public/language/pt_BR/error.json | 1 +
public/language/pt_BR/modules.json | 1 +
public/language/ro/error.json | 1 +
public/language/ro/modules.json | 1 +
public/language/ru/error.json | 1 +
public/language/ru/modules.json | 1 +
public/language/rw/error.json | 1 +
public/language/rw/modules.json | 1 +
public/language/sc/error.json | 1 +
public/language/sc/modules.json | 1 +
public/language/sk/error.json | 1 +
public/language/sk/modules.json | 1 +
public/language/sl/error.json | 1 +
public/language/sl/modules.json | 1 +
public/language/sr/error.json | 1 +
public/language/sr/modules.json | 1 +
public/language/sv/error.json | 1 +
public/language/sv/modules.json | 1 +
public/language/th/error.json | 1 +
public/language/th/modules.json | 1 +
public/language/tr/error.json | 1 +
public/language/tr/modules.json | 1 +
public/language/vi/error.json | 1 +
public/language/vi/modules.json | 1 +
public/language/zh_CN/error.json | 1 +
public/language/zh_CN/modules.json | 1 +
public/language/zh_TW/error.json | 1 +
public/language/zh_TW/modules.json | 1 +
91 files changed, 159 insertions(+), 83 deletions(-)
diff --git a/public/language/ar/error.json b/public/language/ar/error.json
index 7bb7290cec..32e348c4f9 100644
--- a/public/language/ar/error.json
+++ b/public/language/ar/error.json
@@ -78,6 +78,7 @@
"too-many-messages": "لقد أرسلت الكثير من الرسائل، الرجاء اﻹنتظار قليلاً",
"invalid-chat-message": "Invalid chat message",
"chat-message-too-long": "Chat message is too long",
+ "cant-edit-chat-message": "You are not allowed to edit this message",
"reputation-system-disabled": "نظام السمعة معطل",
"downvoting-disabled": "التصويتات السلبية معطلة",
"not-enough-reputation-to-downvote": "ليس لديك سمعة تكفي لإضافة صوت سلبي لهذا الموضوع",
diff --git a/public/language/ar/modules.json b/public/language/ar/modules.json
index 17998d13f9..34c2c43e43 100644
--- a/public/language/ar/modules.json
+++ b/public/language/ar/modules.json
@@ -15,6 +15,7 @@
"chat.seven_days": "7 أيام",
"chat.thirty_days": "30 يومًا",
"chat.three_months": "3 أشهر",
+ "chat.delete_message_confirm": "Are you sure you wish to delete this message?",
"composer.compose": "اكتب",
"composer.show_preview": "عرض المعاينة",
"composer.hide_preview": "إخفاء المعاينة",
diff --git a/public/language/bg/error.json b/public/language/bg/error.json
index cbc97af490..92f321a474 100644
--- a/public/language/bg/error.json
+++ b/public/language/bg/error.json
@@ -78,6 +78,7 @@
"too-many-messages": "Изпратили сте твърде много съобщения. Моля, изчакайте малко.",
"invalid-chat-message": "Невалидно съобщение",
"chat-message-too-long": "Съобщението е твърде дълго",
+ "cant-edit-chat-message": "Нямате право да редактирате това съобщение",
"reputation-system-disabled": "Системата за репутация е изключена.",
"downvoting-disabled": "Отрицателното гласуване е изключено",
"not-enough-reputation-to-downvote": "Нямате достатъчно репутация, за да гласувате отрицателно за тази публикация",
diff --git a/public/language/bg/modules.json b/public/language/bg/modules.json
index cb4509c571..884db0222a 100644
--- a/public/language/bg/modules.json
+++ b/public/language/bg/modules.json
@@ -15,12 +15,13 @@
"chat.seven_days": "7 дни",
"chat.thirty_days": "30 дни",
"chat.three_months": "3 месеца",
+ "chat.delete_message_confirm": "Сигурен/а ли сте, че искате да изтриете това съобщение?",
"composer.compose": "Писане",
"composer.show_preview": "Показване на прегледа",
"composer.hide_preview": "Скриване на прегледа",
"composer.user_said_in": "%1 каза в %2:",
"composer.user_said": "%1 каза:",
- "composer.discard": "Сигурни ли сте, че искате да отхвърлите тази публикация?",
+ "composer.discard": "Сигурен/а ли сте, че искате да отхвърлите тази публикация?",
"composer.submit_and_lock": "Публикуване и заключване",
"composer.toggle_dropdown": "Превключване на падащото меню",
"composer.uploading": "Качване на %1",
diff --git a/public/language/bg/topic.json b/public/language/bg/topic.json
index 217b68c211..f698f949dd 100644
--- a/public/language/bg/topic.json
+++ b/public/language/bg/topic.json
@@ -48,15 +48,15 @@
"thread_tools.move_all": "Преместване на всички",
"thread_tools.fork": "Разделяне на темата",
"thread_tools.delete": "Изтриване на темата",
- "thread_tools.delete_confirm": "Сигурни ли сте, че искате да изтриете тази тема?",
+ "thread_tools.delete_confirm": "Сигурен/а ли сте, че искате да изтриете тази тема?",
"thread_tools.restore": "Възстановяване на темата",
- "thread_tools.restore_confirm": "Сигурни ли сте, че искате да възстановите тази тема?",
+ "thread_tools.restore_confirm": "Сигурен/а ли сте, че искате да възстановите тази тема?",
"thread_tools.purge": "Изчистване на темата",
- "thread_tools.purge_confirm": "Сигурни ли сте, че искате да изчистите тази тема?",
+ "thread_tools.purge_confirm": "Сигурен/а ли сте, че искате да изчистите тази тема?",
"topic_move_success": "Темата беше преместена успешно в %1",
- "post_delete_confirm": "Сигурни ли сте, че искате да изтриете тази публикация?",
- "post_restore_confirm": "Сигурни ли сте, че искате да възстановите тази публикация?",
- "post_purge_confirm": "Сигурни ли сте, че искате да изчистите тази публикация?",
+ "post_delete_confirm": "Сигурен/а ли сте, че искате да изтриете тази публикация?",
+ "post_restore_confirm": "Сигурен/а ли сте, че искате да възстановите тази публикация?",
+ "post_purge_confirm": "Сигурен/а ли сте, че искате да изчистите тази публикация?",
"load_categories": "Зареждане на категориите",
"disabled_categories_note": "Изключените категории са засивени",
"confirm_move": "Преместване",
diff --git a/public/language/bg/user.json b/public/language/bg/user.json
index c6f82b78d8..6b2a9a87b9 100644
--- a/public/language/bg/user.json
+++ b/public/language/bg/user.json
@@ -10,8 +10,8 @@
"ban_account_confirm": "Наистина ли искате да блокирате този потребител?",
"unban_account": "Отблокиране на акаунта",
"delete_account": "Изтриване на акаунта",
- "delete_account_confirm": "Сигурни ли сте, че искате да изтриете акаунта си? Това действие е необратимо и няма да можете да възстановите нищо от данните си
Въведете потребителското си име, за да потвърдите, че искате да унищожите този акаунт.",
- "delete_this_account_confirm": "Сигурни ли сте, че искате да изтриете този акаунт? Това действие е необратимо и няма да можете да възстановите нищо от данните
",
+ "delete_account_confirm": "Сигурен/а ли сте, че искате да изтриете акаунта си? Това действие е необратимо и няма да можете да възстановите нищо от данните си
Въведете потребителското си име, за да потвърдите, че искате да унищожите този акаунт.",
+ "delete_this_account_confirm": "Сигурен/а ли сте, че искате да изтриете този акаунт? Това действие е необратимо и няма да можете да възстановите нищо от данните
",
"account-deleted": "Акаунтът е изтрит",
"fullname": "Цяло име",
"website": "Уеб сайт",
diff --git a/public/language/bn/error.json b/public/language/bn/error.json
index a8273c1320..7905e5e766 100644
--- a/public/language/bn/error.json
+++ b/public/language/bn/error.json
@@ -78,6 +78,7 @@
"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",
+ "cant-edit-chat-message": "You are not allowed to edit this message",
"reputation-system-disabled": "সম্মাননা ব্যাবস্থা নিস্ক্রীয় রাখা হয়েছে",
"downvoting-disabled": "ঋণাত্মক ভোট নিস্ক্রীয় রাখা হয়েছে।",
"not-enough-reputation-to-downvote": "আপনার এই পোস্ট downvote করার জন্য পর্যাপ্ত সম্মাননা নেই",
diff --git a/public/language/bn/modules.json b/public/language/bn/modules.json
index dc26f2fd17..27052c6ef9 100644
--- a/public/language/bn/modules.json
+++ b/public/language/bn/modules.json
@@ -15,6 +15,7 @@
"chat.seven_days": "৭ দিন",
"chat.thirty_days": "৩০ দিন",
"chat.three_months": "৩ মাস",
+ "chat.delete_message_confirm": "Are you sure you wish to delete this message?",
"composer.compose": "Compose",
"composer.show_preview": "Show Preview",
"composer.hide_preview": "Hide Preview",
diff --git a/public/language/cs/error.json b/public/language/cs/error.json
index 3efb6790f4..79ff4e89f2 100644
--- a/public/language/cs/error.json
+++ b/public/language/cs/error.json
@@ -78,6 +78,7 @@
"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",
+ "cant-edit-chat-message": "You are not allowed to edit this message",
"reputation-system-disabled": "Systém reputací je zakázán.",
"downvoting-disabled": "Downvoting is disabled",
"not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post",
diff --git a/public/language/cs/modules.json b/public/language/cs/modules.json
index cd4fe64059..d3687e90bb 100644
--- a/public/language/cs/modules.json
+++ b/public/language/cs/modules.json
@@ -15,6 +15,7 @@
"chat.seven_days": "7 dní",
"chat.thirty_days": "30 dní",
"chat.three_months": "3 měsíce",
+ "chat.delete_message_confirm": "Are you sure you wish to delete this message?",
"composer.compose": "Compose",
"composer.show_preview": "Show Preview",
"composer.hide_preview": "Hide Preview",
diff --git a/public/language/da/error.json b/public/language/da/error.json
index fb02400438..86deb28910 100644
--- a/public/language/da/error.json
+++ b/public/language/da/error.json
@@ -78,6 +78,7 @@
"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",
+ "cant-edit-chat-message": "You are not allowed to edit this message",
"reputation-system-disabled": "Vurderingssystem er slået fra.",
"downvoting-disabled": "Nedvurdering er slået fra",
"not-enough-reputation-to-downvote": "Du har ikke nok omdømme til at nedstemme dette indlæg",
diff --git a/public/language/da/modules.json b/public/language/da/modules.json
index a15c48fffd..6f4af1099c 100644
--- a/public/language/da/modules.json
+++ b/public/language/da/modules.json
@@ -15,6 +15,7 @@
"chat.seven_days": "7 dage",
"chat.thirty_days": "30 dage",
"chat.three_months": "3 måneder",
+ "chat.delete_message_confirm": "Are you sure you wish to delete this message?",
"composer.compose": "Skriv",
"composer.show_preview": "Vis forhåndsvisning",
"composer.hide_preview": "Fjern forhåndsvisning",
diff --git a/public/language/de/error.json b/public/language/de/error.json
index e4918e8800..ae24d940a3 100644
--- a/public/language/de/error.json
+++ b/public/language/de/error.json
@@ -78,6 +78,7 @@
"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",
+ "cant-edit-chat-message": "Du darfst diese Nachricht nicht ändern",
"reputation-system-disabled": "Das Reputationssystem ist deaktiviert.",
"downvoting-disabled": "Downvotes sind deaktiviert.",
"not-enough-reputation-to-downvote": "Deine Reputation ist zu niedrig, um diesen Beitrag negativ zu bewerten.",
diff --git a/public/language/de/modules.json b/public/language/de/modules.json
index 4c0a2058d1..3f4a175a72 100644
--- a/public/language/de/modules.json
+++ b/public/language/de/modules.json
@@ -15,6 +15,7 @@
"chat.seven_days": "7 Tage",
"chat.thirty_days": "30 Tage",
"chat.three_months": "3 Monate",
+ "chat.delete_message_confirm": "Bist du sicher, dass du diese Nachricht löschen möchtest?",
"composer.compose": "Verfassen",
"composer.show_preview": "Vorschau zeigen",
"composer.hide_preview": "Vorschau ausblenden",
diff --git a/public/language/de/pages.json b/public/language/de/pages.json
index 6eda35c198..a914d22356 100644
--- a/public/language/de/pages.json
+++ b/public/language/de/pages.json
@@ -35,7 +35,7 @@
"account/favourites": "Von %1 favorisierte Beiträge",
"account/settings": "Benutzer-Einstellungen",
"account/watched": "Themen angeschaut von %1",
- "maintenance.text": "%1 befindet sich derzeit in der Wartung. Bitte komm später wieder.",
+ "maintenance.text": "%1 befindet sich derzeit in der Wartung. Bitte komme später wieder.",
"maintenance.messageIntro": "Zusätzlich hat der Administrator diese Nachricht hinterlassen:",
"throttled.text": "%1 ist momentan aufgrund von Überlastung nicht verfügbar. Bitte komm später wieder."
}
\ No newline at end of file
diff --git a/public/language/el/error.json b/public/language/el/error.json
index 2b18e271de..2b88dc7741 100644
--- a/public/language/el/error.json
+++ b/public/language/el/error.json
@@ -78,6 +78,7 @@
"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",
+ "cant-edit-chat-message": "You are not allowed to edit this message",
"reputation-system-disabled": "Το σύστημα φήμης έχει απενεργοποιηθεί.",
"downvoting-disabled": "Η καταψήφιση έχει απενεργοποιηθεί",
"not-enough-reputation-to-downvote": "Δεν έχεις αρκετή φήμη για να καταψηφίσεις αυτή την δημοσίευση",
diff --git a/public/language/el/modules.json b/public/language/el/modules.json
index a47af0e58a..f5ebd8bf9d 100644
--- a/public/language/el/modules.json
+++ b/public/language/el/modules.json
@@ -15,6 +15,7 @@
"chat.seven_days": "7 Ημέρες",
"chat.thirty_days": "30 Ημέρες",
"chat.three_months": "3 Μήνες",
+ "chat.delete_message_confirm": "Are you sure you wish to delete this message?",
"composer.compose": "Compose",
"composer.show_preview": "Show Preview",
"composer.hide_preview": "Hide Preview",
diff --git a/public/language/en@pirate/error.json b/public/language/en@pirate/error.json
index 0933126bc8..f942fa3536 100644
--- a/public/language/en@pirate/error.json
+++ b/public/language/en@pirate/error.json
@@ -78,6 +78,7 @@
"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",
+ "cant-edit-chat-message": "You are not allowed to edit this message",
"reputation-system-disabled": "Reputation system is disabled.",
"downvoting-disabled": "Downvoting is disabled",
"not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post",
@@ -87,7 +88,5 @@
"registration-error": "Registration Error",
"parse-error": "Something went wrong while parsing server response",
"wrong-login-type-email": "Please use your email to login",
- "wrong-login-type-username": "Please use your username to login",
-
- "invite-maximum-met": "You have invited the maximum amount of people (%1 out of %2)."
-}
+ "wrong-login-type-username": "Please use your username to login"
+}
\ No newline at end of file
diff --git a/public/language/en@pirate/modules.json b/public/language/en@pirate/modules.json
index 7d2111c556..6a44ff18d9 100644
--- a/public/language/en@pirate/modules.json
+++ b/public/language/en@pirate/modules.json
@@ -15,6 +15,7 @@
"chat.seven_days": "7 Days",
"chat.thirty_days": "30 Days",
"chat.three_months": "3 Months",
+ "chat.delete_message_confirm": "Are you sure you wish to delete this message?",
"composer.compose": "Compose",
"composer.show_preview": "Show Preview",
"composer.hide_preview": "Hide Preview",
diff --git a/public/language/en_US/error.json b/public/language/en_US/error.json
index 00f7ed9236..64f954a4cd 100644
--- a/public/language/en_US/error.json
+++ b/public/language/en_US/error.json
@@ -78,6 +78,7 @@
"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",
+ "cant-edit-chat-message": "You are not allowed to edit this message",
"reputation-system-disabled": "Reputation system is disabled.",
"downvoting-disabled": "Downvoting is disabled",
"not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post",
@@ -87,7 +88,5 @@
"registration-error": "Registration Error",
"parse-error": "Something went wrong while parsing server response",
"wrong-login-type-email": "Please use your email to login",
- "wrong-login-type-username": "Please use your username to login",
-
- "invite-maximum-met": "You have invited the maximum amount of people (%1 out of %2)."
-}
+ "wrong-login-type-username": "Please use your username to login"
+}
\ No newline at end of file
diff --git a/public/language/en_US/modules.json b/public/language/en_US/modules.json
index b089365c68..f38f3f7ed5 100644
--- a/public/language/en_US/modules.json
+++ b/public/language/en_US/modules.json
@@ -15,6 +15,7 @@
"chat.seven_days": "7 Days",
"chat.thirty_days": "30 Days",
"chat.three_months": "3 Months",
+ "chat.delete_message_confirm": "Are you sure you wish to delete this message?",
"composer.compose": "Compose",
"composer.show_preview": "Show Preview",
"composer.hide_preview": "Hide Preview",
diff --git a/public/language/es/error.json b/public/language/es/error.json
index 6b3cf3948b..35705567f6 100644
--- a/public/language/es/error.json
+++ b/public/language/es/error.json
@@ -78,6 +78,7 @@
"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",
+ "cant-edit-chat-message": "You are not allowed to edit this message",
"reputation-system-disabled": "El sistema de reputación está deshabilitado.",
"downvoting-disabled": "La votación negativa está deshabilitada.",
"not-enough-reputation-to-downvote": "No tienes suficiente reputación para votar negativo este post",
diff --git a/public/language/es/modules.json b/public/language/es/modules.json
index 6badc0c562..f16b1e3e1b 100644
--- a/public/language/es/modules.json
+++ b/public/language/es/modules.json
@@ -15,6 +15,7 @@
"chat.seven_days": "7 días",
"chat.thirty_days": "30 días",
"chat.three_months": "3 meses",
+ "chat.delete_message_confirm": "Are you sure you wish to delete this message?",
"composer.compose": "Crear",
"composer.show_preview": "Ver Previsualización",
"composer.hide_preview": "Ocultar Previsualización",
diff --git a/public/language/et/error.json b/public/language/et/error.json
index 666098ff89..5f4fc05e44 100644
--- a/public/language/et/error.json
+++ b/public/language/et/error.json
@@ -78,6 +78,7 @@
"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",
+ "cant-edit-chat-message": "You are not allowed to edit this message",
"reputation-system-disabled": "Reputatsiooni süsteem ei ole aktiveeritud",
"downvoting-disabled": "Negatiivsete häälte andmine ei ole võimaldatud",
"not-enough-reputation-to-downvote": "Sul ei ole piisavalt reputatsiooni, et anda negatiivset hinnangut sellele postitusele.",
diff --git a/public/language/et/modules.json b/public/language/et/modules.json
index 99ab787405..691063085f 100644
--- a/public/language/et/modules.json
+++ b/public/language/et/modules.json
@@ -15,6 +15,7 @@
"chat.seven_days": "7 Päeva",
"chat.thirty_days": "30 Päeva",
"chat.three_months": "3 Kuud",
+ "chat.delete_message_confirm": "Are you sure you wish to delete this message?",
"composer.compose": "Koosta",
"composer.show_preview": "Kuva eelvaadet",
"composer.hide_preview": "Peida eelvaade",
diff --git a/public/language/fa_IR/category.json b/public/language/fa_IR/category.json
index a17a584fd5..a75615859f 100644
--- a/public/language/fa_IR/category.json
+++ b/public/language/fa_IR/category.json
@@ -12,5 +12,5 @@
"ignore": "نادیده گرفتن",
"watch.message": "در حال حاظر شما به روز رسانی های این شاخه را دنبال می کنید",
"ignore.message": "در حال حاظر شما به روز رسانی های این شاخه را نادیده میگیرد",
- "watched-categories": "Watched categories"
+ "watched-categories": "دسته های دیده شده"
}
\ No newline at end of file
diff --git a/public/language/fa_IR/error.json b/public/language/fa_IR/error.json
index be76d173ad..dc30bcb82d 100644
--- a/public/language/fa_IR/error.json
+++ b/public/language/fa_IR/error.json
@@ -39,30 +39,30 @@
"still-uploading": "خواهشمندیم تا پایان بارگذاریها شکیبا باشید.",
"content-too-short": "خواهشمندیم دیدگاه بلندتری بنویسید. دیدگاهها دستکم باید 1% نویسه داشته باشند.",
"content-too-long": "لطفا طول مطلب را کوتاه تر کنید. طول پست نمیتواند بیشتر از %1 کاراکتر باشد.",
- "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).",
- "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).",
- "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again",
- "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again",
- "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)",
- "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)",
- "not-enough-tags": "Not enough tags. Topics must have at least %1 tag(s)",
- "too-many-tags": "Too many tags. Topics can't have more than %1 tag(s)",
- "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file",
+ "title-too-short": "لطفا یک عنوان بلندتر وارد کنید. عنوان باید حداقل 1% کاراکتر داشته باشد.",
+ "title-too-long": "لطفا یک عنوان بلندتر وارد کنید. عنوان باید حداقل 1% کاراکتر داشته باشد.",
+ "too-many-posts": "شما می توانید هر 1% ثانیه یک پست ایجاد کنید - لطفا قبل از ارسال پست جدید صبر کنید",
+ "too-many-posts-newbie": "به عنوان یک کاربر جدید ، تا زمانی که شما 2% اعتبار کسب کنید می توانید هر 1% ثانیه یک پست ایجاد کنید - لطفا قبل از ایجاد پست جدید صبر کنید .",
+ "tag-too-short": "لطفا برچسب بلندتری وارد کنید. برچسبها باید حداقل 1% کاراکتر داشته باشند.",
+ "tag-too-long": "لطفا برچسب کوتاه تری وارد کنید . برچسب ها نباید بیشتر از 1% کاراکتر داشته باشند",
+ "not-enough-tags": "تعداد برچسب ها کافی نیست. موضوع ها یابد حداقل 1% برچسب داشته باشند",
+ "too-many-tags": "تعداد برچسب ها بیشتر از حد مجاز است. موضوع ها نمی توانند بیشتر از 1% برچسب داشته باشند",
+ "file-too-big": "حداکثر مجاز حجم فایل 1% کیلوبایت می باشد - لطفا فایلی با حجم کمتر بارگزاری کنید",
"already-favourited": "شما قبلا این دیدگاه را محبوب کرده اید",
"already-unfavourited": "شما قبلا این دیدگاه را نامحبوب کرده اید",
"cant-ban-other-admins": "شما نمیتوانید دیگر مدیران را محروم کنید!",
- "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin",
+ "cant-remove-last-admin": "شما تنها مدیر می باشید . شما باید قبل از عزل خود از مدیریت یک کاربر دیگر را مدیر کنید",
"invalid-image-type": "نوع تصویر نامعتبر است. نوعهای قابل قبول اینها هستند: %1",
"invalid-image-extension": "پسوند عکس نامعتبر است",
"invalid-file-type": "نوع پرونده نامعتبر است. نوعهای قابل قبول اینها هستند: %1",
"group-name-too-short": "نام گروه خیلی کوتاه است.",
"group-already-exists": "این گروه از پیش وجود دارد.",
"group-name-change-not-allowed": "تغیر نام گروه نیاز به دسترسی دارد.",
- "group-already-member": "Already part of this group",
- "group-not-member": "Not a member of this group",
+ "group-already-member": "شما عضوی از این گروه می باشید",
+ "group-not-member": "شما عضوی از این گروه نمی باشید",
"group-needs-owner": "این گروه حداقل یک مالک باید داشته باشد",
- "group-already-invited": "This user has already been invited",
- "group-already-requested": "Your membership request has already been submitted",
+ "group-already-invited": "این کاربر قبلا به گروه دعوت شده است",
+ "group-already-requested": "درخواست عضویت شما قبلا تایید شده است",
"post-already-deleted": "این دیدگاه پیشتر پاک شده است",
"post-already-restored": "دیدگاه پیشتر بازگردانی شده است.",
"topic-already-deleted": "جستار پیشتر حذف شده است",
@@ -71,18 +71,19 @@
"topic-thumbnails-are-disabled": "چهرکهای جستار غیرفعال شده است.",
"invalid-file": "فایل نامعتبر است.",
"uploads-are-disabled": "امکان بارگذاری غیرفعال شده است.",
- "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).",
- "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).",
+ "signature-too-long": "با عرض پوزش ، امضای شما نمی تواند طولانی تر از 1% کاراکتر باشد",
+ "about-me-too-long": "با عرض پوزش محتوای 'درباره ی من' نمی تواند طولانی تر از 1% کاراکتر باشد",
"cant-chat-with-yourself": "شما نمیتوانید با خودتان گفتگو کنید!",
"chat-restricted": "این کاربر پیام های گفتگوی خود را محدود کرده است . آنها بایدشما را دنبال کنند تا اینکه شما بتوانید به آنها پیامی بفرستید",
"too-many-messages": "شما پیامهای خیلی زیادی فرستاده اید، لطفا مدتی صبر نمایید",
- "invalid-chat-message": "Invalid chat message",
- "chat-message-too-long": "Chat message is too long",
+ "invalid-chat-message": "پیغام نامعتبر",
+ "chat-message-too-long": "پیغام طولانی تر از حد مجاز است",
+ "cant-edit-chat-message": "شما اجازه ی ویرایش این پیغام را ندارید",
"reputation-system-disabled": "سیستم اعتبار غیر فعال شده است",
"downvoting-disabled": "رای منفی غیر فعال شده است",
"not-enough-reputation-to-downvote": "شما اعتبار کافی برای دادن رای منفی به این دیدگاه را ندارید.",
"not-enough-reputation-to-flag": "شما اعتبار کافی برای نشاندار کردن این دیدگاه ندارید",
- "already-flagged": "You have already flagged this post",
+ "already-flagged": "شما قبلا این پست را پرچمگذاری کرده اید",
"reload-failed": "NodeBB در هنگام بارگذاری مجدد با یک مشکل مواجه شده است: \"%1\". NodeBB سرویس رسانی به کلاینت های سرویس گیرنده را ادامه خواهد داد، اگرچه شما کاری را قبل از بارگیری مجدد انجام دادید بازگردانی کنید",
"registration-error": "خطای ثبت نام",
"parse-error": "هنگام تجزیه پاسخ سرور اشتباهی پیش امد",
diff --git a/public/language/fa_IR/global.json b/public/language/fa_IR/global.json
index 76959934cd..6289311c41 100644
--- a/public/language/fa_IR/global.json
+++ b/public/language/fa_IR/global.json
@@ -56,8 +56,8 @@
"posted_ago_by_guest": "ارسال شده در %1 توسط مهمان",
"posted_ago_by": "ارسال شده در %1 توسط %2",
"posted_ago": "ارسال شده در %1",
- "posted_in": "posted in %1",
- "posted_in_by": "posted in %1 by %2",
+ "posted_in": "پست شده در 1%",
+ "posted_in_by": "پشت شده در 1% توسط 2%",
"posted_in_ago": "ارسال شده در %1 %2",
"posted_in_ago_by": "ارسال شده در %1 %2 توسط %3",
"posted_in_ago_by_guest": "ارسال شده در %1 %2 توسط مهمان",
@@ -70,7 +70,7 @@
"recentposts": "دیدگاههای تازه",
"recentips": "آخرین IPها",
"away": "دور از دسترس",
- "dnd": "Do not disturb",
+ "dnd": "مزاحم نشوید",
"invisible": "مخفی",
"offline": "آفلاین",
"email": "رایانامه",
@@ -83,5 +83,5 @@
"follow": "دنبال کن",
"unfollow": "دنبال نکن",
"delete_all": "حذف همه",
- "map": "Map"
+ "map": "نقشه"
}
\ No newline at end of file
diff --git a/public/language/fa_IR/groups.json b/public/language/fa_IR/groups.json
index a997c32629..b378005c8d 100644
--- a/public/language/fa_IR/groups.json
+++ b/public/language/fa_IR/groups.json
@@ -12,9 +12,9 @@
"invited.none": "در حال حاضر هیچ کسی دعوت نشده است",
"invited.uninvite": "لغو دعوت",
"invited.search": "جستجو به دنبال کاربرانی به جهت دعوت به این گروه",
- "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",
+ "invited.notification_title": "شما برای پیوستن به %1 دعوت شده اید",
+ "request.notification_title": "درخواست عضویت در گروه از طرف %1",
+ "request.notification_text": "%1 درخواست عضویت در %2 را دارد",
"cover-save": "ذخیره",
"cover-saving": "در حال ذخیره کردن",
"details.title": "جزئیات گروه",
diff --git a/public/language/fa_IR/login.json b/public/language/fa_IR/login.json
index 7a7c99fd6b..578071109d 100644
--- a/public/language/fa_IR/login.json
+++ b/public/language/fa_IR/login.json
@@ -1,7 +1,7 @@
{
"username-email": "نام کاربری / رایانامه",
"username": "نام کاربری",
- "email": "رایانامه",
+ "email": "ایمیل",
"remember_me": "مرا به یاد بسپار؟",
"forgot_password": "گذرواژه را فراموش کردهاید؟",
"alternative_logins": "روشهای درون آمدن جایگزین",
diff --git a/public/language/fa_IR/modules.json b/public/language/fa_IR/modules.json
index 6548730c12..f49a077dd6 100644
--- a/public/language/fa_IR/modules.json
+++ b/public/language/fa_IR/modules.json
@@ -5,7 +5,7 @@
"chat.no_active": "شما هیچ گفتگوی فعالی ندارید.",
"chat.user_typing": "%1 در حال نوشتن است...",
"chat.user_has_messaged_you": "%1 به شما پیام داده است.",
- "chat.see_all": "See all chats",
+ "chat.see_all": "دیدن همه ی چت ها",
"chat.no-messages": "مشخص کنید تاریخچه گفتگوهایتان با چه کاربری را میخواهید ببینید",
"chat.recent-chats": "گفتگوهای اخیر",
"chat.contacts": "تماسها",
@@ -15,6 +15,7 @@
"chat.seven_days": "7 روز",
"chat.thirty_days": "30 روز",
"chat.three_months": "3 ماه",
+ "chat.delete_message_confirm": "آیا مطمئن هستید که می خواهید این پیغام را حذف کنید؟",
"composer.compose": "ارسال",
"composer.show_preview": "نمایش پیشنمایش",
"composer.hide_preview": "مخفی کردن پیشنمایش",
@@ -23,11 +24,11 @@
"composer.discard": "آیا از دور انداختن این دیدگاه اطمینان دارید؟",
"composer.submit_and_lock": "ارسال و قفل",
"composer.toggle_dropdown": "باز و بسته کردن کرکره",
- "composer.uploading": "Uploading %1",
+ "composer.uploading": "در حال بارگزاری 1%",
"bootbox.ok": "OK",
"bootbox.cancel": "Cancel",
- "bootbox.confirm": "Confirm",
- "cover.dragging_title": "Cover Photo Positioning",
- "cover.dragging_message": "Drag the cover photo to the desired position and click \"Save\"",
- "cover.saved": "Cover photo image and position saved"
+ "bootbox.confirm": "تایید",
+ "cover.dragging_title": "تنظیم مکان عکس کاور",
+ "cover.dragging_message": "عکس کاور با کلیک موس گرفته و در مکان دلخواه رها کنید و بر روی \"ذخیره\" کلیک کنید",
+ "cover.saved": "عکس کاور و مکان آن ذخیره شد"
}
\ No newline at end of file
diff --git a/public/language/fa_IR/notifications.json b/public/language/fa_IR/notifications.json
index 5d4098ccbe..276db73036 100644
--- a/public/language/fa_IR/notifications.json
+++ b/public/language/fa_IR/notifications.json
@@ -1,7 +1,7 @@
{
"title": "آگاهسازیها",
"no_notifs": "هیچ آگاهسازی تازهای ندارید",
- "see_all": "See all notifications",
+ "see_all": "دیدن همه ی اطلاعیه ها",
"mark_all_read": "همه اطلاعیه ها را خوانده شده علامت بزن",
"back_to_home": "بازگشت به %1",
"outgoing_link": "پیوند برونرو",
@@ -12,8 +12,8 @@
"you_have_unread_notifications": "شما آگاهسازیهای نخوانده دارید.",
"new_message_from": "پیام تازه از %1",
"upvoted_your_post_in": "%1 امتیاز مثبت به دیدگاه شما در %2 داده",
- "moved_your_post": "%1 has moved your post to %2",
- "moved_your_topic": "%1 has moved %2",
+ "moved_your_post": "%1 پست شما را به %2 انتقال داده است",
+ "moved_your_topic": "%2%1 را منتقل کرده است",
"favourited_your_post_in": "%1 دیدگاه شما را در %2 برگزیده کرده.",
"user_flagged_post_in": "%1 دیدگاه شما را در %2 علامتدار کرده",
"user_posted_to": "پاسخ دادن به %2 از سوی %1",
diff --git a/public/language/fa_IR/pages.json b/public/language/fa_IR/pages.json
index 6a25342754..d48dcf06c2 100644
--- a/public/language/fa_IR/pages.json
+++ b/public/language/fa_IR/pages.json
@@ -1,16 +1,16 @@
{
"home": "خانه",
"unread": "جستارههای نخوانده",
- "popular-day": "Popular topics today",
- "popular-week": "Popular topics this week",
- "popular-month": "Popular topics this month",
- "popular-alltime": "All time popular topics",
+ "popular-day": "موضوعات پربازدید امروز",
+ "popular-week": "موضوعات پربازدید این هفته",
+ "popular-month": "موضوعات پربازدید این ماه",
+ "popular-alltime": "موضوعات پربازدید",
"recent": "جستارهای تازه",
"users/online": "کاربران آنلاین",
"users/latest": "آخرین کاربران",
"users/sort-posts": "کاربران با بیشترین دیدگاه",
- "users/sort-reputation": "Users with the most reputation",
- "users/map": "User Map",
+ "users/sort-reputation": "کاربران دارای بیشترین اعتبار",
+ "users/map": "نقشه ی کاربر",
"users/search": "جستجوی کاربر",
"notifications": "آگاهسازیها",
"tags": "برچسبها",
@@ -24,9 +24,9 @@
"chats": "گفتگوها",
"chat": "گفتگو با %1",
"account/edit": "ویرایش \"%1\"",
- "account/edit/password": "Editing password of \"%1\"",
- "account/edit/username": "Editing username of \"%1\"",
- "account/edit/email": "Editing email of \"%1\"",
+ "account/edit/password": "ویرایش کلمه ی عبورِ \"1%\"",
+ "account/edit/username": "ویرایش نام کاربریِ \"1%\"",
+ "account/edit/email": "ویرایش ایمیلِ \"1%\"",
"account/following": "کاربرانی که %1 دنبال میکند",
"account/followers": "کاربرانی که %1 را دنبال میکنند",
"account/posts": "دیدگاههای %1",
@@ -34,8 +34,8 @@
"account/groups": "گروههای %1",
"account/favourites": "دیدگاههای مورد پسند %1",
"account/settings": "تنظیمات کاربر",
- "account/watched": "Topics watched by %1",
+ "account/watched": "موضوع های دیده شده توسط \"1%\"",
"maintenance.text": "1% در حال حاضر تحت تعمیر و نگهدارییست. لطفا زمان دیگری مراجعه کنید.",
"maintenance.messageIntro": "علاوه بر این، مدیر این پیام را گذاشته است:",
- "throttled.text": "%1 is currently unavailable due to excessive load. Please come back another time."
+ "throttled.text": "1% به دلیل بارگزاری بیش از حد ، قابل دسترس نمی باشد. لطفا در زمان دیگری دوباره امتحان کنید"
}
\ No newline at end of file
diff --git a/public/language/fa_IR/topic.json b/public/language/fa_IR/topic.json
index aea16ab1ab..211a360e84 100644
--- a/public/language/fa_IR/topic.json
+++ b/public/language/fa_IR/topic.json
@@ -25,7 +25,7 @@
"tools": "ابزارها",
"flag": "پرچم",
"locked": "قفل شده است",
- "bookmark_instructions": "Click here to return to the last unread post in this thread.",
+ "bookmark_instructions": "برای بازگشت به آخرین پست خوانده نشده در این دسته اینجا کلیک کنید",
"flag_title": "پرچمگذاری این جستار برای بررسی ناظران",
"flag_success": "این جستار برای بررسی ناظران پرچم گذاشته شد.",
"deleted_message": "این جستار پاک شده است. تنها کاربرانِ با حق مدیریت جستار میتوانند آن را ببینند.",
@@ -96,12 +96,12 @@
"newest_to_oldest": "جدیدترین به قدیمیترین",
"most_votes": "بیشترین رایها",
"most_posts": "بیشتر دیدگاه ها",
- "stale.title": "Create new topic instead?",
- "stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?",
- "stale.create": "Create a new topic",
- "stale.reply_anyway": "Reply to this topic anyway",
- "stale.link_back": "Re: [%1](%2)",
- "spam": "Spam",
- "offensive": "Offensive",
- "custom-flag-reason": "Enter a flagging reason"
+ "stale.title": "آیا مایلید به جای آن یک موضوع جدید ایجاد کنید؟",
+ "stale.warning": "موضوعی که شما در حال پاسخگویی به آن هستید قدیمی می باشد. آیا میلید به جای آن یک موضوع جدید ایجاد کنید و در آن به این موضوع ارجاع دهید؟",
+ "stale.create": "ایجاد یک موضوع جدید",
+ "stale.reply_anyway": "در هر صورت می خواهم به این موضوع پاسخ دهم",
+ "stale.link_back": "پاسخ : [1%](2%)",
+ "spam": "اسپم",
+ "offensive": "توهین آمیز",
+ "custom-flag-reason": "وارد کردن دلیل پرچمگذاری"
}
\ No newline at end of file
diff --git a/public/language/fa_IR/user.json b/public/language/fa_IR/user.json
index e0249abb3c..f808164656 100644
--- a/public/language/fa_IR/user.json
+++ b/public/language/fa_IR/user.json
@@ -12,7 +12,7 @@
"delete_account": "حذف حساب کاربری",
"delete_account_confirm": "آیا مطمئنید که میخواهید حساب کاربری خود را حذف کنید؟ این عمل غیر قابل بازگشت است و شما قادر نخواهید بود هیچ کدام از اطلاعات خود را بازیابی کنید./strong>
برای تایید حذف این حساب کاربری، نام کاربری خود را وارد کنید",
"delete_this_account_confirm": " آیا مطمئنید که میخواهید این حساب کاربری را حذف کنید؟ این عمل غیر قابل بازگشت است و شما قادر نخواهید بود هیچ کدام از اطلاعات را بازیابی کنید.
",
- "account-deleted": "Account deleted",
+ "account-deleted": "حساب کاربری پاک شد",
"fullname": "نام و نام خانوادگی",
"website": "تارنما",
"location": "محل سکونت",
@@ -30,16 +30,16 @@
"signature": "امضا",
"birthday": "روز تولد",
"chat": "گفتگو",
- "chat_with": "Chat with %1",
+ "chat_with": "چت کردن با 1%",
"follow": "دنبال کن",
"unfollow": "دنبال نکن",
"more": "بیشتر",
"profile_update_success": "نمایه باموفقیت به روز شده است!",
"change_picture": "تغییر تصویر",
- "change_username": "Change Username",
- "change_email": "Change Email",
+ "change_username": "تغییر نام کاربری",
+ "change_email": "تغییر ایمیل",
"edit": "ویرایش",
- "default_picture": "Default Icon",
+ "default_picture": "آیکون پیش فرض",
"uploaded_picture": "تصویر بارشده",
"upload_new_picture": "بارگذاری تصویر تازه",
"upload_new_picture_from_url": "بارگذاری تصویر جدید از نشانی وب",
@@ -54,11 +54,11 @@
"confirm_password": "تکرار گذرواژه",
"password": "گذرواژه",
"username_taken_workaround": "نام کاربری درخواستی شما در حال حاضر گرفته شده است، بنابراین ما آن را کمی تغییر دادهایم. شما هماکنون با نام %1
Date: Mon, 14 Dec 2015 11:31:49 -0500
Subject: [PATCH 18/59] updated modal to not show parentheses when max file
size is not defined...
Also fixing hideAlerts error in ACP when opening the upload modal.
---
public/src/admin/settings.js | 2 --
public/src/modules/uploader.js | 4 ++--
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/public/src/admin/settings.js b/public/src/admin/settings.js
index d6c9ec3407..b80df5450d 100644
--- a/public/src/admin/settings.js
+++ b/public/src/admin/settings.js
@@ -113,8 +113,6 @@ define('admin/settings', ['uploader', 'sounds'], function(uploader, sounds) {
uploader.open(uploadBtn.attr('data-route'), {}, 0, function(image) {
$('#' + uploadBtn.attr('data-target')).val(image);
});
-
- uploader.hideAlerts();
});
});
}
diff --git a/public/src/modules/uploader.js b/public/src/modules/uploader.js
index e951f0aabe..05bf9379b6 100644
--- a/public/src/modules/uploader.js
+++ b/public/src/modules/uploader.js
@@ -21,7 +21,7 @@ define('uploader', ['csrf'], function(csrf) {
if (fileSize) {
uploadForm.find('#file-size-block')
- .translateText('[[uploads:maximum-file-size, ' + fileSize + ']]')
+ .translateText('([[uploads:maximum-file-size, ' + fileSize + ']])')
.removeClass('hide');
} else {
uploadForm.find('#file-size-block').addClass('hide');
@@ -102,7 +102,7 @@ define('uploader', ['csrf'], function(csrf) {
}
module.hideAlerts = function(modal) {
- modal.find('#alert-status, #alert-success, #alert-error, #upload-progress-box').addClass('hide');
+ $(modal).find('#alert-status, #alert-success, #alert-error, #upload-progress-box').addClass('hide');
};
return module;
From c1460d36f0067b737c31948f973914ee13d72e51 Mon Sep 17 00:00:00 2001
From: Julian Lam
Date: Mon, 14 Dec 2015 12:00:24 -0500
Subject: [PATCH 19/59] Deprecated uploader.open, use uploader.show instead
... as it now takes a single Object argument instead of multiple
parameters.
Also, closes #3942. Help text is just omitted now.
---
public/src/admin/settings.js | 7 +++++-
public/src/modules/uploader.js | 32 ++++++++++++++++------------
src/views/admin/settings/general.tpl | 2 +-
3 files changed, 25 insertions(+), 16 deletions(-)
diff --git a/public/src/admin/settings.js b/public/src/admin/settings.js
index b80df5450d..117857d5ec 100644
--- a/public/src/admin/settings.js
+++ b/public/src/admin/settings.js
@@ -110,7 +110,12 @@ define('admin/settings', ['uploader', 'sounds'], function(uploader, sounds) {
$('#content input[data-action="upload"]').each(function() {
var uploadBtn = $(this);
uploadBtn.on('click', function() {
- uploader.open(uploadBtn.attr('data-route'), {}, 0, function(image) {
+ uploader.show({
+ route: uploadBtn.attr('data-route'),
+ params: {},
+ fileSize: 0,
+ showHelp: uploadBtn.attr('data-help') ? uploadBtn.attr('data-help') === 1 : undefined
+ }, function(image) {
$('#' + uploadBtn.attr('data-target')).val(image);
});
});
diff --git a/public/src/modules/uploader.js b/public/src/modules/uploader.js
index 05bf9379b6..d15d5ddda0 100644
--- a/public/src/modules/uploader.js
+++ b/public/src/modules/uploader.js
@@ -2,12 +2,24 @@
/* globals define, templates, translator */
-define('uploader', ['csrf'], function(csrf) {
+define('uploader', ['csrf', 'translator'], function(csrf, translator) {
var module = {};
module.open = function(route, params, fileSize, callback) {
- parseModal(function(uploadModal) {
+ console.warn('[uploader] uploader.open() is deprecated, please use uploader.show() instead, and pass parameters as a singe option with callback, e.g. uploader.show({}, callback);');
+ module.show({
+ route: route,
+ params: params,
+ fileSize: fileSize
+ }, callback);
+ };
+
+ module.show = function(data, callback) {
+ parseModal({
+ showHelp: data.hasOwnProperty('showHelp') && data.showHelp !== undefined ? data.showHelp : true,
+ fileSize: data.hasOwnProperty('fileSize') && data.fileSize !== undefined ? parseInt(data.fileSize, 10) : false
+ }, function(uploadModal) {
uploadModal = $(uploadModal);
uploadModal.modal('show');
@@ -16,16 +28,8 @@ define('uploader', ['csrf'], function(csrf) {
});
var uploadForm = uploadModal.find('#uploadForm');
- uploadForm.attr('action', route);
- uploadForm.find('#params').val(JSON.stringify(params));
-
- if (fileSize) {
- uploadForm.find('#file-size-block')
- .translateText('([[uploads:maximum-file-size, ' + fileSize + ']])')
- .removeClass('hide');
- } else {
- uploadForm.find('#file-size-block').addClass('hide');
- }
+ uploadForm.attr('action', data.route);
+ uploadForm.find('#params').val(JSON.stringify(data.params));
uploadModal.find('#pictureUploadSubmitBtn').off('click').on('click', function() {
uploadForm.submit();
@@ -84,8 +88,8 @@ define('uploader', ['csrf'], function(csrf) {
});
};
- function parseModal(callback) {
- templates.parse('partials/modals/upload_picture_modal', {}, function(html) {
+ function parseModal(tplVals, callback) {
+ templates.parse('partials/modals/upload_picture_modal', tplVals, function(html) {
translator.translate(html, callback);
});
}
diff --git a/src/views/admin/settings/general.tpl b/src/views/admin/settings/general.tpl
index e45f1093eb..147657195e 100644
--- a/src/views/admin/settings/general.tpl
+++ b/src/views/admin/settings/general.tpl
@@ -73,7 +73,7 @@
%2 gemeldet",
+ "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_posted_to": "%1 hat auf %2 geantwortet.",
+ "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",
"user_posted_topic": "%1 hat ein neues Thema erstellt: %2",
"user_started_following_you": "%1 folgt dir jetzt.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 hat eine Registrationsanfrage geschickt.",
"email-confirmed": "E-Mail bestätigt",
"email-confirmed-message": "Vielen Dank für Ihre E-Mail-Validierung. Ihr Konto ist nun vollständig aktiviert.",
diff --git a/public/language/el/notifications.json b/public/language/el/notifications.json
index 5d0447dea8..0ef1347183 100644
--- a/public/language/el/notifications.json
+++ b/public/language/el/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "Έχεις μη αναγνωσμένες ειδοποιήσεις.",
"new_message_from": "Νέο μήνυμα από τον/την %1",
"upvoted_your_post_in": "Ο/Η %1 υπερψήφισε την δημοσίευσή σου στο %2.",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "Η δημοσίευσή σου στο %2 αρέσει στον/ην %1.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "Ο/Η %1 επεσήμανε μια δημοσίευσή σου στο %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_posted_to": "Ο/Η %1 έγραψε μια απάντηση στο: %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",
"user_posted_topic": "%1 has posted a new topic: %2",
"user_started_following_you": "Ο/Η %1 σε ακολουθεί.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 sent a registration request.",
"email-confirmed": "Το Εmail Επιβεβαιώθηκε",
"email-confirmed-message": "Ευχαριστούμε που επιβεβαίωσες το email σου. Ο λογαριασμός σου είναι πλέον πλήρως ενεργοποιημένος.",
diff --git a/public/language/en@pirate/notifications.json b/public/language/en@pirate/notifications.json
index e6986f0684..3efe38a794 100644
--- a/public/language/en@pirate/notifications.json
+++ b/public/language/en@pirate/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "You have unread notifications.",
"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.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 has favourited your post in %2.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"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_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",
"user_posted_topic": "%1 has posted a new topic: %2",
"user_started_following_you": "%1 started following you.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 sent a registration request.",
"email-confirmed": "Email Confirmed",
"email-confirmed-message": "Thank you for validating your email. Your account is now fully activated.",
diff --git a/public/language/en_US/notifications.json b/public/language/en_US/notifications.json
index 1d5a798c26..f7f6f404a8 100644
--- a/public/language/en_US/notifications.json
+++ b/public/language/en_US/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "You have unread notifications.",
"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.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 has favorited your post in %2.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"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_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",
"user_posted_topic": "%1 has posted a new topic: %2",
"user_started_following_you": "%1 started following you.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 sent a registration request.",
"email-confirmed": "Email Confirmed",
"email-confirmed-message": "Thank you for validating your email. Your account is now fully activated.",
diff --git a/public/language/es/notifications.json b/public/language/es/notifications.json
index c9e5798685..506f9f83c4 100644
--- a/public/language/es/notifications.json
+++ b/public/language/es/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "Tienes notificaciones sin leer.",
"new_message_from": "Nuevo mensaje de %1",
"upvoted_your_post_in": "%1 ha votado positivamente tu respuesta en %2.",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 su tema ha sido movido a %2",
"moved_your_topic": "%1 se ha movido %2",
"favourited_your_post_in": "%1 ha marcado como favorito su publicación en %2.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1 ha reportado una respuesta en %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_posted_to": "%1 ha respondido a: %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",
"user_posted_topic": "%1 ha publicado un nuevo tema: %2",
"user_started_following_you": "%1 comenzó a seguirte.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 envió una solicitud de registro.",
"email-confirmed": "Correo electrónico confirmado",
"email-confirmed-message": "Gracias por validar tu correo electrónico. Tu cuenta ya está completamente activa.",
diff --git a/public/language/et/notifications.json b/public/language/et/notifications.json
index beaf377781..9f5bc10fd8 100644
--- a/public/language/et/notifications.json
+++ b/public/language/et/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "Sul ei ole lugemata teateid.",
"new_message_from": "Uus sõnum kasutajalt %1",
"upvoted_your_post_in": "%1 hääletas sinu postituse poolt teemas %2.",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 märgistas sinu postituse lemmikuks teemas %2.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1 raporteeris postitust %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_posted_to": "Kasutaja %1 postitas vastuse teemasse %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",
"user_posted_topic": "%1 on postitanud uue teema: %2",
"user_started_following_you": "%1 hakkas sind jälgima.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 saatis registreerimistaotluse.",
"email-confirmed": "Emaili aadress kinnitatud",
"email-confirmed-message": "Täname, et kinnitasite oma emaili aadressi. Teie kasutaja on nüüd täielikult aktiveeritud.",
diff --git a/public/language/fa_IR/notifications.json b/public/language/fa_IR/notifications.json
index 276db73036..7ca96bd4e6 100644
--- a/public/language/fa_IR/notifications.json
+++ b/public/language/fa_IR/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "شما آگاهسازیهای نخوانده دارید.",
"new_message_from": "پیام تازه از %1",
"upvoted_your_post_in": "%1 امتیاز مثبت به دیدگاه شما در %2 داده",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 پست شما را به %2 انتقال داده است",
"moved_your_topic": "%2%1 را منتقل کرده است",
"favourited_your_post_in": "%1 دیدگاه شما را در %2 برگزیده کرده.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1 دیدگاه شما را در %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_posted_to": "پاسخ دادن به %2 از سوی %1",
+ "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",
"user_posted_topic": "%1 یک جستار جدید ارسال کرده: %2",
"user_started_following_you": "%1 شروع به دنبال کردن شما کرده",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 یک درخواست ثبت نام ارسال کرده است",
"email-confirmed": "رایانامه تایید شد",
"email-confirmed-message": "بابت تایید ایمیلتان سپاسگزاریم. حساب کاربری شما اکنون به صورت کامل فعال شده است.",
diff --git a/public/language/fi/notifications.json b/public/language/fi/notifications.json
index f3c309f82b..377ea60e91 100644
--- a/public/language/fi/notifications.json
+++ b/public/language/fi/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "Sinulla on lukemattomia ilmoituksia.",
"new_message_from": "Uusi viesti käyttäjältä %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.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 has favourited your post in %2.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"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_posted_to": "%1 on vastannut viestiin: %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",
"user_posted_topic": "%1 on kirjoittanut uuden aiheen: %2",
"user_started_following_you": "%1 alkoi seurata sinua.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 sent a registration request.",
"email-confirmed": "Sähköpostiosoite vahvistettu",
"email-confirmed-message": "Kiitos sähköpostiosoitteesi vahvistamisesta. Käyttäjätilisi on nyt täysin aktivoitu.",
diff --git a/public/language/fr/notifications.json b/public/language/fr/notifications.json
index a15584d512..d66e72dc24 100644
--- a/public/language/fr/notifications.json
+++ b/public/language/fr/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "Vous avez des notifications non-lues",
"new_message_from": "Nouveau message de %1",
"upvoted_your_post_in": "%1 a voté pour votre message dans %2.",
+ "upvoted_your_post_in_dual": "%1 et %2 ont voté pour votre message dans %3.",
+ "upvoted_your_post_in_multiple": "%1 et %2 autres on voté pour votre message dans %3.",
"moved_your_post": "%1 a déplacé votre message vers %2",
"moved_your_topic": "%1 a déplacé %2.",
"favourited_your_post_in": "%1 a mis votre message en favoris dans %2.",
+ "favourited_your_post_in_dual": "%1 et %2 ont mis votre message dans %3 en favori.",
+ "favourited_your_post_in_multiple": "%1 et %2 autres on mis votre message en favori %3.",
"user_flagged_post_in": "%1 a signalé un message dans %2.",
+ "user_flagged_post_in_dual": "%1 et %2 ont signalé un message dans %3",
+ "user_flagged_post_in_multiple": "%1 et %2 autres on signalé un message dans %3",
"user_posted_to": "%1 a répondu à : %2",
+ "user_posted_to_dual": "%1 et %2 ont posté une réponse à : %3",
+ "user_posted_to_multiple": "%1 et %2 autres ont posté une réponse à : %3",
"user_posted_topic": "%1 a posté un nouveau sujet: %2.",
"user_started_following_you": "%1 vous suit.",
+ "user_started_following_you_dual": "%1 et %2 vous suivent.",
+ "user_started_following_you_multiple": "%1 et %2 autres vous suivent.",
"new_register": "%1 a envoyé une demande d'incription.",
"email-confirmed": "Email vérifié",
"email-confirmed-message": "Merci pour la validation de votre adresse email. Votre compte est désormais activé.",
diff --git a/public/language/gl/notifications.json b/public/language/gl/notifications.json
index 17547336f7..5c91bd8677 100644
--- a/public/language/gl/notifications.json
+++ b/public/language/gl/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "Tes notificacións non lidas",
"new_message_from": "Nova mensaxe de %1",
"upvoted_your_post_in": "%1 votoute positivo en %2.",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 moveu a túa publicación a%2",
"moved_your_topic": "%1 foi movido %2",
"favourited_your_post_in": "%1 marcou favorita a túa publicación en %2.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1 reportou unha mensaxe en %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_posted_to": "%1 publicou unha resposta en: %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",
"user_posted_topic": "%1 publicou un novo tema: %2",
"user_started_following_you": "%1 comezou a seguirte.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 enviou unha petición de rexistro.",
"email-confirmed": "Correo confirmado",
"email-confirmed-message": "Grazas por validar o teu correo. A túa conta agora está activada.",
diff --git a/public/language/he/notifications.json b/public/language/he/notifications.json
index 6d93c08512..49d1b54c31 100644
--- a/public/language/he/notifications.json
+++ b/public/language/he/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "יש לך התראות שלא נקראו.",
"new_message_from": "הודעה חדשה מ %1",
"upvoted_your_post_in": "%1 הצביע בעד הפוסט שלך ב %2",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 הוסיף את הפוסט שלך ב %2 למועדפים שלו.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1 דיווח על פוסט ב %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_posted_to": "%1 פרסם תגובה ל: %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",
"user_posted_topic": "%1 העלה נושא חדש: %2",
"user_started_following_you": "%1 התחיל לעקוב אחריך.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 sent a registration request.",
"email-confirmed": "כתובת המייל אושרה",
"email-confirmed-message": "תודה שאישרת את כתובת המייל שלך. החשבון שלך פעיל כעת.",
diff --git a/public/language/hu/notifications.json b/public/language/hu/notifications.json
index 643037bd28..f255352a1a 100644
--- a/public/language/hu/notifications.json
+++ b/public/language/hu/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "You have unread notifications.",
"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.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 has favourited your post in %2.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"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_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",
"user_posted_topic": "%1 has posted a new topic: %2",
"user_started_following_you": "%1 started following you.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 sent a registration request.",
"email-confirmed": "Email Confirmed",
"email-confirmed-message": "Thank you for validating your email. Your account is now fully activated.",
diff --git a/public/language/id/notifications.json b/public/language/id/notifications.json
index 296bfd9c2a..7333364bd9 100644
--- a/public/language/id/notifications.json
+++ b/public/language/id/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "Kamu memiliki pemberitahuan yang belum dibaca.",
"new_message_from": "Pesan baru dari %1",
"upvoted_your_post_in": "%1 telah melakukan upvote untuk posting kamu di %2.",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 telah memfavoritkan posting mu di %2.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1 menandai sebuah posting di %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_posted_to": "%1 telah mengirim sebuah balasan kepada: %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",
"user_posted_topic": "%1 telah membuat topik baru: %2",
"user_started_following_you": "%1 mulai mengikutimu.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 mengirim permintaan registrasi.",
"email-confirmed": "Email telah Dikonfirmasi",
"email-confirmed-message": "Terimakasih telah melakukan validasi email. Akunmu saat ini telah aktif sepenuhnya.",
diff --git a/public/language/it/notifications.json b/public/language/it/notifications.json
index 15ca49f2ec..e5d6625d0e 100644
--- a/public/language/it/notifications.json
+++ b/public/language/it/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "Hai notifiche non lette.",
"new_message_from": "Nuovo messaggio da %1",
"upvoted_your_post_in": "%1 ha votato positivamente il tuo post in %2.",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 ha messo nei favoriti il tuo post in %2.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1 ha segnalato un 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_posted_to": "%1 ha postato una risposta a: %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",
"user_posted_topic": "%1 ha postato un nuovo Topic: %2",
"user_started_following_you": "%1 ha iniziato a seguirti.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 ha inviato una richiesta di registrazione.",
"email-confirmed": "Email Confermata",
"email-confirmed-message": "Grazie per aver validato la tua email. Il tuo account è ora completamente attivato.",
diff --git a/public/language/ja/notifications.json b/public/language/ja/notifications.json
index e5f9facc16..963dbc3e6e 100644
--- a/public/language/ja/notifications.json
+++ b/public/language/ja/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "未読の通知があります。",
"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.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 has favourited your post in %2.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"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_posted_to": "%1 は %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",
"user_posted_topic": "%1 has posted a new topic: %2",
"user_started_following_you": "%1 started following you.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 sent a registration request.",
"email-confirmed": "Email Confirmed",
"email-confirmed-message": "Thank you for validating your email. Your account is now fully activated.",
diff --git a/public/language/ko/notifications.json b/public/language/ko/notifications.json
index e7d56db47b..97dcfc3b64 100644
--- a/public/language/ko/notifications.json
+++ b/public/language/ko/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "읽지 않은 알림이 있습니다.",
"new_message_from": "%1님이 메시지를 보냈습니다.",
"upvoted_your_post_in": "%1님이 %2의 내 게시물을 추천했습니다.",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1님이 %2의 내 게시물을 좋아합니다.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1님이 %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_posted_to": "%1님이 %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",
"user_posted_topic": "%1님이 새 주제를 작성했습니다: %2",
"user_started_following_you": "%1님이 나를 팔로우합니다.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 sent a registration request.",
"email-confirmed": "확인된 이메일",
"email-confirmed-message": "이메일을 확인해주셔서 감사합니다. 계정이 완전히 활성화되었습니다.",
diff --git a/public/language/lt/notifications.json b/public/language/lt/notifications.json
index 596993bb92..4bbd1e6fae 100644
--- a/public/language/lt/notifications.json
+++ b/public/language/lt/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "Jūs turite neperskaitytų pranešimų.",
"new_message_from": "Nauja žinutė nuo %1",
"upvoted_your_post_in": "%1 užbalsavo už jūsų pranešima čia %2.",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 patinka jūsų pranešimas čia %2",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1pagrįso nuomone čia %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_posted_to": "%1 parašė atsaką %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",
"user_posted_topic": "%1 paskelbė naują temą: %2",
"user_started_following_you": "%1 pradėjo sekti tave",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 atsiuntė registracijos prašymą",
"email-confirmed": "El. paštas patvirtintas",
"email-confirmed-message": "Dėkojame už el. pašto patvirtinimą. Jūsų paskyra pilnai aktyvuota.",
diff --git a/public/language/ms/notifications.json b/public/language/ms/notifications.json
index 34c1f104a0..196e3971ca 100644
--- a/public/language/ms/notifications.json
+++ b/public/language/ms/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "Ada pemberitahuan yang belum dibaca",
"new_message_from": "Pesanan baru daripada %1",
"upvoted_your_post_in": "%1 telah mengundi naik kiriman and di %2.",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 telah memindahkan kiriman anda ke %2",
"moved_your_topic": "%1 telah memindahkan %2",
"favourited_your_post_in": "%1 menggemari kiriman and di %2.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1 menanda kiriman anda di %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_posted_to": "%1 telah membalas kiriman kepada: %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",
"user_posted_topic": "%1 membuka topik baru : %2",
"user_started_following_you": "%1 mula mengikut anda.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 menghantar jemputan pendaftaran.",
"email-confirmed": "Emel Disahkan",
"email-confirmed-message": "Terima kasih kerana mengesahkan emel anda. Akaun anda telah diaktifkan sepenuhnya.",
diff --git a/public/language/nb/notifications.json b/public/language/nb/notifications.json
index 425997b22f..568357137a 100644
--- a/public/language/nb/notifications.json
+++ b/public/language/nb/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "Du har uleste varsler.",
"new_message_from": "Ny melding fra %1",
"upvoted_your_post_in": "%1 har stemt opp innlegget ditt i %2.",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 har favorittmerket innlegget ditt i %2",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1 har flagget et innlegg i %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_posted_to": "%1 har skrevet et svar til: %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",
"user_posted_topic": "%1 har skrevet et nytt emne: %2",
"user_started_following_you": "%1 begynte å følge deg.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 sendte en forespørsel om registrering",
"email-confirmed": "E-post bekreftet",
"email-confirmed-message": "Takk for at du har validert din e-post. Kontoen din er nå fullstendig aktivert.",
diff --git a/public/language/nl/notifications.json b/public/language/nl/notifications.json
index f049f78b92..d2c8ec0058 100644
--- a/public/language/nl/notifications.json
+++ b/public/language/nl/notifications.json
@@ -9,19 +9,29 @@
"continue_to": "Door naar %1",
"return_to": "Terug naar %1",
"new_notification": "Nieuwe melding",
- "you_have_unread_notifications": "Ongelezen berichten",
+ "you_have_unread_notifications": "Je hebt nieuwe notificaties.",
"new_message_from": "Nieuw bericht van %1",
"upvoted_your_post_in": "%1 heeft voor een bericht gestemd in %2.",
+ "upvoted_your_post_in_dual": "%1 en %2 hebben voor een bericht in gestemd in %3.",
+ "upvoted_your_post_in_multiple": "%1 en %2 andere hebben in gestemd in %3.",
"moved_your_post": "%1 heeft je bericht verplaatst naar %2",
"moved_your_topic": "%1 heeft %2 verplaatst",
"favourited_your_post_in": "%1 heeft een van je berichten in %2 aan zijn of haar favorieten toegevoegd.",
+ "favourited_your_post_in_dual": "%1 en %2 hebben een van je berichten in %3 aan zijn of haar favorieten toegevoegd.",
+ "favourited_your_post_in_multiple": "%1 en %2 hebben een van je berichten in 3 aan hun favorieten toegevoegd.",
"user_flagged_post_in": "%1 rapporteerde een bericht in %2",
+ "user_flagged_post_in_dual": "%1 en %2 rapporteerde een bericht in %3",
+ "user_flagged_post_in_multiple": "%1 en %2 andere rapporteede een bericht in %3",
"user_posted_to": "%1 heeft een reactie geplaatst in %2",
+ "user_posted_to_dual": "%1 en %2 hebben een reactie geplaatst in: %3",
+ "user_posted_to_multiple": "%1 en %2 hebben een reactie geplaatst in: %3",
"user_posted_topic": "%1 heeft een nieuw onderwerp geplaatst: %2",
"user_started_following_you": "%1 volgt jou nu.",
+ "user_started_following_you_dual": "%1 en %2 volgen jou nu.",
+ "user_started_following_you_multiple": "%1 en %2 andere volgen jou nu.",
"new_register": "%1 heeft een registratie verzoek aangevraagd.",
"email-confirmed": "E-mailadres bevestigd",
"email-confirmed-message": "Bedankt voor het bevestigen van je e-mailadres. Dit account is nu volledig geactiveerd.",
"email-confirm-error-message": "Er was een probleem met het bevestigen van dit e-mailadres. Misschien is de code niet goed ingevoerd of was de beschikbare tijd inmiddels verstreken.",
- "email-confirm-sent": "Bevestigingsmail verstuurd"
+ "email-confirm-sent": "Bevestigingsmail verstuurd."
}
\ No newline at end of file
diff --git a/public/language/pl/notifications.json b/public/language/pl/notifications.json
index e655a6253a..e34cb43dc6 100644
--- a/public/language/pl/notifications.json
+++ b/public/language/pl/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "Masz nieprzeczytane powiadomienia.",
"new_message_from": "Nowa wiadomość od %1",
"upvoted_your_post_in": "%1 zagłosował na Twój post w %2",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 polubił Twój post w %2.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1 oflagował Twój post w %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_posted_to": "%1 dodał odpowiedź do %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",
"user_posted_topic": "%1 wysłał nowy temat: %2",
"user_started_following_you": "%1 zaczął Cię śledzić.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 wysłał żądanie rejestracji.",
"email-confirmed": "E-mail potwierdzony",
"email-confirmed-message": "Dziękujemy za potwierdzenie maila. Twoje konto zostało aktywowane.",
diff --git a/public/language/pt_BR/notifications.json b/public/language/pt_BR/notifications.json
index 7a3b36d8d6..58d959e039 100644
--- a/public/language/pt_BR/notifications.json
+++ b/public/language/pt_BR/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "Você possui notificações não lidas.",
"new_message_from": "Nova mensagem de %1",
"upvoted_your_post_in": "%1 deu voto positivo para seu post em %2.",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 moveu seu post para %2",
"moved_your_topic": "%1 movido %2",
"favourited_your_post_in": "%1 adicionou seu tópico aos favoritos em %2.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1 sinalizou um post em %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_posted_to": "%1 postou uma resposta para: %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",
"user_posted_topic": "%1 postou um novo tópico: %2",
"user_started_following_you": "%1 começou a seguir você.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 lhe enviou um pedido de cadastro.",
"email-confirmed": "Email Confirmado",
"email-confirmed-message": "Obrigado por validar o seu email. Agora sua conta está plenamente ativada.",
diff --git a/public/language/ro/notifications.json b/public/language/ro/notifications.json
index a7cc002529..a37dac936b 100644
--- a/public/language/ro/notifications.json
+++ b/public/language/ro/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "Ai notificări necitite.",
"new_message_from": "Un mesaj nou de la %1",
"upvoted_your_post_in": "%1 a votat pozitiv mesajul tău în %2.",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 a adăugat mesajul tău la favorite în %2.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1 a semnalizat un mesaj în %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_posted_to": "%1 a postat un răspuns la: %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",
"user_posted_topic": "%1 has posted a new topic: %2",
"user_started_following_you": "%1 a început să te urmărească.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 sent a registration request.",
"email-confirmed": "Email confirmat",
"email-confirmed-message": "Îți mulțumim pentru validarea emailului. Contul tău este acuma activat.",
diff --git a/public/language/ru/notifications.json b/public/language/ru/notifications.json
index 24be55d9ea..197bfa2c09 100644
--- a/public/language/ru/notifications.json
+++ b/public/language/ru/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "У вас есть непрочитанные уведомления.",
"new_message_from": "Новое сообщение от %1",
"upvoted_your_post_in": "%1 проголосовал за Ваше сообщение в %2.",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 добавил в избранное Ваше сообщение в %2.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1 пометил сообщение в %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_posted_to": "%1 ответил на запись: %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",
"user_posted_topic": "%1 открыл новую тему: %2",
"user_started_following_you": "%1 подписался на Вас.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 отправлен запрос на регистрацию.",
"email-confirmed": "Email подтвержден",
"email-confirmed-message": "Спасибо за подтверждение Вашего Email-адреса. Ваш аккаунт активирован.",
diff --git a/public/language/rw/notifications.json b/public/language/rw/notifications.json
index a9d10be6e2..64f4c98753 100644
--- a/public/language/rw/notifications.json
+++ b/public/language/rw/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "Ufite amatangazo utarasoma. ",
"new_message_from": " %1 yakwandikiye",
"upvoted_your_post_in": "%1 yagushimye aguha inota kuri %2 washyizeho.",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 yatonesheje %2 washyizeho.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1 yatambikanye ikintu muri %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_posted_to": "%1 yanditse kuri: %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",
"user_posted_topic": "%1 yatangije ikiganiro gishya: %2",
"user_started_following_you": "%1 yatangiye kugukurikira.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 yasabye kwandikwa.",
"email-confirmed": "Email Yemejwe",
"email-confirmed-message": "Urakoze kugaragaza ko email yawe ikora. Ubu ngubu konte yawe irakora nta kabuza. ",
diff --git a/public/language/sc/notifications.json b/public/language/sc/notifications.json
index 929f5a55c6..b9d09d0159 100644
--- a/public/language/sc/notifications.json
+++ b/public/language/sc/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "You have unread notifications.",
"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.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 has favourited your post in %2.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"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_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",
"user_posted_topic": "%1 has posted a new topic: %2",
"user_started_following_you": "%1 started following you.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 sent a registration request.",
"email-confirmed": "Email Confirmed",
"email-confirmed-message": "Thank you for validating your email. Your account is now fully activated.",
diff --git a/public/language/sk/notifications.json b/public/language/sk/notifications.json
index a8e6619d33..5b62d0317e 100644
--- a/public/language/sk/notifications.json
+++ b/public/language/sk/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "Máte neprečítané notifikácie",
"new_message_from": "Nova spáva od %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.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 has favourited your post in %2.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"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_posted_to": "%1 odpovedal: %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",
"user_posted_topic": "%1 has posted a new topic: %2",
"user_started_following_you": "%1 started following you.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 sent a registration request.",
"email-confirmed": "Email bol potvrdený",
"email-confirmed-message": "Ďakujeme za potvrdenie tvojho emailu. Účet je plne aktivovaný.",
diff --git a/public/language/sl/notifications.json b/public/language/sl/notifications.json
index b0164f086e..6554627f8f 100644
--- a/public/language/sl/notifications.json
+++ b/public/language/sl/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "Imate neprebrana obvestila.",
"new_message_from": "Novo obvestilo od %1",
"upvoted_your_post_in": "%1 je glasoval za vašo objavo v %2.",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 je dodal med priljubljene vašo objavo v %2.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1je prijavil vašo objavo v %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_posted_to": "%1 je odgovoril na: %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",
"user_posted_topic": "%1 je odprl novo temo: %2",
"user_started_following_you": "%1 te sledi.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 je poslal prošnjo za registracijo.",
"email-confirmed": "E-mail naslov potrjen",
"email-confirmed-message": "Hvala ker ste potrdili svoj naslov. Račun je sedaj aktiviran.",
diff --git a/public/language/sr/notifications.json b/public/language/sr/notifications.json
index e65b13f46d..79d715bae4 100644
--- a/public/language/sr/notifications.json
+++ b/public/language/sr/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "Имате непрочитаних обавештења.",
"new_message_from": "Нова порука од %1",
"upvoted_your_post_in": "%1 дода глас вашој поруци у %2",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 доду вашу поруку из %2 у омиљене.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1 означи поруку у %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_posted_to": "%1 посла нови одговор за: %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",
"user_posted_topic": "%1 постави нову тему:",
"user_started_following_you": "%1 поче да вас прати.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 sent a registration request.",
"email-confirmed": "Е-пошта је је отврђена.",
"email-confirmed-message": "Хвала на овери ваше е-поште. Ваш налог је сада у потпуности активан.",
diff --git a/public/language/sv/notifications.json b/public/language/sv/notifications.json
index dbf4d290c6..dcb54b72ab 100644
--- a/public/language/sv/notifications.json
+++ b/public/language/sv/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "Du har olästa notiser.",
"new_message_from": "Nytt medelande från %1",
"upvoted_your_post_in": "%1 har röstat upp ditt inlägg i %2",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 har favoriserat ditt inlägg i %2.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1 flaggade ett inlägg i %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_posted_to": "%1 har skrivit ett svar på: %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",
"user_posted_topic": "%1 har skapat ett nytt ämne: %2",
"user_started_following_you": "%1 började följa dig.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 skickade en registreringsförfrågan.",
"email-confirmed": "Epost bekräftad",
"email-confirmed-message": "Tack för att du bekräftat din epostadress. Ditt konto är nu fullt ut aktiverat.",
diff --git a/public/language/th/notifications.json b/public/language/th/notifications.json
index 34fc870629..14fd6afc25 100644
--- a/public/language/th/notifications.json
+++ b/public/language/th/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "คุณมีคำเตือนที่ยังไม่ได้อ่าน",
"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.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 has favourited your post in %2.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"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_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",
"user_posted_topic": "%1 has posted a new topic: %2",
"user_started_following_you": "%1 started following you.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 sent a registration request.",
"email-confirmed": "Email ได้รับการยืนยันแล้ว",
"email-confirmed-message": "ขอบคุณที่ยืนยัน Email ของคุณ บัญชีของคุณสามารถใช้งานได้แล้ว",
diff --git a/public/language/tr/notifications.json b/public/language/tr/notifications.json
index b0eb25ec97..b69d4c980f 100644
--- a/public/language/tr/notifications.json
+++ b/public/language/tr/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "Okunmamış bildirimleriniz var.",
"new_message_from": "%1 size bir mesaj gönderdi",
"upvoted_your_post_in": "%1 iletinizi beğendi. %2",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 senin iletin %2 taşındı",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 iletinizi favorilerine ekledi. %2.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1 bir iletiyi bayrakladı. %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_posted_to": "%1%2 başlığına bir ileti gönderdi.",
+ "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",
"user_posted_topic": "%1 yeni bir konu yarattı: %2",
"user_started_following_you": "%1 sizi takip etmeye başladı.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 kayıt olma isteği gönderdi.",
"email-confirmed": "E-posta onaylandı",
"email-confirmed-message": "E-postanızı onaylandığınız için teşekkürler. Hesabınız tamamen aktive edildi.",
diff --git a/public/language/vi/notifications.json b/public/language/vi/notifications.json
index 533e5d2a53..3d3ca64228 100644
--- a/public/language/vi/notifications.json
+++ b/public/language/vi/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "Bạn có thông báo chưa đọc",
"new_message_from": "Tin nhắn mới từ %1",
"upvoted_your_post_in": "%1 đã bình chọn bài của bạn trong %2.",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 đã thích bài của bạn trong %2.",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1 gắn cờ 1 bài trong %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_posted_to": "%1 đã trả lời %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",
"user_posted_topic": "%1 đã gởi chủ đề mới ở %2",
"user_started_following_you": "%1 đã theo dõi bạn.",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 sent a registration request.",
"email-confirmed": "Đã xác nhận email",
"email-confirmed-message": "Cảm ơn bạn đã xác nhận địa chỉ email của bạn. Tài khoản của bạn đã được kích hoạt đầy đủ.",
diff --git a/public/language/zh_CN/notifications.json b/public/language/zh_CN/notifications.json
index 9589fe7507..5fba6a56e0 100644
--- a/public/language/zh_CN/notifications.json
+++ b/public/language/zh_CN/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "您有未读的通知。",
"new_message_from": "来自 %1 的新消息",
"upvoted_your_post_in": "%1 在 %2 点赞了您的帖子。",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "您的帖子已被 %1 移动到了 %2",
"moved_your_topic": "%1 移动到了 %2",
"favourited_your_post_in": "%1 在 %2 收藏了您的帖子。",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1 在 %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_posted_to": "%1 回复了:%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",
"user_posted_topic": "%1 发表了新主题:%2",
"user_started_following_you": "%1关注了您。",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 发出了注册请求",
"email-confirmed": "电子邮箱已确认",
"email-confirmed-message": "感谢您验证您的电子邮箱。您的帐户现已全面激活。",
diff --git a/public/language/zh_TW/notifications.json b/public/language/zh_TW/notifications.json
index 993d2cd3d8..fb2f3baa39 100644
--- a/public/language/zh_TW/notifications.json
+++ b/public/language/zh_TW/notifications.json
@@ -12,13 +12,23 @@
"you_have_unread_notifications": "您有未讀的訊息!",
"new_message_from": "來自 %1 的新訊息",
"upvoted_your_post_in": "%1 upvote了您在 %2的post。",
+ "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
+ "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
"moved_your_topic": "%1 has moved %2",
"favourited_your_post_in": "%1 收藏了你在 %2的post。",
+ "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1 舉報了 %2裡的一個post。",
+ "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_posted_to": "%1 發布一個回覆給: %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",
"user_posted_topic": "%1 發布了一個新的主題: %2",
"user_started_following_you": "%1 開始關注你。",
+ "user_started_following_you_dual": "%1 and %2 started following you.",
+ "user_started_following_you_multiple": "%1 and %2 others started following you.",
"new_register": "%1 sent a registration request.",
"email-confirmed": "已確認電郵",
"email-confirmed-message": "感謝您驗證您的電郵。您的帳戶現已全面啟用。",
From ba719148c1ca0b8aa50e28eca843551c67dd94e3 Mon Sep 17 00:00:00 2001
From: psychobunny
Date: Thu, 17 Dec 2015 16:44:22 -0500
Subject: [PATCH 45/59] generic "related topics" functionality
---
src/topics.js | 7 +++++++
src/topics/tags.js | 38 ++++++++++++++++++++++++++++++++++++++
2 files changed, 45 insertions(+)
diff --git a/src/topics.js b/src/topics.js
index 560e434fb5..423dafe197 100644
--- a/src/topics.js
+++ b/src/topics.js
@@ -192,12 +192,19 @@ var async = require('async'),
}, next);
},
function(results, next) {
+ if (plugins.hasListeners('filter:topic.getRelatedTopics')) {
+ plugins.fireHook('filter:topic.getRelatedTopics', results, next);
+ } else {
+ Topics.getRelatedTopics(results, next);
+ }
+ }, function(results, next) {
topicData.posts = results.posts;
topicData.category = results.category;
topicData.thread_tools = results.threadTools.tools;
topicData.tags = results.tags;
topicData.isFollowing = results.isFollowing[0];
topicData.bookmark = results.bookmark;
+ topicData.related = results.related || [];
topicData.unreplied = parseInt(topicData.postcount, 10) === 1;
topicData.deleted = parseInt(topicData.deleted, 10) === 1;
diff --git a/src/topics/tags.js b/src/topics/tags.js
index bde9beff8f..8d29f947cf 100644
--- a/src/topics/tags.js
+++ b/src/topics/tags.js
@@ -5,6 +5,7 @@ var async = require('async'),
db = require('../database'),
meta = require('../meta'),
+ user = require('../user'),
_ = require('underscore'),
plugins = require('../plugins');
@@ -322,4 +323,41 @@ module.exports = function(Topics) {
});
};
+ Topics.getRelatedTopics = function(topicData, callback) {
+ if (!topicData.tags.length) {
+ return callback(null, topicData);
+ }
+
+ var related = [];
+
+ user.isAdministrator(topicData.threadTools.uid, function(err, isAdministrator) {
+ async.each(topicData.tags, function(tag, next) {
+ tag = tag.value;
+
+ Topics.getTagTids(tag, 0, 5, function(err, tids) {
+ Topics.getTopics(tids, topicData.threadTools.uid, function(err, topics) {
+ related = related.concat(topics.filter(function(topic) {
+ var doesntOwnTopic = parseInt(topic.uid, 10) !== parseInt(topicData.threadTools.uid, 10);
+ var isntSameTopic = parseInt(topic.tid, 10) !== parseInt(topicData.threadTools.topic.tid, 10);
+
+ return doesntOwnTopic && isntSameTopic;
+ }));
+
+ next(err);
+ });
+ });
+ }, function(err) {
+ if (!isAdministrator) {
+ related = related.filter(function(topic) {
+ return topic && !topic.deleted;
+ });
+ }
+
+ related = _.shuffle(related).slice(0, 5);
+
+ topicData.related = related;
+ callback(err, topicData);
+ });
+ });
+ };
};
\ No newline at end of file
From 339b4c30fcc666843b7cca7d1fa3efa8b50e3379 Mon Sep 17 00:00:00 2001
From: psychobunny
Date: Thu, 17 Dec 2015 16:52:02 -0500
Subject: [PATCH 46/59] ability to set maximum related topics rendered in ACP
---
src/topics/tags.js | 6 ++++--
src/views/admin/settings/tags.tpl | 11 +++++++++++
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/src/topics/tags.js b/src/topics/tags.js
index 8d29f947cf..183dbe903c 100644
--- a/src/topics/tags.js
+++ b/src/topics/tags.js
@@ -324,7 +324,9 @@ module.exports = function(Topics) {
};
Topics.getRelatedTopics = function(topicData, callback) {
- if (!topicData.tags.length) {
+ var maximumTopics = typeof meta.config.maximumRelatedTopics !== 'undefined' ? parseInt(meta.config.maximumRelatedTopics, 10) : 5;
+
+ if (!topicData.tags.length || maximumTopics === 0) {
return callback(null, topicData);
}
@@ -353,7 +355,7 @@ module.exports = function(Topics) {
});
}
- related = _.shuffle(related).slice(0, 5);
+ related = _.shuffle(related).slice(0, maximumTopics);
topicData.related = related;
callback(err, topicData);
diff --git a/src/views/admin/settings/tags.tpl b/src/views/admin/settings/tags.tpl
index 6c09dab80e..90aa5ed0bc 100644
--- a/src/views/admin/settings/tags.tpl
+++ b/src/views/admin/settings/tags.tpl
@@ -31,4 +31,15 @@
+
+
Related Topics
+
+
+
+
\ No newline at end of file
From 89901b2caa57efe303b928007b45d1d916f6c3f7 Mon Sep 17 00:00:00 2001
From: psychobunny
Date: Thu, 17 Dec 2015 16:53:42 -0500
Subject: [PATCH 47/59] reorganized settings/tags acp
---
src/views/admin/settings/tags.tpl | 22 ++++++++++++++++------
1 file changed, 16 insertions(+), 6 deletions(-)
diff --git a/src/views/admin/settings/tags.tpl b/src/views/admin/settings/tags.tpl
index 90aa5ed0bc..e1e8f01f1c 100644
--- a/src/views/admin/settings/tags.tpl
+++ b/src/views/admin/settings/tags.tpl
@@ -4,12 +4,6 @@
Tag Settings
+
+
+
Privacy
+
+
+
+
+
Related Topics
@@ -39,6 +48,7 @@
+
From a0910671754b297484adb589e2ff1d8759e304b8 Mon Sep 17 00:00:00 2001
From: Julian Lam
Date: Thu, 17 Dec 2015 18:22:03 -0500
Subject: [PATCH 48/59] closes #3963
---
public/src/modules/translator.js | 36 ++++++++++++++++++++++++--------
1 file changed, 27 insertions(+), 9 deletions(-)
diff --git a/public/src/modules/translator.js b/public/src/modules/translator.js
index 35429af587..ca9cbb9f29 100644
--- a/public/src/modules/translator.js
+++ b/public/src/modules/translator.js
@@ -265,6 +265,7 @@
path = require('path'),
winston = require('winston'),
file = require('../../../src/file'),
+ plugins = require('../../../src/plugins'),
meta = require('../../../src/meta');
language = language || meta.config.defaultLang || 'en_GB';
@@ -275,18 +276,35 @@
}
fs.readFile(path.join(__dirname, '../../language', language, filename + '.json'), function(err, data) {
- if (err) {
- winston.error('Could not load `' + filename + '`: ' + err.message + '. Skipping...');
- return callback({});
+ var onData = function(data) {
+ try {
+ data = JSON.parse(data.toString());
+ } catch (e) {
+ winston.error('Could not parse `' + filename + '.json`, syntax error? Skipping...');
+ data = {};
+ }
+ callback(data);
}
- try {
- data = JSON.parse(data.toString());
- } catch (e) {
- winston.error('Could not parse `' + filename + '.json`, syntax error? Skipping...');
- data = {};
+ if (err) {
+ if (err.code === 'ENOENT' && plugins.customLanguageFallbacks.hasOwnProperty(filename)) {
+ // Resource non-existant but fallback exists
+ return fs.readFile(plugins.customLanguageFallbacks[filename], {
+ encoding: 'utf-8'
+ }, function(err, data) {
+ if (err) {
+ return winston.error('[translator] Could not load fallback language file for resource ' + filename);
+ }
+
+ onData(data);
+ })
+ } else {
+ winston.error('[translator] Could not load `' + filename + '`: ' + err.message + '. Skipping...');
+ return callback({});
+ }
}
- callback(data);
+
+ onData(data);
});
}
From e067d26ca36508cb724fde59c0a5e91c32dc0a4c Mon Sep 17 00:00:00 2001
From: barisusakli
Date: Fri, 18 Dec 2015 13:09:47 +0200
Subject: [PATCH 49/59] closes #3961
---
src/groups/update.js | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/src/groups/update.js b/src/groups/update.js
index b239df39de..0ac688aca0 100644
--- a/src/groups/update.js
+++ b/src/groups/update.js
@@ -45,6 +45,7 @@ module.exports = function(Groups) {
}
async.series([
+ async.apply(checkNameChange, groupName, values.name),
async.apply(updatePrivacy, groupName, values.private),
function(next) {
if (values.hasOwnProperty('hidden')) {
@@ -217,6 +218,23 @@ module.exports = function(Groups) {
});
}
+ function checkNameChange(oldName, newName, callback) {
+ if (oldName === newName) {
+ return callback();
+ }
+ var oldSlug = utils.slugify(oldName);
+ var newSlug = utils.slugify(newName);
+ if (oldSlug === newSlug) {
+ return callback();
+ }
+ Groups.existsBySlug(newSlug, function(err, exists) {
+ if (err || exists) {
+ return callback(err || new Error('[[error:group-already-exists]]'));
+ }
+ callback();
+ });
+ }
+
function renameGroup(oldName, newName, callback) {
if (oldName === newName || !newName || newName.length === 0) {
return callback();
From 99815550423d0886498cc07c77d9a5e2e5e002fe Mon Sep 17 00:00:00 2001
From: barisusakli
Date: Fri, 18 Dec 2015 13:14:44 +0200
Subject: [PATCH 50/59] rename
---
src/groups/update.js | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/groups/update.js b/src/groups/update.js
index 0ac688aca0..26d2b258be 100644
--- a/src/groups/update.js
+++ b/src/groups/update.js
@@ -218,13 +218,13 @@ module.exports = function(Groups) {
});
}
- function checkNameChange(oldName, newName, callback) {
- if (oldName === newName) {
+ function checkNameChange(currentName, newName, callback) {
+ if (currentName === newName) {
return callback();
}
- var oldSlug = utils.slugify(oldName);
+ var currentSlug = utils.slugify(currentName);
var newSlug = utils.slugify(newName);
- if (oldSlug === newSlug) {
+ if (currentSlug === newSlug) {
return callback();
}
Groups.existsBySlug(newSlug, function(err, exists) {
From 270f8130daba7a012ec5310f573a9d2a277a773e Mon Sep 17 00:00:00 2001
From: barisusakli
Date: Fri, 18 Dec 2015 13:32:53 +0200
Subject: [PATCH 51/59] closes #3952
---
src/socket.io/groups.js | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/socket.io/groups.js b/src/socket.io/groups.js
index 3b12693be0..0361560f01 100644
--- a/src/socket.io/groups.js
+++ b/src/socket.io/groups.js
@@ -144,6 +144,9 @@ SocketGroups.update = isOwner(function(socket, data, callback) {
SocketGroups.kick = isOwner(function(socket, data, callback) {
+ if (socket.uid === parseInt(data.uid, 10)) {
+ return callback(new Error('[[error:cant-kick-self]]'));
+ }
groups.leave(data.groupName, data.uid, callback);
});
From af55f55b453197f862820316bf2428928b880d28 Mon Sep 17 00:00:00 2001
From: Julian Lam
Date: Fri, 18 Dec 2015 12:49:10 -0500
Subject: [PATCH 52/59] latest translations
---
public/language/nl/error.json | 2 +-
public/language/nl/modules.json | 2 +-
public/language/ru/error.json | 2 +-
public/language/ru/modules.json | 4 ++--
public/language/ru/notifications.json | 4 ++--
public/language/ru/topic.json | 6 +++---
public/language/ru/user.json | 14 +++++++-------
7 files changed, 17 insertions(+), 17 deletions(-)
diff --git a/public/language/nl/error.json b/public/language/nl/error.json
index cf918a18db..bfe113c836 100644
--- a/public/language/nl/error.json
+++ b/public/language/nl/error.json
@@ -78,7 +78,7 @@
"too-many-messages": "Er zijn in korte tijd teveel berichten verzonden, een moment geduld.",
"invalid-chat-message": "Ongeldig bericht",
"chat-message-too-long": "Het chatbericht is te lang",
- "cant-edit-chat-message": "You are not allowed to edit this message",
+ "cant-edit-chat-message": "Het is niet toegestaan om dit bericht aan te passen",
"reputation-system-disabled": "Reputatie systeem is uitgeschakeld.",
"downvoting-disabled": "Negatief stemmen staat uitgeschakeld.",
"not-enough-reputation-to-downvote": "Dit gebruikersaccount beschikt over onvoldoende reputatie om een negatieve stem uit te mogen brengen.",
diff --git a/public/language/nl/modules.json b/public/language/nl/modules.json
index 75792aec27..39a61ffe9f 100644
--- a/public/language/nl/modules.json
+++ b/public/language/nl/modules.json
@@ -15,7 +15,7 @@
"chat.seven_days": "7 dagen",
"chat.thirty_days": "30 dagen",
"chat.three_months": "3 maanden",
- "chat.delete_message_confirm": "Are you sure you wish to delete this message?",
+ "chat.delete_message_confirm": "Weet u het zeker dat u dit bericht wilt verwijderen?",
"composer.compose": "Samenstellen",
"composer.show_preview": "Voorbeeldweergave",
"composer.hide_preview": "Verberg voorbeeld",
diff --git a/public/language/ru/error.json b/public/language/ru/error.json
index 717d307f63..4dd3c99934 100644
--- a/public/language/ru/error.json
+++ b/public/language/ru/error.json
@@ -78,7 +78,7 @@
"too-many-messages": "Вы отправили слишком много сообщений, подождите немного.",
"invalid-chat-message": "Неверное сообщение чата",
"chat-message-too-long": "Слишком длинное сообщение чата",
- "cant-edit-chat-message": "You are not allowed to edit this message",
+ "cant-edit-chat-message": "У вас нет доступа для редактирования этого сообщения",
"reputation-system-disabled": "Система репутации отключена.",
"downvoting-disabled": "Понижение оценки отключено",
"not-enough-reputation-to-downvote": "У Вас недостаточно репутации для понижения оценки сообщения",
diff --git a/public/language/ru/modules.json b/public/language/ru/modules.json
index cada7e8fc9..b9ea06f477 100644
--- a/public/language/ru/modules.json
+++ b/public/language/ru/modules.json
@@ -15,7 +15,7 @@
"chat.seven_days": "7 дней",
"chat.thirty_days": "30 дней",
"chat.three_months": "3 месяца",
- "chat.delete_message_confirm": "Are you sure you wish to delete this message?",
+ "chat.delete_message_confirm": "Вы уверены, что хотите удалить это сообщение?",
"composer.compose": "Редактор",
"composer.show_preview": "Показать предпросмотр",
"composer.hide_preview": "Скрыть предпросмотр",
@@ -24,7 +24,7 @@
"composer.discard": "Вы уверены, что хотите отменить все изменения?",
"composer.submit_and_lock": "Отправить и закрыть",
"composer.toggle_dropdown": "Показать выпадающий список",
- "composer.uploading": "Uploading %1",
+ "composer.uploading": "Загрузка %1",
"bootbox.ok": "ОК",
"bootbox.cancel": "Отмена",
"bootbox.confirm": "Подтвердить",
diff --git a/public/language/ru/notifications.json b/public/language/ru/notifications.json
index 197bfa2c09..9c258affe0 100644
--- a/public/language/ru/notifications.json
+++ b/public/language/ru/notifications.json
@@ -15,9 +15,9 @@
"upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
"upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.",
"moved_your_post": "%1 has moved your post to %2",
- "moved_your_topic": "%1 has moved %2",
+ "moved_your_topic": "%1 переместил %2",
"favourited_your_post_in": "%1 добавил в избранное Ваше сообщение в %2.",
- "favourited_your_post_in_dual": "%1 and %2 have favourited your post in %3.",
+ "favourited_your_post_in_dual": "%1 и %2 добавили в избранное Ваше сообщение в %3.",
"favourited_your_post_in_multiple": "%1 and %2 others have favourited your post in %3.",
"user_flagged_post_in": "%1 пометил сообщение в %2",
"user_flagged_post_in_dual": "%1 and %2 flagged a post in %3",
diff --git a/public/language/ru/topic.json b/public/language/ru/topic.json
index b733f7305a..0ba0064291 100644
--- a/public/language/ru/topic.json
+++ b/public/language/ru/topic.json
@@ -98,10 +98,10 @@
"most_posts": "По количеству ответов",
"stale.title": "Create new topic instead?",
"stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?",
- "stale.create": "Create a new topic",
- "stale.reply_anyway": "Reply to this topic anyway",
+ "stale.create": "Создать новую тему",
+ "stale.reply_anyway": "Всё равно ответить в этой теме",
"stale.link_back": "Re: [%1](%2)",
- "spam": "Spam",
+ "spam": "Спам",
"offensive": "Offensive",
"custom-flag-reason": "Enter a flagging reason"
}
\ No newline at end of file
diff --git a/public/language/ru/user.json b/public/language/ru/user.json
index 83a614e156..d9b7cc6621 100644
--- a/public/language/ru/user.json
+++ b/public/language/ru/user.json
@@ -30,16 +30,16 @@
"signature": "Подпись",
"birthday": "День рождения",
"chat": "Чат",
- "chat_with": "Chat with %1",
+ "chat_with": "Чат с %1",
"follow": "Читать",
"unfollow": "Не читать",
"more": "Ещё",
"profile_update_success": "Профиль обновлен!",
"change_picture": "Изменить фотографию",
- "change_username": "Change Username",
- "change_email": "Change Email",
+ "change_username": "Изменить имя пользователя",
+ "change_email": "Изменить Email",
"edit": "Редактировать",
- "default_picture": "Default Icon",
+ "default_picture": "Иконка по умолчанию",
"uploaded_picture": "Загруженные фотографии",
"upload_new_picture": "Загрузить новую фотографию",
"upload_new_picture_from_url": "Загрузить новое изображение с адреса URL",
@@ -58,7 +58,7 @@
"upload_picture": "Загрузить фотографию",
"upload_a_picture": "Загрузить фотографию",
"remove_uploaded_picture": "Удалить загруженные фотографии",
- "image_spec": "You may only upload PNG, JPG, or BMP files",
+ "image_spec": "Вы можете загружать только PNG, JPG или BMP файлы",
"settings": "Настройки",
"show_email": "Показывать мой Email",
"show_fullname": "Показывать Полное Имя",
@@ -92,8 +92,8 @@
"grouptitle": "Выберите бейдж группы для отображения",
"no-group-title": "Не показывать бейдж",
"select-skin": "Выбрать скин",
- "select-homepage": "Select a Homepage",
- "homepage": "Homepage",
+ "select-homepage": "Укажите главную страницу",
+ "homepage": "Главная страница",
"homepage_description": "Select a page to use as the forum homepage or 'None' to use the default homepage.",
"custom_route": "Custom Homepage Route",
"custom_route_help": "Enter a route name here, without any preceding slash (e.g. \"recent\", or \"popular\")",
From 668adc5187dfe09050c12c44f147664bb8221170 Mon Sep 17 00:00:00 2001
From: Julian Lam
Date: Fri, 18 Dec 2015 12:56:24 -0500
Subject: [PATCH 53/59] fixing bootswatch integration
---
public/src/admin/appearance/skins.js | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/public/src/admin/appearance/skins.js b/public/src/admin/appearance/skins.js
index 8f838ca8c2..895b97de52 100644
--- a/public/src/admin/appearance/skins.js
+++ b/public/src/admin/appearance/skins.js
@@ -5,9 +5,11 @@ define('admin/appearance/skins', function() {
var Skins = {};
Skins.init = function() {
- var scriptEl = $('');
- scriptEl.attr('src', '//bootswatch.aws.af.cm/3/?callback=bootswatchListener');
- $('body').append(scriptEl);
+ // Populate skins from Bootswatch API
+ $.ajax({
+ method: 'get',
+ url: 'https://bootswatch.com/api/3.json'
+ }).done(Skins.render);
$('#skins').on('click', function(e){
var target = $(e.target);
From 04879e6da2f50f6b5ef85ce3cea1c69b932bcc44 Mon Sep 17 00:00:00 2001
From: Julian Lam
Date: Fri, 18 Dec 2015 14:13:47 -0500
Subject: [PATCH 54/59] updated jquery-timeago locales
---
.../timeago/locales/jquery.timeago.af.js | 20 +++++++++++++++++
.../timeago/locales/jquery.timeago.cz.js | 18 ---------------
.../timeago/locales/jquery.timeago.dv.js | 22 +++++++++++++++++++
.../timeago/locales/jquery.timeago.eu.js | 17 ++++++++++++++
.../locales/jquery.timeago.fa-short.js | 20 +++++++++++++++++
.../locales/jquery.timeago.it-short.js | 2 +-
.../locales/jquery.timeago.pt-short.js | 20 +++++++++++++++++
7 files changed, 100 insertions(+), 19 deletions(-)
create mode 100644 public/vendor/jquery/timeago/locales/jquery.timeago.af.js
delete mode 100644 public/vendor/jquery/timeago/locales/jquery.timeago.cz.js
create mode 100644 public/vendor/jquery/timeago/locales/jquery.timeago.dv.js
create mode 100644 public/vendor/jquery/timeago/locales/jquery.timeago.eu.js
create mode 100644 public/vendor/jquery/timeago/locales/jquery.timeago.fa-short.js
create mode 100644 public/vendor/jquery/timeago/locales/jquery.timeago.pt-short.js
diff --git a/public/vendor/jquery/timeago/locales/jquery.timeago.af.js b/public/vendor/jquery/timeago/locales/jquery.timeago.af.js
new file mode 100644
index 0000000000..ffb23e6f9c
--- /dev/null
+++ b/public/vendor/jquery/timeago/locales/jquery.timeago.af.js
@@ -0,0 +1,20 @@
+// Afrikaans
+jQuery.timeago.settings.strings = {
+ prefixAgo: null,
+ prefixFromNow: null,
+ suffixAgo: "gelede",
+ suffixFromNow: "van nou af",
+ seconds: "%d sekondes",
+ minute: "1 minuut",
+ minutes: "%d minute",
+ hour: "1 uur",
+ hours: "%d ure",
+ day: "1 dag",
+ days: "%d dae",
+ month: "1 maand",
+ months: "%d maande",
+ year: "1 jaar",
+ years: "%d jaar",
+ wordSeparator: " ",
+ numbers: []
+};
\ No newline at end of file
diff --git a/public/vendor/jquery/timeago/locales/jquery.timeago.cz.js b/public/vendor/jquery/timeago/locales/jquery.timeago.cz.js
deleted file mode 100644
index b7137384b1..0000000000
--- a/public/vendor/jquery/timeago/locales/jquery.timeago.cz.js
+++ /dev/null
@@ -1,18 +0,0 @@
-// Czech
-jQuery.timeago.settings.strings = {
- prefixAgo: "před",
- prefixFromNow: null,
- suffixAgo: null,
- suffixFromNow: null,
- seconds: "méně než minutou",
- minute: "minutou",
- minutes: "%d minutami",
- hour: "hodinou",
- hours: "%d hodinami",
- day: "1 dnem",
- days: "%d dny",
- month: "1 měsícem",
- months: "%d měsíci",
- year: "1 rokem",
- years: "%d roky"
-};
\ No newline at end of file
diff --git a/public/vendor/jquery/timeago/locales/jquery.timeago.dv.js b/public/vendor/jquery/timeago/locales/jquery.timeago.dv.js
new file mode 100644
index 0000000000..9f2a9da0ca
--- /dev/null
+++ b/public/vendor/jquery/timeago/locales/jquery.timeago.dv.js
@@ -0,0 +1,22 @@
+/**
+ * Dhivehi time in Thaana for timeago.js
+ **/
+jQuery.timeago.settings.strings = {
+ prefixAgo: null,
+ prefixFromNow: null,
+ suffixAgo: "ކުރިން",
+ suffixFromNow: "ފަހުން",
+ seconds: "ސިކުންތުކޮޅެއް",
+ minute: "މިނިޓެއްވަރު",
+ minutes: "%d މިނިޓު",
+ hour: "ގަޑިއެއްވަރު",
+ hours: "ގާތްގަނޑަކަށް %d ގަޑިއިރު",
+ day: "އެއް ދުވަސް",
+ days: "މީގެ %d ދުވަސް",
+ month: "މަހެއްވަރު",
+ months: "މީގެ %d މަސް",
+ year: "އަހަރެއްވަރު",
+ years: "މީގެ %d އަހަރު",
+ wordSeparator: " ",
+ numbers: []
+};
diff --git a/public/vendor/jquery/timeago/locales/jquery.timeago.eu.js b/public/vendor/jquery/timeago/locales/jquery.timeago.eu.js
new file mode 100644
index 0000000000..4b2c91a237
--- /dev/null
+++ b/public/vendor/jquery/timeago/locales/jquery.timeago.eu.js
@@ -0,0 +1,17 @@
+jQuery.timeago.settings.strings = {
+ prefixAgo: "duela",
+ prefixFromNow: "hemendik",
+ suffixAgo: "",
+ suffixFromNow: "barru",
+ seconds: "minutu bat bainu gutxiago",
+ minute: "minutu bat",
+ minutes: "%d minutu inguru",
+ hour: "ordu bat",
+ hours: "%d ordu",
+ day: "egun bat",
+ days: "%d egun",
+ month: "hilabete bat",
+ months: "%d hilabete",
+ year: "urte bat",
+ years: "%d urte"
+};
diff --git a/public/vendor/jquery/timeago/locales/jquery.timeago.fa-short.js b/public/vendor/jquery/timeago/locales/jquery.timeago.fa-short.js
new file mode 100644
index 0000000000..66d314cd42
--- /dev/null
+++ b/public/vendor/jquery/timeago/locales/jquery.timeago.fa-short.js
@@ -0,0 +1,20 @@
+// persion shortened
+jQuery.timeago.settings.strings = {
+ prefixAgo: null,
+ prefixFromNow: null,
+ suffixAgo: "",
+ suffixFromNow: "",
+ seconds: "1دقیقه",
+ minute: "1دقیقه",
+ minutes: "%dدقیقه",
+ hour: "1ساعت",
+ hours: "%dساعت",
+ day: "1روز",
+ days: "%dروز",
+ month: "1ماه",
+ months: "%dماه",
+ year: "1سال",
+ years: "%dسال",
+ wordSeparator: " ",
+ numbers: []
+};
diff --git a/public/vendor/jquery/timeago/locales/jquery.timeago.it-short.js b/public/vendor/jquery/timeago/locales/jquery.timeago.it-short.js
index f4d92ad209..64328baa85 100644
--- a/public/vendor/jquery/timeago/locales/jquery.timeago.it-short.js
+++ b/public/vendor/jquery/timeago/locales/jquery.timeago.it-short.js
@@ -17,4 +17,4 @@ jQuery.timeago.settings.strings = {
years: "%da",
wordSeparator: " ",
numbers: []
-};
\ No newline at end of file
+};
diff --git a/public/vendor/jquery/timeago/locales/jquery.timeago.pt-short.js b/public/vendor/jquery/timeago/locales/jquery.timeago.pt-short.js
new file mode 100644
index 0000000000..8f6ef76d5c
--- /dev/null
+++ b/public/vendor/jquery/timeago/locales/jquery.timeago.pt-short.js
@@ -0,0 +1,20 @@
+// Portuguese shortened
+jQuery.timeago.settings.strings = {
+ prefixAgo: null,
+ prefixFromNow: null,
+ suffixAgo: "",
+ suffixFromNow: "",
+ seconds: "1m",
+ minute: "1m",
+ minutes: "%dm",
+ hour: "1h",
+ hours: "%dh",
+ day: "1d",
+ days: "%dd",
+ month: "1M",
+ months: "%dM",
+ year: "1a",
+ years: "%da",
+ wordSeparator: " ",
+ numbers: []
+};
From 0ded293cafc31715968095b7a0b2a3ab84bdfb15 Mon Sep 17 00:00:00 2001
From: Julian Lam
Date: Fri, 18 Dec 2015 16:58:29 -0500
Subject: [PATCH 55/59] removed JSONP listener
---
src/views/admin/appearance/skins.tpl | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/src/views/admin/appearance/skins.tpl b/src/views/admin/appearance/skins.tpl
index fe50dead11..ce145eec79 100644
--- a/src/views/admin/appearance/skins.tpl
+++ b/src/views/admin/appearance/skins.tpl
@@ -8,12 +8,4 @@
undo
-
-
-
+
\ No newline at end of file
From b2049e7acf6c7d5cae6288fbdec43c66228d7f11 Mon Sep 17 00:00:00 2001
From: psychobunny
Date: Fri, 18 Dec 2015 16:56:59 -0500
Subject: [PATCH 56/59] add color/bgColor to return
---
src/controllers/unread.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/controllers/unread.js b/src/controllers/unread.js
index af7bf816ef..741e5c673a 100644
--- a/src/controllers/unread.js
+++ b/src/controllers/unread.js
@@ -34,7 +34,7 @@ unreadController.get = function(req, res, next) {
privileges.categories.filterCids('read', results.watchedCategories, req.uid, next);
},
function(cids, next) {
- categories.getCategoriesFields(cids, ['cid', 'name', 'slug', 'icon', 'link'], next);
+ categories.getCategoriesFields(cids, ['cid', 'name', 'slug', 'icon', 'link', 'color', 'bgColor'], next);
},
function(categories, next) {
categories = categories.filter(function(category) {
From fe90dd77c186b72f4a8e02dd16dbb2b4a28ca7da Mon Sep 17 00:00:00 2001
From: Julian Lam
Date: Sat, 19 Dec 2015 09:31:03 -0500
Subject: [PATCH 57/59] fixed #3967
---
src/user/notifications.js | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/user/notifications.js b/src/user/notifications.js
index b085e7654d..2d08b83e33 100644
--- a/src/user/notifications.js
+++ b/src/user/notifications.js
@@ -64,6 +64,8 @@ var async = require('async'),
}
function getNotificationsFromSet(set, read, uid, start, stop, callback) {
+ var setNids;
+
async.waterfall([
async.apply(db.getSortedSetRevRange, set, start, stop),
function(nids, next) {
@@ -71,6 +73,7 @@ var async = require('async'),
return callback(null, []);
}
+ setNids = nids;
UserNotifications.getNotifications(nids, uid, next);
},
function(notifs, next) {
@@ -78,8 +81,8 @@ var async = require('async'),
notifs.forEach(function(notification, index) {
if (!notification) {
- winston.verbose('[notifications.get] nid ' + notification.nid + ' not found. Removing.');
- deletedNids.push(notification.nid);
+ winston.verbose('[notifications.get] nid ' + setNids[index] + ' not found. Removing.');
+ deletedNids.push(setNids[index]);
} else {
notification.read = read;
notification.readClass = !notification.read ? 'unread' : '';
From 80544119dcc76d19fbcd7f10d21ac2d15d80b6f9 Mon Sep 17 00:00:00 2001
From: barisusakli
Date: Sat, 19 Dec 2015 17:21:09 +0200
Subject: [PATCH 58/59] some changes to related code
---
src/topics.js | 11 ++++------
src/topics/tags.js | 55 +++++++++++++++++++---------------------------
2 files changed, 26 insertions(+), 40 deletions(-)
diff --git a/src/topics.js b/src/topics.js
index 423dafe197..d2c4c79359 100644
--- a/src/topics.js
+++ b/src/topics.js
@@ -192,25 +192,22 @@ var async = require('async'),
}, next);
},
function(results, next) {
- if (plugins.hasListeners('filter:topic.getRelatedTopics')) {
- plugins.fireHook('filter:topic.getRelatedTopics', results, next);
- } else {
- Topics.getRelatedTopics(results, next);
- }
- }, function(results, next) {
topicData.posts = results.posts;
topicData.category = results.category;
topicData.thread_tools = results.threadTools.tools;
topicData.tags = results.tags;
topicData.isFollowing = results.isFollowing[0];
topicData.bookmark = results.bookmark;
- topicData.related = results.related || [];
topicData.unreplied = parseInt(topicData.postcount, 10) === 1;
topicData.deleted = parseInt(topicData.deleted, 10) === 1;
topicData.locked = parseInt(topicData.locked, 10) === 1;
topicData.pinned = parseInt(topicData.pinned, 10) === 1;
+ Topics.getRelatedTopics(topicData, uid, next);
+ },
+ function(related, next) {
+ topicData.related = related || [];
plugins.fireHook('filter:topic.get', {topic: topicData, uid: uid}, next);
},
function(data, next) {
diff --git a/src/topics/tags.js b/src/topics/tags.js
index 183dbe903c..d816d5c49c 100644
--- a/src/topics/tags.js
+++ b/src/topics/tags.js
@@ -5,7 +5,6 @@ var async = require('async'),
db = require('../database'),
meta = require('../meta'),
- user = require('../user'),
_ = require('underscore'),
plugins = require('../plugins');
@@ -323,43 +322,33 @@ module.exports = function(Topics) {
});
};
- Topics.getRelatedTopics = function(topicData, callback) {
- var maximumTopics = typeof meta.config.maximumRelatedTopics !== 'undefined' ? parseInt(meta.config.maximumRelatedTopics, 10) : 5;
+ Topics.getRelatedTopics = function(topicData, uid, callback) {
+ if (plugins.hasListeners('filter:topic.getRelatedTopics')) {
+ return plugins.fireHook('filter:topic.getRelatedTopics', {topic: topicData, uid: uid}, callback);
+ }
+
+ var maximumTopics = parseInt(meta.config.maximumRelatedTopics, 10) || 5;
if (!topicData.tags.length || maximumTopics === 0) {
return callback(null, topicData);
}
- var related = [];
-
- user.isAdministrator(topicData.threadTools.uid, function(err, isAdministrator) {
- async.each(topicData.tags, function(tag, next) {
- tag = tag.value;
-
- Topics.getTagTids(tag, 0, 5, function(err, tids) {
- Topics.getTopics(tids, topicData.threadTools.uid, function(err, topics) {
- related = related.concat(topics.filter(function(topic) {
- var doesntOwnTopic = parseInt(topic.uid, 10) !== parseInt(topicData.threadTools.uid, 10);
- var isntSameTopic = parseInt(topic.tid, 10) !== parseInt(topicData.threadTools.topic.tid, 10);
-
- return doesntOwnTopic && isntSameTopic;
- }));
-
- next(err);
- });
+ async.waterfall([
+ function (next) {
+ async.map(topicData.tags, function (tag, next) {
+ Topics.getTagTids(tag.value, 0, 5, next);
+ }, next);
+ },
+ function (tids, next) {
+ tids = _.shuffle(_.unique(_.flatten(tids))).slice(0, maximumTopics);
+ Topics.getTopics(tids, uid, next);
+ },
+ function (topics, next) {
+ topics = topics.filter(function(topic) {
+ return topic && !topic.deleted && parseInt(topic.uid, 10) !== parseInt(uid, 10);
});
- }, function(err) {
- if (!isAdministrator) {
- related = related.filter(function(topic) {
- return topic && !topic.deleted;
- });
- }
-
- related = _.shuffle(related).slice(0, maximumTopics);
-
- topicData.related = related;
- callback(err, topicData);
- });
- });
+ next(null, topics);
+ }
+ ], callback);
};
};
\ No newline at end of file
From 5d43da0d1c653160e3eb50958269a0c0d384082d Mon Sep 17 00:00:00 2001
From: Julian Lam
Date: Sat, 19 Dec 2015 11:06:38 -0500
Subject: [PATCH 59/59] 0.9.3
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 44b1847559..305eeb4416 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "nodebb",
"license": "GPL-3.0",
"description": "NodeBB Forum",
- "version": "0.9.2",
+ "version": "0.9.3",
"homepage": "http://www.nodebb.org",
"repository": {
"type": "git",